blob: c2b3b1a6d30e8d93010cdfe44d509b319fd90c63 [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040033import math
David Zeuthen21e95262016-07-27 17:58:40 -040034import os
35import struct
36import subprocess
37import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040038import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080039import time
David Zeuthen21e95262016-07-27 17:58:40 -040040
David Zeuthene3cadca2017-02-22 21:25:46 -050041# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040042AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080043AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
Jan Monsch2c7be992020-04-03 14:37:13 +020052# Configuration for enabling logging of calls to avbtool.
53AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
54
David Zeuthene3cadca2017-02-22 21:25:46 -050055
David Zeuthen21e95262016-07-27 17:58:40 -040056class AvbError(Exception):
57 """Application-specific errors.
58
59 These errors represent issues for which a stack-trace should not be
60 presented.
61
62 Attributes:
63 message: Error message.
64 """
65
66 def __init__(self, message):
67 Exception.__init__(self, message)
68
69
70class Algorithm(object):
71 """Contains details about an algorithm.
72
Tao Bao80418a52018-07-20 11:41:22 -070073 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040074
75 The constant |ALGORITHMS| is a dictionary from human-readable
76 names (e.g 'SHA256_RSA2048') to instances of this class.
77
78 Attributes:
79 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040080 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040081 hash_num_bytes: Number of bytes used to store the hash.
82 signature_num_bytes: Number of bytes used to store the signature.
83 public_key_num_bytes: Number of bytes used to store the public key.
84 padding: Padding used for signature, if any.
85 """
86
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
88 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040089 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040090 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040091 self.hash_num_bytes = hash_num_bytes
92 self.signature_num_bytes = signature_num_bytes
93 self.public_key_num_bytes = public_key_num_bytes
94 self.padding = padding
95
David Zeuthenb623d8b2017-04-04 16:05:53 -040096
David Zeuthen21e95262016-07-27 17:58:40 -040097# This must be kept in sync with the avb_crypto.h file.
98#
99# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
100# obtained from section 5.2.2 of RFC 4880.
101ALGORITHMS = {
102 'NONE': Algorithm(
103 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400104 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400105 hash_num_bytes=0,
106 signature_num_bytes=0,
107 public_key_num_bytes=0,
108 padding=[]),
109 'SHA256_RSA2048': Algorithm(
110 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400111 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400112 hash_num_bytes=32,
113 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100114 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*202 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA4096': Algorithm(
124 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400125 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400126 hash_num_bytes=32,
127 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100128 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*458 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA256_RSA8192': Algorithm(
138 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400139 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400140 hash_num_bytes=32,
141 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100142 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400143 padding=[
144 # PKCS1-v1_5 padding
145 0x00, 0x01] + [0xff]*970 + [0x00] + [
146 # ASN.1 header
147 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
148 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
149 0x00, 0x04, 0x20,
150 ]),
151 'SHA512_RSA2048': Algorithm(
152 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400153 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400154 hash_num_bytes=64,
155 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100156 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400157 padding=[
158 # PKCS1-v1_5 padding
159 0x00, 0x01] + [0xff]*170 + [0x00] + [
160 # ASN.1 header
161 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
162 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
163 0x00, 0x04, 0x40
164 ]),
165 'SHA512_RSA4096': Algorithm(
166 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400167 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400168 hash_num_bytes=64,
169 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100170 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400171 padding=[
172 # PKCS1-v1_5 padding
173 0x00, 0x01] + [0xff]*426 + [0x00] + [
174 # ASN.1 header
175 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
176 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
177 0x00, 0x04, 0x40
178 ]),
179 'SHA512_RSA8192': Algorithm(
180 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400181 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400182 hash_num_bytes=64,
183 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100184 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400185 padding=[
186 # PKCS1-v1_5 padding
187 0x00, 0x01] + [0xff]*938 + [0x00] + [
188 # ASN.1 header
189 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
190 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
191 0x00, 0x04, 0x40
192 ]),
193}
194
195
David Zeuthene3cadca2017-02-22 21:25:46 -0500196def get_release_string():
197 """Calculates the release string to use in the VBMeta struct."""
198 # Keep in sync with libavb/avb_version.c:avb_version_string().
199 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
200 AVB_VERSION_MINOR,
201 AVB_VERSION_SUB)
202
203
David Zeuthen21e95262016-07-27 17:58:40 -0400204def round_to_multiple(number, size):
205 """Rounds a number up to nearest multiple of another number.
206
Jan Monschfe00c0a2019-12-11 11:19:40 +0100207 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400208 number: The number to round up.
209 size: The multiple to round up to.
210
211 Returns:
212 If |number| is a multiple of |size|, returns |number|, otherwise
213 returns |number| + |size|.
214 """
215 remainder = number % size
216 if remainder == 0:
217 return number
218 return number + size - remainder
219
220
221def round_to_pow2(number):
222 """Rounds a number up to the next power of 2.
223
Jan Monschfe00c0a2019-12-11 11:19:40 +0100224 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400225 number: The number to round up.
226
227 Returns:
228 If |number| is already a power of 2 then |number| is
229 returned. Otherwise the smallest power of 2 greater than |number|
230 is returned.
231 """
232 return 2**((number - 1).bit_length())
233
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235def encode_long(num_bits, value):
236 """Encodes a long to a bytearray() using a given amount of bits.
237
238 This number is written big-endian, e.g. with the most significant
239 bit first.
240
David Zeuthenb623d8b2017-04-04 16:05:53 -0400241 This is the reverse of decode_long().
242
David Zeuthen21e95262016-07-27 17:58:40 -0400243 Arguments:
244 num_bits: The number of bits to write, e.g. 2048.
245 value: The value to write.
246
247 Returns:
248 A bytearray() with the encoded long.
249 """
250 ret = bytearray()
251 for bit_pos in range(num_bits, 0, -8):
252 octet = (value >> (bit_pos - 8)) & 0xff
253 ret.extend(struct.pack('!B', octet))
254 return ret
255
256
David Zeuthenb623d8b2017-04-04 16:05:53 -0400257def decode_long(blob):
258 """Decodes a long from a bytearray() using a given amount of bits.
259
260 This number is expected to be in big-endian, e.g. with the most
261 significant bit first.
262
263 This is the reverse of encode_long().
264
265 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100266 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400267
268 Returns:
269 The decoded value.
270 """
271 ret = 0
272 for b in bytearray(blob):
273 ret *= 256
274 ret += b
275 return ret
276
277
David Zeuthen21e95262016-07-27 17:58:40 -0400278def egcd(a, b):
279 """Calculate greatest common divisor of two numbers.
280
281 This implementation uses a recursive version of the extended
282 Euclidian algorithm.
283
284 Arguments:
285 a: First number.
286 b: Second number.
287
288 Returns:
289 A tuple (gcd, x, y) that where |gcd| is the greatest common
290 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
291 """
292 if a == 0:
293 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100294 g, y, x = egcd(b % a, a)
295 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400296
297
298def modinv(a, m):
299 """Calculate modular multiplicative inverse of |a| modulo |m|.
300
301 This calculates the number |x| such that |a| * |x| == 1 (modulo
302 |m|). This number only exists if |a| and |m| are co-prime - |None|
303 is returned if this isn't true.
304
305 Arguments:
306 a: The number to calculate a modular inverse of.
307 m: The modulo to use.
308
309 Returns:
310 The modular multiplicative inverse of |a| and |m| or |None| if
311 these numbers are not co-prime.
312 """
313 gcd, x, _ = egcd(a, m)
314 if gcd != 1:
315 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100316 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400317
318
319def parse_number(string):
320 """Parse a string as a number.
321
322 This is just a short-hand for int(string, 0) suitable for use in the
323 |type| parameter of |ArgumentParser|'s add_argument() function. An
324 improvement to just using type=int is that this function supports
325 numbers in other bases, e.g. "0x1234".
326
327 Arguments:
328 string: The string to parse.
329
330 Returns:
331 The parsed integer.
332
333 Raises:
334 ValueError: If the number could not be parsed.
335 """
336 return int(string, 0)
337
338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339class RSAPublicKey(object):
340 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400341
David Zeuthenc68f0822017-03-31 17:22:35 -0400342 Attributes:
343 exponent: The key exponent.
344 modulus: The key modulus.
345 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400346 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400347
Jan Monsch38865f22020-04-08 09:32:58 +0200348 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400349
350 def __init__(self, key_path):
351 """Loads and parses an RSA key from either a private or public key file.
352
353 Arguments:
354 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100355
356 Raises:
357 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400358 """
359 # We used to have something as simple as this:
360 #
361 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
362 # self.exponent = key.e
363 # self.modulus = key.n
364 # self.num_bits = key.size() + 1
365 #
366 # but unfortunately PyCrypto is not available in the builder. So
367 # instead just parse openssl(1) output to get this
368 # information. It's ugly but...
369 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
370 p = subprocess.Popen(args,
371 stdin=subprocess.PIPE,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE)
374 (pout, perr) = p.communicate()
375 if p.wait() != 0:
376 # Could be just a public key is passed, try that.
377 args.append('-pubin')
378 p = subprocess.Popen(args,
379 stdin=subprocess.PIPE,
380 stdout=subprocess.PIPE,
381 stderr=subprocess.PIPE)
382 (pout, perr) = p.communicate()
383 if p.wait() != 0:
384 raise AvbError('Error getting public key: {}'.format(perr))
385
386 if not pout.lower().startswith(self.MODULUS_PREFIX):
387 raise AvbError('Unexpected modulus output')
388
389 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
390
391 # The exponent is assumed to always be 65537 and the number of
392 # bits can be derived from the modulus by rounding up to the
393 # nearest power of 2.
394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
398
David Zeuthenc68f0822017-03-31 17:22:35 -0400399def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400400 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
401
402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
404
405 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
408 Returns:
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 Monsch23e0c622019-12-11 11:23:58 +0100419 b = 2L**32 # pylint: disable=long-suffix
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 Monsch23e0c622019-12-11 11:23:58 +0100422 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
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.
1547 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1548 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001549 try:
1550 self.partition_name = data[
1551 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1552 ].decode('utf-8')
1553 except UnicodeDecodeError as e:
1554 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1555 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001556 o += partition_name_len
1557 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1558 o += salt_len
1559 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001560 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001561 if digest_len != 0:
1562 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001563
1564 else:
1565 self.image_size = 0
1566 self.hash_algorithm = ''
1567 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001568 self.salt = b''
1569 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001570 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001571
1572 def print_desc(self, o):
1573 """Print the descriptor.
1574
1575 Arguments:
1576 o: The object to write the output to.
1577 """
1578 o.write(' Hash descriptor:\n')
1579 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1580 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1581 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001582 o.write(' Salt: {}\n'.format(
1583 binascii.hexlify(self.salt).decode('ascii')))
1584 o.write(' Digest: {}\n'.format(
1585 binascii.hexlify(self.digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001586 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001587
1588 def encode(self):
1589 """Serializes the descriptor.
1590
1591 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001592 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001593 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001594 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1595 partition_name_encoded = self.partition_name.encode('utf-8')
1596 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1597 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001598 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1599 padding_size = nbf_with_padding - num_bytes_following
1600 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001601 self.image_size, hash_algorithm_encoded,
1602 len(partition_name_encoded), len(self.salt),
1603 len(self.digest), self.flags, self.RESERVED * b'\0')
1604 ret = (desc + partition_name_encoded + self.salt + self.digest +
1605 padding_size * b'\0')
1606 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001607
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001608 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001609 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001610 """Verifies contents of the descriptor - used in verify_image sub-command.
1611
1612 Arguments:
1613 image_dir: The directory of the file being verified.
1614 image_ext: The extension of the file being verified (e.g. '.img').
1615 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001616 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001617 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001618 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1619 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001620
1621 Returns:
1622 True if the descriptor verifies, False otherwise.
1623 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001624 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001625 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001626 image = image_containing_descriptor
1627 else:
1628 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1629 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001630 data = image.read(self.image_size)
1631 ha = hashlib.new(self.hash_algorithm)
1632 ha.update(self.salt)
1633 ha.update(data)
1634 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001635 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001636 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001637 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1638 format(self.hash_algorithm, image_filename))
1639 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001640 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1641 .format(self.partition_name, self.hash_algorithm, image.filename,
1642 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001643 return True
1644
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646class AvbKernelCmdlineDescriptor(AvbDescriptor):
1647 """A class for kernel command-line descriptors.
1648
1649 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1650
1651 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001652 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001653 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001654 """
1655
1656 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001657 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001658 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001659 'L' # flags
1660 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001661
David Zeuthenfd41eb92016-11-17 12:24:47 -05001662 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1663 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1664
David Zeuthen21e95262016-07-27 17:58:40 -04001665 def __init__(self, data=None):
1666 """Initializes a new kernel cmdline descriptor.
1667
1668 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001669 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001670
1671 Raises:
1672 LookupError: If the given descriptor is malformed.
1673 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001674 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001675 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1676
1677 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001678 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001679 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1680 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1681 8)
1682 if tag != self.TAG or num_bytes_following != expected_size:
1683 raise LookupError('Given data does not look like a kernel cmdline '
1684 'descriptor.')
1685 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001686 try:
1687 self.kernel_cmdline = data[
1688 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1689 except UnicodeDecodeError as e:
1690 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1691 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001692 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001693 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.kernel_cmdline = ''
1695
1696 def print_desc(self, o):
1697 """Print the descriptor.
1698
1699 Arguments:
1700 o: The object to write the output to.
1701 """
1702 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001703 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001704 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001705
1706 def encode(self):
1707 """Serializes the descriptor.
1708
1709 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001710 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001711 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001712 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1713 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001714 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1715 padding_size = nbf_with_padding - num_bytes_following
1716 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001717 self.flags, len(kernel_cmd_encoded))
1718 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1719 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001720
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001721 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001722 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001723 """Verifies contents of the descriptor - used in verify_image sub-command.
1724
1725 Arguments:
1726 image_dir: The directory of the file being verified.
1727 image_ext: The extension of the file being verified (e.g. '.img').
1728 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001729 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001730 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001731 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1732 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001733
1734 Returns:
1735 True if the descriptor verifies, False otherwise.
1736 """
1737 # Nothing to verify.
1738 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001739
Jan Monscheeb28b62019-12-05 16:17:09 +01001740
David Zeuthen21e95262016-07-27 17:58:40 -04001741class AvbChainPartitionDescriptor(AvbDescriptor):
1742 """A class for chained partition descriptors.
1743
1744 See the |AvbChainPartitionDescriptor| C struct for more information.
1745
1746 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001747 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001748 partition_name: Partition name as string.
1749 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001750 """
1751
1752 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001753 RESERVED = 64
1754 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001755 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001756 'L' # rollback_index_location
1757 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001758 'L' + # public_key_size (bytes)
1759 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001760
1761 def __init__(self, data=None):
1762 """Initializes a new chain partition descriptor.
1763
1764 Arguments:
1765 data: If not None, must be a bytearray of size |SIZE|.
1766
1767 Raises:
1768 LookupError: If the given descriptor is malformed.
1769 """
1770 AvbDescriptor.__init__(self, None)
1771 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1772
1773 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001774 (tag, num_bytes_following, self.rollback_index_location,
1775 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001776 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001777 expected_size = round_to_multiple(
1778 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1779 if tag != self.TAG or num_bytes_following != expected_size:
1780 raise LookupError('Given data does not look like a chain partition '
1781 'descriptor.')
1782 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001783 try:
1784 self.partition_name = data[
1785 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1786 ].decode('utf-8')
1787 except UnicodeDecodeError as e:
1788 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1789 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001790 o += partition_name_len
1791 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1792
1793 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001794 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001795 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001796 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001797
1798 def print_desc(self, o):
1799 """Print the descriptor.
1800
1801 Arguments:
1802 o: The object to write the output to.
1803 """
1804 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001805 o.write(' Partition Name: {}\n'.format(self.partition_name))
1806 o.write(' Rollback Index Location: {}\n'.format(
1807 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001808 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001809 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1810 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 def encode(self):
1813 """Serializes the descriptor.
1814
1815 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001816 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001817 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001818 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001819 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001820 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001821 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1822 padding_size = nbf_with_padding - num_bytes_following
1823 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001824 self.rollback_index_location,
1825 len(partition_name_encoded), len(self.public_key),
1826 self.RESERVED * b'\0')
1827 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1828 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001829
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001830 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001831 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001832 """Verifies contents of the descriptor - used in verify_image sub-command.
1833
1834 Arguments:
1835 image_dir: The directory of the file being verified.
1836 image_ext: The extension of the file being verified (e.g. '.img').
1837 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001838 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001839 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001840 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1841 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001842
1843 Returns:
1844 True if the descriptor verifies, False otherwise.
1845 """
1846 value = expected_chain_partitions_map.get(self.partition_name)
1847 if not value:
1848 sys.stderr.write('No expected chain partition for partition {}. Use '
1849 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001850 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001851 format(self.partition_name))
1852 return False
1853 rollback_index_location, pk_blob = value
1854
1855 if self.rollback_index_location != rollback_index_location:
1856 sys.stderr.write('Expected rollback_index_location {} does not '
1857 'match {} in descriptor for partition {}\n'.
1858 format(rollback_index_location,
1859 self.rollback_index_location,
1860 self.partition_name))
1861 return False
1862
1863 if self.public_key != pk_blob:
1864 sys.stderr.write('Expected public key blob does not match public '
1865 'key blob in descriptor for partition {}\n'.
1866 format(self.partition_name))
1867 return False
1868
Jan Monsch23e0c622019-12-11 11:23:58 +01001869 print('{}: Successfully verified chain partition descriptor matches '
1870 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001871
1872 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001873
1874DESCRIPTOR_CLASSES = [
1875 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1876 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1877]
1878
1879
1880def parse_descriptors(data):
1881 """Parses a blob of data into descriptors.
1882
1883 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001884 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001885
1886 Returns:
1887 A list of instances of objects derived from AvbDescriptor. For
1888 unknown descriptors, the class AvbDescriptor is used.
1889 """
1890 o = 0
1891 ret = []
1892 while o < len(data):
1893 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1894 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001895 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001896 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001897 clazz = AvbDescriptor
1898 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001899 o += 16 + nb_following
1900 return ret
1901
1902
1903class AvbFooter(object):
1904 """A class for parsing and writing footers.
1905
1906 Footers are stored at the end of partitions and point to where the
1907 AvbVBMeta blob is located. They also contain the original size of
1908 the image before AVB information was added.
1909
1910 Attributes:
1911 magic: Magic for identifying the footer, see |MAGIC|.
1912 version_major: The major version of avbtool that wrote the footer.
1913 version_minor: The minor version of avbtool that wrote the footer.
1914 original_image_size: Original image size.
1915 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1916 vbmeta_size: Size of the AvbVBMeta blob.
1917 """
1918
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001919 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001920 SIZE = 64
1921 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001922 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1923 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001924 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001925 'Q' # Original image size.
1926 'Q' # Offset of VBMeta blob.
1927 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001928 str(RESERVED) + 'x') # padding for reserved bytes
1929
1930 def __init__(self, data=None):
1931 """Initializes a new footer object.
1932
1933 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001934 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001935
1936 Raises:
1937 LookupError: If the given footer is malformed.
1938 struct.error: If the given data has no footer.
1939 """
1940 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1941
1942 if data:
1943 (self.magic, self.version_major, self.version_minor,
1944 self.original_image_size, self.vbmeta_offset,
1945 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1946 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001947 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001948 else:
1949 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001950 self.version_major = self.FOOTER_VERSION_MAJOR
1951 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001952 self.original_image_size = 0
1953 self.vbmeta_offset = 0
1954 self.vbmeta_size = 0
1955
David Zeuthena4fee8b2016-08-22 15:20:43 -04001956 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001957 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001958
David Zeuthena4fee8b2016-08-22 15:20:43 -04001959 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001960 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001961 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001962 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1963 self.version_minor, self.original_image_size,
1964 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001965
1966
1967class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001968 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001969
Jan Monschfe00c0a2019-12-11 11:19:40 +01001970 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1971 avb_vbmeta_image.h.
1972
David Zeuthen21e95262016-07-27 17:58:40 -04001973 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001974 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1975 required_libavb_version_major: The major version of libavb required for this
1976 header.
1977 required_libavb_version_minor: The minor version of libavb required for this
1978 header.
1979 authentication_data_block_size: The size of the signature block.
1980 auxiliary_data_block_size: The size of the auxiliary data block.
1981 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1982 enum.
1983 hash_offset: Offset into the "Authentication data" block of hash data.
1984 hash_size: Length of the hash data.
1985 signature_offset: Offset into the "Authentication data" block of signature
1986 data.
1987 signature_size: Length of the signature data.
1988 public_key_offset: Offset into the "Auxiliary data" block of public key
1989 data.
1990 public_key_size: Length of the public key data.
1991 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1992 key metadata.
1993 public_key_metadata_size: Length of the public key metadata. Must be set to
1994 zero if there is no public key metadata.
1995 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1996 data.
1997 descriptors_size: Length of descriptor data.
1998 rollback_index: The rollback index which can be used to prevent rollback to
1999 older versions.
2000 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2001 zero if the vbmeta image is not a top-level image.
2002 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2003 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2004 terminated. Applications must not make assumptions about how this
2005 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002006 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002007 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002008 SIZE = 256
2009
David Zeuthene3cadca2017-02-22 21:25:46 -05002010 # Keep in sync with |reserved0| and |reserved| field of
2011 # |AvbVBMetaImageHeader|.
2012 RESERVED0 = 4
2013 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002014
2015 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002016 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2017 '2Q' # 2 x block size
2018 'L' # algorithm type
2019 '2Q' # offset, size (hash)
2020 '2Q' # offset, size (signature)
2021 '2Q' # offset, size (public key)
2022 '2Q' # offset, size (public key metadata)
2023 '2Q' # offset, size (descriptors)
2024 'Q' # rollback_index
2025 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002026 str(RESERVED0) + 'x' + # padding for reserved bytes
2027 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002028 str(RESERVED) + 'x') # padding for reserved bytes
2029
2030 def __init__(self, data=None):
2031 """Initializes a new header object.
2032
2033 Arguments:
2034 data: If not None, must be a bytearray of size 8192.
2035
2036 Raises:
2037 Exception: If the given data is malformed.
2038 """
2039 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2040
2041 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002042 (self.magic, self.required_libavb_version_major,
2043 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002044 self.authentication_data_block_size, self.auxiliary_data_block_size,
2045 self.algorithm_type, self.hash_offset, self.hash_size,
2046 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002047 self.public_key_size, self.public_key_metadata_offset,
2048 self.public_key_metadata_size, self.descriptors_offset,
2049 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002050 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002051 self.flags,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002052 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002053 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002054 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002055 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002056 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002057 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002058 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002059 # Start by just requiring version 1.0. Code that adds features
2060 # in a future version can use bump_required_libavb_version_minor() to
2061 # bump the minor.
2062 self.required_libavb_version_major = AVB_VERSION_MAJOR
2063 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002064 self.authentication_data_block_size = 0
2065 self.auxiliary_data_block_size = 0
2066 self.algorithm_type = 0
2067 self.hash_offset = 0
2068 self.hash_size = 0
2069 self.signature_offset = 0
2070 self.signature_size = 0
2071 self.public_key_offset = 0
2072 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002073 self.public_key_metadata_offset = 0
2074 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002075 self.descriptors_offset = 0
2076 self.descriptors_size = 0
2077 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002078 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002079 self.release_string = get_release_string()
2080
2081 def bump_required_libavb_version_minor(self, minor):
2082 """Function to bump required_libavb_version_minor.
2083
2084 Call this when writing data that requires a specific libavb
2085 version to parse it.
2086
2087 Arguments:
2088 minor: The minor version of libavb that has support for the feature.
2089 """
2090 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002091 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002092
David Zeuthen21e95262016-07-27 17:58:40 -04002093 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002094 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002095
2096 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002097 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002098 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002099 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002100 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002101 self.required_libavb_version_major,
2102 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002103 self.authentication_data_block_size,
2104 self.auxiliary_data_block_size, self.algorithm_type,
2105 self.hash_offset, self.hash_size, self.signature_offset,
2106 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002107 self.public_key_size, self.public_key_metadata_offset,
2108 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002109 self.descriptors_size, self.rollback_index, self.flags,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002110 release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002111
2112
2113class Avb(object):
2114 """Business logic for avbtool command-line tool."""
2115
David Zeuthen8b6973b2016-09-20 12:39:49 -04002116 # Keep in sync with avb_ab_flow.h.
2117 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002118 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002119 AB_MAJOR_VERSION = 1
2120 AB_MINOR_VERSION = 0
2121 AB_MISC_METADATA_OFFSET = 2048
2122
David Zeuthen09692692016-09-30 16:16:40 -04002123 # Constants for maximum metadata size. These are used to give
2124 # meaningful errors if the value passed in via --partition_size is
2125 # too small and when --calc_max_image_size is used. We use
2126 # conservative figures.
2127 MAX_VBMETA_SIZE = 64 * 1024
2128 MAX_FOOTER_SIZE = 4096
2129
Jan Monsch2c7be992020-04-03 14:37:13 +02002130 def generate_test_image(self, output, image_size, start_byte):
2131 """Generates a test image for testing avbtool with known content.
2132
2133 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2134
2135 Arguments:
2136 output: Write test image to this file.
2137 image_size: The size of the requested file in bytes.
2138 start_byte: The integer value of the start byte to use for pattern
2139 generation.
2140 """
2141 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2142 buf = bytearray()
2143 c = int(math.ceil(image_size / 256.0))
2144 for _ in range(0, c):
2145 buf.extend(pattern)
2146 output.write(buf[0:image_size])
2147
David Zeuthen49936b42018-08-07 17:38:58 -04002148 def extract_vbmeta_image(self, output, image_filename, padding_size):
2149 """Implements the 'extract_vbmeta_image' command.
2150
2151 Arguments:
2152 output: Write vbmeta struct to this file.
2153 image_filename: File to extract vbmeta data from (with a footer).
2154 padding_size: If not 0, pads output so size is a multiple of the number.
2155
2156 Raises:
2157 AvbError: If there's no footer in the image.
2158 """
2159 image = ImageHandler(image_filename)
David Zeuthen49936b42018-08-07 17:38:58 -04002160 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002161 if not footer:
2162 raise AvbError('Given image does not have a footer.')
2163
2164 image.seek(footer.vbmeta_offset)
2165 vbmeta_blob = image.read(footer.vbmeta_size)
2166 output.write(vbmeta_blob)
2167
2168 if padding_size > 0:
2169 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2170 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002171 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002172
David Zeuthena4fee8b2016-08-22 15:20:43 -04002173 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002174 """Implements the 'erase_footer' command.
2175
2176 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002177 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002178 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002179
2180 Raises:
2181 AvbError: If there's no footer in the image.
2182 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002183 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002184 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002185 if not footer:
2186 raise AvbError('Given image does not have a footer.')
2187
2188 new_image_size = None
2189 if not keep_hashtree:
2190 new_image_size = footer.original_image_size
2191 else:
2192 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002193 # descriptor to figure out the location and size of the hashtree
2194 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002195 for desc in descriptors:
2196 if isinstance(desc, AvbHashtreeDescriptor):
2197 # The hashtree is always just following the main data so the
2198 # new size is easily derived.
2199 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002200 # If the image has FEC codes, also keep those.
2201 if desc.fec_offset > 0:
2202 fec_end = desc.fec_offset + desc.fec_size
2203 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002204 break
2205 if not new_image_size:
2206 raise AvbError('Requested to keep hashtree but no hashtree '
2207 'descriptor was found.')
2208
2209 # And cut...
2210 image.truncate(new_image_size)
2211
David Zeuthen1394f762019-04-30 10:20:11 -04002212 def zero_hashtree(self, image_filename):
2213 """Implements the 'zero_hashtree' command.
2214
2215 Arguments:
2216 image_filename: File to zero hashtree and FEC data from.
2217
2218 Raises:
2219 AvbError: If there's no footer in the image.
2220 """
David Zeuthen1394f762019-04-30 10:20:11 -04002221 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002222 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002223 if not footer:
2224 raise AvbError('Given image does not have a footer.')
2225
2226 # Search for a hashtree descriptor to figure out the location and
2227 # size of the hashtree and FEC.
2228 ht_desc = None
2229 for desc in descriptors:
2230 if isinstance(desc, AvbHashtreeDescriptor):
2231 ht_desc = desc
2232 break
2233
2234 if not ht_desc:
2235 raise AvbError('No hashtree descriptor was found.')
2236
2237 zero_ht_start_offset = ht_desc.tree_offset
2238 zero_ht_num_bytes = ht_desc.tree_size
2239 zero_fec_start_offset = None
2240 zero_fec_num_bytes = 0
2241 if ht_desc.fec_offset > 0:
2242 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2243 raise AvbError('Hash-tree and FEC data must be adjacent.')
2244 zero_fec_start_offset = ht_desc.fec_offset
2245 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002246 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2247 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002248 image.seek(zero_end_offset)
2249 data = image.read(image.image_size - zero_end_offset)
2250
2251 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2252 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2253 # beginning of both hashtree and FEC. (That way, in the future we can add
2254 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2255 #
2256 # Applications can use these markers to detect that the hashtree and/or
2257 # FEC needs to be recomputed.
2258 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002259 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002260 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002261 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002262 if zero_fec_start_offset:
2263 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002264 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002265 image.append_raw(data)
2266
David Zeuthen2bc232b2017-04-19 14:25:19 -04002267 def resize_image(self, image_filename, partition_size):
2268 """Implements the 'resize_image' command.
2269
2270 Arguments:
2271 image_filename: File with footer to resize.
2272 partition_size: The new size of the image.
2273
2274 Raises:
2275 AvbError: If there's no footer in the image.
2276 """
2277
2278 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002279 if partition_size % image.block_size != 0:
2280 raise AvbError('Partition size of {} is not a multiple of the image '
2281 'block size {}.'.format(partition_size,
2282 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002283 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002284 if not footer:
2285 raise AvbError('Given image does not have a footer.')
2286
2287 # The vbmeta blob is always at the end of the data so resizing an
2288 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002289 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2290 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002291 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2292 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002293
Jan Monschb1d920f2020-04-09 12:59:28 +02002294 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002295 raise AvbError('Requested size of {} is too small for an image '
2296 'of size {}.'
2297 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002298 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002299
2300 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2301 # with enough bytes such that the final Footer block is at the end
2302 # of partition_size.
2303 image.truncate(vbmeta_end_offset)
2304 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002305 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002306
2307 # Just reuse the same footer - only difference is that we're
2308 # writing it in a different place.
2309 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002310 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002311 footer_blob)
2312 image.append_raw(footer_blob_with_padding)
2313
David Zeuthen8b6973b2016-09-20 12:39:49 -04002314 def set_ab_metadata(self, misc_image, slot_data):
2315 """Implements the 'set_ab_metadata' command.
2316
2317 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2318 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2319
2320 Arguments:
2321 misc_image: The misc image to write to.
2322 slot_data: Slot data as a string
2323
2324 Raises:
2325 AvbError: If slot data is malformed.
2326 """
2327 tokens = slot_data.split(':')
2328 if len(tokens) != 6:
2329 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2330 a_priority = int(tokens[0])
2331 a_tries_remaining = int(tokens[1])
2332 a_success = True if int(tokens[2]) != 0 else False
2333 b_priority = int(tokens[3])
2334 b_tries_remaining = int(tokens[4])
2335 b_success = True if int(tokens[5]) != 0 else False
2336
2337 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2338 self.AB_MAGIC,
2339 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2340 a_priority, a_tries_remaining, a_success,
2341 b_priority, b_tries_remaining, b_success)
2342 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2343 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2344 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2345 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2346 misc_image.write(ab_data)
2347
David Zeuthena4fee8b2016-08-22 15:20:43 -04002348 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002349 """Implements the 'info_image' command.
2350
2351 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002352 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002353 output: Output file to write human-readable information to (file object).
2354 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002355 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002356 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002357 (footer, header, descriptors, image_size) = self._parse_image(image)
2358
Bowgo Tsaid7145942020-03-20 17:03:51 +08002359 # To show the SHA1 of the public key.
2360 vbmeta_blob = self._load_vbmeta_blob(image)
2361 key_offset = (header.SIZE +
2362 header.authentication_data_block_size +
2363 header.public_key_offset)
2364 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2365
David Zeuthen21e95262016-07-27 17:58:40 -04002366 if footer:
2367 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2368 footer.version_minor))
2369 o.write('Image size: {} bytes\n'.format(image_size))
2370 o.write('Original image size: {} bytes\n'.format(
2371 footer.original_image_size))
2372 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2373 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2374 o.write('--\n')
2375
2376 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2377
David Zeuthene3cadca2017-02-22 21:25:46 -05002378 o.write('Minimum libavb version: {}.{}{}\n'.format(
2379 header.required_libavb_version_major,
2380 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002381 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002382 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2383 o.write('Authentication Block: {} bytes\n'.format(
2384 header.authentication_data_block_size))
2385 o.write('Auxiliary Block: {} bytes\n'.format(
2386 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002387 if key_blob:
2388 hexdig = hashlib.sha1(key_blob).hexdigest()
2389 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002390 o.write('Algorithm: {}\n'.format(alg_name))
2391 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002392 o.write('Flags: {}\n'.format(header.flags))
Jan Monschb1d920f2020-04-09 12:59:28 +02002393 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002394
2395 # Print descriptors.
2396 num_printed = 0
2397 o.write('Descriptors:\n')
2398 for desc in descriptors:
2399 desc.print_desc(o)
2400 num_printed += 1
2401 if num_printed == 0:
2402 o.write(' (none)\n')
2403
Jan Monscheeb28b62019-12-05 16:17:09 +01002404 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2405 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002406 """Implements the 'verify_image' command.
2407
2408 Arguments:
2409 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002410 key_path: None or check that embedded public key matches key at given
2411 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002412 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002413 follow_chain_partitions:
2414 If True, will follows chain partitions even when not specified with
2415 the --expected_chain_partition option
2416 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2417 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002418
2419 Raises:
2420 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002421 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002422 expected_chain_partitions_map = {}
2423 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002424 for cp in expected_chain_partitions:
2425 cp_tokens = cp.split(':')
2426 if len(cp_tokens) != 3:
2427 raise AvbError('Malformed chained partition "{}".'.format(cp))
2428 partition_name = cp_tokens[0]
2429 rollback_index_location = int(cp_tokens[1])
2430 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002431 with open(file_path, 'rb') as f:
2432 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002433 expected_chain_partitions_map[partition_name] = (
2434 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002435
2436 image_dir = os.path.dirname(image_filename)
2437 image_ext = os.path.splitext(image_filename)[1]
2438
2439 key_blob = None
2440 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002441 print('Verifying image {} using key at {}'.format(image_filename,
2442 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002443 key_blob = encode_rsa_key(key_path)
2444 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002445 print('Verifying image {} using embedded public key'.format(
2446 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002447
David Zeuthenb623d8b2017-04-04 16:05:53 -04002448 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002449 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002450 offset = 0
2451 if footer:
2452 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002453
David Zeuthenb623d8b2017-04-04 16:05:53 -04002454 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002455 vbmeta_blob = image.read(header.SIZE
2456 + header.authentication_data_block_size
2457 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002458
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002459 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002460 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002461 raise AvbError('Signature check failed for {} vbmeta struct {}'
2462 .format(alg_name, image_filename))
2463
2464 if key_blob:
2465 # The embedded public key is in the auxiliary block at an offset.
2466 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002467 key_offset += header.authentication_data_block_size
2468 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002469 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2470 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002471 if key_blob != key_blob_in_vbmeta:
2472 raise AvbError('Embedded public key does not match given key.')
2473
2474 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002475 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2476 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002477 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002478 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2479 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002480
2481 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002482 if (isinstance(desc, AvbChainPartitionDescriptor)
2483 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002484 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002485 # In this case we're processing a chain descriptor but don't have a
2486 # --expect_chain_partition ... however --follow_chain_partitions was
2487 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002488 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2489 'and KEY (which has sha1 {}) not specified'
2490 .format(desc.partition_name, desc.rollback_index_location,
2491 hashlib.sha1(desc.public_key).hexdigest()))
2492 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002493 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002494 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002495 # Honor --follow_chain_partitions - add '--' to make the output more
2496 # readable.
2497 if (isinstance(desc, AvbChainPartitionDescriptor)
2498 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002499 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002500 chained_image_filename = os.path.join(image_dir,
2501 desc.partition_name + image_ext)
2502 self.verify_image(chained_image_filename, key_path, None, False,
2503 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002504
David Zeuthenb8643c02018-05-17 17:21:18 -04002505 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2506 """Implements the 'calculate_vbmeta_digest' command.
2507
2508 Arguments:
2509 image_filename: Image file to get information from (file object).
2510 hash_algorithm: Hash algorithm used.
2511 output: Output file to write human-readable information to (file object).
2512 """
2513
2514 image_dir = os.path.dirname(image_filename)
2515 image_ext = os.path.splitext(image_filename)[1]
2516
2517 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002518 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002519 offset = 0
2520 if footer:
2521 offset = footer.vbmeta_offset
2522 size = (header.SIZE + header.authentication_data_block_size +
2523 header.auxiliary_data_block_size)
2524 image.seek(offset)
2525 vbmeta_blob = image.read(size)
2526
Jan Monsch6f27bb12020-04-07 07:33:26 +02002527 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002528 hasher.update(vbmeta_blob)
2529
2530 for desc in descriptors:
2531 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002532 ch_image_filename = os.path.join(image_dir,
2533 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002534 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002535 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002536 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002537 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2538 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002539 if ch_footer:
2540 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002541 ch_image.seek(ch_offset)
2542 ch_vbmeta_blob = ch_image.read(ch_size)
2543 hasher.update(ch_vbmeta_blob)
2544
2545 digest = hasher.digest()
Jan Monschb1d920f2020-04-09 12:59:28 +02002546 output.write('{}\n'.format(binascii.hexlify(digest).decode('ascii')))
David Zeuthenb8643c02018-05-17 17:21:18 -04002547
David Zeuthenf7d2e752018-09-20 13:30:41 -04002548 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2549 """Implements the 'calculate_kernel_cmdline' command.
2550
2551 Arguments:
2552 image_filename: Image file to get information from (file object).
2553 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2554 output: Output file to write human-readable information to (file object).
2555 """
2556
2557 image = ImageHandler(image_filename)
2558 _, _, descriptors, _ = self._parse_image(image)
2559
2560 image_dir = os.path.dirname(image_filename)
2561 image_ext = os.path.splitext(image_filename)[1]
2562
2563 cmdline_descriptors = []
2564 for desc in descriptors:
2565 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002566 ch_image_filename = os.path.join(image_dir,
2567 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002568 ch_image = ImageHandler(ch_image_filename)
2569 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2570 for ch_desc in ch_descriptors:
2571 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2572 cmdline_descriptors.append(ch_desc)
2573 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2574 cmdline_descriptors.append(desc)
2575
2576 kernel_cmdline_snippets = []
2577 for desc in cmdline_descriptors:
2578 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002579 if ((desc.flags &
2580 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2581 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002582 if hashtree_disabled:
2583 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002584 if (desc.flags &
2585 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002586 if not hashtree_disabled:
2587 use_cmdline = False
2588 if use_cmdline:
2589 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2590 output.write(' '.join(kernel_cmdline_snippets))
2591
David Zeuthen21e95262016-07-27 17:58:40 -04002592 def _parse_image(self, image):
2593 """Gets information about an image.
2594
2595 The image can either be a vbmeta or an image with a footer.
2596
2597 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002598 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002599
2600 Returns:
2601 A tuple where the first argument is a AvbFooter (None if there
2602 is no footer on the image), the second argument is a
2603 AvbVBMetaHeader, the third argument is a list of
2604 AvbDescriptor-derived instances, and the fourth argument is the
2605 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002606
2607 Raises:
2608 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002609 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002610 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002611 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002612 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002613 try:
2614 footer = AvbFooter(image.read(AvbFooter.SIZE))
2615 except (LookupError, struct.error):
2616 # Nope, just seek back to the start.
2617 image.seek(0)
2618
2619 vbmeta_offset = 0
2620 if footer:
2621 vbmeta_offset = footer.vbmeta_offset
2622
2623 image.seek(vbmeta_offset)
2624 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2625
2626 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2627 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2628 desc_start_offset = aux_block_offset + h.descriptors_offset
2629 image.seek(desc_start_offset)
2630 descriptors = parse_descriptors(image.read(h.descriptors_size))
2631
David Zeuthen09692692016-09-30 16:16:40 -04002632 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002633
David Zeuthenb1b994d2017-03-06 18:01:31 -05002634 def _load_vbmeta_blob(self, image):
2635 """Gets the vbmeta struct and associated sections.
2636
2637 The image can either be a vbmeta.img or an image with a footer.
2638
2639 Arguments:
2640 image: An ImageHandler (vbmeta or footer).
2641
2642 Returns:
2643 A blob with the vbmeta struct and other sections.
2644 """
2645 assert isinstance(image, ImageHandler)
2646 footer = None
2647 image.seek(image.image_size - AvbFooter.SIZE)
2648 try:
2649 footer = AvbFooter(image.read(AvbFooter.SIZE))
2650 except (LookupError, struct.error):
2651 # Nope, just seek back to the start.
2652 image.seek(0)
2653
2654 vbmeta_offset = 0
2655 if footer:
2656 vbmeta_offset = footer.vbmeta_offset
2657
2658 image.seek(vbmeta_offset)
2659 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2660
2661 image.seek(vbmeta_offset)
2662 data_size = AvbVBMetaHeader.SIZE
2663 data_size += h.authentication_data_block_size
2664 data_size += h.auxiliary_data_block_size
2665 return image.read(data_size)
2666
David Zeuthen73f2afa2017-05-17 16:54:11 -04002667 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002668 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002669
2670 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002671 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002672
2673 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002674 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2675 instructions. There is one for when hashtree is not disabled and one for
2676 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002677
David Zeuthen21e95262016-07-27 17:58:40 -04002678 """
David Zeuthen21e95262016-07-27 17:58:40 -04002679 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002680 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002681 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002682 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2683 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2684 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2685 c += ' {}'.format(ht.data_block_size) # data_block
2686 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002687 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2688 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002689 c += ' {}'.format(ht.hash_algorithm) # hash_alg
Jan Monschb1d920f2020-04-09 12:59:28 +02002690 c += ' {}'.format(
2691 binascii.hexlify(ht.root_digest).decode('ascii')) # root_digest
2692 c += ' {}'.format(binascii.hexlify(ht.salt).decode('ascii')) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002693 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002694 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002695 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002696 c += ' ignore_zero_blocks'
2697 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2698 c += ' fec_roots {}'.format(ht.fec_num_roots)
2699 # Note that fec_blocks is the size that FEC covers, *not* the
2700 # size of the FEC data. Since we use FEC for everything up until
2701 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002702 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2703 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002704 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002705 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002706 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002707 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002708 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002709
David Zeuthenfd41eb92016-11-17 12:24:47 -05002710 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002711 desc = AvbKernelCmdlineDescriptor()
2712 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002713 desc.flags = (
2714 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2715
2716 # The descriptor for when hashtree verification is disabled is a lot
2717 # simpler - we just set the root to the partition.
2718 desc_no_ht = AvbKernelCmdlineDescriptor()
2719 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2720 desc_no_ht.flags = (
2721 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2722
2723 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002724
David Zeuthen73f2afa2017-05-17 16:54:11 -04002725 def _get_cmdline_descriptors_for_dm_verity(self, image):
2726 """Generate kernel cmdline descriptors for dm-verity.
2727
2728 Arguments:
2729 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2730
2731 Returns:
2732 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2733 instructions. There is one for when hashtree is not disabled and one for
2734 when it is.
2735
2736 Raises:
2737 AvbError: If |image| doesn't have a hashtree descriptor.
2738
2739 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002740 (_, _, descriptors, _) = self._parse_image(image)
2741
2742 ht = None
2743 for desc in descriptors:
2744 if isinstance(desc, AvbHashtreeDescriptor):
2745 ht = desc
2746 break
2747
2748 if not ht:
2749 raise AvbError('No hashtree descriptor in given image')
2750
2751 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2752
David Zeuthen21e95262016-07-27 17:58:40 -04002753 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002754 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002755 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002756 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002757 include_descriptors_from_image,
2758 signing_helper,
2759 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002760 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002761 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002762 print_required_libavb_version,
2763 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002764 """Implements the 'make_vbmeta_image' command.
2765
2766 Arguments:
2767 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002768 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002769 algorithm_name: Name of algorithm to use.
2770 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002771 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002772 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002773 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002774 props: Properties to insert (list of strings of the form 'key:value').
2775 props_from_file: Properties to insert (list of strings 'key:<path>').
2776 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002777 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002778 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002779 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002780 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002781 release_string: None or avbtool release string to use instead of default.
2782 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002783 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002784 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002785
2786 Raises:
2787 AvbError: If a chained partition is malformed.
2788 """
David Zeuthen1097a782017-05-31 15:53:17 -04002789 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002790 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002791 if include_descriptors_from_image:
2792 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2793 # version of all included descriptors.
2794 tmp_header = AvbVBMetaHeader()
2795 for image in include_descriptors_from_image:
2796 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2797 tmp_header.bump_required_libavb_version_minor(
2798 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002799 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002800 else:
2801 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002802 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002803 return
2804
2805 if not output:
2806 raise AvbError('No output file given')
2807
David Zeuthen21e95262016-07-27 17:58:40 -04002808 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002809 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002810 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002811 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002812 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002813 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002814 include_descriptors_from_image, signing_helper,
2815 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002816 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002817
2818 # Write entire vbmeta blob (header, authentication, auxiliary).
2819 output.seek(0)
2820 output.write(vbmeta_blob)
2821
David Zeuthen97cb5802017-06-01 16:14:05 -04002822 if padding_size > 0:
2823 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2824 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002825 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002826
David Zeuthen18666ab2016-11-15 11:18:05 -05002827 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2828 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002829 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002830 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002831 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002832 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002833 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002834 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002835 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002836 release_string, append_to_release_string,
2837 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002838 """Generates a VBMeta blob.
2839
2840 This blob contains the header (struct AvbVBMetaHeader), the
2841 authentication data block (which contains the hash and signature
2842 for the header and auxiliary block), and the auxiliary block
2843 (which contains descriptors, the public key used, and other data).
2844
2845 The |key| parameter can |None| only if the |algorithm_name| is
2846 'NONE'.
2847
2848 Arguments:
2849 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2850 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002851 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002852 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002853 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002854 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002855 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002856 props: Properties to insert (List of strings of the form 'key:value').
2857 props_from_file: Properties to insert (List of strings 'key:<path>').
2858 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002859 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002860 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002861 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2862 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002863 include_descriptors_from_image: List of file objects for which
2864 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002865 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002866 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002867 release_string: None or avbtool release string.
2868 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002869 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002870
2871 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02002872 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002873
2874 Raises:
2875 Exception: If the |algorithm_name| is not found, if no key has
2876 been given and the given algorithm requires one, or the key is
2877 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04002878 """
2879 try:
2880 alg = ALGORITHMS[algorithm_name]
2881 except KeyError:
2882 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2883
David Zeuthena5fd3a42017-02-27 16:38:54 -05002884 if not descriptors:
2885 descriptors = []
2886
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002887 h = AvbVBMetaHeader()
2888 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2889
David Zeuthena5fd3a42017-02-27 16:38:54 -05002890 # Insert chained partition descriptors, if any
2891 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002892 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002893 for cp in chain_partitions:
2894 cp_tokens = cp.split(':')
2895 if len(cp_tokens) != 3:
2896 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002897 partition_name = cp_tokens[0]
2898 rollback_index_location = int(cp_tokens[1])
2899 file_path = cp_tokens[2]
2900 # Check that the same rollback location isn't being used by
2901 # multiple chained partitions.
2902 if used_locations.get(rollback_index_location):
2903 raise AvbError('Rollback Index Location {} is already in use.'.format(
2904 rollback_index_location))
2905 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002906 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002907 desc.partition_name = partition_name
2908 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002909 if desc.rollback_index_location < 1:
2910 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02002911 with open(file_path, 'rb') as f:
2912 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002913 descriptors.append(desc)
2914
David Zeuthen21e95262016-07-27 17:58:40 -04002915 # Descriptors.
2916 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002917 for desc in descriptors:
2918 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002919
2920 # Add properties.
2921 if props:
2922 for prop in props:
2923 idx = prop.find(':')
2924 if idx == -1:
2925 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002926 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002927 desc = AvbPropertyDescriptor()
2928 desc.key = prop[0:idx]
2929 desc.value = prop[(idx + 1):]
2930 encoded_descriptors.extend(desc.encode())
2931 if props_from_file:
2932 for prop in props_from_file:
2933 idx = prop.find(':')
2934 if idx == -1:
2935 raise AvbError('Malformed property "{}".'.format(prop))
2936 desc = AvbPropertyDescriptor()
2937 desc.key = prop[0:idx]
2938 desc.value = prop[(idx + 1):]
2939 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02002940 with open(file_path, 'rb') as f:
2941 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04002942 encoded_descriptors.extend(desc.encode())
2943
David Zeuthen73f2afa2017-05-17 16:54:11 -04002944 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002945 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002946 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002947 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002948 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2949 encoded_descriptors.extend(cmdline_desc[0].encode())
2950 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002951
David Zeuthen73f2afa2017-05-17 16:54:11 -04002952 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2953 if ht_desc_to_setup:
2954 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2955 ht_desc_to_setup)
2956 encoded_descriptors.extend(cmdline_desc[0].encode())
2957 encoded_descriptors.extend(cmdline_desc[1].encode())
2958
David Zeuthen21e95262016-07-27 17:58:40 -04002959 # Add kernel command-lines.
2960 if kernel_cmdlines:
2961 for i in kernel_cmdlines:
2962 desc = AvbKernelCmdlineDescriptor()
2963 desc.kernel_cmdline = i
2964 encoded_descriptors.extend(desc.encode())
2965
2966 # Add descriptors from other images.
2967 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002968 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002969 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002970 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002971 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2972 image_handler)
2973 # Bump the required libavb version to support all included descriptors.
2974 h.bump_required_libavb_version_minor(
2975 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002976 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002977 # The --include_descriptors_from_image option is used in some setups
2978 # with images A and B where both A and B contain a descriptor
2979 # for a partition with the same name. Since it's not meaningful
2980 # to include both descriptors, only include the last seen descriptor.
2981 # See bug 76386656 for details.
2982 if hasattr(desc, 'partition_name'):
2983 key = type(desc).__name__ + '_' + desc.partition_name
2984 descriptors_dict[key] = desc.encode()
2985 else:
2986 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002987 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002988 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002989
David Zeuthen18666ab2016-11-15 11:18:05 -05002990 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02002991 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05002992 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02002993 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05002994 pkmd_blob = f.read()
2995
David Zeuthen21e95262016-07-27 17:58:40 -04002996 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02002997 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04002998 if alg.public_key_num_bytes > 0:
2999 if not key_path:
3000 raise AvbError('Key is required for algorithm {}'.format(
3001 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003002 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003003 if len(encoded_key) != alg.public_key_num_bytes:
3004 raise AvbError('Key is wrong size for algorithm {}'.format(
3005 algorithm_name))
3006
David Zeuthene3cadca2017-02-22 21:25:46 -05003007 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003008 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003009 h.release_string = release_string
3010
3011 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003012 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003013 h.release_string += ' ' + append_to_release_string
3014
David Zeuthen18666ab2016-11-15 11:18:05 -05003015 # For the Auxiliary data block, descriptors are stored at offset 0,
3016 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003017 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003018 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003019 h.descriptors_offset = 0
3020 h.descriptors_size = len(encoded_descriptors)
3021 h.public_key_offset = h.descriptors_size
3022 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003023 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3024 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003025
3026 # For the Authentication data block, the hash is first and then
3027 # the signature.
3028 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003029 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003030 h.algorithm_type = alg.algorithm_type
3031 h.hash_offset = 0
3032 h.hash_size = alg.hash_num_bytes
3033 # Signature offset and size - it's stored right after the hash
3034 # (in Authentication data block).
3035 h.signature_offset = alg.hash_num_bytes
3036 h.signature_size = alg.signature_num_bytes
3037
3038 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003039 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003040
3041 # Generate Header data block.
3042 header_data_blob = h.encode()
3043
3044 # Generate Auxiliary data block.
3045 aux_data_blob = bytearray()
3046 aux_data_blob.extend(encoded_descriptors)
3047 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003048 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003049 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003050 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003051
3052 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003053 binary_hash = b''
3054 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003055 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003056 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003057 ha.update(header_data_blob)
3058 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003059 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003060
3061 # Calculate the signature.
Jan Monschb1d920f2020-04-09 12:59:28 +02003062 padding_and_hash = bytearray(alg.padding) + binary_hash
3063 binary_signature = raw_sign(signing_helper, signing_helper_with_files,
3064 algorithm_name, alg.signature_num_bytes,
3065 key_path, padding_and_hash)
David Zeuthen21e95262016-07-27 17:58:40 -04003066
3067 # Generate Authentication data block.
3068 auth_data_blob = bytearray()
3069 auth_data_blob.extend(binary_hash)
3070 auth_data_blob.extend(binary_signature)
3071 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003072 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003073
Jan Monschb1d920f2020-04-09 12:59:28 +02003074 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003075
3076 def extract_public_key(self, key_path, output):
3077 """Implements the 'extract_public_key' command.
3078
3079 Arguments:
3080 key_path: The path to a RSA private key file.
3081 output: The file to write to.
3082 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003083 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003084
David Zeuthenb1b994d2017-03-06 18:01:31 -05003085 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3086 partition_size):
3087 """Implementation of the append_vbmeta_image command.
3088
3089 Arguments:
3090 image_filename: File to add the footer to.
3091 vbmeta_image_filename: File to get vbmeta struct from.
3092 partition_size: Size of partition.
3093
3094 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003095 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003096 """
3097 image = ImageHandler(image_filename)
3098
3099 if partition_size % image.block_size != 0:
3100 raise AvbError('Partition size of {} is not a multiple of the image '
3101 'block size {}.'.format(partition_size,
3102 image.block_size))
3103
3104 # If there's already a footer, truncate the image to its original
3105 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003106 if image.image_size >= AvbFooter.SIZE:
3107 image.seek(image.image_size - AvbFooter.SIZE)
3108 try:
3109 footer = AvbFooter(image.read(AvbFooter.SIZE))
3110 # Existing footer found. Just truncate.
3111 original_image_size = footer.original_image_size
3112 image.truncate(footer.original_image_size)
3113 except (LookupError, struct.error):
3114 original_image_size = image.image_size
3115 else:
3116 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003117 original_image_size = image.image_size
3118
3119 # If anything goes wrong from here-on, restore the image back to
3120 # its original size.
3121 try:
3122 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3123 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3124
3125 # If the image isn't sparse, its size might not be a multiple of
3126 # the block size. This will screw up padding later so just grow it.
3127 if image.image_size % image.block_size != 0:
3128 assert not image.is_sparse
3129 padding_needed = image.block_size - (image.image_size%image.block_size)
3130 image.truncate(image.image_size + padding_needed)
3131
3132 # The append_raw() method requires content with size being a
3133 # multiple of |block_size| so add padding as needed. Also record
3134 # where this is written to since we'll need to put that in the
3135 # footer.
3136 vbmeta_offset = image.image_size
3137 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3138 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003139 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003140
3141 # Append vbmeta blob and footer
3142 image.append_raw(vbmeta_blob_with_padding)
3143 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3144
3145 # Now insert a DONT_CARE chunk with enough bytes such that the
3146 # final Footer block is at the end of partition_size..
3147 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003148 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003149
3150 # Generate the Footer that tells where the VBMeta footer
3151 # is. Also put enough padding in the front of the footer since
3152 # we'll write out an entire block.
3153 footer = AvbFooter()
3154 footer.original_image_size = original_image_size
3155 footer.vbmeta_offset = vbmeta_offset
3156 footer.vbmeta_size = len(vbmeta_blob)
3157 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003158 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003159 footer_blob)
3160 image.append_raw(footer_blob_with_padding)
3161
Jan Monschb1d920f2020-04-09 12:59:28 +02003162 except Exception as e:
3163 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003164 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003165 raise AvbError('Appending VBMeta image failed: {}.'.format(e))
David Zeuthenb1b994d2017-03-06 18:01:31 -05003166
David Zeuthena4fee8b2016-08-22 15:20:43 -04003167 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003168 hash_algorithm, salt, chain_partitions, algorithm_name,
3169 key_path,
3170 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003171 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003172 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003173 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003174 signing_helper, signing_helper_with_files,
3175 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003176 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003177 print_required_libavb_version, use_persistent_digest,
3178 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003179 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003180
3181 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003182 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003183 partition_size: Size of partition.
3184 partition_name: Name of partition (without A/B suffix).
3185 hash_algorithm: Hash algorithm to use.
3186 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003187 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003188 algorithm_name: Name of algorithm to use.
3189 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003190 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003191 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003192 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003193 props: Properties to insert (List of strings of the form 'key:value').
3194 props_from_file: Properties to insert (List of strings 'key:<path>').
3195 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003196 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003197 dm-verity kernel cmdline from.
3198 include_descriptors_from_image: List of file objects for which
3199 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003200 calc_max_image_size: Don't store the footer - instead calculate the
3201 maximum image size leaving enough room for metadata with the
3202 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003203 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003204 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003205 release_string: None or avbtool release string.
3206 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003207 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3208 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003209 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003210 use_persistent_digest: Use a persistent digest on device.
3211 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003212
3213 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003214 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003215 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003216 required_libavb_version_minor = 0
3217 if use_persistent_digest or do_not_use_ab:
3218 required_libavb_version_minor = 1
3219
David Zeuthen1097a782017-05-31 15:53:17 -04003220 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003221 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003222 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003223 return
3224
David Zeuthenbf562452017-05-17 18:04:43 -04003225 # First, calculate the maximum image size such that an image
3226 # this size + metadata (footer + vbmeta struct) fits in
3227 # |partition_size|.
3228 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003229 if partition_size < max_metadata_size:
3230 raise AvbError('Parition size of {} is too small. '
3231 'Needs to be at least {}'.format(
3232 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003233 max_image_size = partition_size - max_metadata_size
3234
3235 # If we're asked to only calculate the maximum image size, we're done.
3236 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003237 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003238 return
3239
David Zeuthena4fee8b2016-08-22 15:20:43 -04003240 image = ImageHandler(image_filename)
3241
3242 if partition_size % image.block_size != 0:
3243 raise AvbError('Partition size of {} is not a multiple of the image '
3244 'block size {}.'.format(partition_size,
3245 image.block_size))
3246
David Zeuthen21e95262016-07-27 17:58:40 -04003247 # If there's already a footer, truncate the image to its original
3248 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3249 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003250 if image.image_size >= AvbFooter.SIZE:
3251 image.seek(image.image_size - AvbFooter.SIZE)
3252 try:
3253 footer = AvbFooter(image.read(AvbFooter.SIZE))
3254 # Existing footer found. Just truncate.
3255 original_image_size = footer.original_image_size
3256 image.truncate(footer.original_image_size)
3257 except (LookupError, struct.error):
3258 original_image_size = image.image_size
3259 else:
3260 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003261 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003262
3263 # If anything goes wrong from here-on, restore the image back to
3264 # its original size.
3265 try:
David Zeuthen09692692016-09-30 16:16:40 -04003266 # If image size exceeds the maximum image size, fail.
3267 if image.image_size > max_image_size:
3268 raise AvbError('Image size of {} exceeds maximum image '
3269 'size of {} in order to fit in a partition '
3270 'size of {}.'.format(image.image_size, max_image_size,
3271 partition_size))
3272
Jan Monsch6f27bb12020-04-07 07:33:26 +02003273 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003274 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003275 salt = binascii.unhexlify(salt)
3276 elif salt is None and not use_persistent_digest:
3277 # If salt is not explicitly specified, choose a hash that's the same
3278 # size as the hash size. Don't populate a random salt if this
3279 # descriptor is being created to use a persistent digest on device.
3280 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003281 with open('/dev/urandom', 'rb') as f:
3282 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003283 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003284 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003285
Jan Monsch6f27bb12020-04-07 07:33:26 +02003286 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003287 # TODO(zeuthen): might want to read this in chunks to avoid
3288 # memory pressure, then again, this is only supposed to be used
3289 # on kernel/initramfs partitions. Possible optimization.
3290 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003291 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003292 digest = hasher.digest()
3293
3294 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003295 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003296 h_desc.hash_algorithm = hash_algorithm
3297 h_desc.partition_name = partition_name
3298 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003299 h_desc.flags = 0
3300 if do_not_use_ab:
3301 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3302 if not use_persistent_digest:
3303 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003304
3305 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003306 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003307 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003308 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003309 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003310 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003311 include_descriptors_from_image, signing_helper,
3312 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003313 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003314
David Zeuthend247fcb2017-02-16 12:09:27 -05003315 # Write vbmeta blob, if requested.
3316 if output_vbmeta_image:
3317 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003318
David Zeuthend247fcb2017-02-16 12:09:27 -05003319 # Append vbmeta blob and footer, unless requested not to.
3320 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003321 # If the image isn't sparse, its size might not be a multiple of
3322 # the block size. This will screw up padding later so just grow it.
3323 if image.image_size % image.block_size != 0:
3324 assert not image.is_sparse
3325 padding_needed = image.block_size - (
3326 image.image_size % image.block_size)
3327 image.truncate(image.image_size + padding_needed)
3328
3329 # The append_raw() method requires content with size being a
3330 # multiple of |block_size| so add padding as needed. Also record
3331 # where this is written to since we'll need to put that in the
3332 # footer.
3333 vbmeta_offset = image.image_size
3334 padding_needed = (
3335 round_to_multiple(len(vbmeta_blob), image.block_size) -
3336 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003337 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003338
David Zeuthend247fcb2017-02-16 12:09:27 -05003339 image.append_raw(vbmeta_blob_with_padding)
3340 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3341
3342 # Now insert a DONT_CARE chunk with enough bytes such that the
3343 # final Footer block is at the end of partition_size..
3344 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003345 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003346
3347 # Generate the Footer that tells where the VBMeta footer
3348 # is. Also put enough padding in the front of the footer since
3349 # we'll write out an entire block.
3350 footer = AvbFooter()
3351 footer.original_image_size = original_image_size
3352 footer.vbmeta_offset = vbmeta_offset
3353 footer.vbmeta_size = len(vbmeta_blob)
3354 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003355 footer_blob_with_padding = (
3356 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003357 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003358 except Exception as e:
3359 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003360 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003361 raise AvbError('Adding hash_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003362
David Zeuthena4fee8b2016-08-22 15:20:43 -04003363 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003364 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003365 block_size, salt, chain_partitions, algorithm_name,
3366 key_path,
3367 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003368 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003369 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003370 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003371 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003372 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003373 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003374 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003375 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003376 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003377 use_persistent_root_digest, do_not_use_ab,
3378 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003379 """Implements the 'add_hashtree_footer' command.
3380
3381 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3382 more information about dm-verity and these hashes.
3383
3384 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003385 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003386 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003387 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003388 generate_fec: If True, generate FEC codes.
3389 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003390 hash_algorithm: Hash algorithm to use.
3391 block_size: Block size to use.
3392 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003393 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003394 algorithm_name: Name of algorithm to use.
3395 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003396 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003397 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003398 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003399 props: Properties to insert (List of strings of the form 'key:value').
3400 props_from_file: Properties to insert (List of strings 'key:<path>').
3401 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003402 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003403 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003404 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3405 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003406 include_descriptors_from_image: List of file objects for which
3407 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003408 calc_max_image_size: Don't store the hashtree or footer - instead
3409 calculate the maximum image size leaving enough room for hashtree
3410 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003411 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003412 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003413 release_string: None or avbtool release string.
3414 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003415 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3416 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003417 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003418 use_persistent_root_digest: Use a persistent root digest on device.
3419 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003420 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003421
3422 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003423 AvbError: If an argument is incorrect or adding the hashtree footer
3424 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003425 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003426 required_libavb_version_minor = 0
3427 if use_persistent_root_digest or do_not_use_ab:
3428 required_libavb_version_minor = 1
3429
David Zeuthen1097a782017-05-31 15:53:17 -04003430 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003431 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003432 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003433 return
3434
Jan Monsch6f27bb12020-04-07 07:33:26 +02003435 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen09692692016-09-30 16:16:40 -04003436 digest_padding = round_to_pow2(digest_size) - digest_size
3437
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003438 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3439 # size such that an image this size + the hashtree + metadata (footer +
3440 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3441 # for metadata.
3442 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003443 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003444 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003445 if not no_hashtree:
3446 (_, max_tree_size) = calc_hash_level_offsets(
3447 partition_size, block_size, digest_size + digest_padding)
3448 if generate_fec:
3449 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003450 max_metadata_size = (max_fec_size + max_tree_size +
3451 self.MAX_VBMETA_SIZE +
3452 self.MAX_FOOTER_SIZE)
3453 max_image_size = partition_size - max_metadata_size
3454 else:
3455 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003456
3457 # If we're asked to only calculate the maximum image size, we're done.
3458 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003459 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003460 return
3461
David Zeuthena4fee8b2016-08-22 15:20:43 -04003462 image = ImageHandler(image_filename)
3463
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003464 if partition_size > 0:
3465 if partition_size % image.block_size != 0:
3466 raise AvbError('Partition size of {} is not a multiple of the image '
3467 'block size {}.'.format(partition_size,
3468 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003469 elif image.image_size % image.block_size != 0:
3470 raise AvbError('File size of {} is not a multiple of the image '
3471 'block size {}.'.format(image.image_size,
3472 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003473
David Zeuthen21e95262016-07-27 17:58:40 -04003474 # If there's already a footer, truncate the image to its original
3475 # size. This way 'avbtool add_hashtree_footer' is idempotent
3476 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003477 if image.image_size >= AvbFooter.SIZE:
3478 image.seek(image.image_size - AvbFooter.SIZE)
3479 try:
3480 footer = AvbFooter(image.read(AvbFooter.SIZE))
3481 # Existing footer found. Just truncate.
3482 original_image_size = footer.original_image_size
3483 image.truncate(footer.original_image_size)
3484 except (LookupError, struct.error):
3485 original_image_size = image.image_size
3486 else:
3487 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003488 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003489
3490 # If anything goes wrong from here-on, restore the image back to
3491 # its original size.
3492 try:
3493 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003494 rounded_image_size = round_to_multiple(image.image_size, block_size)
3495 if rounded_image_size > image.image_size:
3496 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003497
David Zeuthen09692692016-09-30 16:16:40 -04003498 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003499 if partition_size > 0:
3500 if image.image_size > max_image_size:
3501 raise AvbError('Image size of {} exceeds maximum image '
3502 'size of {} in order to fit in a partition '
3503 'size of {}.'.format(image.image_size, max_image_size,
3504 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003505
3506 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003507 salt = binascii.unhexlify(salt)
3508 elif salt is None and not use_persistent_root_digest:
3509 # If salt is not explicitly specified, choose a hash that's the same
3510 # size as the hash size. Don't populate a random salt if this
3511 # descriptor is being created to use a persistent digest on device.
3512 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003513 with open('/dev/urandom') as f:
3514 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003515 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003516 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003517
David Zeuthena4fee8b2016-08-22 15:20:43 -04003518 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003519 # offsets in advance.
3520 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003521 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003522
David Zeuthena4fee8b2016-08-22 15:20:43 -04003523 # If the image isn't sparse, its size might not be a multiple of
3524 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003525 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003526 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003527 padding_needed = image.block_size - (image.image_size%image.block_size)
3528 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003529
David Zeuthena4fee8b2016-08-22 15:20:43 -04003530 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003531 tree_offset = image.image_size
3532 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003533 block_size,
3534 hash_algorithm, salt,
3535 digest_padding,
3536 hash_level_offsets,
3537 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003538
3539 # Generate HashtreeDescriptor with details about the tree we
3540 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003541 if no_hashtree:
3542 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003543 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003544 ht_desc = AvbHashtreeDescriptor()
3545 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003546 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003547 ht_desc.tree_offset = tree_offset
3548 ht_desc.tree_size = tree_size
3549 ht_desc.data_block_size = block_size
3550 ht_desc.hash_block_size = block_size
3551 ht_desc.hash_algorithm = hash_algorithm
3552 ht_desc.partition_name = partition_name
3553 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003554 if do_not_use_ab:
3555 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3556 if not use_persistent_root_digest:
3557 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003558
David Zeuthen09692692016-09-30 16:16:40 -04003559 # Write the hash tree
3560 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3561 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003562 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003563 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003564 len_hashtree_and_fec = len(hash_tree_with_padding)
3565
3566 # Generate FEC codes, if requested.
3567 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003568 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003569 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003570 else:
3571 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003572 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3573 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003574 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003575 fec_offset = image.image_size
3576 image.append_raw(fec_data_with_padding)
3577 len_hashtree_and_fec += len(fec_data_with_padding)
3578 # Update the hashtree descriptor.
3579 ht_desc.fec_num_roots = fec_num_roots
3580 ht_desc.fec_offset = fec_offset
3581 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003582
David Zeuthen73f2afa2017-05-17 16:54:11 -04003583 ht_desc_to_setup = None
3584 if setup_as_rootfs_from_kernel:
3585 ht_desc_to_setup = ht_desc
3586
David Zeuthena4fee8b2016-08-22 15:20:43 -04003587 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003588 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003589 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003590 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003591 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003592 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003593 include_descriptors_from_image, signing_helper,
3594 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003595 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003596 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3597 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003598 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003599
David Zeuthend247fcb2017-02-16 12:09:27 -05003600 # Write vbmeta blob, if requested.
3601 if output_vbmeta_image:
3602 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003603
David Zeuthend247fcb2017-02-16 12:09:27 -05003604 # Append vbmeta blob and footer, unless requested not to.
3605 if not do_not_append_vbmeta_image:
3606 image.append_raw(vbmeta_blob_with_padding)
3607
3608 # Now insert a DONT_CARE chunk with enough bytes such that the
3609 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003610 if partition_size > 0:
3611 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003612 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003613
3614 # Generate the Footer that tells where the VBMeta footer
3615 # is. Also put enough padding in the front of the footer since
3616 # we'll write out an entire block.
3617 footer = AvbFooter()
3618 footer.original_image_size = original_image_size
3619 footer.vbmeta_offset = vbmeta_offset
3620 footer.vbmeta_size = len(vbmeta_blob)
3621 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003622 footer_blob_with_padding = (
3623 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003624 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003625
Jan Monschb1d920f2020-04-09 12:59:28 +02003626 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003627 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003628 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003629 raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003630
David Zeuthenc68f0822017-03-31 17:22:35 -04003631 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003632 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003633 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003634 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003635 """Implements the 'make_atx_certificate' command.
3636
3637 Android Things certificates are required for Android Things public key
3638 metadata. They chain the vbmeta signing key for a particular product back to
3639 a fused, permanent root key. These certificates are fixed-length and fixed-
3640 format with the explicit goal of not parsing ASN.1 in bootloader code.
3641
3642 Arguments:
3643 output: Certificate will be written to this file on success.
3644 authority_key_path: A PEM file path with the authority private key.
3645 If None, then a certificate will be created without a
3646 signature. The signature can be created out-of-band
3647 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003648 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003649 subject_key_version: A 64-bit version value. If this is None, the number
3650 of seconds since the epoch is used.
3651 subject: A subject identifier. For Product Signing Key certificates this
3652 should be the same Product ID found in the permanent attributes.
3653 is_intermediate_authority: True if the certificate is for an intermediate
3654 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003655 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003656 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003657 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003658 """
3659 signed_data = bytearray()
3660 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003661 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003662 hasher = hashlib.sha256()
3663 hasher.update(subject)
3664 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003665 if not usage:
3666 usage = 'com.google.android.things.vboot'
3667 if is_intermediate_authority:
3668 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003669 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003670 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003671 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003672 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003673 subject_key_version = int(time.time())
3674 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003675 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003676 if authority_key_path:
Darren Krahn43e12d82017-02-24 16:26:31 -08003677 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003678 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003679 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Darren Krahn147b08d2016-12-20 16:38:29 -08003680 hasher.update(signed_data)
Jan Monschb1d920f2020-04-09 12:59:28 +02003681
3682 padding_and_hash = bytearray()
3683 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003684 padding_and_hash.extend(hasher.digest())
Jan Monschb1d920f2020-04-09 12:59:28 +02003685
3686 signature = raw_sign(signing_helper, signing_helper_with_files,
3687 algorithm_name, alg.signature_num_bytes,
3688 authority_key_path, padding_and_hash)
Darren Krahn147b08d2016-12-20 16:38:29 -08003689 output.write(signed_data)
3690 output.write(signature)
3691
David Zeuthenc68f0822017-03-31 17:22:35 -04003692 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003693 product_id):
3694 """Implements the 'make_atx_permanent_attributes' command.
3695
3696 Android Things permanent attributes are designed to be permanent for a
3697 particular product and a hash of these attributes should be fused into
3698 hardware to enforce this.
3699
3700 Arguments:
3701 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003702 root_authority_key_path: Path to a PEM or DER public key for
3703 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003704 product_id: A 16-byte Product ID.
3705
3706 Raises:
3707 AvbError: If an argument is incorrect.
3708 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003709 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003710 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003711 raise AvbError('Invalid Product ID length.')
3712 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003713 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003714 output.write(product_id)
3715
3716 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003717 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003718 """Implements the 'make_atx_metadata' command.
3719
3720 Android Things metadata are included in vbmeta images to facilitate
3721 verification. The output of this command can be used as the
3722 public_key_metadata argument to other commands.
3723
3724 Arguments:
3725 output: Metadata will be written to this file on success.
3726 intermediate_key_certificate: A certificate file as output by
3727 make_atx_certificate with
3728 is_intermediate_authority set to true.
3729 product_key_certificate: A certificate file as output by
3730 make_atx_certificate with
3731 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003732
3733 Raises:
3734 AvbError: If an argument is incorrect.
3735 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003736 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003737 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003738 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003739 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003740 raise AvbError('Invalid product key certificate length.')
3741 output.write(struct.pack('<I', 1)) # Format Version
3742 output.write(intermediate_key_certificate)
3743 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003744
Darren Krahnfccd64e2018-01-16 17:39:35 -08003745 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3746 unlock_key_certificate, challenge_path,
3747 unlock_key_path, signing_helper,
3748 signing_helper_with_files):
3749 """Implements the 'make_atx_unlock_credential' command.
3750
3751 Android Things unlock credentials can be used to authorize the unlock of AVB
3752 on a device. These credentials are presented to an Android Things bootloader
3753 via the fastboot interface in response to a 16-byte challenge. This method
3754 creates all fields of the credential except the challenge signature field
3755 (which is the last field) and can optionally create the challenge signature
3756 field as well if a challenge and the unlock_key_path is provided.
3757
3758 Arguments:
3759 output: The credential will be written to this file on success.
3760 intermediate_key_certificate: A certificate file as output by
3761 make_atx_certificate with
3762 is_intermediate_authority set to true.
3763 unlock_key_certificate: A certificate file as output by
3764 make_atx_certificate with
3765 is_intermediate_authority set to false and the
3766 usage set to
3767 'com.google.android.things.vboot.unlock'.
3768 challenge_path: [optional] A path to the challenge to sign.
3769 unlock_key_path: [optional] A PEM file path with the unlock private key.
3770 signing_helper: Program which signs a hash and returns the signature.
3771 signing_helper_with_files: Same as signing_helper but uses files instead.
3772
3773 Raises:
3774 AvbError: If an argument is incorrect.
3775 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003776 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3777 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003778 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3779 raise AvbError('Invalid intermediate key certificate length.')
3780 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3781 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003782 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003783 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003784 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003785 challenge = f.read()
3786 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3787 raise AvbError('Invalid unlock challenge length.')
3788 output.write(struct.pack('<I', 1)) # Format Version
3789 output.write(intermediate_key_certificate)
3790 output.write(unlock_key_certificate)
3791 if challenge_path and unlock_key_path:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003792 algorithm_name = 'SHA512_RSA4096'
3793 alg = ALGORITHMS[algorithm_name]
3794 hasher = hashlib.sha512()
Darren Krahnfccd64e2018-01-16 17:39:35 -08003795 hasher.update(challenge)
Jan Monschb1d920f2020-04-09 12:59:28 +02003796
3797 padding_and_hash = bytearray()
3798 padding_and_hash.extend(alg.padding)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003799 padding_and_hash.extend(hasher.digest())
Jan Monschb1d920f2020-04-09 12:59:28 +02003800
3801 signature = raw_sign(signing_helper, signing_helper_with_files,
3802 algorithm_name, alg.signature_num_bytes,
3803 unlock_key_path, padding_and_hash)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003804 output.write(signature)
3805
David Zeuthen21e95262016-07-27 17:58:40 -04003806
3807def calc_hash_level_offsets(image_size, block_size, digest_size):
3808 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3809
3810 Arguments:
3811 image_size: The size of the image to calculate a Merkle-tree for.
3812 block_size: The block size, e.g. 4096.
3813 digest_size: The size of each hash, e.g. 32 for SHA-256.
3814
3815 Returns:
3816 A tuple where the first argument is an array of offsets and the
3817 second is size of the tree, in bytes.
3818 """
3819 level_offsets = []
3820 level_sizes = []
3821 tree_size = 0
3822
3823 num_levels = 0
3824 size = image_size
3825 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003826 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003827 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3828
3829 level_sizes.append(level_size)
3830 tree_size += level_size
3831 num_levels += 1
3832
3833 size = level_size
3834
3835 for n in range(0, num_levels):
3836 offset = 0
3837 for m in range(n + 1, num_levels):
3838 offset += level_sizes[m]
3839 level_offsets.append(offset)
3840
David Zeuthena4fee8b2016-08-22 15:20:43 -04003841 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003842
3843
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003844# See system/extras/libfec/include/fec/io.h for these definitions.
3845FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3846FEC_MAGIC = 0xfecfecfe
3847
3848
3849def calc_fec_data_size(image_size, num_roots):
3850 """Calculates how much space FEC data will take.
3851
Jan Monschfe00c0a2019-12-11 11:19:40 +01003852 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003853 image_size: The size of the image.
3854 num_roots: Number of roots.
3855
3856 Returns:
3857 The number of bytes needed for FEC for an image of the given size
3858 and with the requested number of FEC roots.
3859
3860 Raises:
3861 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003862 """
3863 p = subprocess.Popen(
3864 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3865 stdout=subprocess.PIPE,
3866 stderr=subprocess.PIPE)
3867 (pout, perr) = p.communicate()
3868 retcode = p.wait()
3869 if retcode != 0:
3870 raise ValueError('Error invoking fec: {}'.format(perr))
3871 return int(pout)
3872
3873
3874def generate_fec_data(image_filename, num_roots):
3875 """Generate FEC codes for an image.
3876
Jan Monschfe00c0a2019-12-11 11:19:40 +01003877 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003878 image_filename: The filename of the image.
3879 num_roots: Number of roots.
3880
3881 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003882 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003883
3884 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003885 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003886 """
Jan Monschb1d920f2020-04-09 12:59:28 +02003887 with tempfile.NamedTemporaryFile() as fec_tmpfile:
3888 try:
3889 subprocess.check_call(
3890 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3891 fec_tmpfile.name],
3892 stderr=open(os.devnull, 'wb'))
3893 except subprocess.CalledProcessError as e:
3894 raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
3895 fec_data = fec_tmpfile.read()
3896
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003897 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3898 footer_data = fec_data[-footer_size:]
3899 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3900 footer_data)
3901 if magic != FEC_MAGIC:
3902 raise ValueError('Unexpected magic in FEC footer')
3903 return fec_data[0:fec_size]
3904
3905
David Zeuthen21e95262016-07-27 17:58:40 -04003906def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003907 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003908 """Generates a Merkle-tree for a file.
3909
Jan Monschfe00c0a2019-12-11 11:19:40 +01003910 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003911 image: The image, as a file.
3912 image_size: The size of the image.
3913 block_size: The block size, e.g. 4096.
3914 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3915 salt: The salt to use.
3916 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003917 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003918 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003919
3920 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003921 A tuple where the first element is the top-level hash as bytes and the
3922 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003923 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003924 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003925 hash_src_offset = 0
3926 hash_src_size = image_size
3927 level_num = 0
3928 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003929 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003930 remaining = hash_src_size
3931 while remaining > 0:
Jan Monsch6f27bb12020-04-07 07:33:26 +02003932 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003933 # Only read from the file for the first level - for subsequent
3934 # levels, access the array we're building.
3935 if level_num == 0:
3936 image.seek(hash_src_offset + hash_src_size - remaining)
3937 data = image.read(min(remaining, block_size))
3938 else:
3939 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3940 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003941 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003942
3943 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003944 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02003945 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003946 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003947 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02003948 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08003949
Jan Monschb1d920f2020-04-09 12:59:28 +02003950 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003951
3952 padding_needed = (round_to_multiple(
3953 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02003954 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003955
David Zeuthena4fee8b2016-08-22 15:20:43 -04003956 # Copy level-output into resulting tree.
3957 offset = hash_level_offsets[level_num]
3958 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003959
David Zeuthena4fee8b2016-08-22 15:20:43 -04003960 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003961 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003962 level_num += 1
3963
Jan Monsch6f27bb12020-04-07 07:33:26 +02003964 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003965 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02003966 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04003967
3968
3969class AvbTool(object):
3970 """Object for avbtool command-line tool."""
3971
3972 def __init__(self):
3973 """Initializer method."""
3974 self.avb = Avb()
3975
3976 def _add_common_args(self, sub_parser):
3977 """Adds arguments used by several sub-commands.
3978
3979 Arguments:
3980 sub_parser: The parser to add arguments to.
3981 """
3982 sub_parser.add_argument('--algorithm',
3983 help='Algorithm to use (default: NONE)',
3984 metavar='ALGORITHM',
3985 default='NONE')
3986 sub_parser.add_argument('--key',
3987 help='Path to RSA private key file',
3988 metavar='KEY',
3989 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003990 sub_parser.add_argument('--signing_helper',
3991 help='Path to helper used for signing',
3992 metavar='APP',
3993 default=None,
3994 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003995 sub_parser.add_argument('--signing_helper_with_files',
3996 help='Path to helper used for signing using files',
3997 metavar='APP',
3998 default=None,
3999 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004000 sub_parser.add_argument('--public_key_metadata',
4001 help='Path to public key metadata file',
4002 metavar='KEY_METADATA',
4003 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004004 sub_parser.add_argument('--rollback_index',
4005 help='Rollback Index',
4006 type=parse_number,
4007 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004008 # This is used internally for unit tests. Do not include in --help output.
4009 sub_parser.add_argument('--internal_release_string',
4010 help=argparse.SUPPRESS)
4011 sub_parser.add_argument('--append_to_release_string',
4012 help='Text to append to release string',
4013 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004014 sub_parser.add_argument('--prop',
4015 help='Add property',
4016 metavar='KEY:VALUE',
4017 action='append')
4018 sub_parser.add_argument('--prop_from_file',
4019 help='Add property from file',
4020 metavar='KEY:PATH',
4021 action='append')
4022 sub_parser.add_argument('--kernel_cmdline',
4023 help='Add kernel cmdline',
4024 metavar='CMDLINE',
4025 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004026 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4027 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4028 # at some future point.
4029 sub_parser.add_argument('--setup_rootfs_from_kernel',
4030 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004031 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004032 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004033 type=argparse.FileType('rb'))
4034 sub_parser.add_argument('--include_descriptors_from_image',
4035 help='Include descriptors from image',
4036 metavar='IMAGE',
4037 action='append',
4038 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004039 sub_parser.add_argument('--print_required_libavb_version',
4040 help=('Don\'t store the footer - '
4041 'instead calculate the required libavb '
4042 'version for the given options.'),
4043 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004044 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4045 sub_parser.add_argument('--chain_partition',
4046 help='Allow signed integrity-data for partition',
4047 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4048 action='append')
4049 sub_parser.add_argument('--flags',
4050 help='VBMeta flags',
4051 type=parse_number,
4052 default=0)
4053 sub_parser.add_argument('--set_hashtree_disabled_flag',
4054 help='Set the HASHTREE_DISABLED flag',
4055 action='store_true')
4056
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004057 def _add_common_footer_args(self, sub_parser):
4058 """Adds arguments used by add_*_footer sub-commands.
4059
4060 Arguments:
4061 sub_parser: The parser to add arguments to.
4062 """
4063 sub_parser.add_argument('--use_persistent_digest',
4064 help='Use a persistent digest on device instead of '
4065 'storing the digest in the descriptor. This '
4066 'cannot be used with A/B so must be combined '
4067 'with --do_not_use_ab when an A/B suffix is '
4068 'expected at runtime.',
4069 action='store_true')
4070 sub_parser.add_argument('--do_not_use_ab',
4071 help='The partition does not use A/B even when an '
4072 'A/B suffix is present. This must not be used '
4073 'for vbmeta or chained partitions.',
4074 action='store_true')
4075
David Zeuthena5fd3a42017-02-27 16:38:54 -05004076 def _fixup_common_args(self, args):
4077 """Common fixups needed by subcommands.
4078
4079 Arguments:
4080 args: Arguments to modify.
4081
4082 Returns:
4083 The modified arguments.
4084 """
4085 if args.set_hashtree_disabled_flag:
4086 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4087 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004088
4089 def run(self, argv):
4090 """Command-line processor.
4091
4092 Arguments:
4093 argv: Pass sys.argv from main.
4094 """
4095 parser = argparse.ArgumentParser()
4096 subparsers = parser.add_subparsers(title='subcommands')
4097
Jan Monsch2c7be992020-04-03 14:37:13 +02004098 sub_parser = subparsers.add_parser(
4099 'generate_test_image',
4100 help=('Generates a test image with a known pattern for testing: '
4101 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4102 sub_parser.add_argument('--image_size',
4103 help='Size of image to generate.',
4104 type=parse_number,
4105 required=True)
4106 sub_parser.add_argument('--start_byte',
4107 help='Integer for the start byte of the pattern.',
4108 type=parse_number,
4109 default=0)
4110 sub_parser.add_argument('--output',
4111 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004112 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004113 default=sys.stdout)
4114 sub_parser.set_defaults(func=self.generate_test_image)
4115
David Zeuthen21e95262016-07-27 17:58:40 -04004116 sub_parser = subparsers.add_parser('version',
4117 help='Prints version of avbtool.')
4118 sub_parser.set_defaults(func=self.version)
4119
4120 sub_parser = subparsers.add_parser('extract_public_key',
4121 help='Extract public key.')
4122 sub_parser.add_argument('--key',
4123 help='Path to RSA private key file',
4124 required=True)
4125 sub_parser.add_argument('--output',
4126 help='Output file name',
4127 type=argparse.FileType('wb'),
4128 required=True)
4129 sub_parser.set_defaults(func=self.extract_public_key)
4130
4131 sub_parser = subparsers.add_parser('make_vbmeta_image',
4132 help='Makes a vbmeta image.')
4133 sub_parser.add_argument('--output',
4134 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004135 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004136 sub_parser.add_argument('--padding_size',
4137 metavar='NUMBER',
4138 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004139 'its size is a multiple of NUMBER '
4140 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004141 type=parse_number,
4142 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004143 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004144 sub_parser.set_defaults(func=self.make_vbmeta_image)
4145
4146 sub_parser = subparsers.add_parser('add_hash_footer',
4147 help='Add hashes and footer to image.')
4148 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004149 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004150 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004151 sub_parser.add_argument('--partition_size',
4152 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004153 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004154 sub_parser.add_argument('--partition_name',
4155 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004156 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004157 sub_parser.add_argument('--hash_algorithm',
4158 help='Hash algorithm to use (default: sha256)',
4159 default='sha256')
4160 sub_parser.add_argument('--salt',
4161 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004162 sub_parser.add_argument('--calc_max_image_size',
4163 help=('Don\'t store the footer - '
4164 'instead calculate the maximum image size '
4165 'leaving enough room for metadata with '
4166 'the given partition size.'),
4167 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004168 sub_parser.add_argument('--output_vbmeta_image',
4169 help='Also write vbmeta struct to file',
4170 type=argparse.FileType('wb'))
4171 sub_parser.add_argument('--do_not_append_vbmeta_image',
4172 help=('Do not append vbmeta struct or footer '
4173 'to the image'),
4174 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004175 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004176 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004177 sub_parser.set_defaults(func=self.add_hash_footer)
4178
David Zeuthenb1b994d2017-03-06 18:01:31 -05004179 sub_parser = subparsers.add_parser('append_vbmeta_image',
4180 help='Append vbmeta image to image.')
4181 sub_parser.add_argument('--image',
4182 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004183 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004184 sub_parser.add_argument('--partition_size',
4185 help='Partition size',
4186 type=parse_number,
4187 required=True)
4188 sub_parser.add_argument('--vbmeta_image',
4189 help='Image with vbmeta blob to append',
4190 type=argparse.FileType('rb'))
4191 sub_parser.set_defaults(func=self.append_vbmeta_image)
4192
Jan Monscheeb28b62019-12-05 16:17:09 +01004193 sub_parser = subparsers.add_parser(
4194 'add_hashtree_footer',
4195 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004196 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004197 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004198 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004199 sub_parser.add_argument('--partition_size',
4200 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004201 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004202 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004203 sub_parser.add_argument('--partition_name',
4204 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004205 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004206 sub_parser.add_argument('--hash_algorithm',
4207 help='Hash algorithm to use (default: sha1)',
4208 default='sha1')
4209 sub_parser.add_argument('--salt',
4210 help='Salt in hex (default: /dev/urandom)')
4211 sub_parser.add_argument('--block_size',
4212 help='Block size (default: 4096)',
4213 type=parse_number,
4214 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004215 # TODO(zeuthen): The --generate_fec option was removed when we
4216 # moved to generating FEC by default. To avoid breaking existing
4217 # users needing to transition we simply just print a warning below
4218 # in add_hashtree_footer(). Remove this option and the warning at
4219 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004220 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004221 help=argparse.SUPPRESS,
4222 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004223 sub_parser.add_argument(
4224 '--do_not_generate_fec',
4225 help='Do not generate forward-error-correction codes',
4226 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004227 sub_parser.add_argument('--fec_num_roots',
4228 help='Number of roots for FEC (default: 2)',
4229 type=parse_number,
4230 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004231 sub_parser.add_argument('--calc_max_image_size',
4232 help=('Don\'t store the hashtree or footer - '
4233 'instead calculate the maximum image size '
4234 'leaving enough room for hashtree '
4235 'and metadata with the given partition '
4236 'size.'),
4237 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004238 sub_parser.add_argument('--output_vbmeta_image',
4239 help='Also write vbmeta struct to file',
4240 type=argparse.FileType('wb'))
4241 sub_parser.add_argument('--do_not_append_vbmeta_image',
4242 help=('Do not append vbmeta struct or footer '
4243 'to the image'),
4244 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004245 # This is different from --setup_rootfs_from_kernel insofar that
4246 # it doesn't take an IMAGE, the generated cmdline will be for the
4247 # hashtree we're adding.
4248 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4249 action='store_true',
4250 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004251 sub_parser.add_argument('--no_hashtree',
4252 action='store_true',
4253 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004254 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004255 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004256 sub_parser.set_defaults(func=self.add_hashtree_footer)
4257
4258 sub_parser = subparsers.add_parser('erase_footer',
4259 help='Erase footer from an image.')
4260 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004261 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004262 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004263 required=True)
4264 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004265 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004266 action='store_true')
4267 sub_parser.set_defaults(func=self.erase_footer)
4268
David Zeuthen1394f762019-04-30 10:20:11 -04004269 sub_parser = subparsers.add_parser('zero_hashtree',
4270 help='Zero out hashtree and FEC data.')
4271 sub_parser.add_argument('--image',
4272 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004273 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004274 required=True)
4275 sub_parser.set_defaults(func=self.zero_hashtree)
4276
Jan Monscheeb28b62019-12-05 16:17:09 +01004277 sub_parser = subparsers.add_parser(
4278 'extract_vbmeta_image',
4279 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004280 sub_parser.add_argument('--image',
4281 help='Image with footer',
4282 type=argparse.FileType('rb'),
4283 required=True)
4284 sub_parser.add_argument('--output',
4285 help='Output file name',
4286 type=argparse.FileType('wb'))
4287 sub_parser.add_argument('--padding_size',
4288 metavar='NUMBER',
4289 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004290 'its size is a multiple of NUMBER '
4291 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004292 type=parse_number,
4293 default=0)
4294 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4295
David Zeuthen2bc232b2017-04-19 14:25:19 -04004296 sub_parser = subparsers.add_parser('resize_image',
4297 help='Resize image with a footer.')
4298 sub_parser.add_argument('--image',
4299 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004300 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004301 required=True)
4302 sub_parser.add_argument('--partition_size',
4303 help='New partition size',
4304 type=parse_number)
4305 sub_parser.set_defaults(func=self.resize_image)
4306
David Zeuthen21e95262016-07-27 17:58:40 -04004307 sub_parser = subparsers.add_parser(
4308 'info_image',
4309 help='Show information about vbmeta or footer.')
4310 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004311 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004312 type=argparse.FileType('rb'),
4313 required=True)
4314 sub_parser.add_argument('--output',
4315 help='Write info to file',
4316 type=argparse.FileType('wt'),
4317 default=sys.stdout)
4318 sub_parser.set_defaults(func=self.info_image)
4319
David Zeuthenb623d8b2017-04-04 16:05:53 -04004320 sub_parser = subparsers.add_parser(
4321 'verify_image',
4322 help='Verify an image.')
4323 sub_parser.add_argument('--image',
4324 help='Image to verify',
4325 type=argparse.FileType('rb'),
4326 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004327 sub_parser.add_argument('--key',
4328 help='Check embedded public key matches KEY',
4329 metavar='KEY',
4330 required=False)
4331 sub_parser.add_argument('--expected_chain_partition',
4332 help='Expected chain partition',
4333 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4334 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004335 sub_parser.add_argument(
4336 '--follow_chain_partitions',
4337 help=('Follows chain partitions even when not '
4338 'specified with the --expected_chain_partition option'),
4339 action='store_true')
4340 sub_parser.add_argument(
4341 '--accept_zeroed_hashtree',
4342 help=('Accept images where the hashtree or FEC data is zeroed out'),
4343 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004344 sub_parser.set_defaults(func=self.verify_image)
4345
David Zeuthenb8643c02018-05-17 17:21:18 -04004346 sub_parser = subparsers.add_parser(
4347 'calculate_vbmeta_digest',
4348 help='Calculate vbmeta digest.')
4349 sub_parser.add_argument('--image',
4350 help='Image to calculate digest for',
4351 type=argparse.FileType('rb'),
4352 required=True)
4353 sub_parser.add_argument('--hash_algorithm',
4354 help='Hash algorithm to use (default: sha256)',
4355 default='sha256')
4356 sub_parser.add_argument('--output',
4357 help='Write hex digest to file (default: stdout)',
4358 type=argparse.FileType('wt'),
4359 default=sys.stdout)
4360 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4361
David Zeuthenf7d2e752018-09-20 13:30:41 -04004362 sub_parser = subparsers.add_parser(
4363 'calculate_kernel_cmdline',
4364 help='Calculate kernel cmdline.')
4365 sub_parser.add_argument('--image',
4366 help='Image to calculate kernel cmdline for',
4367 type=argparse.FileType('rb'),
4368 required=True)
4369 sub_parser.add_argument('--hashtree_disabled',
4370 help='Return the cmdline for hashtree disabled',
4371 action='store_true')
4372 sub_parser.add_argument('--output',
4373 help='Write cmdline to file (default: stdout)',
4374 type=argparse.FileType('wt'),
4375 default=sys.stdout)
4376 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4377
David Zeuthen8b6973b2016-09-20 12:39:49 -04004378 sub_parser = subparsers.add_parser('set_ab_metadata',
4379 help='Set A/B metadata.')
4380 sub_parser.add_argument('--misc_image',
4381 help=('The misc image to modify. If the image does '
4382 'not exist, it will be created.'),
4383 type=argparse.FileType('r+b'),
4384 required=True)
4385 sub_parser.add_argument('--slot_data',
4386 help=('Slot data of the form "priority", '
4387 '"tries_remaining", "sucessful_boot" for '
4388 'slot A followed by the same for slot B, '
4389 'separated by colons. The default value '
4390 'is 15:7:0:14:7:0.'),
4391 default='15:7:0:14:7:0')
4392 sub_parser.set_defaults(func=self.set_ab_metadata)
4393
Darren Krahn147b08d2016-12-20 16:38:29 -08004394 sub_parser = subparsers.add_parser(
4395 'make_atx_certificate',
4396 help='Create an Android Things eXtension (ATX) certificate.')
4397 sub_parser.add_argument('--output',
4398 help='Write certificate to file',
4399 type=argparse.FileType('wb'),
4400 default=sys.stdout)
4401 sub_parser.add_argument('--subject',
4402 help=('Path to subject file'),
4403 type=argparse.FileType('rb'),
4404 required=True)
4405 sub_parser.add_argument('--subject_key',
4406 help=('Path to subject RSA public key file'),
4407 type=argparse.FileType('rb'),
4408 required=True)
4409 sub_parser.add_argument('--subject_key_version',
4410 help=('Version of the subject key'),
4411 type=parse_number,
4412 required=False)
4413 sub_parser.add_argument('--subject_is_intermediate_authority',
4414 help=('Generate an intermediate authority '
4415 'certificate'),
4416 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004417 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004418 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004419 'string'),
4420 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004421 sub_parser.add_argument('--authority_key',
4422 help='Path to authority RSA private key file',
4423 required=False)
4424 sub_parser.add_argument('--signing_helper',
4425 help='Path to helper used for signing',
4426 metavar='APP',
4427 default=None,
4428 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004429 sub_parser.add_argument('--signing_helper_with_files',
4430 help='Path to helper used for signing using files',
4431 metavar='APP',
4432 default=None,
4433 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004434 sub_parser.set_defaults(func=self.make_atx_certificate)
4435
4436 sub_parser = subparsers.add_parser(
4437 'make_atx_permanent_attributes',
4438 help='Create Android Things eXtension (ATX) permanent attributes.')
4439 sub_parser.add_argument('--output',
4440 help='Write attributes to file',
4441 type=argparse.FileType('wb'),
4442 default=sys.stdout)
4443 sub_parser.add_argument('--root_authority_key',
4444 help='Path to authority RSA public key file',
4445 type=argparse.FileType('rb'),
4446 required=True)
4447 sub_parser.add_argument('--product_id',
4448 help=('Path to Product ID file'),
4449 type=argparse.FileType('rb'),
4450 required=True)
4451 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4452
4453 sub_parser = subparsers.add_parser(
4454 'make_atx_metadata',
4455 help='Create Android Things eXtension (ATX) metadata.')
4456 sub_parser.add_argument('--output',
4457 help='Write metadata to file',
4458 type=argparse.FileType('wb'),
4459 default=sys.stdout)
4460 sub_parser.add_argument('--intermediate_key_certificate',
4461 help='Path to intermediate key certificate file',
4462 type=argparse.FileType('rb'),
4463 required=True)
4464 sub_parser.add_argument('--product_key_certificate',
4465 help='Path to product key certificate file',
4466 type=argparse.FileType('rb'),
4467 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004468 sub_parser.set_defaults(func=self.make_atx_metadata)
4469
Darren Krahnfccd64e2018-01-16 17:39:35 -08004470 sub_parser = subparsers.add_parser(
4471 'make_atx_unlock_credential',
4472 help='Create an Android Things eXtension (ATX) unlock credential.')
4473 sub_parser.add_argument('--output',
4474 help='Write credential to file',
4475 type=argparse.FileType('wb'),
4476 default=sys.stdout)
4477 sub_parser.add_argument('--intermediate_key_certificate',
4478 help='Path to intermediate key certificate file',
4479 type=argparse.FileType('rb'),
4480 required=True)
4481 sub_parser.add_argument('--unlock_key_certificate',
4482 help='Path to unlock key certificate file',
4483 type=argparse.FileType('rb'),
4484 required=True)
4485 sub_parser.add_argument('--challenge',
4486 help='Path to the challenge to sign (optional). If '
4487 'this is not provided the challenge signature '
4488 'field is omitted and can be concatenated '
4489 'later.',
4490 required=False)
4491 sub_parser.add_argument('--unlock_key',
4492 help='Path to unlock key (optional). Must be '
4493 'provided if using --challenge.',
4494 required=False)
4495 sub_parser.add_argument('--signing_helper',
4496 help='Path to helper used for signing',
4497 metavar='APP',
4498 default=None,
4499 required=False)
4500 sub_parser.add_argument('--signing_helper_with_files',
4501 help='Path to helper used for signing using files',
4502 metavar='APP',
4503 default=None,
4504 required=False)
4505 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4506
David Zeuthen21e95262016-07-27 17:58:40 -04004507 args = parser.parse_args(argv[1:])
4508 try:
4509 args.func(args)
4510 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004511 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004512 sys.exit(1)
4513
4514 def version(self, _):
4515 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004516 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004517
Jan Monsch2c7be992020-04-03 14:37:13 +02004518 def generate_test_image(self, args):
4519 """Implements the 'generate_test_image' sub-command."""
4520 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4521
David Zeuthen21e95262016-07-27 17:58:40 -04004522 def extract_public_key(self, args):
4523 """Implements the 'extract_public_key' sub-command."""
4524 self.avb.extract_public_key(args.key, args.output)
4525
4526 def make_vbmeta_image(self, args):
4527 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004528 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004529 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004530 args.algorithm, args.key,
4531 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004532 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004533 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004534 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004535 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004536 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004537 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004538 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004539 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004540 args.print_required_libavb_version,
4541 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004542
David Zeuthenb1b994d2017-03-06 18:01:31 -05004543 def append_vbmeta_image(self, args):
4544 """Implements the 'append_vbmeta_image' sub-command."""
4545 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4546 args.partition_size)
4547
David Zeuthen21e95262016-07-27 17:58:40 -04004548 def add_hash_footer(self, args):
4549 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004550 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004551 self.avb.add_hash_footer(args.image.name if args.image else None,
4552 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004553 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004554 args.salt, args.chain_partition, args.algorithm,
4555 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004556 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004557 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004558 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 Zeuthena156d3d2017-06-01 12:08:09 -04004561 args.calc_max_image_size,
4562 args.signing_helper,
4563 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004564 args.internal_release_string,
4565 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004566 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004567 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004568 args.print_required_libavb_version,
4569 args.use_persistent_digest,
4570 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004571
4572 def add_hashtree_footer(self, args):
4573 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004574 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004575 # TODO(zeuthen): Remove when removing support for the
4576 # '--generate_fec' option above.
4577 if args.generate_fec:
4578 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4579 'is now generated by default. Use the option '
4580 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004581 self.avb.add_hashtree_footer(
4582 args.image.name if args.image else None,
4583 args.partition_size,
4584 args.partition_name,
4585 not args.do_not_generate_fec, args.fec_num_roots,
4586 args.hash_algorithm, args.block_size,
4587 args.salt, args.chain_partition, args.algorithm,
4588 args.key, args.public_key_metadata,
4589 args.rollback_index, args.flags, args.prop,
4590 args.prop_from_file,
4591 args.kernel_cmdline,
4592 args.setup_rootfs_from_kernel,
4593 args.setup_as_rootfs_from_kernel,
4594 args.include_descriptors_from_image,
4595 args.calc_max_image_size,
4596 args.signing_helper,
4597 args.signing_helper_with_files,
4598 args.internal_release_string,
4599 args.append_to_release_string,
4600 args.output_vbmeta_image,
4601 args.do_not_append_vbmeta_image,
4602 args.print_required_libavb_version,
4603 args.use_persistent_digest,
4604 args.do_not_use_ab,
4605 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004606
David Zeuthen21e95262016-07-27 17:58:40 -04004607 def erase_footer(self, args):
4608 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004609 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004610
David Zeuthen1394f762019-04-30 10:20:11 -04004611 def zero_hashtree(self, args):
4612 """Implements the 'zero_hashtree' sub-command."""
4613 self.avb.zero_hashtree(args.image.name)
4614
David Zeuthen49936b42018-08-07 17:38:58 -04004615 def extract_vbmeta_image(self, args):
4616 """Implements the 'extract_vbmeta_image' sub-command."""
4617 self.avb.extract_vbmeta_image(args.output, args.image.name,
4618 args.padding_size)
4619
David Zeuthen2bc232b2017-04-19 14:25:19 -04004620 def resize_image(self, args):
4621 """Implements the 'resize_image' sub-command."""
4622 self.avb.resize_image(args.image.name, args.partition_size)
4623
David Zeuthen8b6973b2016-09-20 12:39:49 -04004624 def set_ab_metadata(self, args):
4625 """Implements the 'set_ab_metadata' sub-command."""
4626 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4627
David Zeuthen21e95262016-07-27 17:58:40 -04004628 def info_image(self, args):
4629 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004630 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004631
David Zeuthenb623d8b2017-04-04 16:05:53 -04004632 def verify_image(self, args):
4633 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004634 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004635 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004636 args.follow_chain_partitions,
4637 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004638
David Zeuthenb8643c02018-05-17 17:21:18 -04004639 def calculate_vbmeta_digest(self, args):
4640 """Implements the 'calculate_vbmeta_digest' sub-command."""
4641 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4642 args.output)
4643
David Zeuthenf7d2e752018-09-20 13:30:41 -04004644 def calculate_kernel_cmdline(self, args):
4645 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004646 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4647 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004648
Darren Krahn147b08d2016-12-20 16:38:29 -08004649 def make_atx_certificate(self, args):
4650 """Implements the 'make_atx_certificate' sub-command."""
4651 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004652 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004653 args.subject_key_version,
4654 args.subject.read(),
4655 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004656 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004657 args.signing_helper,
4658 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004659
4660 def make_atx_permanent_attributes(self, args):
4661 """Implements the 'make_atx_permanent_attributes' sub-command."""
4662 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004663 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004664 args.product_id.read())
4665
4666 def make_atx_metadata(self, args):
4667 """Implements the 'make_atx_metadata' sub-command."""
4668 self.avb.make_atx_metadata(args.output,
4669 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004670 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004671
Darren Krahnfccd64e2018-01-16 17:39:35 -08004672 def make_atx_unlock_credential(self, args):
4673 """Implements the 'make_atx_unlock_credential' sub-command."""
4674 self.avb.make_atx_unlock_credential(
4675 args.output,
4676 args.intermediate_key_certificate.read(),
4677 args.unlock_key_certificate.read(),
4678 args.challenge,
4679 args.unlock_key,
4680 args.signing_helper,
4681 args.signing_helper_with_files)
4682
David Zeuthen21e95262016-07-27 17:58:40 -04004683
4684if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004685 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004686 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4687 log.write(' '.join(sys.argv))
4688 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004689
David Zeuthen21e95262016-07-27 17:58:40 -04004690 tool = AvbTool()
4691 tool.run(sys.argv)