blob: b914f130583b4818984519803a76a0181cd90199 [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
Varun Sharmade538272020-04-10 15:22:31 -070043AVB_VERSION_MINOR = 2
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
Jan Monsch2c7be992020-04-03 14:37:13 +020052# Configuration for enabling logging of calls to avbtool.
53AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
54
David Zeuthene3cadca2017-02-22 21:25:46 -050055
David Zeuthen21e95262016-07-27 17:58:40 -040056class AvbError(Exception):
57 """Application-specific errors.
58
59 These errors represent issues for which a stack-trace should not be
60 presented.
61
62 Attributes:
63 message: Error message.
64 """
65
66 def __init__(self, message):
67 Exception.__init__(self, message)
68
69
70class Algorithm(object):
71 """Contains details about an algorithm.
72
Tao Bao80418a52018-07-20 11:41:22 -070073 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040074
75 The constant |ALGORITHMS| is a dictionary from human-readable
76 names (e.g 'SHA256_RSA2048') to instances of this class.
77
78 Attributes:
79 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040080 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040081 hash_num_bytes: Number of bytes used to store the hash.
82 signature_num_bytes: Number of bytes used to store the signature.
83 public_key_num_bytes: Number of bytes used to store the public key.
84 padding: Padding used for signature, if any.
85 """
86
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
88 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040089 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040090 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040091 self.hash_num_bytes = hash_num_bytes
92 self.signature_num_bytes = signature_num_bytes
93 self.public_key_num_bytes = public_key_num_bytes
94 self.padding = padding
95
David Zeuthenb623d8b2017-04-04 16:05:53 -040096
David Zeuthen21e95262016-07-27 17:58:40 -040097# This must be kept in sync with the avb_crypto.h file.
98#
99# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
100# obtained from section 5.2.2 of RFC 4880.
101ALGORITHMS = {
102 'NONE': Algorithm(
103 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400104 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400105 hash_num_bytes=0,
106 signature_num_bytes=0,
107 public_key_num_bytes=0,
108 padding=[]),
109 'SHA256_RSA2048': Algorithm(
110 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400111 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400112 hash_num_bytes=32,
113 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100114 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*202 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA4096': Algorithm(
124 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400125 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400126 hash_num_bytes=32,
127 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100128 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*458 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA256_RSA8192': Algorithm(
138 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400139 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400140 hash_num_bytes=32,
141 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100142 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400143 padding=[
144 # PKCS1-v1_5 padding
145 0x00, 0x01] + [0xff]*970 + [0x00] + [
146 # ASN.1 header
147 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
148 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
149 0x00, 0x04, 0x20,
150 ]),
151 'SHA512_RSA2048': Algorithm(
152 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400153 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400154 hash_num_bytes=64,
155 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100156 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400157 padding=[
158 # PKCS1-v1_5 padding
159 0x00, 0x01] + [0xff]*170 + [0x00] + [
160 # ASN.1 header
161 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
162 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
163 0x00, 0x04, 0x40
164 ]),
165 'SHA512_RSA4096': Algorithm(
166 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400167 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400168 hash_num_bytes=64,
169 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100170 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400171 padding=[
172 # PKCS1-v1_5 padding
173 0x00, 0x01] + [0xff]*426 + [0x00] + [
174 # ASN.1 header
175 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
176 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
177 0x00, 0x04, 0x40
178 ]),
179 'SHA512_RSA8192': Algorithm(
180 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400181 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400182 hash_num_bytes=64,
183 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100184 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400185 padding=[
186 # PKCS1-v1_5 padding
187 0x00, 0x01] + [0xff]*938 + [0x00] + [
188 # ASN.1 header
189 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
190 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
191 0x00, 0x04, 0x40
192 ]),
193}
194
195
David Zeuthene3cadca2017-02-22 21:25:46 -0500196def get_release_string():
197 """Calculates the release string to use in the VBMeta struct."""
198 # Keep in sync with libavb/avb_version.c:avb_version_string().
199 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
200 AVB_VERSION_MINOR,
201 AVB_VERSION_SUB)
202
203
David Zeuthen21e95262016-07-27 17:58:40 -0400204def round_to_multiple(number, size):
205 """Rounds a number up to nearest multiple of another number.
206
Jan Monschfe00c0a2019-12-11 11:19:40 +0100207 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400208 number: The number to round up.
209 size: The multiple to round up to.
210
211 Returns:
212 If |number| is a multiple of |size|, returns |number|, otherwise
213 returns |number| + |size|.
214 """
215 remainder = number % size
216 if remainder == 0:
217 return number
218 return number + size - remainder
219
220
221def round_to_pow2(number):
222 """Rounds a number up to the next power of 2.
223
Jan Monschfe00c0a2019-12-11 11:19:40 +0100224 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400225 number: The number to round up.
226
227 Returns:
228 If |number| is already a power of 2 then |number| is
229 returned. Otherwise the smallest power of 2 greater than |number|
230 is returned.
231 """
232 return 2**((number - 1).bit_length())
233
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235def encode_long(num_bits, value):
236 """Encodes a long to a bytearray() using a given amount of bits.
237
238 This number is written big-endian, e.g. with the most significant
239 bit first.
240
David Zeuthenb623d8b2017-04-04 16:05:53 -0400241 This is the reverse of decode_long().
242
David Zeuthen21e95262016-07-27 17:58:40 -0400243 Arguments:
244 num_bits: The number of bits to write, e.g. 2048.
245 value: The value to write.
246
247 Returns:
248 A bytearray() with the encoded long.
249 """
250 ret = bytearray()
251 for bit_pos in range(num_bits, 0, -8):
252 octet = (value >> (bit_pos - 8)) & 0xff
253 ret.extend(struct.pack('!B', octet))
254 return ret
255
256
David Zeuthenb623d8b2017-04-04 16:05:53 -0400257def decode_long(blob):
258 """Decodes a long from a bytearray() using a given amount of bits.
259
260 This number is expected to be in big-endian, e.g. with the most
261 significant bit first.
262
263 This is the reverse of encode_long().
264
265 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100266 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400267
268 Returns:
269 The decoded value.
270 """
271 ret = 0
272 for b in bytearray(blob):
273 ret *= 256
274 ret += b
275 return ret
276
277
David Zeuthen21e95262016-07-27 17:58:40 -0400278def egcd(a, b):
279 """Calculate greatest common divisor of two numbers.
280
281 This implementation uses a recursive version of the extended
282 Euclidian algorithm.
283
284 Arguments:
285 a: First number.
286 b: Second number.
287
288 Returns:
289 A tuple (gcd, x, y) that where |gcd| is the greatest common
290 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
291 """
292 if a == 0:
293 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100294 g, y, x = egcd(b % a, a)
295 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400296
297
298def modinv(a, m):
299 """Calculate modular multiplicative inverse of |a| modulo |m|.
300
301 This calculates the number |x| such that |a| * |x| == 1 (modulo
302 |m|). This number only exists if |a| and |m| are co-prime - |None|
303 is returned if this isn't true.
304
305 Arguments:
306 a: The number to calculate a modular inverse of.
307 m: The modulo to use.
308
309 Returns:
310 The modular multiplicative inverse of |a| and |m| or |None| if
311 these numbers are not co-prime.
312 """
313 gcd, x, _ = egcd(a, m)
314 if gcd != 1:
315 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100316 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400317
318
319def parse_number(string):
320 """Parse a string as a number.
321
322 This is just a short-hand for int(string, 0) suitable for use in the
323 |type| parameter of |ArgumentParser|'s add_argument() function. An
324 improvement to just using type=int is that this function supports
325 numbers in other bases, e.g. "0x1234".
326
327 Arguments:
328 string: The string to parse.
329
330 Returns:
331 The parsed integer.
332
333 Raises:
334 ValueError: If the number could not be parsed.
335 """
336 return int(string, 0)
337
338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339class RSAPublicKey(object):
340 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400341
David Zeuthenc68f0822017-03-31 17:22:35 -0400342 Attributes:
343 exponent: The key exponent.
344 modulus: The key modulus.
345 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400346 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400347
Jan Monsch38865f22020-04-08 09:32:58 +0200348 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400349
350 def __init__(self, key_path):
351 """Loads and parses an RSA key from either a private or public key file.
352
353 Arguments:
354 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100355
356 Raises:
357 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400358 """
359 # We used to have something as simple as this:
360 #
361 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
362 # self.exponent = key.e
363 # self.modulus = key.n
364 # self.num_bits = key.size() + 1
365 #
366 # but unfortunately PyCrypto is not available in the builder. So
367 # instead just parse openssl(1) output to get this
368 # information. It's ugly but...
369 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
370 p = subprocess.Popen(args,
371 stdin=subprocess.PIPE,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE)
374 (pout, perr) = p.communicate()
375 if p.wait() != 0:
376 # Could be just a public key is passed, try that.
377 args.append('-pubin')
378 p = subprocess.Popen(args,
379 stdin=subprocess.PIPE,
380 stdout=subprocess.PIPE,
381 stderr=subprocess.PIPE)
382 (pout, perr) = p.communicate()
383 if p.wait() != 0:
384 raise AvbError('Error getting public key: {}'.format(perr))
385
386 if not pout.lower().startswith(self.MODULUS_PREFIX):
387 raise AvbError('Unexpected modulus output')
388
389 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
390
391 # The exponent is assumed to always be 65537 and the number of
392 # bits can be derived from the modulus by rounding up to the
393 # nearest power of 2.
394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
398
David Zeuthenc68f0822017-03-31 17:22:35 -0400399def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400400 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
401
402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
404
405 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
408 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +0200409 The |AvbRSAPublicKeyHeader| as bytes.
Jan Monsch77cd2022019-12-10 17:18:04 +0100410
411 Raises:
412 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400413 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 key = RSAPublicKey(key_path)
415 if key.exponent != 65537:
416 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400417 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monschcc6a15c2020-04-09 02:06:54 +0200419 b = 2 ** 32
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400421 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monschcc6a15c2020-04-09 02:06:54 +0200422 r = 2 ** key.modulus.bit_length()
David Zeuthenc68f0822017-03-31 17:22:35 -0400423 rrmodn = r * r % key.modulus
424 ret.extend(struct.pack('!II', key.num_bits, n0inv))
425 ret.extend(encode_long(key.num_bits, key.modulus))
426 ret.extend(encode_long(key.num_bits, rrmodn))
Jan Monschb1d920f2020-04-09 12:59:28 +0200427 return bytes(ret)
David Zeuthen21e95262016-07-27 17:58:40 -0400428
429
430def lookup_algorithm_by_type(alg_type):
431 """Looks up algorithm by type.
432
433 Arguments:
434 alg_type: The integer representing the type.
435
436 Returns:
437 A tuple with the algorithm name and an |Algorithm| instance.
438
439 Raises:
440 Exception: If the algorithm cannot be found
441 """
442 for alg_name in ALGORITHMS:
443 alg_data = ALGORITHMS[alg_name]
444 if alg_data.algorithm_type == alg_type:
445 return (alg_name, alg_data)
446 raise AvbError('Unknown algorithm type {}'.format(alg_type))
447
Jan Monsch77cd2022019-12-10 17:18:04 +0100448
Dan Austina7bc4962019-12-02 13:26:08 -0800449def lookup_hash_size_by_type(alg_type):
450 """Looks up hash size by type.
451
452 Arguments:
453 alg_type: The integer representing the type.
454
455 Returns:
456 The corresponding hash size.
457
458 Raises:
459 AvbError: If the algorithm cannot be found.
460 """
461 for alg_name in ALGORITHMS:
462 alg_data = ALGORITHMS[alg_name]
463 if alg_data.algorithm_type == alg_type:
464 return alg_data.hash_num_bytes
465 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400466
Jan Monsch77cd2022019-12-10 17:18:04 +0100467
David Zeuthena156d3d2017-06-01 12:08:09 -0400468def raw_sign(signing_helper, signing_helper_with_files,
469 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900470 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 """Computes a raw RSA signature using |signing_helper| or openssl.
472
473 Arguments:
474 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400475 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800476 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900477 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800478 key_path: Path to the private key file. Must be PEM format.
Jan Monsch38865f22020-04-08 09:32:58 +0200479 raw_data_to_sign: Data to sign as bytes or bytearray.
Darren Krahn147b08d2016-12-20 16:38:29 -0800480
481 Returns:
Jan Monsch38865f22020-04-08 09:32:58 +0200482 The signature as bytes.
Darren Krahn147b08d2016-12-20 16:38:29 -0800483
484 Raises:
Jan Monsch38865f22020-04-08 09:32:58 +0200485 AvbError: If an error occurred during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -0800486 """
487 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400488 if signing_helper_with_files is not None:
Jan Monschb1d920f2020-04-09 12:59:28 +0200489 with tempfile.NamedTemporaryFile() as signing_file:
490 signing_file.write(raw_data_to_sign)
491 signing_file.flush()
492 p = subprocess.Popen([
493 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
494 retcode = p.wait()
495 if retcode != 0:
496 raise AvbError('Error signing')
497 signing_file.seek(0)
498 signature = signing_file.read()
Darren Krahn147b08d2016-12-20 16:38:29 -0800499 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400500 if signing_helper is not None:
501 p = subprocess.Popen(
502 [signing_helper, algorithm_name, key_path],
503 stdin=subprocess.PIPE,
504 stdout=subprocess.PIPE,
505 stderr=subprocess.PIPE)
506 else:
507 p = subprocess.Popen(
508 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
509 stdin=subprocess.PIPE,
510 stdout=subprocess.PIPE,
511 stderr=subprocess.PIPE)
Jan Monsch38865f22020-04-08 09:32:58 +0200512 (pout, perr) = p.communicate(raw_data_to_sign)
David Zeuthena156d3d2017-06-01 12:08:09 -0400513 retcode = p.wait()
514 if retcode != 0:
515 raise AvbError('Error signing: {}'.format(perr))
Jan Monsch38865f22020-04-08 09:32:58 +0200516 signature = pout
Esun Kimff44f232017-03-30 10:34:54 +0900517 if len(signature) != signature_num_bytes:
518 raise AvbError('Error signing: Invalid length of signature')
519 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800520
521
David Zeuthenb623d8b2017-04-04 16:05:53 -0400522def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100523 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400524
525 Arguments:
526 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200527 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
528 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400529
530 Returns:
531 True if the signature is valid and corresponds to the embedded
532 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100533
534 Raises:
535 AvbError: If there errors calling out to openssl command during
536 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537 """
538 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100539 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400540 return True
541 header_blob = vbmeta_blob[0:256]
542 auth_offset = 256
543 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
544 aux_size = vbmeta_header.auxiliary_data_block_size
545 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
546 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
547 pubkey_size = vbmeta_header.public_key_size
548 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
549
550 digest_offset = auth_offset + vbmeta_header.hash_offset
551 digest_size = vbmeta_header.hash_size
552 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
553
554 sig_offset = auth_offset + vbmeta_header.signature_offset
555 sig_size = vbmeta_header.signature_size
556 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
557
558 # Now that we've got the stored digest, public key, and signature
559 # all we need to do is to verify. This is the exactly the same
560 # steps as performed in the avb_vbmeta_image_verify() function in
561 # libavb/avb_vbmeta_image.c.
562
563 ha = hashlib.new(alg.hash_name)
564 ha.update(header_blob)
565 ha.update(aux_blob)
566 computed_digest = ha.digest()
567
568 if computed_digest != digest_blob:
569 return False
570
571 padding_and_digest = bytearray(alg.padding)
572 padding_and_digest.extend(computed_digest)
573
574 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100575 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400576 modulus = decode_long(modulus_blob)
577 exponent = 65537
578
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500579 # We used to have this:
580 #
581 # import Crypto.PublicKey.RSA
582 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
583 # if not key.verify(decode_long(padding_and_digest),
584 # (decode_long(sig_blob), None)):
585 # return False
586 # return True
587 #
588 # but since 'avbtool verify_image' is used on the builders we don't want
589 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
590 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
591 '\n'
592 '[pubkeyinfo]\n'
593 'algorithm=SEQUENCE:rsa_alg\n'
594 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
595 '\n'
596 '[rsa_alg]\n'
597 'algorithm=OID:rsaEncryption\n'
598 'parameter=NULL\n'
599 '\n'
600 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200601 'n=INTEGER:{}\n'
602 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
603 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500604
Jan Monsch38865f22020-04-08 09:32:58 +0200605 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
606 asn1_tmpfile.write(asn1_str.encode('ascii'))
607 asn1_tmpfile.flush()
608
609 with tempfile.NamedTemporaryFile() as der_tmpfile:
610 p = subprocess.Popen(
611 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
612 der_tmpfile.name, '-noout'])
613 retcode = p.wait()
614 if retcode != 0:
615 raise AvbError('Error generating DER file')
616
617 p = subprocess.Popen(
618 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
619 '-keyform', 'DER', '-raw'],
620 stdin=subprocess.PIPE,
621 stdout=subprocess.PIPE,
622 stderr=subprocess.PIPE)
623 (pout, perr) = p.communicate(sig_blob)
624 retcode = p.wait()
625 if retcode != 0:
626 raise AvbError('Error verifying data: {}'.format(perr))
627 if pout != padding_and_digest:
628 sys.stderr.write('Signature not correct\n')
629 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400630 return True
631
632
David Zeuthena4fee8b2016-08-22 15:20:43 -0400633class ImageChunk(object):
634 """Data structure used for representing chunks in Android sparse files.
635
636 Attributes:
637 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
638 chunk_offset: Offset in the sparse file where this chunk begins.
639 output_offset: Offset in de-sparsified file where output begins.
640 output_size: Number of bytes in output.
641 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
642 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
643 """
644
645 FORMAT = '<2H2I'
646 TYPE_RAW = 0xcac1
647 TYPE_FILL = 0xcac2
648 TYPE_DONT_CARE = 0xcac3
649 TYPE_CRC32 = 0xcac4
650
651 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
652 input_offset, fill_data):
653 """Initializes an ImageChunk object.
654
655 Arguments:
656 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
657 chunk_offset: Offset in the sparse file where this chunk begins.
658 output_offset: Offset in de-sparsified file.
659 output_size: Number of bytes in output.
660 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200661 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400662
663 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200664 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400665 """
666 self.chunk_type = chunk_type
667 self.chunk_offset = chunk_offset
668 self.output_offset = output_offset
669 self.output_size = output_size
670 self.input_offset = input_offset
671 self.fill_data = fill_data
672 # Check invariants.
673 if self.chunk_type == self.TYPE_RAW:
674 if self.fill_data is not None:
675 raise ValueError('RAW chunk cannot have fill_data set.')
676 if not self.input_offset:
677 raise ValueError('RAW chunk must have input_offset set.')
678 elif self.chunk_type == self.TYPE_FILL:
679 if self.fill_data is None:
680 raise ValueError('FILL chunk must have fill_data set.')
681 if self.input_offset:
682 raise ValueError('FILL chunk cannot have input_offset set.')
683 elif self.chunk_type == self.TYPE_DONT_CARE:
684 if self.fill_data is not None:
685 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
686 if self.input_offset:
687 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
688 else:
689 raise ValueError('Invalid chunk type')
690
691
692class ImageHandler(object):
693 """Abstraction for image I/O with support for Android sparse images.
694
695 This class provides an interface for working with image files that
696 may be using the Android Sparse Image format. When an instance is
697 constructed, we test whether it's an Android sparse file. If so,
698 operations will be on the sparse file by interpreting the sparse
699 format, otherwise they will be directly on the file. Either way the
700 operations do the same.
701
702 For reading, this interface mimics a file object - it has seek(),
703 tell(), and read() methods. For writing, only truncation
704 (truncate()) and appending is supported (append_raw() and
705 append_dont_care()). Additionally, data can only be written in units
706 of the block size.
707
708 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400709 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400710 is_sparse: Whether the file being operated on is sparse.
711 block_size: The block size, typically 4096.
712 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 """
714 # See system/core/libsparse/sparse_format.h for details.
715 MAGIC = 0xed26ff3a
716 HEADER_FORMAT = '<I4H4I'
717
718 # These are formats and offset of just the |total_chunks| and
719 # |total_blocks| fields.
720 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
721 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
722
723 def __init__(self, image_filename):
724 """Initializes an image handler.
725
726 Arguments:
727 image_filename: The name of the file to operate on.
728
729 Raises:
730 ValueError: If data in the file is invalid.
731 """
David Zeuthen49936b42018-08-07 17:38:58 -0400732 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100733 self._num_total_blocks = 0
734 self._num_total_chunks = 0
735 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400736 self._read_header()
737
738 def _read_header(self):
739 """Initializes internal data structures used for reading file.
740
741 This may be called multiple times and is typically called after
742 modifying the file (e.g. appending, truncation).
743
744 Raises:
745 ValueError: If data in the file is invalid.
746 """
747 self.is_sparse = False
748 self.block_size = 4096
749 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400750 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400751 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400752 self.image_size = self._image.tell()
753
754 self._image.seek(0, os.SEEK_SET)
755 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
756 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
757 block_size, self._num_total_blocks, self._num_total_chunks,
758 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
759 if magic != self.MAGIC:
760 # Not a sparse image, our job here is done.
761 return
762 if not (major_version == 1 and minor_version == 0):
763 raise ValueError('Encountered sparse image format version {}.{} but '
764 'only 1.0 is supported'.format(major_version,
765 minor_version))
766 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
767 raise ValueError('Unexpected file_hdr_sz value {}.'.
768 format(file_hdr_sz))
769 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
770 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
771 format(chunk_hdr_sz))
772
773 self.block_size = block_size
774
775 # Build an list of chunks by parsing the file.
776 self._chunks = []
777
778 # Find the smallest offset where only "Don't care" chunks
779 # follow. This will be the size of the content in the sparse
780 # image.
781 offset = 0
782 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100783 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400784 chunk_offset = self._image.tell()
785
786 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
787 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
788 header_bin)
789 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
790
David Zeuthena4fee8b2016-08-22 15:20:43 -0400791 if chunk_type == ImageChunk.TYPE_RAW:
792 if data_sz != (chunk_sz * self.block_size):
793 raise ValueError('Raw chunk input size ({}) does not match output '
794 'size ({})'.
795 format(data_sz, chunk_sz*self.block_size))
796 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
797 chunk_offset,
798 output_offset,
799 chunk_sz*self.block_size,
800 self._image.tell(),
801 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700802 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400803
804 elif chunk_type == ImageChunk.TYPE_FILL:
805 if data_sz != 4:
806 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
807 'has {}'.format(data_sz))
808 fill_data = self._image.read(4)
809 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
810 chunk_offset,
811 output_offset,
812 chunk_sz*self.block_size,
813 None,
814 fill_data))
815 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
816 if data_sz != 0:
817 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
818 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400819 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
820 chunk_offset,
821 output_offset,
822 chunk_sz*self.block_size,
823 None,
824 None))
825 elif chunk_type == ImageChunk.TYPE_CRC32:
826 if data_sz != 4:
827 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
828 'this has {}'.format(data_sz))
829 self._image.read(4)
830 else:
831 raise ValueError('Unknown chunk type {}'.format(chunk_type))
832
833 offset += chunk_sz
834 output_offset += chunk_sz*self.block_size
835
836 # Record where sparse data end.
837 self._sparse_end = self._image.tell()
838
839 # Now that we've traversed all chunks, sanity check.
840 if self._num_total_blocks != offset:
841 raise ValueError('The header said we should have {} output blocks, '
842 'but we saw {}'.format(self._num_total_blocks, offset))
843 junk_len = len(self._image.read())
844 if junk_len > 0:
845 raise ValueError('There were {} bytes of extra data at the end of the '
846 'file.'.format(junk_len))
847
David Zeuthen09692692016-09-30 16:16:40 -0400848 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400849 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400850
851 # This is used when bisecting in read() to find the initial slice.
852 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
853
854 self.is_sparse = True
855
856 def _update_chunks_and_blocks(self):
857 """Helper function to update the image header.
858
859 The the |total_chunks| and |total_blocks| fields in the header
860 will be set to value of the |_num_total_blocks| and
861 |_num_total_chunks| attributes.
862
863 """
864 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
865 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
866 self._num_total_blocks,
867 self._num_total_chunks))
868
869 def append_dont_care(self, num_bytes):
870 """Appends a DONT_CARE chunk to the sparse file.
871
872 The given number of bytes must be a multiple of the block size.
873
874 Arguments:
875 num_bytes: Size in number of bytes of the DONT_CARE chunk.
876 """
877 assert num_bytes % self.block_size == 0
878
879 if not self.is_sparse:
880 self._image.seek(0, os.SEEK_END)
881 # This is more efficient that writing NUL bytes since it'll add
882 # a hole on file systems that support sparse files (native
883 # sparse, not Android sparse).
884 self._image.truncate(self._image.tell() + num_bytes)
885 self._read_header()
886 return
887
888 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100889 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400890 self._update_chunks_and_blocks()
891
892 self._image.seek(self._sparse_end, os.SEEK_SET)
893 self._image.write(struct.pack(ImageChunk.FORMAT,
894 ImageChunk.TYPE_DONT_CARE,
895 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100896 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400897 struct.calcsize(ImageChunk.FORMAT)))
898 self._read_header()
899
900 def append_raw(self, data):
901 """Appends a RAW chunk to the sparse file.
902
903 The length of the given data must be a multiple of the block size.
904
905 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200906 data: Data to append as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400907 """
908 assert len(data) % self.block_size == 0
909
910 if not self.is_sparse:
911 self._image.seek(0, os.SEEK_END)
912 self._image.write(data)
913 self._read_header()
914 return
915
916 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100917 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400918 self._update_chunks_and_blocks()
919
920 self._image.seek(self._sparse_end, os.SEEK_SET)
921 self._image.write(struct.pack(ImageChunk.FORMAT,
922 ImageChunk.TYPE_RAW,
923 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100924 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400925 len(data) +
926 struct.calcsize(ImageChunk.FORMAT)))
927 self._image.write(data)
928 self._read_header()
929
930 def append_fill(self, fill_data, size):
931 """Appends a fill chunk to the sparse file.
932
933 The total length of the fill data must be a multiple of the block size.
934
935 Arguments:
936 fill_data: Fill data to append - must be four bytes.
937 size: Number of chunk - must be a multiple of four and the block size.
938 """
939 assert len(fill_data) == 4
940 assert size % 4 == 0
941 assert size % self.block_size == 0
942
943 if not self.is_sparse:
944 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 self._read_header()
947 return
948
949 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100950 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400951 self._update_chunks_and_blocks()
952
953 self._image.seek(self._sparse_end, os.SEEK_SET)
954 self._image.write(struct.pack(ImageChunk.FORMAT,
955 ImageChunk.TYPE_FILL,
956 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100957 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400958 4 + struct.calcsize(ImageChunk.FORMAT)))
959 self._image.write(fill_data)
960 self._read_header()
961
962 def seek(self, offset):
963 """Sets the cursor position for reading from unsparsified file.
964
965 Arguments:
966 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100967
968 Raises:
969 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400970 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700971 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +0200972 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400973 self._file_pos = offset
974
975 def read(self, size):
976 """Reads data from the unsparsified file.
977
978 This method may return fewer than |size| bytes of data if the end
979 of the file was encountered.
980
981 The file cursor for reading is advanced by the number of bytes
982 read.
983
984 Arguments:
985 size: Number of bytes to read.
986
987 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +0200988 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400989 """
990 if not self.is_sparse:
991 self._image.seek(self._file_pos)
992 data = self._image.read(size)
993 self._file_pos += len(data)
994 return data
995
996 # Iterate over all chunks.
997 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
998 self._file_pos) - 1
999 data = bytearray()
1000 to_go = size
1001 while to_go > 0:
1002 chunk = self._chunks[chunk_idx]
1003 chunk_pos_offset = self._file_pos - chunk.output_offset
1004 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1005
1006 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1007 self._image.seek(chunk.input_offset + chunk_pos_offset)
1008 data.extend(self._image.read(chunk_pos_to_go))
1009 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001010 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001011 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1012 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1013 else:
1014 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001015 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001016
1017 to_go -= chunk_pos_to_go
1018 self._file_pos += chunk_pos_to_go
1019 chunk_idx += 1
1020 # Generate partial read in case of EOF.
1021 if chunk_idx >= len(self._chunks):
1022 break
1023
Jan Monsch8347da92020-04-08 12:41:49 +02001024 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001025
1026 def tell(self):
1027 """Returns the file cursor position for reading from unsparsified file.
1028
1029 Returns:
1030 The file cursor position for reading.
1031 """
1032 return self._file_pos
1033
1034 def truncate(self, size):
1035 """Truncates the unsparsified file.
1036
1037 Arguments:
1038 size: Desired size of unsparsified file.
1039
1040 Raises:
1041 ValueError: If desired size isn't a multiple of the block size.
1042 """
1043 if not self.is_sparse:
1044 self._image.truncate(size)
1045 self._read_header()
1046 return
1047
1048 if size % self.block_size != 0:
1049 raise ValueError('Cannot truncate to a size which is not a multiple '
1050 'of the block size')
1051
1052 if size == self.image_size:
1053 # Trivial where there's nothing to do.
1054 return
1055 elif size < self.image_size:
1056 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1057 chunk = self._chunks[chunk_idx]
1058 if chunk.output_offset != size:
1059 # Truncation in the middle of a trunk - need to keep the chunk
1060 # and modify it.
1061 chunk_idx_for_update = chunk_idx + 1
1062 num_to_keep = size - chunk.output_offset
1063 assert num_to_keep % self.block_size == 0
1064 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1065 truncate_at = (chunk.chunk_offset +
1066 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1067 data_sz = num_to_keep
1068 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1069 truncate_at = (chunk.chunk_offset +
1070 struct.calcsize(ImageChunk.FORMAT) + 4)
1071 data_sz = 4
1072 else:
1073 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1074 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1075 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001076 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001077 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1078 self._image.seek(chunk.chunk_offset)
1079 self._image.write(struct.pack(ImageChunk.FORMAT,
1080 chunk.chunk_type,
1081 0, # Reserved
1082 chunk_sz,
1083 total_sz))
1084 chunk.output_size = num_to_keep
1085 else:
1086 # Truncation at trunk boundary.
1087 truncate_at = chunk.chunk_offset
1088 chunk_idx_for_update = chunk_idx
1089
1090 self._num_total_chunks = chunk_idx_for_update
1091 self._num_total_blocks = 0
1092 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001093 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001094 self._update_chunks_and_blocks()
1095 self._image.truncate(truncate_at)
1096
1097 # We've modified the file so re-read all data.
1098 self._read_header()
1099 else:
1100 # Truncating to grow - just add a DONT_CARE section.
1101 self.append_dont_care(size - self.image_size)
1102
1103
David Zeuthen21e95262016-07-27 17:58:40 -04001104class AvbDescriptor(object):
1105 """Class for AVB descriptor.
1106
1107 See the |AvbDescriptor| C struct for more information.
1108
1109 Attributes:
1110 tag: The tag identifying what kind of descriptor this is.
1111 data: The data in the descriptor.
1112 """
1113
1114 SIZE = 16
1115 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1116
1117 def __init__(self, data):
1118 """Initializes a new property descriptor.
1119
1120 Arguments:
1121 data: If not None, must be a bytearray().
1122
1123 Raises:
1124 LookupError: If the given descriptor is malformed.
1125 """
1126 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1127
1128 if data:
1129 (self.tag, num_bytes_following) = (
1130 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1131 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1132 else:
1133 self.tag = None
1134 self.data = None
1135
1136 def print_desc(self, o):
1137 """Print the descriptor.
1138
1139 Arguments:
1140 o: The object to write the output to.
1141 """
1142 o.write(' Unknown descriptor:\n')
1143 o.write(' Tag: {}\n'.format(self.tag))
1144 if len(self.data) < 256:
1145 o.write(' Data: {} ({} bytes)\n'.format(
1146 repr(str(self.data)), len(self.data)))
1147 else:
1148 o.write(' Data: {} bytes\n'.format(len(self.data)))
1149
1150 def encode(self):
1151 """Serializes the descriptor.
1152
1153 Returns:
1154 A bytearray() with the descriptor data.
1155 """
1156 num_bytes_following = len(self.data)
1157 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1158 padding_size = nbf_with_padding - num_bytes_following
1159 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1160 padding = struct.pack(str(padding_size) + 'x')
1161 ret = desc + self.data + padding
1162 return bytearray(ret)
1163
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001164 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001165 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001166 """Verifies contents of the descriptor - used in verify_image sub-command.
1167
1168 Arguments:
1169 image_dir: The directory of the file being verified.
1170 image_ext: The extension of the file being verified (e.g. '.img').
1171 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001172 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001173 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001174 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1175 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001176
1177 Returns:
1178 True if the descriptor verifies, False otherwise.
1179 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001180 # Deletes unused parameters to prevent pylint warning unused-argument.
1181 del image_dir, image_ext, expected_chain_partitions_map
1182 del image_containing_descriptor, accept_zeroed_hashtree
1183
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001184 # Nothing to do.
1185 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001186
Jan Monscheeb28b62019-12-05 16:17:09 +01001187
David Zeuthen21e95262016-07-27 17:58:40 -04001188class AvbPropertyDescriptor(AvbDescriptor):
1189 """A class for property descriptors.
1190
1191 See the |AvbPropertyDescriptor| C struct for more information.
1192
1193 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001194 key: The key as string.
1195 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001196 """
1197
1198 TAG = 0
1199 SIZE = 32
1200 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001201 'Q' # key size (bytes)
1202 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001203
1204 def __init__(self, data=None):
1205 """Initializes a new property descriptor.
1206
1207 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001208 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001209
1210 Raises:
1211 LookupError: If the given descriptor is malformed.
1212 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001213 super(AvbPropertyDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001214 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1215
1216 if data:
1217 (tag, num_bytes_following, key_size,
1218 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1219 expected_size = round_to_multiple(
1220 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1221 if tag != self.TAG or num_bytes_following != expected_size:
1222 raise LookupError('Given data does not look like a property '
1223 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001224 try:
1225 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1226 except UnicodeDecodeError as e:
1227 raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001228 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1229 value_size)]
1230 else:
1231 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001232 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001233
1234 def print_desc(self, o):
1235 """Print the descriptor.
1236
1237 Arguments:
1238 o: The object to write the output to.
1239 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001240 # Go forward with python 3, bytes are represented with the 'b' prefix,
1241 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1242 # the same between python 2 and python 3.
1243 printable_value = repr(self.value)
1244 if printable_value.startswith('b\''):
1245 printable_value = printable_value[1:]
1246
David Zeuthen21e95262016-07-27 17:58:40 -04001247 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001248 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001249 else:
1250 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1251
1252 def encode(self):
1253 """Serializes the descriptor.
1254
1255 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001256 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001257 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001258 key_encoded = self.key.encode('utf-8')
1259 num_bytes_following = (
1260 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001261 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1262 padding_size = nbf_with_padding - num_bytes_following
1263 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001264 len(key_encoded), len(self.value))
1265 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1266 padding_size * b'\0')
1267 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001268
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001269 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001270 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001271 """Verifies contents of the descriptor - used in verify_image sub-command.
1272
1273 Arguments:
1274 image_dir: The directory of the file being verified.
1275 image_ext: The extension of the file being verified (e.g. '.img').
1276 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001277 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001278 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001279 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1280 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001281
1282 Returns:
1283 True if the descriptor verifies, False otherwise.
1284 """
1285 # Nothing to do.
1286 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001287
Jan Monscheeb28b62019-12-05 16:17:09 +01001288
David Zeuthen21e95262016-07-27 17:58:40 -04001289class AvbHashtreeDescriptor(AvbDescriptor):
1290 """A class for hashtree descriptors.
1291
1292 See the |AvbHashtreeDescriptor| C struct for more information.
1293
1294 Attributes:
1295 dm_verity_version: dm-verity version used.
1296 image_size: Size of the image, after rounding up to |block_size|.
1297 tree_offset: Offset of the hash tree in the file.
1298 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001299 data_block_size: Data block size.
1300 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001301 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1302 fec_offset: Offset of FEC data (0 if FEC is not used).
1303 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001304 hash_algorithm: Hash algorithm used as string.
1305 partition_name: Partition name as string.
1306 salt: Salt used as bytes.
1307 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001308 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001309 """
1310
1311 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001312 RESERVED = 60
1313 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001314 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001315 'L' # dm-verity version used
1316 'Q' # image size (bytes)
1317 'Q' # tree offset (bytes)
1318 'Q' # tree size (bytes)
1319 'L' # data block size (bytes)
1320 'L' # hash block size (bytes)
1321 'L' # FEC number of roots
1322 'Q' # FEC offset (bytes)
1323 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001324 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001325 'L' # partition name (bytes)
1326 'L' # salt length (bytes)
1327 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001328 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001329 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001330
1331 def __init__(self, data=None):
1332 """Initializes a new hashtree descriptor.
1333
1334 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001335 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001336
1337 Raises:
1338 LookupError: If the given descriptor is malformed.
1339 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001340 super(AvbHashtreeDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001341 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1342
1343 if data:
1344 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1345 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001346 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1347 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001348 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1349 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001350 expected_size = round_to_multiple(
1351 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1352 if tag != self.TAG or num_bytes_following != expected_size:
1353 raise LookupError('Given data does not look like a hashtree '
1354 'descriptor.')
1355 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001356 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001357 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001358 try:
1359 self.partition_name = data[
1360 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1361 ].decode('utf-8')
1362 except UnicodeDecodeError as e:
1363 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1364 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001365 o += partition_name_len
1366 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1367 o += salt_len
1368 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001369 if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001370 if root_digest_len != 0:
1371 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001372
1373 else:
1374 self.dm_verity_version = 0
1375 self.image_size = 0
1376 self.tree_offset = 0
1377 self.tree_size = 0
1378 self.data_block_size = 0
1379 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001380 self.fec_num_roots = 0
1381 self.fec_offset = 0
1382 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001383 self.hash_algorithm = ''
1384 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001385 self.salt = b''
1386 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001387 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001388
1389 def print_desc(self, o):
1390 """Print the descriptor.
1391
1392 Arguments:
1393 o: The object to write the output to.
1394 """
1395 o.write(' Hashtree descriptor:\n')
1396 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1397 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1398 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1399 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1400 o.write(' Data Block Size: {} bytes\n'.format(
1401 self.data_block_size))
1402 o.write(' Hash Block Size: {} bytes\n'.format(
1403 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001404 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1405 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1406 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001407 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1408 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001409 o.write(' Salt: {}\n'.format(
1410 binascii.hexlify(self.salt).decode('ascii')))
1411 o.write(' Root Digest: {}\n'.format(
1412 binascii.hexlify(self.root_digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001413 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001414
1415 def encode(self):
1416 """Serializes the descriptor.
1417
1418 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001419 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001420 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001421 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1422 partition_name_encoded = self.partition_name.encode('utf-8')
1423 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1424 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001425 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1426 padding_size = nbf_with_padding - num_bytes_following
1427 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1428 self.dm_verity_version, self.image_size,
1429 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001430 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001431 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1432 len(partition_name_encoded), len(self.salt),
1433 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1434 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1435 padding_size * b'\0')
1436 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001437
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001438 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001439 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001440 """Verifies contents of the descriptor - used in verify_image sub-command.
1441
1442 Arguments:
1443 image_dir: The directory of the file being verified.
1444 image_ext: The extension of the file being verified (e.g. '.img').
1445 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001446 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001447 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001448 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1449 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001450
1451 Returns:
1452 True if the descriptor verifies, False otherwise.
1453 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001454 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001455 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001456 image = image_containing_descriptor
1457 else:
1458 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1459 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001460 # Generate the hashtree and checks that it matches what's in the file.
Jan Monsch6f27bb12020-04-07 07:33:26 +02001461 digest_size = len(hashlib.new(self.hash_algorithm).digest())
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001462 digest_padding = round_to_pow2(digest_size) - digest_size
1463 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001464 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001465 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1466 self.data_block_size,
1467 self.hash_algorithm, self.salt,
1468 digest_padding,
1469 hash_level_offsets,
1470 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001471 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001472 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001473 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1474 format(image_filename))
1475 return False
1476 # ... also check that the on-disk hashtree matches
1477 image.seek(self.tree_offset)
1478 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001479 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001480 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001481 print('{}: skipping verification since hashtree is zeroed and '
1482 '--accept_zeroed_hashtree was given'
1483 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001484 else:
1485 if hash_tree != hash_tree_ondisk:
1486 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001487 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001488 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001489 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1490 .format(self.partition_name, self.hash_algorithm, image.filename,
1491 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001492 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1493 # correct but this a) currently requires the 'fec' binary; and b) takes a
1494 # long time; and c) is not strictly needed for verification purposes as
1495 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001496 return True
1497
David Zeuthen21e95262016-07-27 17:58:40 -04001498
1499class AvbHashDescriptor(AvbDescriptor):
1500 """A class for hash descriptors.
1501
1502 See the |AvbHashDescriptor| C struct for more information.
1503
1504 Attributes:
1505 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001506 hash_algorithm: Hash algorithm used as string.
1507 partition_name: Partition name as string.
1508 salt: Salt used as bytes.
1509 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001510 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001511 """
1512
1513 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001514 RESERVED = 60
1515 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001516 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001517 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001518 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001519 'L' # partition name (bytes)
1520 'L' # salt length (bytes)
1521 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001522 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001523 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001524
1525 def __init__(self, data=None):
1526 """Initializes a new hash descriptor.
1527
1528 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001529 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001530
1531 Raises:
1532 LookupError: If the given descriptor is malformed.
1533 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001534 super(AvbHashDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001535 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1536
1537 if data:
1538 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1539 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001540 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1541 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001542 expected_size = round_to_multiple(
1543 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1544 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001545 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001546 # Nuke NUL-bytes at the end.
Jan Monschee6fccd2020-04-09 19:36:13 +02001547 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001548 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001549 try:
1550 self.partition_name = data[
1551 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1552 ].decode('utf-8')
1553 except UnicodeDecodeError as e:
1554 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1555 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001556 o += partition_name_len
1557 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1558 o += salt_len
1559 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001560 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001561 if digest_len != 0:
1562 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001563
1564 else:
1565 self.image_size = 0
1566 self.hash_algorithm = ''
1567 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001568 self.salt = b''
1569 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001570 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001571
1572 def print_desc(self, o):
1573 """Print the descriptor.
1574
1575 Arguments:
1576 o: The object to write the output to.
1577 """
1578 o.write(' Hash descriptor:\n')
1579 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1580 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1581 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001582 o.write(' Salt: {}\n'.format(
1583 binascii.hexlify(self.salt).decode('ascii')))
1584 o.write(' Digest: {}\n'.format(
1585 binascii.hexlify(self.digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001586 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001587
1588 def encode(self):
1589 """Serializes the descriptor.
1590
1591 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001592 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001593 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001594 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1595 partition_name_encoded = self.partition_name.encode('utf-8')
1596 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1597 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001598 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1599 padding_size = nbf_with_padding - num_bytes_following
1600 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001601 self.image_size, hash_algorithm_encoded,
1602 len(partition_name_encoded), len(self.salt),
1603 len(self.digest), self.flags, self.RESERVED * b'\0')
1604 ret = (desc + partition_name_encoded + self.salt + self.digest +
1605 padding_size * b'\0')
1606 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001607
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001608 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001609 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001610 """Verifies contents of the descriptor - used in verify_image sub-command.
1611
1612 Arguments:
1613 image_dir: The directory of the file being verified.
1614 image_ext: The extension of the file being verified (e.g. '.img').
1615 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001616 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001617 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001618 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1619 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001620
1621 Returns:
1622 True if the descriptor verifies, False otherwise.
1623 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001624 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001625 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001626 image = image_containing_descriptor
1627 else:
1628 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1629 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001630 data = image.read(self.image_size)
1631 ha = hashlib.new(self.hash_algorithm)
1632 ha.update(self.salt)
1633 ha.update(data)
1634 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001635 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001636 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001637 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1638 format(self.hash_algorithm, image_filename))
1639 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001640 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1641 .format(self.partition_name, self.hash_algorithm, image.filename,
1642 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001643 return True
1644
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646class AvbKernelCmdlineDescriptor(AvbDescriptor):
1647 """A class for kernel command-line descriptors.
1648
1649 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1650
1651 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001652 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001653 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001654 """
1655
1656 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001657 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001658 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001659 'L' # flags
1660 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001661
David Zeuthenfd41eb92016-11-17 12:24:47 -05001662 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1663 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1664
David Zeuthen21e95262016-07-27 17:58:40 -04001665 def __init__(self, data=None):
1666 """Initializes a new kernel cmdline descriptor.
1667
1668 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001669 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001670
1671 Raises:
1672 LookupError: If the given descriptor is malformed.
1673 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001674 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001675 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1676
1677 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001678 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001679 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1680 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1681 8)
1682 if tag != self.TAG or num_bytes_following != expected_size:
1683 raise LookupError('Given data does not look like a kernel cmdline '
1684 'descriptor.')
1685 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001686 try:
1687 self.kernel_cmdline = data[
1688 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1689 except UnicodeDecodeError as e:
1690 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1691 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001692 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001693 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.kernel_cmdline = ''
1695
1696 def print_desc(self, o):
1697 """Print the descriptor.
1698
1699 Arguments:
1700 o: The object to write the output to.
1701 """
1702 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001703 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001704 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001705
1706 def encode(self):
1707 """Serializes the descriptor.
1708
1709 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001710 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001711 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001712 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1713 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001714 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1715 padding_size = nbf_with_padding - num_bytes_following
1716 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001717 self.flags, len(kernel_cmd_encoded))
1718 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1719 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001720
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001721 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001722 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001723 """Verifies contents of the descriptor - used in verify_image sub-command.
1724
1725 Arguments:
1726 image_dir: The directory of the file being verified.
1727 image_ext: The extension of the file being verified (e.g. '.img').
1728 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001729 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001730 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001731 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1732 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001733
1734 Returns:
1735 True if the descriptor verifies, False otherwise.
1736 """
1737 # Nothing to verify.
1738 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001739
Jan Monscheeb28b62019-12-05 16:17:09 +01001740
David Zeuthen21e95262016-07-27 17:58:40 -04001741class AvbChainPartitionDescriptor(AvbDescriptor):
1742 """A class for chained partition descriptors.
1743
1744 See the |AvbChainPartitionDescriptor| C struct for more information.
1745
1746 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001747 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001748 partition_name: Partition name as string.
1749 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001750 """
1751
1752 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001753 RESERVED = 64
1754 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001755 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001756 'L' # rollback_index_location
1757 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001758 'L' + # public_key_size (bytes)
1759 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001760
1761 def __init__(self, data=None):
1762 """Initializes a new chain partition descriptor.
1763
1764 Arguments:
1765 data: If not None, must be a bytearray of size |SIZE|.
1766
1767 Raises:
1768 LookupError: If the given descriptor is malformed.
1769 """
1770 AvbDescriptor.__init__(self, None)
1771 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1772
1773 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001774 (tag, num_bytes_following, self.rollback_index_location,
1775 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001776 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001777 expected_size = round_to_multiple(
1778 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1779 if tag != self.TAG or num_bytes_following != expected_size:
1780 raise LookupError('Given data does not look like a chain partition '
1781 'descriptor.')
1782 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001783 try:
1784 self.partition_name = data[
1785 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1786 ].decode('utf-8')
1787 except UnicodeDecodeError as e:
1788 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1789 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001790 o += partition_name_len
1791 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1792
1793 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001794 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001795 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001796 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001797
1798 def print_desc(self, o):
1799 """Print the descriptor.
1800
1801 Arguments:
1802 o: The object to write the output to.
1803 """
1804 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001805 o.write(' Partition Name: {}\n'.format(self.partition_name))
1806 o.write(' Rollback Index Location: {}\n'.format(
1807 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001808 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001809 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1810 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 def encode(self):
1813 """Serializes the descriptor.
1814
1815 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001816 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001817 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001818 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001819 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001820 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001821 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1822 padding_size = nbf_with_padding - num_bytes_following
1823 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001824 self.rollback_index_location,
1825 len(partition_name_encoded), len(self.public_key),
1826 self.RESERVED * b'\0')
1827 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1828 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001829
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001830 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001831 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001832 """Verifies contents of the descriptor - used in verify_image sub-command.
1833
1834 Arguments:
1835 image_dir: The directory of the file being verified.
1836 image_ext: The extension of the file being verified (e.g. '.img').
1837 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001838 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001839 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001840 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1841 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001842
1843 Returns:
1844 True if the descriptor verifies, False otherwise.
1845 """
1846 value = expected_chain_partitions_map.get(self.partition_name)
1847 if not value:
1848 sys.stderr.write('No expected chain partition for partition {}. Use '
1849 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001850 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001851 format(self.partition_name))
1852 return False
1853 rollback_index_location, pk_blob = value
1854
1855 if self.rollback_index_location != rollback_index_location:
1856 sys.stderr.write('Expected rollback_index_location {} does not '
1857 'match {} in descriptor for partition {}\n'.
1858 format(rollback_index_location,
1859 self.rollback_index_location,
1860 self.partition_name))
1861 return False
1862
1863 if self.public_key != pk_blob:
1864 sys.stderr.write('Expected public key blob does not match public '
1865 'key blob in descriptor for partition {}\n'.
1866 format(self.partition_name))
1867 return False
1868
Jan Monsch23e0c622019-12-11 11:23:58 +01001869 print('{}: Successfully verified chain partition descriptor matches '
1870 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001871
1872 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001873
1874DESCRIPTOR_CLASSES = [
1875 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1876 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1877]
1878
1879
1880def parse_descriptors(data):
1881 """Parses a blob of data into descriptors.
1882
1883 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001884 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001885
1886 Returns:
1887 A list of instances of objects derived from AvbDescriptor. For
1888 unknown descriptors, the class AvbDescriptor is used.
1889 """
1890 o = 0
1891 ret = []
1892 while o < len(data):
1893 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1894 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001895 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001896 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001897 clazz = AvbDescriptor
1898 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001899 o += 16 + nb_following
1900 return ret
1901
1902
1903class AvbFooter(object):
1904 """A class for parsing and writing footers.
1905
1906 Footers are stored at the end of partitions and point to where the
1907 AvbVBMeta blob is located. They also contain the original size of
1908 the image before AVB information was added.
1909
1910 Attributes:
1911 magic: Magic for identifying the footer, see |MAGIC|.
1912 version_major: The major version of avbtool that wrote the footer.
1913 version_minor: The minor version of avbtool that wrote the footer.
1914 original_image_size: Original image size.
1915 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1916 vbmeta_size: Size of the AvbVBMeta blob.
1917 """
1918
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001919 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001920 SIZE = 64
1921 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001922 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1923 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001924 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001925 'Q' # Original image size.
1926 'Q' # Offset of VBMeta blob.
1927 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001928 str(RESERVED) + 'x') # padding for reserved bytes
1929
1930 def __init__(self, data=None):
1931 """Initializes a new footer object.
1932
1933 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001934 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001935
1936 Raises:
1937 LookupError: If the given footer is malformed.
1938 struct.error: If the given data has no footer.
1939 """
1940 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1941
1942 if data:
1943 (self.magic, self.version_major, self.version_minor,
1944 self.original_image_size, self.vbmeta_offset,
1945 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1946 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001947 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001948 else:
1949 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001950 self.version_major = self.FOOTER_VERSION_MAJOR
1951 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001952 self.original_image_size = 0
1953 self.vbmeta_offset = 0
1954 self.vbmeta_size = 0
1955
David Zeuthena4fee8b2016-08-22 15:20:43 -04001956 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001957 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001958
David Zeuthena4fee8b2016-08-22 15:20:43 -04001959 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001960 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001961 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001962 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1963 self.version_minor, self.original_image_size,
1964 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001965
1966
1967class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001968 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001969
Jan Monschfe00c0a2019-12-11 11:19:40 +01001970 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1971 avb_vbmeta_image.h.
1972
David Zeuthen21e95262016-07-27 17:58:40 -04001973 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001974 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1975 required_libavb_version_major: The major version of libavb required for this
1976 header.
1977 required_libavb_version_minor: The minor version of libavb required for this
1978 header.
1979 authentication_data_block_size: The size of the signature block.
1980 auxiliary_data_block_size: The size of the auxiliary data block.
1981 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1982 enum.
1983 hash_offset: Offset into the "Authentication data" block of hash data.
1984 hash_size: Length of the hash data.
1985 signature_offset: Offset into the "Authentication data" block of signature
1986 data.
1987 signature_size: Length of the signature data.
1988 public_key_offset: Offset into the "Auxiliary data" block of public key
1989 data.
1990 public_key_size: Length of the public key data.
1991 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1992 key metadata.
1993 public_key_metadata_size: Length of the public key metadata. Must be set to
1994 zero if there is no public key metadata.
1995 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1996 data.
1997 descriptors_size: Length of descriptor data.
1998 rollback_index: The rollback index which can be used to prevent rollback to
1999 older versions.
2000 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2001 zero if the vbmeta image is not a top-level image.
Varun Sharmade538272020-04-10 15:22:31 -07002002 rollback_index_location: The location of the rollback index defined in this
2003 header. Only valid for the main vbmeta. For chained partitions, the
2004 rollback index location must be specified in the
2005 AvbChainPartitionDescriptor and this value must be set to 0.
Jan Monschfe00c0a2019-12-11 11:19:40 +01002006 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2007 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2008 terminated. Applications must not make assumptions about how this
2009 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002010 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002011 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002012 SIZE = 256
2013
Varun Sharmade538272020-04-10 15:22:31 -07002014 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthene3cadca2017-02-22 21:25:46 -05002015 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002016
2017 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002018 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2019 '2Q' # 2 x block size
2020 'L' # algorithm type
2021 '2Q' # offset, size (hash)
2022 '2Q' # offset, size (signature)
2023 '2Q' # offset, size (public key)
2024 '2Q' # offset, size (public key metadata)
2025 '2Q' # offset, size (descriptors)
2026 'Q' # rollback_index
Varun Sharmade538272020-04-10 15:22:31 -07002027 'L' # flags
2028 'L' # rollback_index_location
David Zeuthene3cadca2017-02-22 21:25:46 -05002029 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002030 str(RESERVED) + 'x') # padding for reserved bytes
2031
2032 def __init__(self, data=None):
2033 """Initializes a new header object.
2034
2035 Arguments:
2036 data: If not None, must be a bytearray of size 8192.
2037
2038 Raises:
2039 Exception: If the given data is malformed.
2040 """
2041 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2042
2043 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002044 (self.magic, self.required_libavb_version_major,
2045 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002046 self.authentication_data_block_size, self.auxiliary_data_block_size,
2047 self.algorithm_type, self.hash_offset, self.hash_size,
2048 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002049 self.public_key_size, self.public_key_metadata_offset,
2050 self.public_key_metadata_size, self.descriptors_offset,
2051 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002052 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002053 self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002054 self.rollback_index_location,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002055 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002056 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002057 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002058 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002059 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002060 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002061 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002062 # Start by just requiring version 1.0. Code that adds features
2063 # in a future version can use bump_required_libavb_version_minor() to
2064 # bump the minor.
2065 self.required_libavb_version_major = AVB_VERSION_MAJOR
2066 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002067 self.authentication_data_block_size = 0
2068 self.auxiliary_data_block_size = 0
2069 self.algorithm_type = 0
2070 self.hash_offset = 0
2071 self.hash_size = 0
2072 self.signature_offset = 0
2073 self.signature_size = 0
2074 self.public_key_offset = 0
2075 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002076 self.public_key_metadata_offset = 0
2077 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002078 self.descriptors_offset = 0
2079 self.descriptors_size = 0
2080 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002081 self.flags = 0
Varun Sharmade538272020-04-10 15:22:31 -07002082 self.rollback_index_location = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002083 self.release_string = get_release_string()
2084
2085 def bump_required_libavb_version_minor(self, minor):
2086 """Function to bump required_libavb_version_minor.
2087
2088 Call this when writing data that requires a specific libavb
2089 version to parse it.
2090
2091 Arguments:
2092 minor: The minor version of libavb that has support for the feature.
2093 """
2094 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002095 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002096
David Zeuthen21e95262016-07-27 17:58:40 -04002097 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002098 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002099
2100 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002101 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002102 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002103 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002104 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002105 self.required_libavb_version_major,
2106 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002107 self.authentication_data_block_size,
2108 self.auxiliary_data_block_size, self.algorithm_type,
2109 self.hash_offset, self.hash_size, self.signature_offset,
2110 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002111 self.public_key_size, self.public_key_metadata_offset,
2112 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002113 self.descriptors_size, self.rollback_index, self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002114 self.rollback_index_location, release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002115
2116
2117class Avb(object):
2118 """Business logic for avbtool command-line tool."""
2119
David Zeuthen8b6973b2016-09-20 12:39:49 -04002120 # Keep in sync with avb_ab_flow.h.
2121 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002122 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002123 AB_MAJOR_VERSION = 1
2124 AB_MINOR_VERSION = 0
2125 AB_MISC_METADATA_OFFSET = 2048
2126
David Zeuthen09692692016-09-30 16:16:40 -04002127 # Constants for maximum metadata size. These are used to give
2128 # meaningful errors if the value passed in via --partition_size is
2129 # too small and when --calc_max_image_size is used. We use
2130 # conservative figures.
2131 MAX_VBMETA_SIZE = 64 * 1024
2132 MAX_FOOTER_SIZE = 4096
2133
Jan Monsch2c7be992020-04-03 14:37:13 +02002134 def generate_test_image(self, output, image_size, start_byte):
2135 """Generates a test image for testing avbtool with known content.
2136
2137 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2138
2139 Arguments:
2140 output: Write test image to this file.
2141 image_size: The size of the requested file in bytes.
2142 start_byte: The integer value of the start byte to use for pattern
2143 generation.
2144 """
2145 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2146 buf = bytearray()
2147 c = int(math.ceil(image_size / 256.0))
2148 for _ in range(0, c):
2149 buf.extend(pattern)
2150 output.write(buf[0:image_size])
2151
David Zeuthen49936b42018-08-07 17:38:58 -04002152 def extract_vbmeta_image(self, output, image_filename, padding_size):
2153 """Implements the 'extract_vbmeta_image' command.
2154
2155 Arguments:
2156 output: Write vbmeta struct to this file.
2157 image_filename: File to extract vbmeta data from (with a footer).
2158 padding_size: If not 0, pads output so size is a multiple of the number.
2159
2160 Raises:
2161 AvbError: If there's no footer in the image.
2162 """
2163 image = ImageHandler(image_filename)
David Zeuthen49936b42018-08-07 17:38:58 -04002164 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002165 if not footer:
2166 raise AvbError('Given image does not have a footer.')
2167
2168 image.seek(footer.vbmeta_offset)
2169 vbmeta_blob = image.read(footer.vbmeta_size)
2170 output.write(vbmeta_blob)
2171
2172 if padding_size > 0:
2173 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2174 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002175 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002176
David Zeuthena4fee8b2016-08-22 15:20:43 -04002177 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002178 """Implements the 'erase_footer' command.
2179
2180 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002181 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002182 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002183
2184 Raises:
2185 AvbError: If there's no footer in the image.
2186 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002187 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002188 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002189 if not footer:
2190 raise AvbError('Given image does not have a footer.')
2191
2192 new_image_size = None
2193 if not keep_hashtree:
2194 new_image_size = footer.original_image_size
2195 else:
2196 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002197 # descriptor to figure out the location and size of the hashtree
2198 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002199 for desc in descriptors:
2200 if isinstance(desc, AvbHashtreeDescriptor):
2201 # The hashtree is always just following the main data so the
2202 # new size is easily derived.
2203 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002204 # If the image has FEC codes, also keep those.
2205 if desc.fec_offset > 0:
2206 fec_end = desc.fec_offset + desc.fec_size
2207 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002208 break
2209 if not new_image_size:
2210 raise AvbError('Requested to keep hashtree but no hashtree '
2211 'descriptor was found.')
2212
2213 # And cut...
2214 image.truncate(new_image_size)
2215
David Zeuthen1394f762019-04-30 10:20:11 -04002216 def zero_hashtree(self, image_filename):
2217 """Implements the 'zero_hashtree' command.
2218
2219 Arguments:
2220 image_filename: File to zero hashtree and FEC data from.
2221
2222 Raises:
2223 AvbError: If there's no footer in the image.
2224 """
David Zeuthen1394f762019-04-30 10:20:11 -04002225 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002226 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002227 if not footer:
2228 raise AvbError('Given image does not have a footer.')
2229
2230 # Search for a hashtree descriptor to figure out the location and
2231 # size of the hashtree and FEC.
2232 ht_desc = None
2233 for desc in descriptors:
2234 if isinstance(desc, AvbHashtreeDescriptor):
2235 ht_desc = desc
2236 break
2237
2238 if not ht_desc:
2239 raise AvbError('No hashtree descriptor was found.')
2240
2241 zero_ht_start_offset = ht_desc.tree_offset
2242 zero_ht_num_bytes = ht_desc.tree_size
2243 zero_fec_start_offset = None
2244 zero_fec_num_bytes = 0
2245 if ht_desc.fec_offset > 0:
2246 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2247 raise AvbError('Hash-tree and FEC data must be adjacent.')
2248 zero_fec_start_offset = ht_desc.fec_offset
2249 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002250 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2251 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002252 image.seek(zero_end_offset)
2253 data = image.read(image.image_size - zero_end_offset)
2254
2255 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2256 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2257 # beginning of both hashtree and FEC. (That way, in the future we can add
2258 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2259 #
2260 # Applications can use these markers to detect that the hashtree and/or
2261 # FEC needs to be recomputed.
2262 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002263 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002264 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002265 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002266 if zero_fec_start_offset:
2267 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002268 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002269 image.append_raw(data)
2270
David Zeuthen2bc232b2017-04-19 14:25:19 -04002271 def resize_image(self, image_filename, partition_size):
2272 """Implements the 'resize_image' command.
2273
2274 Arguments:
2275 image_filename: File with footer to resize.
2276 partition_size: The new size of the image.
2277
2278 Raises:
2279 AvbError: If there's no footer in the image.
2280 """
2281
2282 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002283 if partition_size % image.block_size != 0:
2284 raise AvbError('Partition size of {} is not a multiple of the image '
2285 'block size {}.'.format(partition_size,
2286 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002287 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002288 if not footer:
2289 raise AvbError('Given image does not have a footer.')
2290
2291 # The vbmeta blob is always at the end of the data so resizing an
2292 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002293 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2294 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002295 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2296 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002297
Jan Monschb1d920f2020-04-09 12:59:28 +02002298 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002299 raise AvbError('Requested size of {} is too small for an image '
2300 'of size {}.'
2301 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002302 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002303
2304 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2305 # with enough bytes such that the final Footer block is at the end
2306 # of partition_size.
2307 image.truncate(vbmeta_end_offset)
2308 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002309 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002310
2311 # Just reuse the same footer - only difference is that we're
2312 # writing it in a different place.
2313 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002314 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002315 footer_blob)
2316 image.append_raw(footer_blob_with_padding)
2317
David Zeuthen8b6973b2016-09-20 12:39:49 -04002318 def set_ab_metadata(self, misc_image, slot_data):
2319 """Implements the 'set_ab_metadata' command.
2320
2321 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2322 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2323
2324 Arguments:
2325 misc_image: The misc image to write to.
2326 slot_data: Slot data as a string
2327
2328 Raises:
2329 AvbError: If slot data is malformed.
2330 """
2331 tokens = slot_data.split(':')
2332 if len(tokens) != 6:
2333 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2334 a_priority = int(tokens[0])
2335 a_tries_remaining = int(tokens[1])
2336 a_success = True if int(tokens[2]) != 0 else False
2337 b_priority = int(tokens[3])
2338 b_tries_remaining = int(tokens[4])
2339 b_success = True if int(tokens[5]) != 0 else False
2340
2341 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2342 self.AB_MAGIC,
2343 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2344 a_priority, a_tries_remaining, a_success,
2345 b_priority, b_tries_remaining, b_success)
2346 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2347 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2348 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2349 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2350 misc_image.write(ab_data)
2351
David Zeuthena4fee8b2016-08-22 15:20:43 -04002352 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002353 """Implements the 'info_image' command.
2354
2355 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002356 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002357 output: Output file to write human-readable information to (file object).
2358 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002359 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002360 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002361 (footer, header, descriptors, image_size) = self._parse_image(image)
2362
Bowgo Tsaid7145942020-03-20 17:03:51 +08002363 # To show the SHA1 of the public key.
2364 vbmeta_blob = self._load_vbmeta_blob(image)
2365 key_offset = (header.SIZE +
2366 header.authentication_data_block_size +
2367 header.public_key_offset)
2368 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2369
David Zeuthen21e95262016-07-27 17:58:40 -04002370 if footer:
2371 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2372 footer.version_minor))
2373 o.write('Image size: {} bytes\n'.format(image_size))
2374 o.write('Original image size: {} bytes\n'.format(
2375 footer.original_image_size))
2376 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2377 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2378 o.write('--\n')
2379
2380 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2381
David Zeuthene3cadca2017-02-22 21:25:46 -05002382 o.write('Minimum libavb version: {}.{}{}\n'.format(
2383 header.required_libavb_version_major,
2384 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002385 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002386 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2387 o.write('Authentication Block: {} bytes\n'.format(
2388 header.authentication_data_block_size))
2389 o.write('Auxiliary Block: {} bytes\n'.format(
2390 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002391 if key_blob:
2392 hexdig = hashlib.sha1(key_blob).hexdigest()
2393 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002394 o.write('Algorithm: {}\n'.format(alg_name))
2395 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002396 o.write('Flags: {}\n'.format(header.flags))
Varun Sharmade538272020-04-10 15:22:31 -07002397 o.write('Rollback Index Location: {}\n'.format(header.rollback_index_location))
Jan Monschb1d920f2020-04-09 12:59:28 +02002398 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002399
2400 # Print descriptors.
2401 num_printed = 0
2402 o.write('Descriptors:\n')
2403 for desc in descriptors:
2404 desc.print_desc(o)
2405 num_printed += 1
2406 if num_printed == 0:
2407 o.write(' (none)\n')
2408
Jan Monscheeb28b62019-12-05 16:17:09 +01002409 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2410 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002411 """Implements the 'verify_image' command.
2412
2413 Arguments:
2414 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002415 key_path: None or check that embedded public key matches key at given
2416 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002417 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002418 follow_chain_partitions:
2419 If True, will follows chain partitions even when not specified with
2420 the --expected_chain_partition option
2421 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2422 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002423
2424 Raises:
2425 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002426 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002427 expected_chain_partitions_map = {}
2428 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002429 for cp in expected_chain_partitions:
2430 cp_tokens = cp.split(':')
2431 if len(cp_tokens) != 3:
2432 raise AvbError('Malformed chained partition "{}".'.format(cp))
2433 partition_name = cp_tokens[0]
2434 rollback_index_location = int(cp_tokens[1])
2435 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002436 with open(file_path, 'rb') as f:
2437 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002438 expected_chain_partitions_map[partition_name] = (
2439 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002440
2441 image_dir = os.path.dirname(image_filename)
2442 image_ext = os.path.splitext(image_filename)[1]
2443
2444 key_blob = None
2445 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002446 print('Verifying image {} using key at {}'.format(image_filename,
2447 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002448 key_blob = encode_rsa_key(key_path)
2449 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002450 print('Verifying image {} using embedded public key'.format(
2451 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002452
David Zeuthenb623d8b2017-04-04 16:05:53 -04002453 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002454 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002455 offset = 0
2456 if footer:
2457 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002458
David Zeuthenb623d8b2017-04-04 16:05:53 -04002459 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002460 vbmeta_blob = image.read(header.SIZE
2461 + header.authentication_data_block_size
2462 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002463
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002464 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002465 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002466 raise AvbError('Signature check failed for {} vbmeta struct {}'
2467 .format(alg_name, image_filename))
2468
2469 if key_blob:
2470 # The embedded public key is in the auxiliary block at an offset.
2471 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002472 key_offset += header.authentication_data_block_size
2473 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002474 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2475 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002476 if key_blob != key_blob_in_vbmeta:
2477 raise AvbError('Embedded public key does not match given key.')
2478
2479 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002480 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2481 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002482 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002483 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2484 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002485
2486 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002487 if (isinstance(desc, AvbChainPartitionDescriptor)
2488 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002489 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002490 # In this case we're processing a chain descriptor but don't have a
2491 # --expect_chain_partition ... however --follow_chain_partitions was
2492 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002493 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2494 'and KEY (which has sha1 {}) not specified'
2495 .format(desc.partition_name, desc.rollback_index_location,
2496 hashlib.sha1(desc.public_key).hexdigest()))
2497 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002498 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002499 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002500 # Honor --follow_chain_partitions - add '--' to make the output more
2501 # readable.
2502 if (isinstance(desc, AvbChainPartitionDescriptor)
2503 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002504 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002505 chained_image_filename = os.path.join(image_dir,
2506 desc.partition_name + image_ext)
2507 self.verify_image(chained_image_filename, key_path, None, False,
2508 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002509
David Zeuthenb8643c02018-05-17 17:21:18 -04002510 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2511 """Implements the 'calculate_vbmeta_digest' command.
2512
2513 Arguments:
2514 image_filename: Image file to get information from (file object).
2515 hash_algorithm: Hash algorithm used.
2516 output: Output file to write human-readable information to (file object).
2517 """
2518
2519 image_dir = os.path.dirname(image_filename)
2520 image_ext = os.path.splitext(image_filename)[1]
2521
2522 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002523 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002524 offset = 0
2525 if footer:
2526 offset = footer.vbmeta_offset
2527 size = (header.SIZE + header.authentication_data_block_size +
2528 header.auxiliary_data_block_size)
2529 image.seek(offset)
2530 vbmeta_blob = image.read(size)
2531
Jan Monsch6f27bb12020-04-07 07:33:26 +02002532 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002533 hasher.update(vbmeta_blob)
2534
2535 for desc in descriptors:
2536 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002537 ch_image_filename = os.path.join(image_dir,
2538 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002539 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002540 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002541 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002542 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2543 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002544 if ch_footer:
2545 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002546 ch_image.seek(ch_offset)
2547 ch_vbmeta_blob = ch_image.read(ch_size)
2548 hasher.update(ch_vbmeta_blob)
2549
2550 digest = hasher.digest()
Jan Monschb1d920f2020-04-09 12:59:28 +02002551 output.write('{}\n'.format(binascii.hexlify(digest).decode('ascii')))
David Zeuthenb8643c02018-05-17 17:21:18 -04002552
David Zeuthenf7d2e752018-09-20 13:30:41 -04002553 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2554 """Implements the 'calculate_kernel_cmdline' command.
2555
2556 Arguments:
2557 image_filename: Image file to get information from (file object).
2558 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2559 output: Output file to write human-readable information to (file object).
2560 """
2561
2562 image = ImageHandler(image_filename)
2563 _, _, descriptors, _ = self._parse_image(image)
2564
2565 image_dir = os.path.dirname(image_filename)
2566 image_ext = os.path.splitext(image_filename)[1]
2567
2568 cmdline_descriptors = []
2569 for desc in descriptors:
2570 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002571 ch_image_filename = os.path.join(image_dir,
2572 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002573 ch_image = ImageHandler(ch_image_filename)
2574 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2575 for ch_desc in ch_descriptors:
2576 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2577 cmdline_descriptors.append(ch_desc)
2578 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2579 cmdline_descriptors.append(desc)
2580
2581 kernel_cmdline_snippets = []
2582 for desc in cmdline_descriptors:
2583 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002584 if ((desc.flags &
2585 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2586 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002587 if hashtree_disabled:
2588 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002589 if (desc.flags &
2590 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002591 if not hashtree_disabled:
2592 use_cmdline = False
2593 if use_cmdline:
2594 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2595 output.write(' '.join(kernel_cmdline_snippets))
2596
David Zeuthen21e95262016-07-27 17:58:40 -04002597 def _parse_image(self, image):
2598 """Gets information about an image.
2599
2600 The image can either be a vbmeta or an image with a footer.
2601
2602 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002603 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002604
2605 Returns:
2606 A tuple where the first argument is a AvbFooter (None if there
2607 is no footer on the image), the second argument is a
2608 AvbVBMetaHeader, the third argument is a list of
2609 AvbDescriptor-derived instances, and the fourth argument is the
2610 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002611
2612 Raises:
2613 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002614 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002615 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002616 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002617 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002618 try:
2619 footer = AvbFooter(image.read(AvbFooter.SIZE))
2620 except (LookupError, struct.error):
2621 # Nope, just seek back to the start.
2622 image.seek(0)
2623
2624 vbmeta_offset = 0
2625 if footer:
2626 vbmeta_offset = footer.vbmeta_offset
2627
2628 image.seek(vbmeta_offset)
2629 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2630
2631 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2632 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2633 desc_start_offset = aux_block_offset + h.descriptors_offset
2634 image.seek(desc_start_offset)
2635 descriptors = parse_descriptors(image.read(h.descriptors_size))
2636
David Zeuthen09692692016-09-30 16:16:40 -04002637 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002638
David Zeuthenb1b994d2017-03-06 18:01:31 -05002639 def _load_vbmeta_blob(self, image):
2640 """Gets the vbmeta struct and associated sections.
2641
2642 The image can either be a vbmeta.img or an image with a footer.
2643
2644 Arguments:
2645 image: An ImageHandler (vbmeta or footer).
2646
2647 Returns:
2648 A blob with the vbmeta struct and other sections.
2649 """
2650 assert isinstance(image, ImageHandler)
2651 footer = None
2652 image.seek(image.image_size - AvbFooter.SIZE)
2653 try:
2654 footer = AvbFooter(image.read(AvbFooter.SIZE))
2655 except (LookupError, struct.error):
2656 # Nope, just seek back to the start.
2657 image.seek(0)
2658
2659 vbmeta_offset = 0
2660 if footer:
2661 vbmeta_offset = footer.vbmeta_offset
2662
2663 image.seek(vbmeta_offset)
2664 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2665
2666 image.seek(vbmeta_offset)
2667 data_size = AvbVBMetaHeader.SIZE
2668 data_size += h.authentication_data_block_size
2669 data_size += h.auxiliary_data_block_size
2670 return image.read(data_size)
2671
David Zeuthen73f2afa2017-05-17 16:54:11 -04002672 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002673 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002674
2675 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002676 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002677
2678 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002679 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2680 instructions. There is one for when hashtree is not disabled and one for
2681 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002682
David Zeuthen21e95262016-07-27 17:58:40 -04002683 """
David Zeuthen21e95262016-07-27 17:58:40 -04002684 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002685 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002686 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002687 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2688 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2689 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2690 c += ' {}'.format(ht.data_block_size) # data_block
2691 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002692 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2693 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002694 c += ' {}'.format(ht.hash_algorithm) # hash_alg
Jan Monschb1d920f2020-04-09 12:59:28 +02002695 c += ' {}'.format(
2696 binascii.hexlify(ht.root_digest).decode('ascii')) # root_digest
2697 c += ' {}'.format(binascii.hexlify(ht.salt).decode('ascii')) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002698 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002699 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002700 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002701 c += ' ignore_zero_blocks'
2702 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2703 c += ' fec_roots {}'.format(ht.fec_num_roots)
2704 # Note that fec_blocks is the size that FEC covers, *not* the
2705 # size of the FEC data. Since we use FEC for everything up until
2706 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002707 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2708 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002709 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002710 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002711 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002712 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002713 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002714
David Zeuthenfd41eb92016-11-17 12:24:47 -05002715 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002716 desc = AvbKernelCmdlineDescriptor()
2717 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002718 desc.flags = (
2719 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2720
2721 # The descriptor for when hashtree verification is disabled is a lot
2722 # simpler - we just set the root to the partition.
2723 desc_no_ht = AvbKernelCmdlineDescriptor()
2724 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2725 desc_no_ht.flags = (
2726 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2727
2728 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002729
David Zeuthen73f2afa2017-05-17 16:54:11 -04002730 def _get_cmdline_descriptors_for_dm_verity(self, image):
2731 """Generate kernel cmdline descriptors for dm-verity.
2732
2733 Arguments:
2734 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2735
2736 Returns:
2737 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2738 instructions. There is one for when hashtree is not disabled and one for
2739 when it is.
2740
2741 Raises:
2742 AvbError: If |image| doesn't have a hashtree descriptor.
2743
2744 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002745 (_, _, descriptors, _) = self._parse_image(image)
2746
2747 ht = None
2748 for desc in descriptors:
2749 if isinstance(desc, AvbHashtreeDescriptor):
2750 ht = desc
2751 break
2752
2753 if not ht:
2754 raise AvbError('No hashtree descriptor in given image')
2755
2756 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2757
David Zeuthen21e95262016-07-27 17:58:40 -04002758 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002759 key_path, public_key_metadata_path, rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07002760 flags, rollback_index_location,
2761 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002762 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002763 include_descriptors_from_image,
2764 signing_helper,
2765 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002766 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002767 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002768 print_required_libavb_version,
2769 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002770 """Implements the 'make_vbmeta_image' command.
2771
2772 Arguments:
2773 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002774 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002775 algorithm_name: Name of algorithm to use.
2776 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002777 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002778 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002779 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002780 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002781 props: Properties to insert (list of strings of the form 'key:value').
2782 props_from_file: Properties to insert (list of strings 'key:<path>').
2783 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002784 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002785 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002786 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002787 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002788 release_string: None or avbtool release string to use instead of default.
2789 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002790 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002791 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002792
2793 Raises:
2794 AvbError: If a chained partition is malformed.
2795 """
David Zeuthen1097a782017-05-31 15:53:17 -04002796 # If we're asked to calculate minimum required libavb version, we're done.
Varun Sharmade538272020-04-10 15:22:31 -07002797 tmp_header = AvbVBMetaHeader()
2798 if rollback_index_location > 0:
2799 tmp_header.bump_required_libavb_version_minor(2)
2800 if include_descriptors_from_image:
2801 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2802 # version of all included descriptors.
2803 for image in include_descriptors_from_image:
2804 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2805 tmp_header.bump_required_libavb_version_minor(
2806 image_header.required_libavb_version_minor)
2807
David Zeuthen1097a782017-05-31 15:53:17 -04002808 if print_required_libavb_version:
Varun Sharmade538272020-04-10 15:22:31 -07002809 print('1.{}'.format(tmp_header.required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04002810 return
2811
2812 if not output:
2813 raise AvbError('No output file given')
2814
David Zeuthen21e95262016-07-27 17:58:40 -04002815 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002816 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002817 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002818 algorithm_name, key_path, public_key_metadata_path, descriptors,
Varun Sharmade538272020-04-10 15:22:31 -07002819 chain_partitions, rollback_index, flags, rollback_index_location,
2820 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002821 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002822 include_descriptors_from_image, signing_helper,
2823 signing_helper_with_files, release_string,
Varun Sharmade538272020-04-10 15:22:31 -07002824 append_to_release_string, tmp_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002825
2826 # Write entire vbmeta blob (header, authentication, auxiliary).
2827 output.seek(0)
2828 output.write(vbmeta_blob)
2829
David Zeuthen97cb5802017-06-01 16:14:05 -04002830 if padding_size > 0:
2831 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2832 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002833 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002834
David Zeuthen18666ab2016-11-15 11:18:05 -05002835 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2836 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002837 chain_partitions,
Varun Sharmade538272020-04-10 15:22:31 -07002838 rollback_index, flags, rollback_index_location,
2839 props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002840 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002841 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002842 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002843 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002844 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002845 release_string, append_to_release_string,
2846 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002847 """Generates a VBMeta blob.
2848
2849 This blob contains the header (struct AvbVBMetaHeader), the
2850 authentication data block (which contains the hash and signature
2851 for the header and auxiliary block), and the auxiliary block
2852 (which contains descriptors, the public key used, and other data).
2853
2854 The |key| parameter can |None| only if the |algorithm_name| is
2855 'NONE'.
2856
2857 Arguments:
2858 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2859 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002860 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002861 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002862 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002863 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002864 flags: Flags to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002865 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002866 props: Properties to insert (List of strings of the form 'key:value').
2867 props_from_file: Properties to insert (List of strings 'key:<path>').
2868 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002869 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002870 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002871 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2872 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002873 include_descriptors_from_image: List of file objects for which
2874 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002875 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002876 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002877 release_string: None or avbtool release string.
2878 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002879 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002880
2881 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02002882 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002883
2884 Raises:
2885 Exception: If the |algorithm_name| is not found, if no key has
2886 been given and the given algorithm requires one, or the key is
2887 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04002888 """
2889 try:
2890 alg = ALGORITHMS[algorithm_name]
2891 except KeyError:
2892 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2893
David Zeuthena5fd3a42017-02-27 16:38:54 -05002894 if not descriptors:
2895 descriptors = []
2896
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002897 h = AvbVBMetaHeader()
2898 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2899
David Zeuthena5fd3a42017-02-27 16:38:54 -05002900 # Insert chained partition descriptors, if any
2901 if chain_partitions:
Varun Sharmade538272020-04-10 15:22:31 -07002902 used_locations = {rollback_index_location: True}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002903 for cp in chain_partitions:
2904 cp_tokens = cp.split(':')
2905 if len(cp_tokens) != 3:
2906 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002907 partition_name = cp_tokens[0]
Varun Sharmade538272020-04-10 15:22:31 -07002908 chained_rollback_index_location = int(cp_tokens[1])
David Zeuthend8e48582017-04-21 11:31:51 -04002909 file_path = cp_tokens[2]
2910 # Check that the same rollback location isn't being used by
2911 # multiple chained partitions.
Varun Sharmade538272020-04-10 15:22:31 -07002912 if used_locations.get(chained_rollback_index_location):
David Zeuthend8e48582017-04-21 11:31:51 -04002913 raise AvbError('Rollback Index Location {} is already in use.'.format(
Varun Sharmade538272020-04-10 15:22:31 -07002914 chained_rollback_index_location))
2915 used_locations[chained_rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002916 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002917 desc.partition_name = partition_name
Varun Sharmade538272020-04-10 15:22:31 -07002918 desc.rollback_index_location = chained_rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002919 if desc.rollback_index_location < 1:
2920 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02002921 with open(file_path, 'rb') as f:
2922 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002923 descriptors.append(desc)
2924
David Zeuthen21e95262016-07-27 17:58:40 -04002925 # Descriptors.
2926 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002927 for desc in descriptors:
2928 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002929
2930 # Add properties.
2931 if props:
2932 for prop in props:
2933 idx = prop.find(':')
2934 if idx == -1:
2935 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002936 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002937 desc = AvbPropertyDescriptor()
2938 desc.key = prop[0:idx]
Jan Monschee6fccd2020-04-09 19:36:13 +02002939 desc.value = prop[(idx + 1):].encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002940 encoded_descriptors.extend(desc.encode())
2941 if props_from_file:
2942 for prop in props_from_file:
2943 idx = prop.find(':')
2944 if idx == -1:
2945 raise AvbError('Malformed property "{}".'.format(prop))
2946 desc = AvbPropertyDescriptor()
2947 desc.key = prop[0:idx]
David Zeuthen21e95262016-07-27 17:58:40 -04002948 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02002949 with open(file_path, 'rb') as f:
2950 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04002951 encoded_descriptors.extend(desc.encode())
2952
David Zeuthen73f2afa2017-05-17 16:54:11 -04002953 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002954 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002955 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002956 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002957 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2958 encoded_descriptors.extend(cmdline_desc[0].encode())
2959 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002960
David Zeuthen73f2afa2017-05-17 16:54:11 -04002961 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2962 if ht_desc_to_setup:
2963 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2964 ht_desc_to_setup)
2965 encoded_descriptors.extend(cmdline_desc[0].encode())
2966 encoded_descriptors.extend(cmdline_desc[1].encode())
2967
David Zeuthen21e95262016-07-27 17:58:40 -04002968 # Add kernel command-lines.
2969 if kernel_cmdlines:
2970 for i in kernel_cmdlines:
2971 desc = AvbKernelCmdlineDescriptor()
2972 desc.kernel_cmdline = i
2973 encoded_descriptors.extend(desc.encode())
2974
2975 # Add descriptors from other images.
2976 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002977 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002978 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002979 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002980 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2981 image_handler)
2982 # Bump the required libavb version to support all included descriptors.
2983 h.bump_required_libavb_version_minor(
2984 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002985 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002986 # The --include_descriptors_from_image option is used in some setups
2987 # with images A and B where both A and B contain a descriptor
2988 # for a partition with the same name. Since it's not meaningful
2989 # to include both descriptors, only include the last seen descriptor.
2990 # See bug 76386656 for details.
2991 if hasattr(desc, 'partition_name'):
2992 key = type(desc).__name__ + '_' + desc.partition_name
2993 descriptors_dict[key] = desc.encode()
2994 else:
2995 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002996 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002997 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002998
David Zeuthen18666ab2016-11-15 11:18:05 -05002999 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003000 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05003001 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003002 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05003003 pkmd_blob = f.read()
3004
David Zeuthen21e95262016-07-27 17:58:40 -04003005 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02003006 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003007 if alg.public_key_num_bytes > 0:
3008 if not key_path:
3009 raise AvbError('Key is required for algorithm {}'.format(
3010 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003011 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003012 if len(encoded_key) != alg.public_key_num_bytes:
3013 raise AvbError('Key is wrong size for algorithm {}'.format(
3014 algorithm_name))
3015
David Zeuthene3cadca2017-02-22 21:25:46 -05003016 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003017 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003018 h.release_string = release_string
3019
3020 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003021 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003022 h.release_string += ' ' + append_to_release_string
3023
David Zeuthen18666ab2016-11-15 11:18:05 -05003024 # For the Auxiliary data block, descriptors are stored at offset 0,
3025 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003026 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003027 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003028 h.descriptors_offset = 0
3029 h.descriptors_size = len(encoded_descriptors)
3030 h.public_key_offset = h.descriptors_size
3031 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003032 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3033 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003034
3035 # For the Authentication data block, the hash is first and then
3036 # the signature.
3037 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003038 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003039 h.algorithm_type = alg.algorithm_type
3040 h.hash_offset = 0
3041 h.hash_size = alg.hash_num_bytes
3042 # Signature offset and size - it's stored right after the hash
3043 # (in Authentication data block).
3044 h.signature_offset = alg.hash_num_bytes
3045 h.signature_size = alg.signature_num_bytes
3046
3047 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003048 h.flags = flags
Varun Sharmade538272020-04-10 15:22:31 -07003049 h.rollback_index_location = rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04003050
3051 # Generate Header data block.
3052 header_data_blob = h.encode()
3053
3054 # Generate Auxiliary data block.
3055 aux_data_blob = bytearray()
3056 aux_data_blob.extend(encoded_descriptors)
3057 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003058 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003059 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003060 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003061
3062 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003063 binary_hash = b''
3064 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003065 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003066 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003067 ha.update(header_data_blob)
3068 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003069 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003070
3071 # Calculate the signature.
Jan Monschb1d920f2020-04-09 12:59:28 +02003072 padding_and_hash = bytearray(alg.padding) + binary_hash
3073 binary_signature = raw_sign(signing_helper, signing_helper_with_files,
3074 algorithm_name, alg.signature_num_bytes,
3075 key_path, padding_and_hash)
David Zeuthen21e95262016-07-27 17:58:40 -04003076
3077 # Generate Authentication data block.
3078 auth_data_blob = bytearray()
3079 auth_data_blob.extend(binary_hash)
3080 auth_data_blob.extend(binary_signature)
3081 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003082 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003083
Jan Monschb1d920f2020-04-09 12:59:28 +02003084 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003085
3086 def extract_public_key(self, key_path, output):
3087 """Implements the 'extract_public_key' command.
3088
3089 Arguments:
3090 key_path: The path to a RSA private key file.
3091 output: The file to write to.
3092 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003093 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003094
David Zeuthenb1b994d2017-03-06 18:01:31 -05003095 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3096 partition_size):
3097 """Implementation of the append_vbmeta_image command.
3098
3099 Arguments:
3100 image_filename: File to add the footer to.
3101 vbmeta_image_filename: File to get vbmeta struct from.
3102 partition_size: Size of partition.
3103
3104 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003105 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003106 """
3107 image = ImageHandler(image_filename)
3108
3109 if partition_size % image.block_size != 0:
3110 raise AvbError('Partition size of {} is not a multiple of the image '
3111 'block size {}.'.format(partition_size,
3112 image.block_size))
3113
3114 # If there's already a footer, truncate the image to its original
3115 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003116 if image.image_size >= AvbFooter.SIZE:
3117 image.seek(image.image_size - AvbFooter.SIZE)
3118 try:
3119 footer = AvbFooter(image.read(AvbFooter.SIZE))
3120 # Existing footer found. Just truncate.
3121 original_image_size = footer.original_image_size
3122 image.truncate(footer.original_image_size)
3123 except (LookupError, struct.error):
3124 original_image_size = image.image_size
3125 else:
3126 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003127 original_image_size = image.image_size
3128
3129 # If anything goes wrong from here-on, restore the image back to
3130 # its original size.
3131 try:
3132 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3133 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3134
3135 # If the image isn't sparse, its size might not be a multiple of
3136 # the block size. This will screw up padding later so just grow it.
3137 if image.image_size % image.block_size != 0:
3138 assert not image.is_sparse
3139 padding_needed = image.block_size - (image.image_size%image.block_size)
3140 image.truncate(image.image_size + padding_needed)
3141
3142 # The append_raw() method requires content with size being a
3143 # multiple of |block_size| so add padding as needed. Also record
3144 # where this is written to since we'll need to put that in the
3145 # footer.
3146 vbmeta_offset = image.image_size
3147 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3148 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003149 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003150
3151 # Append vbmeta blob and footer
3152 image.append_raw(vbmeta_blob_with_padding)
3153 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3154
3155 # Now insert a DONT_CARE chunk with enough bytes such that the
3156 # final Footer block is at the end of partition_size..
3157 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003158 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003159
3160 # Generate the Footer that tells where the VBMeta footer
3161 # is. Also put enough padding in the front of the footer since
3162 # we'll write out an entire block.
3163 footer = AvbFooter()
3164 footer.original_image_size = original_image_size
3165 footer.vbmeta_offset = vbmeta_offset
3166 footer.vbmeta_size = len(vbmeta_blob)
3167 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003168 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003169 footer_blob)
3170 image.append_raw(footer_blob_with_padding)
3171
Jan Monschb1d920f2020-04-09 12:59:28 +02003172 except Exception as e:
3173 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003174 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003175 raise AvbError('Appending VBMeta image failed: {}.'.format(e))
David Zeuthenb1b994d2017-03-06 18:01:31 -05003176
David Zeuthena4fee8b2016-08-22 15:20:43 -04003177 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003178 hash_algorithm, salt, chain_partitions, algorithm_name,
3179 key_path,
Varun Sharmade538272020-04-10 15:22:31 -07003180 public_key_metadata_path, rollback_index, flags,
3181 rollback_index_location, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003182 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003183 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003184 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003185 signing_helper, signing_helper_with_files,
3186 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003187 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003188 print_required_libavb_version, use_persistent_digest,
3189 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003190 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003191
3192 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003193 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003194 partition_size: Size of partition.
3195 partition_name: Name of partition (without A/B suffix).
3196 hash_algorithm: Hash algorithm to use.
3197 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003198 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003199 algorithm_name: Name of algorithm to use.
3200 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003201 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003202 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003203 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003204 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003205 props: Properties to insert (List of strings of the form 'key:value').
3206 props_from_file: Properties to insert (List of strings 'key:<path>').
3207 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003208 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003209 dm-verity kernel cmdline from.
3210 include_descriptors_from_image: List of file objects for which
3211 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003212 calc_max_image_size: Don't store the footer - instead calculate the
3213 maximum image size leaving enough room for metadata with the
3214 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003215 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003216 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003217 release_string: None or avbtool release string.
3218 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003219 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3220 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003221 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003222 use_persistent_digest: Use a persistent digest on device.
3223 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003224
3225 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003226 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003227 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003228 required_libavb_version_minor = 0
3229 if use_persistent_digest or do_not_use_ab:
3230 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003231 if rollback_index_location > 0:
3232 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003233
David Zeuthen1097a782017-05-31 15:53:17 -04003234 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003235 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003236 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003237 return
3238
David Zeuthenbf562452017-05-17 18:04:43 -04003239 # First, calculate the maximum image size such that an image
3240 # this size + metadata (footer + vbmeta struct) fits in
3241 # |partition_size|.
3242 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003243 if partition_size < max_metadata_size:
3244 raise AvbError('Parition size of {} is too small. '
3245 'Needs to be at least {}'.format(
3246 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003247 max_image_size = partition_size - max_metadata_size
3248
3249 # If we're asked to only calculate the maximum image size, we're done.
3250 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003251 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003252 return
3253
David Zeuthena4fee8b2016-08-22 15:20:43 -04003254 image = ImageHandler(image_filename)
3255
3256 if partition_size % image.block_size != 0:
3257 raise AvbError('Partition size of {} is not a multiple of the image '
3258 'block size {}.'.format(partition_size,
3259 image.block_size))
3260
David Zeuthen21e95262016-07-27 17:58:40 -04003261 # If there's already a footer, truncate the image to its original
3262 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3263 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003264 if image.image_size >= AvbFooter.SIZE:
3265 image.seek(image.image_size - AvbFooter.SIZE)
3266 try:
3267 footer = AvbFooter(image.read(AvbFooter.SIZE))
3268 # Existing footer found. Just truncate.
3269 original_image_size = footer.original_image_size
3270 image.truncate(footer.original_image_size)
3271 except (LookupError, struct.error):
3272 original_image_size = image.image_size
3273 else:
3274 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003275 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003276
3277 # If anything goes wrong from here-on, restore the image back to
3278 # its original size.
3279 try:
David Zeuthen09692692016-09-30 16:16:40 -04003280 # If image size exceeds the maximum image size, fail.
3281 if image.image_size > max_image_size:
3282 raise AvbError('Image size of {} exceeds maximum image '
3283 'size of {} in order to fit in a partition '
3284 'size of {}.'.format(image.image_size, max_image_size,
3285 partition_size))
3286
Jan Monsch6f27bb12020-04-07 07:33:26 +02003287 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003288 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003289 salt = binascii.unhexlify(salt)
3290 elif salt is None and not use_persistent_digest:
3291 # If salt is not explicitly specified, choose a hash that's the same
3292 # size as the hash size. Don't populate a random salt if this
3293 # descriptor is being created to use a persistent digest on device.
3294 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003295 with open('/dev/urandom', 'rb') as f:
3296 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003297 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003298 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003299
Jan Monsch6f27bb12020-04-07 07:33:26 +02003300 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003301 # TODO(zeuthen): might want to read this in chunks to avoid
3302 # memory pressure, then again, this is only supposed to be used
3303 # on kernel/initramfs partitions. Possible optimization.
3304 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003305 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003306 digest = hasher.digest()
3307
3308 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003309 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003310 h_desc.hash_algorithm = hash_algorithm
3311 h_desc.partition_name = partition_name
3312 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003313 h_desc.flags = 0
3314 if do_not_use_ab:
3315 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3316 if not use_persistent_digest:
3317 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003318
3319 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003320 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003321 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003322 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003323 chain_partitions, rollback_index, flags, rollback_index_location,
3324 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003325 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003326 include_descriptors_from_image, signing_helper,
3327 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003328 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003329
David Zeuthend247fcb2017-02-16 12:09:27 -05003330 # Write vbmeta blob, if requested.
3331 if output_vbmeta_image:
3332 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003333
David Zeuthend247fcb2017-02-16 12:09:27 -05003334 # Append vbmeta blob and footer, unless requested not to.
3335 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003336 # If the image isn't sparse, its size might not be a multiple of
3337 # the block size. This will screw up padding later so just grow it.
3338 if image.image_size % image.block_size != 0:
3339 assert not image.is_sparse
3340 padding_needed = image.block_size - (
3341 image.image_size % image.block_size)
3342 image.truncate(image.image_size + padding_needed)
3343
3344 # The append_raw() method requires content with size being a
3345 # multiple of |block_size| so add padding as needed. Also record
3346 # where this is written to since we'll need to put that in the
3347 # footer.
3348 vbmeta_offset = image.image_size
3349 padding_needed = (
3350 round_to_multiple(len(vbmeta_blob), image.block_size) -
3351 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003352 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003353
David Zeuthend247fcb2017-02-16 12:09:27 -05003354 image.append_raw(vbmeta_blob_with_padding)
3355 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3356
3357 # Now insert a DONT_CARE chunk with enough bytes such that the
3358 # final Footer block is at the end of partition_size..
3359 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003360 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003361
3362 # Generate the Footer that tells where the VBMeta footer
3363 # is. Also put enough padding in the front of the footer since
3364 # we'll write out an entire block.
3365 footer = AvbFooter()
3366 footer.original_image_size = original_image_size
3367 footer.vbmeta_offset = vbmeta_offset
3368 footer.vbmeta_size = len(vbmeta_blob)
3369 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003370 footer_blob_with_padding = (
3371 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003372 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003373 except Exception as e:
3374 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003375 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003376 raise AvbError('Adding hash_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003377
David Zeuthena4fee8b2016-08-22 15:20:43 -04003378 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003379 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003380 block_size, salt, chain_partitions, algorithm_name,
3381 key_path,
3382 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003383 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003384 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003385 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003386 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003387 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003388 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003389 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003390 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003391 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003392 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003393 use_persistent_root_digest, do_not_use_ab,
3394 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003395 """Implements the 'add_hashtree_footer' command.
3396
3397 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3398 more information about dm-verity and these hashes.
3399
3400 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003401 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003402 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003403 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003404 generate_fec: If True, generate FEC codes.
3405 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003406 hash_algorithm: Hash algorithm to use.
3407 block_size: Block size to use.
3408 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003409 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003410 algorithm_name: Name of algorithm to use.
3411 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003412 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003413 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003414 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003415 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003416 props: Properties to insert (List of strings of the form 'key:value').
3417 props_from_file: Properties to insert (List of strings 'key:<path>').
3418 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003419 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003420 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003421 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3422 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003423 include_descriptors_from_image: List of file objects for which
3424 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003425 calc_max_image_size: Don't store the hashtree or footer - instead
3426 calculate the maximum image size leaving enough room for hashtree
3427 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003428 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003429 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003430 release_string: None or avbtool release string.
3431 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003432 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3433 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003434 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003435 use_persistent_root_digest: Use a persistent root digest on device.
3436 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003437 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003438
3439 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003440 AvbError: If an argument is incorrect or adding the hashtree footer
3441 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003442 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003443 required_libavb_version_minor = 0
3444 if use_persistent_root_digest or do_not_use_ab:
3445 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003446 if rollback_index_location > 0:
3447 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003448
David Zeuthen1097a782017-05-31 15:53:17 -04003449 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003450 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003451 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003452 return
3453
Jan Monsch6f27bb12020-04-07 07:33:26 +02003454 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen09692692016-09-30 16:16:40 -04003455 digest_padding = round_to_pow2(digest_size) - digest_size
3456
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003457 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3458 # size such that an image this size + the hashtree + metadata (footer +
3459 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3460 # for metadata.
3461 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003462 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003463 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003464 if not no_hashtree:
3465 (_, max_tree_size) = calc_hash_level_offsets(
3466 partition_size, block_size, digest_size + digest_padding)
3467 if generate_fec:
3468 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003469 max_metadata_size = (max_fec_size + max_tree_size +
3470 self.MAX_VBMETA_SIZE +
3471 self.MAX_FOOTER_SIZE)
3472 max_image_size = partition_size - max_metadata_size
3473 else:
3474 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003475
3476 # If we're asked to only calculate the maximum image size, we're done.
3477 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003478 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003479 return
3480
David Zeuthena4fee8b2016-08-22 15:20:43 -04003481 image = ImageHandler(image_filename)
3482
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003483 if partition_size > 0:
3484 if partition_size % image.block_size != 0:
3485 raise AvbError('Partition size of {} is not a multiple of the image '
3486 'block size {}.'.format(partition_size,
3487 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003488 elif image.image_size % image.block_size != 0:
3489 raise AvbError('File size of {} is not a multiple of the image '
3490 'block size {}.'.format(image.image_size,
3491 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003492
David Zeuthen21e95262016-07-27 17:58:40 -04003493 # If there's already a footer, truncate the image to its original
3494 # size. This way 'avbtool add_hashtree_footer' is idempotent
3495 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003496 if image.image_size >= AvbFooter.SIZE:
3497 image.seek(image.image_size - AvbFooter.SIZE)
3498 try:
3499 footer = AvbFooter(image.read(AvbFooter.SIZE))
3500 # Existing footer found. Just truncate.
3501 original_image_size = footer.original_image_size
3502 image.truncate(footer.original_image_size)
3503 except (LookupError, struct.error):
3504 original_image_size = image.image_size
3505 else:
3506 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003507 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003508
3509 # If anything goes wrong from here-on, restore the image back to
3510 # its original size.
3511 try:
3512 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003513 rounded_image_size = round_to_multiple(image.image_size, block_size)
3514 if rounded_image_size > image.image_size:
3515 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003516
David Zeuthen09692692016-09-30 16:16:40 -04003517 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003518 if partition_size > 0:
3519 if image.image_size > max_image_size:
3520 raise AvbError('Image size of {} exceeds maximum image '
3521 'size of {} in order to fit in a partition '
3522 'size of {}.'.format(image.image_size, max_image_size,
3523 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003524
3525 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003526 salt = binascii.unhexlify(salt)
3527 elif salt is None and not use_persistent_root_digest:
3528 # If salt is not explicitly specified, choose a hash that's the same
3529 # size as the hash size. Don't populate a random salt if this
3530 # descriptor is being created to use a persistent digest on device.
3531 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003532 with open('/dev/urandom') as f:
3533 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003534 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003535 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003536
David Zeuthena4fee8b2016-08-22 15:20:43 -04003537 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003538 # offsets in advance.
3539 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003540 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003541
David Zeuthena4fee8b2016-08-22 15:20:43 -04003542 # If the image isn't sparse, its size might not be a multiple of
3543 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003544 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003545 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003546 padding_needed = image.block_size - (image.image_size%image.block_size)
3547 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003548
David Zeuthena4fee8b2016-08-22 15:20:43 -04003549 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003550 tree_offset = image.image_size
3551 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003552 block_size,
3553 hash_algorithm, salt,
3554 digest_padding,
3555 hash_level_offsets,
3556 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003557
3558 # Generate HashtreeDescriptor with details about the tree we
3559 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003560 if no_hashtree:
3561 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003562 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003563 ht_desc = AvbHashtreeDescriptor()
3564 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003565 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003566 ht_desc.tree_offset = tree_offset
3567 ht_desc.tree_size = tree_size
3568 ht_desc.data_block_size = block_size
3569 ht_desc.hash_block_size = block_size
3570 ht_desc.hash_algorithm = hash_algorithm
3571 ht_desc.partition_name = partition_name
3572 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003573 if do_not_use_ab:
3574 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3575 if not use_persistent_root_digest:
3576 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003577
David Zeuthen09692692016-09-30 16:16:40 -04003578 # Write the hash tree
3579 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3580 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003581 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003582 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003583 len_hashtree_and_fec = len(hash_tree_with_padding)
3584
3585 # Generate FEC codes, if requested.
3586 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003587 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003588 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003589 else:
3590 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003591 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3592 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003593 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003594 fec_offset = image.image_size
3595 image.append_raw(fec_data_with_padding)
3596 len_hashtree_and_fec += len(fec_data_with_padding)
3597 # Update the hashtree descriptor.
3598 ht_desc.fec_num_roots = fec_num_roots
3599 ht_desc.fec_offset = fec_offset
3600 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003601
David Zeuthen73f2afa2017-05-17 16:54:11 -04003602 ht_desc_to_setup = None
3603 if setup_as_rootfs_from_kernel:
3604 ht_desc_to_setup = ht_desc
3605
David Zeuthena4fee8b2016-08-22 15:20:43 -04003606 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003607 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003608 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003609 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003610 chain_partitions, rollback_index, flags, rollback_index_location,
3611 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003612 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003613 include_descriptors_from_image, signing_helper,
3614 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003615 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003616 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3617 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003618 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003619
David Zeuthend247fcb2017-02-16 12:09:27 -05003620 # Write vbmeta blob, if requested.
3621 if output_vbmeta_image:
3622 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003623
David Zeuthend247fcb2017-02-16 12:09:27 -05003624 # Append vbmeta blob and footer, unless requested not to.
3625 if not do_not_append_vbmeta_image:
3626 image.append_raw(vbmeta_blob_with_padding)
3627
3628 # Now insert a DONT_CARE chunk with enough bytes such that the
3629 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003630 if partition_size > 0:
3631 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003632 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003633
3634 # Generate the Footer that tells where the VBMeta footer
3635 # is. Also put enough padding in the front of the footer since
3636 # we'll write out an entire block.
3637 footer = AvbFooter()
3638 footer.original_image_size = original_image_size
3639 footer.vbmeta_offset = vbmeta_offset
3640 footer.vbmeta_size = len(vbmeta_blob)
3641 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003642 footer_blob_with_padding = (
3643 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003644 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003645
Jan Monschb1d920f2020-04-09 12:59:28 +02003646 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003647 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003648 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003649 raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003650
David Zeuthenc68f0822017-03-31 17:22:35 -04003651 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003652 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003653 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003654 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003655 """Implements the 'make_atx_certificate' command.
3656
3657 Android Things certificates are required for Android Things public key
3658 metadata. They chain the vbmeta signing key for a particular product back to
3659 a fused, permanent root key. These certificates are fixed-length and fixed-
3660 format with the explicit goal of not parsing ASN.1 in bootloader code.
3661
3662 Arguments:
3663 output: Certificate will be written to this file on success.
3664 authority_key_path: A PEM file path with the authority private key.
3665 If None, then a certificate will be created without a
3666 signature. The signature can be created out-of-band
3667 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003668 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003669 subject_key_version: A 64-bit version value. If this is None, the number
3670 of seconds since the epoch is used.
3671 subject: A subject identifier. For Product Signing Key certificates this
3672 should be the same Product ID found in the permanent attributes.
3673 is_intermediate_authority: True if the certificate is for an intermediate
3674 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003675 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003676 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003677 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003678 """
3679 signed_data = bytearray()
3680 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003681 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003682 hasher = hashlib.sha256()
3683 hasher.update(subject)
3684 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003685 if not usage:
3686 usage = 'com.google.android.things.vboot'
3687 if is_intermediate_authority:
3688 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003689 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003690 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003691 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003692 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003693 subject_key_version = int(time.time())
3694 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003695 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003696 if authority_key_path:
Darren Krahn43e12d82017-02-24 16:26:31 -08003697 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003698 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003699 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Darren Krahn147b08d2016-12-20 16:38:29 -08003700 hasher.update(signed_data)
Jan Monschb1d920f2020-04-09 12:59:28 +02003701
3702 padding_and_hash = bytearray()
3703 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003704 padding_and_hash.extend(hasher.digest())
Jan Monschb1d920f2020-04-09 12:59:28 +02003705
3706 signature = raw_sign(signing_helper, signing_helper_with_files,
3707 algorithm_name, alg.signature_num_bytes,
3708 authority_key_path, padding_and_hash)
Darren Krahn147b08d2016-12-20 16:38:29 -08003709 output.write(signed_data)
3710 output.write(signature)
3711
David Zeuthenc68f0822017-03-31 17:22:35 -04003712 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003713 product_id):
3714 """Implements the 'make_atx_permanent_attributes' command.
3715
3716 Android Things permanent attributes are designed to be permanent for a
3717 particular product and a hash of these attributes should be fused into
3718 hardware to enforce this.
3719
3720 Arguments:
3721 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003722 root_authority_key_path: Path to a PEM or DER public key for
3723 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003724 product_id: A 16-byte Product ID.
3725
3726 Raises:
3727 AvbError: If an argument is incorrect.
3728 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003729 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003730 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003731 raise AvbError('Invalid Product ID length.')
3732 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003733 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003734 output.write(product_id)
3735
3736 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003737 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003738 """Implements the 'make_atx_metadata' command.
3739
3740 Android Things metadata are included in vbmeta images to facilitate
3741 verification. The output of this command can be used as the
3742 public_key_metadata argument to other commands.
3743
3744 Arguments:
3745 output: Metadata will be written to this file on success.
3746 intermediate_key_certificate: A certificate file as output by
3747 make_atx_certificate with
3748 is_intermediate_authority set to true.
3749 product_key_certificate: A certificate file as output by
3750 make_atx_certificate with
3751 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003752
3753 Raises:
3754 AvbError: If an argument is incorrect.
3755 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003756 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003757 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003758 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003759 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003760 raise AvbError('Invalid product key certificate length.')
3761 output.write(struct.pack('<I', 1)) # Format Version
3762 output.write(intermediate_key_certificate)
3763 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003764
Darren Krahnfccd64e2018-01-16 17:39:35 -08003765 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3766 unlock_key_certificate, challenge_path,
3767 unlock_key_path, signing_helper,
3768 signing_helper_with_files):
3769 """Implements the 'make_atx_unlock_credential' command.
3770
3771 Android Things unlock credentials can be used to authorize the unlock of AVB
3772 on a device. These credentials are presented to an Android Things bootloader
3773 via the fastboot interface in response to a 16-byte challenge. This method
3774 creates all fields of the credential except the challenge signature field
3775 (which is the last field) and can optionally create the challenge signature
3776 field as well if a challenge and the unlock_key_path is provided.
3777
3778 Arguments:
3779 output: The credential will be written to this file on success.
3780 intermediate_key_certificate: A certificate file as output by
3781 make_atx_certificate with
3782 is_intermediate_authority set to true.
3783 unlock_key_certificate: A certificate file as output by
3784 make_atx_certificate with
3785 is_intermediate_authority set to false and the
3786 usage set to
3787 'com.google.android.things.vboot.unlock'.
3788 challenge_path: [optional] A path to the challenge to sign.
3789 unlock_key_path: [optional] A PEM file path with the unlock private key.
3790 signing_helper: Program which signs a hash and returns the signature.
3791 signing_helper_with_files: Same as signing_helper but uses files instead.
3792
3793 Raises:
3794 AvbError: If an argument is incorrect.
3795 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003796 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3797 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003798 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3799 raise AvbError('Invalid intermediate key certificate length.')
3800 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3801 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003802 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003803 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003804 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003805 challenge = f.read()
3806 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3807 raise AvbError('Invalid unlock challenge length.')
3808 output.write(struct.pack('<I', 1)) # Format Version
3809 output.write(intermediate_key_certificate)
3810 output.write(unlock_key_certificate)
3811 if challenge_path and unlock_key_path:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003812 algorithm_name = 'SHA512_RSA4096'
3813 alg = ALGORITHMS[algorithm_name]
3814 hasher = hashlib.sha512()
Darren Krahnfccd64e2018-01-16 17:39:35 -08003815 hasher.update(challenge)
Jan Monschb1d920f2020-04-09 12:59:28 +02003816
3817 padding_and_hash = bytearray()
3818 padding_and_hash.extend(alg.padding)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003819 padding_and_hash.extend(hasher.digest())
Jan Monschb1d920f2020-04-09 12:59:28 +02003820
3821 signature = raw_sign(signing_helper, signing_helper_with_files,
3822 algorithm_name, alg.signature_num_bytes,
3823 unlock_key_path, padding_and_hash)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003824 output.write(signature)
3825
David Zeuthen21e95262016-07-27 17:58:40 -04003826
3827def calc_hash_level_offsets(image_size, block_size, digest_size):
3828 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3829
3830 Arguments:
3831 image_size: The size of the image to calculate a Merkle-tree for.
3832 block_size: The block size, e.g. 4096.
3833 digest_size: The size of each hash, e.g. 32 for SHA-256.
3834
3835 Returns:
3836 A tuple where the first argument is an array of offsets and the
3837 second is size of the tree, in bytes.
3838 """
3839 level_offsets = []
3840 level_sizes = []
3841 tree_size = 0
3842
3843 num_levels = 0
3844 size = image_size
3845 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003846 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003847 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3848
3849 level_sizes.append(level_size)
3850 tree_size += level_size
3851 num_levels += 1
3852
3853 size = level_size
3854
3855 for n in range(0, num_levels):
3856 offset = 0
3857 for m in range(n + 1, num_levels):
3858 offset += level_sizes[m]
3859 level_offsets.append(offset)
3860
David Zeuthena4fee8b2016-08-22 15:20:43 -04003861 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003862
3863
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003864# See system/extras/libfec/include/fec/io.h for these definitions.
3865FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3866FEC_MAGIC = 0xfecfecfe
3867
3868
3869def calc_fec_data_size(image_size, num_roots):
3870 """Calculates how much space FEC data will take.
3871
Jan Monschfe00c0a2019-12-11 11:19:40 +01003872 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003873 image_size: The size of the image.
3874 num_roots: Number of roots.
3875
3876 Returns:
3877 The number of bytes needed for FEC for an image of the given size
3878 and with the requested number of FEC roots.
3879
3880 Raises:
3881 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003882 """
3883 p = subprocess.Popen(
3884 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3885 stdout=subprocess.PIPE,
3886 stderr=subprocess.PIPE)
3887 (pout, perr) = p.communicate()
3888 retcode = p.wait()
3889 if retcode != 0:
3890 raise ValueError('Error invoking fec: {}'.format(perr))
3891 return int(pout)
3892
3893
3894def generate_fec_data(image_filename, num_roots):
3895 """Generate FEC codes for an image.
3896
Jan Monschfe00c0a2019-12-11 11:19:40 +01003897 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003898 image_filename: The filename of the image.
3899 num_roots: Number of roots.
3900
3901 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003902 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003903
3904 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003905 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003906 """
Jan Monschb1d920f2020-04-09 12:59:28 +02003907 with tempfile.NamedTemporaryFile() as fec_tmpfile:
3908 try:
3909 subprocess.check_call(
3910 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3911 fec_tmpfile.name],
3912 stderr=open(os.devnull, 'wb'))
3913 except subprocess.CalledProcessError as e:
3914 raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
3915 fec_data = fec_tmpfile.read()
3916
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003917 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3918 footer_data = fec_data[-footer_size:]
3919 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3920 footer_data)
3921 if magic != FEC_MAGIC:
3922 raise ValueError('Unexpected magic in FEC footer')
3923 return fec_data[0:fec_size]
3924
3925
David Zeuthen21e95262016-07-27 17:58:40 -04003926def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003927 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003928 """Generates a Merkle-tree for a file.
3929
Jan Monschfe00c0a2019-12-11 11:19:40 +01003930 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003931 image: The image, as a file.
3932 image_size: The size of the image.
3933 block_size: The block size, e.g. 4096.
3934 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3935 salt: The salt to use.
3936 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003937 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003938 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003939
3940 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003941 A tuple where the first element is the top-level hash as bytes and the
3942 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003943 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003944 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003945 hash_src_offset = 0
3946 hash_src_size = image_size
3947 level_num = 0
3948 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003949 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003950 remaining = hash_src_size
3951 while remaining > 0:
Jan Monsch6f27bb12020-04-07 07:33:26 +02003952 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003953 # Only read from the file for the first level - for subsequent
3954 # levels, access the array we're building.
3955 if level_num == 0:
3956 image.seek(hash_src_offset + hash_src_size - remaining)
3957 data = image.read(min(remaining, block_size))
3958 else:
3959 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3960 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003961 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003962
3963 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003964 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02003965 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003966 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003967 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02003968 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08003969
Jan Monschb1d920f2020-04-09 12:59:28 +02003970 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003971
3972 padding_needed = (round_to_multiple(
3973 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02003974 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003975
David Zeuthena4fee8b2016-08-22 15:20:43 -04003976 # Copy level-output into resulting tree.
3977 offset = hash_level_offsets[level_num]
3978 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003979
David Zeuthena4fee8b2016-08-22 15:20:43 -04003980 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003981 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003982 level_num += 1
3983
Jan Monsch6f27bb12020-04-07 07:33:26 +02003984 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003985 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02003986 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04003987
3988
3989class AvbTool(object):
3990 """Object for avbtool command-line tool."""
3991
3992 def __init__(self):
3993 """Initializer method."""
3994 self.avb = Avb()
3995
3996 def _add_common_args(self, sub_parser):
3997 """Adds arguments used by several sub-commands.
3998
3999 Arguments:
4000 sub_parser: The parser to add arguments to.
4001 """
4002 sub_parser.add_argument('--algorithm',
4003 help='Algorithm to use (default: NONE)',
4004 metavar='ALGORITHM',
4005 default='NONE')
4006 sub_parser.add_argument('--key',
4007 help='Path to RSA private key file',
4008 metavar='KEY',
4009 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004010 sub_parser.add_argument('--signing_helper',
4011 help='Path to helper used for signing',
4012 metavar='APP',
4013 default=None,
4014 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004015 sub_parser.add_argument('--signing_helper_with_files',
4016 help='Path to helper used for signing using files',
4017 metavar='APP',
4018 default=None,
4019 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004020 sub_parser.add_argument('--public_key_metadata',
4021 help='Path to public key metadata file',
4022 metavar='KEY_METADATA',
4023 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004024 sub_parser.add_argument('--rollback_index',
4025 help='Rollback Index',
4026 type=parse_number,
4027 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004028 sub_parser.add_argument('--rollback_index_location',
4029 help='Location of main vbmeta Rollback Index',
4030 type=parse_number,
4031 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004032 # This is used internally for unit tests. Do not include in --help output.
4033 sub_parser.add_argument('--internal_release_string',
4034 help=argparse.SUPPRESS)
4035 sub_parser.add_argument('--append_to_release_string',
4036 help='Text to append to release string',
4037 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004038 sub_parser.add_argument('--prop',
4039 help='Add property',
4040 metavar='KEY:VALUE',
4041 action='append')
4042 sub_parser.add_argument('--prop_from_file',
4043 help='Add property from file',
4044 metavar='KEY:PATH',
4045 action='append')
4046 sub_parser.add_argument('--kernel_cmdline',
4047 help='Add kernel cmdline',
4048 metavar='CMDLINE',
4049 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004050 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4051 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4052 # at some future point.
4053 sub_parser.add_argument('--setup_rootfs_from_kernel',
4054 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004055 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004056 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004057 type=argparse.FileType('rb'))
4058 sub_parser.add_argument('--include_descriptors_from_image',
4059 help='Include descriptors from image',
4060 metavar='IMAGE',
4061 action='append',
4062 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004063 sub_parser.add_argument('--print_required_libavb_version',
4064 help=('Don\'t store the footer - '
4065 'instead calculate the required libavb '
4066 'version for the given options.'),
4067 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004068 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4069 sub_parser.add_argument('--chain_partition',
4070 help='Allow signed integrity-data for partition',
4071 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4072 action='append')
4073 sub_parser.add_argument('--flags',
4074 help='VBMeta flags',
4075 type=parse_number,
4076 default=0)
4077 sub_parser.add_argument('--set_hashtree_disabled_flag',
4078 help='Set the HASHTREE_DISABLED flag',
4079 action='store_true')
4080
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004081 def _add_common_footer_args(self, sub_parser):
4082 """Adds arguments used by add_*_footer sub-commands.
4083
4084 Arguments:
4085 sub_parser: The parser to add arguments to.
4086 """
4087 sub_parser.add_argument('--use_persistent_digest',
4088 help='Use a persistent digest on device instead of '
4089 'storing the digest in the descriptor. This '
4090 'cannot be used with A/B so must be combined '
4091 'with --do_not_use_ab when an A/B suffix is '
4092 'expected at runtime.',
4093 action='store_true')
4094 sub_parser.add_argument('--do_not_use_ab',
4095 help='The partition does not use A/B even when an '
4096 'A/B suffix is present. This must not be used '
4097 'for vbmeta or chained partitions.',
4098 action='store_true')
4099
David Zeuthena5fd3a42017-02-27 16:38:54 -05004100 def _fixup_common_args(self, args):
4101 """Common fixups needed by subcommands.
4102
4103 Arguments:
4104 args: Arguments to modify.
4105
4106 Returns:
4107 The modified arguments.
4108 """
4109 if args.set_hashtree_disabled_flag:
4110 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4111 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004112
4113 def run(self, argv):
4114 """Command-line processor.
4115
4116 Arguments:
4117 argv: Pass sys.argv from main.
4118 """
4119 parser = argparse.ArgumentParser()
4120 subparsers = parser.add_subparsers(title='subcommands')
4121
Jan Monsch2c7be992020-04-03 14:37:13 +02004122 sub_parser = subparsers.add_parser(
4123 'generate_test_image',
4124 help=('Generates a test image with a known pattern for testing: '
4125 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4126 sub_parser.add_argument('--image_size',
4127 help='Size of image to generate.',
4128 type=parse_number,
4129 required=True)
4130 sub_parser.add_argument('--start_byte',
4131 help='Integer for the start byte of the pattern.',
4132 type=parse_number,
4133 default=0)
4134 sub_parser.add_argument('--output',
4135 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004136 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004137 default=sys.stdout)
4138 sub_parser.set_defaults(func=self.generate_test_image)
4139
David Zeuthen21e95262016-07-27 17:58:40 -04004140 sub_parser = subparsers.add_parser('version',
4141 help='Prints version of avbtool.')
4142 sub_parser.set_defaults(func=self.version)
4143
4144 sub_parser = subparsers.add_parser('extract_public_key',
4145 help='Extract public key.')
4146 sub_parser.add_argument('--key',
4147 help='Path to RSA private key file',
4148 required=True)
4149 sub_parser.add_argument('--output',
4150 help='Output file name',
4151 type=argparse.FileType('wb'),
4152 required=True)
4153 sub_parser.set_defaults(func=self.extract_public_key)
4154
4155 sub_parser = subparsers.add_parser('make_vbmeta_image',
4156 help='Makes a vbmeta image.')
4157 sub_parser.add_argument('--output',
4158 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004159 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004160 sub_parser.add_argument('--padding_size',
4161 metavar='NUMBER',
4162 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004163 'its size is a multiple of NUMBER '
4164 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004165 type=parse_number,
4166 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004167 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004168 sub_parser.set_defaults(func=self.make_vbmeta_image)
4169
4170 sub_parser = subparsers.add_parser('add_hash_footer',
4171 help='Add hashes and footer to image.')
4172 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004173 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004174 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004175 sub_parser.add_argument('--partition_size',
4176 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004177 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004178 sub_parser.add_argument('--partition_name',
4179 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004180 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004181 sub_parser.add_argument('--hash_algorithm',
4182 help='Hash algorithm to use (default: sha256)',
4183 default='sha256')
4184 sub_parser.add_argument('--salt',
4185 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004186 sub_parser.add_argument('--calc_max_image_size',
4187 help=('Don\'t store the footer - '
4188 'instead calculate the maximum image size '
4189 'leaving enough room for metadata with '
4190 'the given partition size.'),
4191 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004192 sub_parser.add_argument('--output_vbmeta_image',
4193 help='Also write vbmeta struct to file',
4194 type=argparse.FileType('wb'))
4195 sub_parser.add_argument('--do_not_append_vbmeta_image',
4196 help=('Do not append vbmeta struct or footer '
4197 'to the image'),
4198 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004199 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004200 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004201 sub_parser.set_defaults(func=self.add_hash_footer)
4202
David Zeuthenb1b994d2017-03-06 18:01:31 -05004203 sub_parser = subparsers.add_parser('append_vbmeta_image',
4204 help='Append vbmeta image to image.')
4205 sub_parser.add_argument('--image',
4206 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004207 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004208 sub_parser.add_argument('--partition_size',
4209 help='Partition size',
4210 type=parse_number,
4211 required=True)
4212 sub_parser.add_argument('--vbmeta_image',
4213 help='Image with vbmeta blob to append',
4214 type=argparse.FileType('rb'))
4215 sub_parser.set_defaults(func=self.append_vbmeta_image)
4216
Jan Monscheeb28b62019-12-05 16:17:09 +01004217 sub_parser = subparsers.add_parser(
4218 'add_hashtree_footer',
4219 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004220 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004221 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004222 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004223 sub_parser.add_argument('--partition_size',
4224 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004225 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004226 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004227 sub_parser.add_argument('--partition_name',
4228 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004229 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004230 sub_parser.add_argument('--hash_algorithm',
4231 help='Hash algorithm to use (default: sha1)',
4232 default='sha1')
4233 sub_parser.add_argument('--salt',
4234 help='Salt in hex (default: /dev/urandom)')
4235 sub_parser.add_argument('--block_size',
4236 help='Block size (default: 4096)',
4237 type=parse_number,
4238 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004239 # TODO(zeuthen): The --generate_fec option was removed when we
4240 # moved to generating FEC by default. To avoid breaking existing
4241 # users needing to transition we simply just print a warning below
4242 # in add_hashtree_footer(). Remove this option and the warning at
4243 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004244 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004245 help=argparse.SUPPRESS,
4246 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004247 sub_parser.add_argument(
4248 '--do_not_generate_fec',
4249 help='Do not generate forward-error-correction codes',
4250 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004251 sub_parser.add_argument('--fec_num_roots',
4252 help='Number of roots for FEC (default: 2)',
4253 type=parse_number,
4254 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004255 sub_parser.add_argument('--calc_max_image_size',
4256 help=('Don\'t store the hashtree or footer - '
4257 'instead calculate the maximum image size '
4258 'leaving enough room for hashtree '
4259 'and metadata with the given partition '
4260 'size.'),
4261 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004262 sub_parser.add_argument('--output_vbmeta_image',
4263 help='Also write vbmeta struct to file',
4264 type=argparse.FileType('wb'))
4265 sub_parser.add_argument('--do_not_append_vbmeta_image',
4266 help=('Do not append vbmeta struct or footer '
4267 'to the image'),
4268 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004269 # This is different from --setup_rootfs_from_kernel insofar that
4270 # it doesn't take an IMAGE, the generated cmdline will be for the
4271 # hashtree we're adding.
4272 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4273 action='store_true',
4274 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004275 sub_parser.add_argument('--no_hashtree',
4276 action='store_true',
4277 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004278 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004279 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004280 sub_parser.set_defaults(func=self.add_hashtree_footer)
4281
4282 sub_parser = subparsers.add_parser('erase_footer',
4283 help='Erase footer from an image.')
4284 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004285 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004286 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004287 required=True)
4288 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004289 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004290 action='store_true')
4291 sub_parser.set_defaults(func=self.erase_footer)
4292
David Zeuthen1394f762019-04-30 10:20:11 -04004293 sub_parser = subparsers.add_parser('zero_hashtree',
4294 help='Zero out hashtree and FEC data.')
4295 sub_parser.add_argument('--image',
4296 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004297 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004298 required=True)
4299 sub_parser.set_defaults(func=self.zero_hashtree)
4300
Jan Monscheeb28b62019-12-05 16:17:09 +01004301 sub_parser = subparsers.add_parser(
4302 'extract_vbmeta_image',
4303 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004304 sub_parser.add_argument('--image',
4305 help='Image with footer',
4306 type=argparse.FileType('rb'),
4307 required=True)
4308 sub_parser.add_argument('--output',
4309 help='Output file name',
4310 type=argparse.FileType('wb'))
4311 sub_parser.add_argument('--padding_size',
4312 metavar='NUMBER',
4313 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004314 'its size is a multiple of NUMBER '
4315 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004316 type=parse_number,
4317 default=0)
4318 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4319
David Zeuthen2bc232b2017-04-19 14:25:19 -04004320 sub_parser = subparsers.add_parser('resize_image',
4321 help='Resize image with a footer.')
4322 sub_parser.add_argument('--image',
4323 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004324 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004325 required=True)
4326 sub_parser.add_argument('--partition_size',
4327 help='New partition size',
4328 type=parse_number)
4329 sub_parser.set_defaults(func=self.resize_image)
4330
David Zeuthen21e95262016-07-27 17:58:40 -04004331 sub_parser = subparsers.add_parser(
4332 'info_image',
4333 help='Show information about vbmeta or footer.')
4334 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004335 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004336 type=argparse.FileType('rb'),
4337 required=True)
4338 sub_parser.add_argument('--output',
4339 help='Write info to file',
4340 type=argparse.FileType('wt'),
4341 default=sys.stdout)
4342 sub_parser.set_defaults(func=self.info_image)
4343
David Zeuthenb623d8b2017-04-04 16:05:53 -04004344 sub_parser = subparsers.add_parser(
4345 'verify_image',
4346 help='Verify an image.')
4347 sub_parser.add_argument('--image',
4348 help='Image to verify',
4349 type=argparse.FileType('rb'),
4350 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004351 sub_parser.add_argument('--key',
4352 help='Check embedded public key matches KEY',
4353 metavar='KEY',
4354 required=False)
4355 sub_parser.add_argument('--expected_chain_partition',
4356 help='Expected chain partition',
4357 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4358 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004359 sub_parser.add_argument(
4360 '--follow_chain_partitions',
4361 help=('Follows chain partitions even when not '
4362 'specified with the --expected_chain_partition option'),
4363 action='store_true')
4364 sub_parser.add_argument(
4365 '--accept_zeroed_hashtree',
4366 help=('Accept images where the hashtree or FEC data is zeroed out'),
4367 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004368 sub_parser.set_defaults(func=self.verify_image)
4369
David Zeuthenb8643c02018-05-17 17:21:18 -04004370 sub_parser = subparsers.add_parser(
4371 'calculate_vbmeta_digest',
4372 help='Calculate vbmeta digest.')
4373 sub_parser.add_argument('--image',
4374 help='Image to calculate digest for',
4375 type=argparse.FileType('rb'),
4376 required=True)
4377 sub_parser.add_argument('--hash_algorithm',
4378 help='Hash algorithm to use (default: sha256)',
4379 default='sha256')
4380 sub_parser.add_argument('--output',
4381 help='Write hex digest to file (default: stdout)',
4382 type=argparse.FileType('wt'),
4383 default=sys.stdout)
4384 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4385
David Zeuthenf7d2e752018-09-20 13:30:41 -04004386 sub_parser = subparsers.add_parser(
4387 'calculate_kernel_cmdline',
4388 help='Calculate kernel cmdline.')
4389 sub_parser.add_argument('--image',
4390 help='Image to calculate kernel cmdline for',
4391 type=argparse.FileType('rb'),
4392 required=True)
4393 sub_parser.add_argument('--hashtree_disabled',
4394 help='Return the cmdline for hashtree disabled',
4395 action='store_true')
4396 sub_parser.add_argument('--output',
4397 help='Write cmdline to file (default: stdout)',
4398 type=argparse.FileType('wt'),
4399 default=sys.stdout)
4400 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4401
David Zeuthen8b6973b2016-09-20 12:39:49 -04004402 sub_parser = subparsers.add_parser('set_ab_metadata',
4403 help='Set A/B metadata.')
4404 sub_parser.add_argument('--misc_image',
4405 help=('The misc image to modify. If the image does '
4406 'not exist, it will be created.'),
4407 type=argparse.FileType('r+b'),
4408 required=True)
4409 sub_parser.add_argument('--slot_data',
4410 help=('Slot data of the form "priority", '
4411 '"tries_remaining", "sucessful_boot" for '
4412 'slot A followed by the same for slot B, '
4413 'separated by colons. The default value '
4414 'is 15:7:0:14:7:0.'),
4415 default='15:7:0:14:7:0')
4416 sub_parser.set_defaults(func=self.set_ab_metadata)
4417
Darren Krahn147b08d2016-12-20 16:38:29 -08004418 sub_parser = subparsers.add_parser(
4419 'make_atx_certificate',
4420 help='Create an Android Things eXtension (ATX) certificate.')
4421 sub_parser.add_argument('--output',
4422 help='Write certificate to file',
4423 type=argparse.FileType('wb'),
4424 default=sys.stdout)
4425 sub_parser.add_argument('--subject',
4426 help=('Path to subject file'),
4427 type=argparse.FileType('rb'),
4428 required=True)
4429 sub_parser.add_argument('--subject_key',
4430 help=('Path to subject RSA public key file'),
4431 type=argparse.FileType('rb'),
4432 required=True)
4433 sub_parser.add_argument('--subject_key_version',
4434 help=('Version of the subject key'),
4435 type=parse_number,
4436 required=False)
4437 sub_parser.add_argument('--subject_is_intermediate_authority',
4438 help=('Generate an intermediate authority '
4439 'certificate'),
4440 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004441 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004442 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004443 'string'),
4444 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004445 sub_parser.add_argument('--authority_key',
4446 help='Path to authority RSA private key file',
4447 required=False)
4448 sub_parser.add_argument('--signing_helper',
4449 help='Path to helper used for signing',
4450 metavar='APP',
4451 default=None,
4452 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004453 sub_parser.add_argument('--signing_helper_with_files',
4454 help='Path to helper used for signing using files',
4455 metavar='APP',
4456 default=None,
4457 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004458 sub_parser.set_defaults(func=self.make_atx_certificate)
4459
4460 sub_parser = subparsers.add_parser(
4461 'make_atx_permanent_attributes',
4462 help='Create Android Things eXtension (ATX) permanent attributes.')
4463 sub_parser.add_argument('--output',
4464 help='Write attributes to file',
4465 type=argparse.FileType('wb'),
4466 default=sys.stdout)
4467 sub_parser.add_argument('--root_authority_key',
4468 help='Path to authority RSA public key file',
4469 type=argparse.FileType('rb'),
4470 required=True)
4471 sub_parser.add_argument('--product_id',
4472 help=('Path to Product ID file'),
4473 type=argparse.FileType('rb'),
4474 required=True)
4475 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4476
4477 sub_parser = subparsers.add_parser(
4478 'make_atx_metadata',
4479 help='Create Android Things eXtension (ATX) metadata.')
4480 sub_parser.add_argument('--output',
4481 help='Write metadata to file',
4482 type=argparse.FileType('wb'),
4483 default=sys.stdout)
4484 sub_parser.add_argument('--intermediate_key_certificate',
4485 help='Path to intermediate key certificate file',
4486 type=argparse.FileType('rb'),
4487 required=True)
4488 sub_parser.add_argument('--product_key_certificate',
4489 help='Path to product key certificate file',
4490 type=argparse.FileType('rb'),
4491 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004492 sub_parser.set_defaults(func=self.make_atx_metadata)
4493
Darren Krahnfccd64e2018-01-16 17:39:35 -08004494 sub_parser = subparsers.add_parser(
4495 'make_atx_unlock_credential',
4496 help='Create an Android Things eXtension (ATX) unlock credential.')
4497 sub_parser.add_argument('--output',
4498 help='Write credential to file',
4499 type=argparse.FileType('wb'),
4500 default=sys.stdout)
4501 sub_parser.add_argument('--intermediate_key_certificate',
4502 help='Path to intermediate key certificate file',
4503 type=argparse.FileType('rb'),
4504 required=True)
4505 sub_parser.add_argument('--unlock_key_certificate',
4506 help='Path to unlock key certificate file',
4507 type=argparse.FileType('rb'),
4508 required=True)
4509 sub_parser.add_argument('--challenge',
4510 help='Path to the challenge to sign (optional). If '
4511 'this is not provided the challenge signature '
4512 'field is omitted and can be concatenated '
4513 'later.',
4514 required=False)
4515 sub_parser.add_argument('--unlock_key',
4516 help='Path to unlock key (optional). Must be '
4517 'provided if using --challenge.',
4518 required=False)
4519 sub_parser.add_argument('--signing_helper',
4520 help='Path to helper used for signing',
4521 metavar='APP',
4522 default=None,
4523 required=False)
4524 sub_parser.add_argument('--signing_helper_with_files',
4525 help='Path to helper used for signing using files',
4526 metavar='APP',
4527 default=None,
4528 required=False)
4529 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4530
David Zeuthen21e95262016-07-27 17:58:40 -04004531 args = parser.parse_args(argv[1:])
4532 try:
4533 args.func(args)
4534 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004535 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004536 sys.exit(1)
4537
4538 def version(self, _):
4539 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004540 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004541
Jan Monsch2c7be992020-04-03 14:37:13 +02004542 def generate_test_image(self, args):
4543 """Implements the 'generate_test_image' sub-command."""
4544 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4545
David Zeuthen21e95262016-07-27 17:58:40 -04004546 def extract_public_key(self, args):
4547 """Implements the 'extract_public_key' sub-command."""
4548 self.avb.extract_public_key(args.key, args.output)
4549
4550 def make_vbmeta_image(self, args):
4551 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004552 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004553 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004554 args.algorithm, args.key,
4555 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004556 args.flags, args.rollback_index_location,
4557 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004558 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004559 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004560 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004561 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004562 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004563 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004564 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004565 args.print_required_libavb_version,
4566 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004567
David Zeuthenb1b994d2017-03-06 18:01:31 -05004568 def append_vbmeta_image(self, args):
4569 """Implements the 'append_vbmeta_image' sub-command."""
4570 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4571 args.partition_size)
4572
David Zeuthen21e95262016-07-27 17:58:40 -04004573 def add_hash_footer(self, args):
4574 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004575 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004576 self.avb.add_hash_footer(args.image.name if args.image else None,
4577 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004578 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004579 args.salt, args.chain_partition, args.algorithm,
4580 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004581 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004582 args.flags, args.rollback_index_location,
4583 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004584 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004585 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004586 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004587 args.calc_max_image_size,
4588 args.signing_helper,
4589 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004590 args.internal_release_string,
4591 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004592 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004593 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004594 args.print_required_libavb_version,
4595 args.use_persistent_digest,
4596 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004597
4598 def add_hashtree_footer(self, args):
4599 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004600 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004601 # TODO(zeuthen): Remove when removing support for the
4602 # '--generate_fec' option above.
4603 if args.generate_fec:
4604 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4605 'is now generated by default. Use the option '
4606 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004607 self.avb.add_hashtree_footer(
4608 args.image.name if args.image else None,
4609 args.partition_size,
4610 args.partition_name,
4611 not args.do_not_generate_fec, args.fec_num_roots,
4612 args.hash_algorithm, args.block_size,
4613 args.salt, args.chain_partition, args.algorithm,
4614 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004615 args.rollback_index, args.flags,
4616 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004617 args.prop_from_file,
4618 args.kernel_cmdline,
4619 args.setup_rootfs_from_kernel,
4620 args.setup_as_rootfs_from_kernel,
4621 args.include_descriptors_from_image,
4622 args.calc_max_image_size,
4623 args.signing_helper,
4624 args.signing_helper_with_files,
4625 args.internal_release_string,
4626 args.append_to_release_string,
4627 args.output_vbmeta_image,
4628 args.do_not_append_vbmeta_image,
4629 args.print_required_libavb_version,
4630 args.use_persistent_digest,
4631 args.do_not_use_ab,
4632 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004633
David Zeuthen21e95262016-07-27 17:58:40 -04004634 def erase_footer(self, args):
4635 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004636 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004637
David Zeuthen1394f762019-04-30 10:20:11 -04004638 def zero_hashtree(self, args):
4639 """Implements the 'zero_hashtree' sub-command."""
4640 self.avb.zero_hashtree(args.image.name)
4641
David Zeuthen49936b42018-08-07 17:38:58 -04004642 def extract_vbmeta_image(self, args):
4643 """Implements the 'extract_vbmeta_image' sub-command."""
4644 self.avb.extract_vbmeta_image(args.output, args.image.name,
4645 args.padding_size)
4646
David Zeuthen2bc232b2017-04-19 14:25:19 -04004647 def resize_image(self, args):
4648 """Implements the 'resize_image' sub-command."""
4649 self.avb.resize_image(args.image.name, args.partition_size)
4650
David Zeuthen8b6973b2016-09-20 12:39:49 -04004651 def set_ab_metadata(self, args):
4652 """Implements the 'set_ab_metadata' sub-command."""
4653 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4654
David Zeuthen21e95262016-07-27 17:58:40 -04004655 def info_image(self, args):
4656 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004657 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004658
David Zeuthenb623d8b2017-04-04 16:05:53 -04004659 def verify_image(self, args):
4660 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004661 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004662 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004663 args.follow_chain_partitions,
4664 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004665
David Zeuthenb8643c02018-05-17 17:21:18 -04004666 def calculate_vbmeta_digest(self, args):
4667 """Implements the 'calculate_vbmeta_digest' sub-command."""
4668 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4669 args.output)
4670
David Zeuthenf7d2e752018-09-20 13:30:41 -04004671 def calculate_kernel_cmdline(self, args):
4672 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004673 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4674 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004675
Darren Krahn147b08d2016-12-20 16:38:29 -08004676 def make_atx_certificate(self, args):
4677 """Implements the 'make_atx_certificate' sub-command."""
4678 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004679 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004680 args.subject_key_version,
4681 args.subject.read(),
4682 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004683 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004684 args.signing_helper,
4685 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004686
4687 def make_atx_permanent_attributes(self, args):
4688 """Implements the 'make_atx_permanent_attributes' sub-command."""
4689 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004690 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004691 args.product_id.read())
4692
4693 def make_atx_metadata(self, args):
4694 """Implements the 'make_atx_metadata' sub-command."""
4695 self.avb.make_atx_metadata(args.output,
4696 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004697 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004698
Darren Krahnfccd64e2018-01-16 17:39:35 -08004699 def make_atx_unlock_credential(self, args):
4700 """Implements the 'make_atx_unlock_credential' sub-command."""
4701 self.avb.make_atx_unlock_credential(
4702 args.output,
4703 args.intermediate_key_certificate.read(),
4704 args.unlock_key_certificate.read(),
4705 args.challenge,
4706 args.unlock_key,
4707 args.signing_helper,
4708 args.signing_helper_with_files)
4709
David Zeuthen21e95262016-07-27 17:58:40 -04004710
4711if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004712 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004713 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4714 log.write(' '.join(sys.argv))
4715 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004716
David Zeuthen21e95262016-07-27 17:58:40 -04004717 tool = AvbTool()
4718 tool.run(sys.argv)