blob: f944af4c3665129aaa7aea6042d9ab569fc2149c [file] [log] [blame]
Jan Monsch13efb5f2020-04-22 17:34:24 +02001#!/usr/bin/env python3
David Zeuthen21e95262016-07-27 17:58:40 -04002
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
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthen34b6b492020-04-13 14:45:02 -040031import json
David Zeuthenc68f0822017-03-31 17:22:35 -040032import math
David Zeuthen21e95262016-07-27 17:58:40 -040033import os
34import struct
35import subprocess
36import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040037import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080038import time
David Zeuthen21e95262016-07-27 17:58:40 -040039
David Zeuthene3cadca2017-02-22 21:25:46 -050040# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040041AVB_VERSION_MAJOR = 1
Varun Sharmade538272020-04-10 15:22:31 -070042AVB_VERSION_MINOR = 2
David Zeuthene3cadca2017-02-22 21:25:46 -050043AVB_VERSION_SUB = 0
44
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080045# Keep in sync with libavb/avb_footer.h.
46AVB_FOOTER_VERSION_MAJOR = 1
47AVB_FOOTER_VERSION_MINOR = 0
48
David Zeuthen58305522017-01-11 17:42:47 -050049AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040050
Jan Monsch2c7be992020-04-03 14:37:13 +020051# Configuration for enabling logging of calls to avbtool.
52AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
53
David Zeuthene3cadca2017-02-22 21:25:46 -050054
David Zeuthen21e95262016-07-27 17:58:40 -040055class AvbError(Exception):
56 """Application-specific errors.
57
58 These errors represent issues for which a stack-trace should not be
59 presented.
60
61 Attributes:
62 message: Error message.
63 """
64
65 def __init__(self, message):
66 Exception.__init__(self, message)
67
68
69class Algorithm(object):
70 """Contains details about an algorithm.
71
Tao Bao80418a52018-07-20 11:41:22 -070072 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040073
74 The constant |ALGORITHMS| is a dictionary from human-readable
75 names (e.g 'SHA256_RSA2048') to instances of this class.
76
77 Attributes:
78 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040079 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040080 hash_num_bytes: Number of bytes used to store the hash.
81 signature_num_bytes: Number of bytes used to store the signature.
82 public_key_num_bytes: Number of bytes used to store the public key.
Jan Monsch9c130122020-04-14 13:43:51 +020083 padding: Padding used for signature as bytes, if any.
David Zeuthen21e95262016-07-27 17:58:40 -040084 """
85
David Zeuthenb623d8b2017-04-04 16:05:53 -040086 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
87 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040088 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040089 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040090 self.hash_num_bytes = hash_num_bytes
91 self.signature_num_bytes = signature_num_bytes
92 self.public_key_num_bytes = public_key_num_bytes
93 self.padding = padding
94
David Zeuthenb623d8b2017-04-04 16:05:53 -040095
David Zeuthen21e95262016-07-27 17:58:40 -040096# This must be kept in sync with the avb_crypto.h file.
97#
98# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
99# obtained from section 5.2.2 of RFC 4880.
100ALGORITHMS = {
101 'NONE': Algorithm(
102 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400103 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400104 hash_num_bytes=0,
105 signature_num_bytes=0,
106 public_key_num_bytes=0,
Jan Monsch9c130122020-04-14 13:43:51 +0200107 padding=b''),
David Zeuthen21e95262016-07-27 17:58:40 -0400108 'SHA256_RSA2048': Algorithm(
109 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400110 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400111 hash_num_bytes=32,
112 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100113 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200114 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400115 # PKCS1-v1_5 padding
116 0x00, 0x01] + [0xff]*202 + [0x00] + [
117 # ASN.1 header
118 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
119 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
120 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200121 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400122 'SHA256_RSA4096': Algorithm(
123 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400124 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400125 hash_num_bytes=32,
126 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100127 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200128 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400129 # PKCS1-v1_5 padding
130 0x00, 0x01] + [0xff]*458 + [0x00] + [
131 # ASN.1 header
132 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
133 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
134 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200135 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400136 'SHA256_RSA8192': Algorithm(
137 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400138 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400139 hash_num_bytes=32,
140 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100141 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200142 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400143 # PKCS1-v1_5 padding
144 0x00, 0x01] + [0xff]*970 + [0x00] + [
145 # ASN.1 header
146 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
147 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
148 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200149 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400150 'SHA512_RSA2048': Algorithm(
151 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400152 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400153 hash_num_bytes=64,
154 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100155 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200156 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400157 # PKCS1-v1_5 padding
158 0x00, 0x01] + [0xff]*170 + [0x00] + [
159 # ASN.1 header
160 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
161 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
162 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200163 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400164 'SHA512_RSA4096': Algorithm(
165 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400166 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400167 hash_num_bytes=64,
168 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100169 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200170 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400171 # PKCS1-v1_5 padding
172 0x00, 0x01] + [0xff]*426 + [0x00] + [
173 # ASN.1 header
174 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
175 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
176 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200177 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400178 'SHA512_RSA8192': Algorithm(
179 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400180 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400181 hash_num_bytes=64,
182 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100183 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200184 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400185 # PKCS1-v1_5 padding
186 0x00, 0x01] + [0xff]*938 + [0x00] + [
187 # ASN.1 header
188 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
189 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
190 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200191 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400192}
193
194
David Zeuthene3cadca2017-02-22 21:25:46 -0500195def get_release_string():
196 """Calculates the release string to use in the VBMeta struct."""
197 # Keep in sync with libavb/avb_version.c:avb_version_string().
198 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
199 AVB_VERSION_MINOR,
200 AVB_VERSION_SUB)
201
202
David Zeuthen21e95262016-07-27 17:58:40 -0400203def round_to_multiple(number, size):
204 """Rounds a number up to nearest multiple of another number.
205
Jan Monschfe00c0a2019-12-11 11:19:40 +0100206 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400207 number: The number to round up.
208 size: The multiple to round up to.
209
210 Returns:
211 If |number| is a multiple of |size|, returns |number|, otherwise
212 returns |number| + |size|.
213 """
214 remainder = number % size
215 if remainder == 0:
216 return number
217 return number + size - remainder
218
219
220def round_to_pow2(number):
221 """Rounds a number up to the next power of 2.
222
Jan Monschfe00c0a2019-12-11 11:19:40 +0100223 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400224 number: The number to round up.
225
226 Returns:
227 If |number| is already a power of 2 then |number| is
228 returned. Otherwise the smallest power of 2 greater than |number|
229 is returned.
230 """
231 return 2**((number - 1).bit_length())
232
233
David Zeuthen21e95262016-07-27 17:58:40 -0400234def encode_long(num_bits, value):
235 """Encodes a long to a bytearray() using a given amount of bits.
236
237 This number is written big-endian, e.g. with the most significant
238 bit first.
239
David Zeuthenb623d8b2017-04-04 16:05:53 -0400240 This is the reverse of decode_long().
241
David Zeuthen21e95262016-07-27 17:58:40 -0400242 Arguments:
243 num_bits: The number of bits to write, e.g. 2048.
244 value: The value to write.
245
246 Returns:
247 A bytearray() with the encoded long.
248 """
249 ret = bytearray()
250 for bit_pos in range(num_bits, 0, -8):
251 octet = (value >> (bit_pos - 8)) & 0xff
252 ret.extend(struct.pack('!B', octet))
253 return ret
254
255
David Zeuthenb623d8b2017-04-04 16:05:53 -0400256def decode_long(blob):
257 """Decodes a long from a bytearray() using a given amount of bits.
258
259 This number is expected to be in big-endian, e.g. with the most
260 significant bit first.
261
262 This is the reverse of encode_long().
263
264 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100265 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400266
267 Returns:
268 The decoded value.
269 """
270 ret = 0
271 for b in bytearray(blob):
272 ret *= 256
273 ret += b
274 return ret
275
276
David Zeuthen21e95262016-07-27 17:58:40 -0400277def egcd(a, b):
278 """Calculate greatest common divisor of two numbers.
279
280 This implementation uses a recursive version of the extended
281 Euclidian algorithm.
282
283 Arguments:
284 a: First number.
285 b: Second number.
286
287 Returns:
288 A tuple (gcd, x, y) that where |gcd| is the greatest common
289 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
290 """
291 if a == 0:
292 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100293 g, y, x = egcd(b % a, a)
294 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400295
296
297def modinv(a, m):
298 """Calculate modular multiplicative inverse of |a| modulo |m|.
299
300 This calculates the number |x| such that |a| * |x| == 1 (modulo
301 |m|). This number only exists if |a| and |m| are co-prime - |None|
302 is returned if this isn't true.
303
304 Arguments:
305 a: The number to calculate a modular inverse of.
306 m: The modulo to use.
307
308 Returns:
309 The modular multiplicative inverse of |a| and |m| or |None| if
310 these numbers are not co-prime.
311 """
312 gcd, x, _ = egcd(a, m)
313 if gcd != 1:
314 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100315 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400316
317
318def parse_number(string):
319 """Parse a string as a number.
320
321 This is just a short-hand for int(string, 0) suitable for use in the
322 |type| parameter of |ArgumentParser|'s add_argument() function. An
323 improvement to just using type=int is that this function supports
324 numbers in other bases, e.g. "0x1234".
325
326 Arguments:
327 string: The string to parse.
328
329 Returns:
330 The parsed integer.
331
332 Raises:
333 ValueError: If the number could not be parsed.
334 """
335 return int(string, 0)
336
337
David Zeuthenc68f0822017-03-31 17:22:35 -0400338class RSAPublicKey(object):
339 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400340
David Zeuthenc68f0822017-03-31 17:22:35 -0400341 Attributes:
342 exponent: The key exponent.
343 modulus: The key modulus.
344 num_bits: The key size.
Jan Monsch7a722ee2021-06-17 11:49:05 +0200345 key_path: The path to a key file.
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.
Jan Monsch9c130122020-04-14 13:43:51 +0200394 self.key_path = key_path
David Zeuthenc68f0822017-03-31 17:22:35 -0400395 self.modulus = int(modulus_hexstr, 16)
396 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
397 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400398
Jan Monsch9c130122020-04-14 13:43:51 +0200399 def encode(self):
400 """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
David Zeuthen21e95262016-07-27 17:58:40 -0400401
Jan Monsch9c130122020-04-14 13:43:51 +0200402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
David Zeuthen21e95262016-07-27 17:58:40 -0400404
Jan Monsch9c130122020-04-14 13:43:51 +0200405 Returns:
406 The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
Jan Monsch9c130122020-04-14 13:43:51 +0200408 Raises:
409 AvbError: If given RSA key exponent is not 65537.
410 """
411 if self.exponent != 65537:
412 raise AvbError('Only RSA keys with exponent 65537 are supported.')
413 ret = bytearray()
414 # Calculate n0inv = -1/n[0] (mod 2^32)
415 b = 2 ** 32
416 n0inv = b - modinv(self.modulus, b)
417 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
418 r = 2 ** self.modulus.bit_length()
419 rrmodn = r * r % self.modulus
420 ret.extend(struct.pack('!II', self.num_bits, n0inv))
421 ret.extend(encode_long(self.num_bits, self.modulus))
422 ret.extend(encode_long(self.num_bits, rrmodn))
423 return bytes(ret)
David Zeuthen21e95262016-07-27 17:58:40 -0400424
Jan Monsch9c130122020-04-14 13:43:51 +0200425 def sign(self, algorithm_name, data_to_sign, signing_helper=None,
426 signing_helper_with_files=None):
427 """Sign given data using |signing_helper| or openssl.
Jan Monsch77cd2022019-12-10 17:18:04 +0100428
Jan Monsch9c130122020-04-14 13:43:51 +0200429 openssl is used if neither the parameters signing_helper nor
430 signing_helper_with_files are given.
431
432 Arguments:
433 algorithm_name: The algorithm name as per the ALGORITHMS dict.
434 data_to_sign: Data to sign as bytes or bytearray.
435 signing_helper: Program which signs a hash and returns the signature.
436 signing_helper_with_files: Same as signing_helper but uses files instead.
437
438 Returns:
439 The signature as bytes.
440
441 Raises:
442 AvbError: If an error occurred during signing.
443 """
444 # Checks requested algorithm for validity.
445 algorithm = ALGORITHMS.get(algorithm_name)
446 if not algorithm:
447 raise AvbError('Algorithm with name {} is not supported.'
448 .format(algorithm_name))
449
450 if self.num_bits != (algorithm.signature_num_bytes * 8):
451 raise AvbError('Key size of key ({} bits) does not match key size '
452 '({} bits) of given algorithm {}.'
453 .format(self.num_bits, algorithm.signature_num_bytes * 8,
454 algorithm_name))
455
456 # Hashes the data.
457 hasher = hashlib.new(algorithm.hash_name)
458 hasher.update(data_to_sign)
459 digest = hasher.digest()
460
461 # Calculates the signature.
462 padding_and_hash = algorithm.padding + digest
463 p = None
464 if signing_helper_with_files is not None:
465 with tempfile.NamedTemporaryFile() as signing_file:
466 signing_file.write(padding_and_hash)
467 signing_file.flush()
468 p = subprocess.Popen([signing_helper_with_files, algorithm_name,
469 self.key_path, signing_file.name])
470 retcode = p.wait()
471 if retcode != 0:
472 raise AvbError('Error signing')
473 signing_file.seek(0)
474 signature = signing_file.read()
475 else:
476 if signing_helper is not None:
477 p = subprocess.Popen(
478 [signing_helper, algorithm_name, self.key_path],
479 stdin=subprocess.PIPE,
480 stdout=subprocess.PIPE,
481 stderr=subprocess.PIPE)
482 else:
483 p = subprocess.Popen(
484 ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
485 stdin=subprocess.PIPE,
486 stdout=subprocess.PIPE,
487 stderr=subprocess.PIPE)
488 (pout, perr) = p.communicate(padding_and_hash)
489 retcode = p.wait()
490 if retcode != 0:
491 raise AvbError('Error signing: {}'.format(perr))
492 signature = pout
493 if len(signature) != algorithm.signature_num_bytes:
494 raise AvbError('Error signing: Invalid length of signature')
495 return signature
David Zeuthen21e95262016-07-27 17:58:40 -0400496
497
498def lookup_algorithm_by_type(alg_type):
499 """Looks up algorithm by type.
500
501 Arguments:
502 alg_type: The integer representing the type.
503
504 Returns:
505 A tuple with the algorithm name and an |Algorithm| instance.
506
507 Raises:
508 Exception: If the algorithm cannot be found
509 """
510 for alg_name in ALGORITHMS:
511 alg_data = ALGORITHMS[alg_name]
512 if alg_data.algorithm_type == alg_type:
513 return (alg_name, alg_data)
514 raise AvbError('Unknown algorithm type {}'.format(alg_type))
515
Jan Monsch77cd2022019-12-10 17:18:04 +0100516
Dan Austina7bc4962019-12-02 13:26:08 -0800517def lookup_hash_size_by_type(alg_type):
518 """Looks up hash size by type.
519
520 Arguments:
521 alg_type: The integer representing the type.
522
523 Returns:
524 The corresponding hash size.
525
526 Raises:
527 AvbError: If the algorithm cannot be found.
528 """
529 for alg_name in ALGORITHMS:
530 alg_data = ALGORITHMS[alg_name]
531 if alg_data.algorithm_type == alg_type:
532 return alg_data.hash_num_bytes
533 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400534
Jan Monsch77cd2022019-12-10 17:18:04 +0100535
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100537 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400538
539 Arguments:
540 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200541 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
542 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400543
544 Returns:
545 True if the signature is valid and corresponds to the embedded
546 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100547
548 Raises:
549 AvbError: If there errors calling out to openssl command during
550 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400551 """
552 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100553 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400554 return True
555 header_blob = vbmeta_blob[0:256]
556 auth_offset = 256
557 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
558 aux_size = vbmeta_header.auxiliary_data_block_size
559 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
560 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
561 pubkey_size = vbmeta_header.public_key_size
562 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
563
564 digest_offset = auth_offset + vbmeta_header.hash_offset
565 digest_size = vbmeta_header.hash_size
566 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
567
568 sig_offset = auth_offset + vbmeta_header.signature_offset
569 sig_size = vbmeta_header.signature_size
570 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
571
572 # Now that we've got the stored digest, public key, and signature
573 # all we need to do is to verify. This is the exactly the same
574 # steps as performed in the avb_vbmeta_image_verify() function in
575 # libavb/avb_vbmeta_image.c.
576
577 ha = hashlib.new(alg.hash_name)
578 ha.update(header_blob)
579 ha.update(aux_blob)
580 computed_digest = ha.digest()
581
582 if computed_digest != digest_blob:
583 return False
584
Jan Monsch9c130122020-04-14 13:43:51 +0200585 padding_and_digest = alg.padding + computed_digest
David Zeuthenb623d8b2017-04-04 16:05:53 -0400586
587 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100588 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400589 modulus = decode_long(modulus_blob)
590 exponent = 65537
591
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500592 # We used to have this:
593 #
594 # import Crypto.PublicKey.RSA
595 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
596 # if not key.verify(decode_long(padding_and_digest),
597 # (decode_long(sig_blob), None)):
598 # return False
599 # return True
600 #
601 # but since 'avbtool verify_image' is used on the builders we don't want
602 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
603 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
604 '\n'
605 '[pubkeyinfo]\n'
606 'algorithm=SEQUENCE:rsa_alg\n'
607 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
608 '\n'
609 '[rsa_alg]\n'
610 'algorithm=OID:rsaEncryption\n'
611 'parameter=NULL\n'
612 '\n'
613 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200614 'n=INTEGER:{}\n'
615 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
616 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500617
Jan Monsch38865f22020-04-08 09:32:58 +0200618 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
619 asn1_tmpfile.write(asn1_str.encode('ascii'))
620 asn1_tmpfile.flush()
621
622 with tempfile.NamedTemporaryFile() as der_tmpfile:
623 p = subprocess.Popen(
624 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
625 der_tmpfile.name, '-noout'])
626 retcode = p.wait()
627 if retcode != 0:
628 raise AvbError('Error generating DER file')
629
630 p = subprocess.Popen(
631 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
632 '-keyform', 'DER', '-raw'],
633 stdin=subprocess.PIPE,
634 stdout=subprocess.PIPE,
635 stderr=subprocess.PIPE)
636 (pout, perr) = p.communicate(sig_blob)
637 retcode = p.wait()
638 if retcode != 0:
639 raise AvbError('Error verifying data: {}'.format(perr))
640 if pout != padding_and_digest:
641 sys.stderr.write('Signature not correct\n')
642 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400643 return True
644
645
Tianjie62ad0222021-02-22 14:31:12 -0800646def create_avb_hashtree_hasher(algorithm, salt):
647 """Create the hasher for AVB hashtree based on the input algorithm."""
648
649 if algorithm.lower() == 'blake2b-256':
650 return hashlib.new('blake2b', salt, digest_size=32)
651
652 return hashlib.new(algorithm, salt)
653
654
David Zeuthena4fee8b2016-08-22 15:20:43 -0400655class ImageChunk(object):
656 """Data structure used for representing chunks in Android sparse files.
657
658 Attributes:
659 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
660 chunk_offset: Offset in the sparse file where this chunk begins.
661 output_offset: Offset in de-sparsified file where output begins.
662 output_size: Number of bytes in output.
663 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
664 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
665 """
666
667 FORMAT = '<2H2I'
668 TYPE_RAW = 0xcac1
669 TYPE_FILL = 0xcac2
670 TYPE_DONT_CARE = 0xcac3
671 TYPE_CRC32 = 0xcac4
672
673 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
674 input_offset, fill_data):
675 """Initializes an ImageChunk object.
676
677 Arguments:
678 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
679 chunk_offset: Offset in the sparse file where this chunk begins.
680 output_offset: Offset in de-sparsified file.
681 output_size: Number of bytes in output.
682 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200683 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400684
685 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200686 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400687 """
688 self.chunk_type = chunk_type
689 self.chunk_offset = chunk_offset
690 self.output_offset = output_offset
691 self.output_size = output_size
692 self.input_offset = input_offset
693 self.fill_data = fill_data
694 # Check invariants.
695 if self.chunk_type == self.TYPE_RAW:
696 if self.fill_data is not None:
697 raise ValueError('RAW chunk cannot have fill_data set.')
698 if not self.input_offset:
699 raise ValueError('RAW chunk must have input_offset set.')
700 elif self.chunk_type == self.TYPE_FILL:
701 if self.fill_data is None:
702 raise ValueError('FILL chunk must have fill_data set.')
703 if self.input_offset:
704 raise ValueError('FILL chunk cannot have input_offset set.')
705 elif self.chunk_type == self.TYPE_DONT_CARE:
706 if self.fill_data is not None:
707 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
708 if self.input_offset:
709 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
710 else:
711 raise ValueError('Invalid chunk type')
712
713
714class ImageHandler(object):
715 """Abstraction for image I/O with support for Android sparse images.
716
717 This class provides an interface for working with image files that
718 may be using the Android Sparse Image format. When an instance is
719 constructed, we test whether it's an Android sparse file. If so,
720 operations will be on the sparse file by interpreting the sparse
721 format, otherwise they will be directly on the file. Either way the
722 operations do the same.
723
724 For reading, this interface mimics a file object - it has seek(),
725 tell(), and read() methods. For writing, only truncation
726 (truncate()) and appending is supported (append_raw() and
727 append_dont_care()). Additionally, data can only be written in units
728 of the block size.
729
730 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400731 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400732 is_sparse: Whether the file being operated on is sparse.
733 block_size: The block size, typically 4096.
734 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400735 """
736 # See system/core/libsparse/sparse_format.h for details.
737 MAGIC = 0xed26ff3a
738 HEADER_FORMAT = '<I4H4I'
739
740 # These are formats and offset of just the |total_chunks| and
741 # |total_blocks| fields.
742 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
743 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
744
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200745 def __init__(self, image_filename, read_only=False):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400746 """Initializes an image handler.
747
748 Arguments:
749 image_filename: The name of the file to operate on.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200750 read_only: True if file is only opened for read-only operations.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400751
752 Raises:
753 ValueError: If data in the file is invalid.
754 """
David Zeuthen49936b42018-08-07 17:38:58 -0400755 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100756 self._num_total_blocks = 0
757 self._num_total_chunks = 0
758 self._file_pos = 0
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200759 self._read_only = read_only
David Zeuthena4fee8b2016-08-22 15:20:43 -0400760 self._read_header()
761
762 def _read_header(self):
763 """Initializes internal data structures used for reading file.
764
765 This may be called multiple times and is typically called after
766 modifying the file (e.g. appending, truncation).
767
768 Raises:
769 ValueError: If data in the file is invalid.
770 """
771 self.is_sparse = False
772 self.block_size = 4096
773 self._file_pos = 0
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200774 if self._read_only:
775 self._image = open(self.filename, 'rb')
776 else:
777 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400778 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400779 self.image_size = self._image.tell()
780
781 self._image.seek(0, os.SEEK_SET)
782 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
783 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
784 block_size, self._num_total_blocks, self._num_total_chunks,
785 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
786 if magic != self.MAGIC:
787 # Not a sparse image, our job here is done.
788 return
789 if not (major_version == 1 and minor_version == 0):
790 raise ValueError('Encountered sparse image format version {}.{} but '
791 'only 1.0 is supported'.format(major_version,
792 minor_version))
793 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
794 raise ValueError('Unexpected file_hdr_sz value {}.'.
795 format(file_hdr_sz))
796 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
797 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
798 format(chunk_hdr_sz))
799
800 self.block_size = block_size
801
802 # Build an list of chunks by parsing the file.
803 self._chunks = []
804
805 # Find the smallest offset where only "Don't care" chunks
806 # follow. This will be the size of the content in the sparse
807 # image.
808 offset = 0
809 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100810 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400811 chunk_offset = self._image.tell()
812
813 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
814 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
815 header_bin)
816 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
817
David Zeuthena4fee8b2016-08-22 15:20:43 -0400818 if chunk_type == ImageChunk.TYPE_RAW:
819 if data_sz != (chunk_sz * self.block_size):
820 raise ValueError('Raw chunk input size ({}) does not match output '
821 'size ({})'.
822 format(data_sz, chunk_sz*self.block_size))
823 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
824 chunk_offset,
825 output_offset,
826 chunk_sz*self.block_size,
827 self._image.tell(),
828 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700829 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400830
831 elif chunk_type == ImageChunk.TYPE_FILL:
832 if data_sz != 4:
833 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
834 'has {}'.format(data_sz))
835 fill_data = self._image.read(4)
836 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
837 chunk_offset,
838 output_offset,
839 chunk_sz*self.block_size,
840 None,
841 fill_data))
842 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
843 if data_sz != 0:
844 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
845 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400846 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
847 chunk_offset,
848 output_offset,
849 chunk_sz*self.block_size,
850 None,
851 None))
852 elif chunk_type == ImageChunk.TYPE_CRC32:
853 if data_sz != 4:
854 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
855 'this has {}'.format(data_sz))
856 self._image.read(4)
857 else:
858 raise ValueError('Unknown chunk type {}'.format(chunk_type))
859
860 offset += chunk_sz
861 output_offset += chunk_sz*self.block_size
862
863 # Record where sparse data end.
864 self._sparse_end = self._image.tell()
865
866 # Now that we've traversed all chunks, sanity check.
867 if self._num_total_blocks != offset:
868 raise ValueError('The header said we should have {} output blocks, '
869 'but we saw {}'.format(self._num_total_blocks, offset))
870 junk_len = len(self._image.read())
871 if junk_len > 0:
872 raise ValueError('There were {} bytes of extra data at the end of the '
873 'file.'.format(junk_len))
874
David Zeuthen09692692016-09-30 16:16:40 -0400875 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400876 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400877
878 # This is used when bisecting in read() to find the initial slice.
879 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
880
881 self.is_sparse = True
882
883 def _update_chunks_and_blocks(self):
884 """Helper function to update the image header.
885
886 The the |total_chunks| and |total_blocks| fields in the header
887 will be set to value of the |_num_total_blocks| and
888 |_num_total_chunks| attributes.
889
890 """
891 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
892 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
893 self._num_total_blocks,
894 self._num_total_chunks))
895
896 def append_dont_care(self, num_bytes):
897 """Appends a DONT_CARE chunk to the sparse file.
898
899 The given number of bytes must be a multiple of the block size.
900
901 Arguments:
902 num_bytes: Size in number of bytes of the DONT_CARE chunk.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200903
Jan Monsch7a722ee2021-06-17 11:49:05 +0200904 Raises:
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200905 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400906 """
907 assert num_bytes % self.block_size == 0
908
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200909 if self._read_only:
910 raise OSError('ImageHandler is in read-only mode.')
911
David Zeuthena4fee8b2016-08-22 15:20:43 -0400912 if not self.is_sparse:
913 self._image.seek(0, os.SEEK_END)
914 # This is more efficient that writing NUL bytes since it'll add
915 # a hole on file systems that support sparse files (native
916 # sparse, not Android sparse).
917 self._image.truncate(self._image.tell() + num_bytes)
918 self._read_header()
919 return
920
921 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100922 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400923 self._update_chunks_and_blocks()
924
925 self._image.seek(self._sparse_end, os.SEEK_SET)
926 self._image.write(struct.pack(ImageChunk.FORMAT,
927 ImageChunk.TYPE_DONT_CARE,
928 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100929 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400930 struct.calcsize(ImageChunk.FORMAT)))
931 self._read_header()
932
933 def append_raw(self, data):
934 """Appends a RAW chunk to the sparse file.
935
936 The length of the given data must be a multiple of the block size.
937
938 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200939 data: Data to append as bytes.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200940
Jan Monsch7a722ee2021-06-17 11:49:05 +0200941 Raises:
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200942 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400943 """
944 assert len(data) % self.block_size == 0
945
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200946 if self._read_only:
947 raise OSError('ImageHandler is in read-only mode.')
948
David Zeuthena4fee8b2016-08-22 15:20:43 -0400949 if not self.is_sparse:
950 self._image.seek(0, os.SEEK_END)
951 self._image.write(data)
952 self._read_header()
953 return
954
955 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100956 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400957 self._update_chunks_and_blocks()
958
959 self._image.seek(self._sparse_end, os.SEEK_SET)
960 self._image.write(struct.pack(ImageChunk.FORMAT,
961 ImageChunk.TYPE_RAW,
962 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100963 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400964 len(data) +
965 struct.calcsize(ImageChunk.FORMAT)))
966 self._image.write(data)
967 self._read_header()
968
969 def append_fill(self, fill_data, size):
970 """Appends a fill chunk to the sparse file.
971
972 The total length of the fill data must be a multiple of the block size.
973
974 Arguments:
975 fill_data: Fill data to append - must be four bytes.
976 size: Number of chunk - must be a multiple of four and the block size.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200977
Jan Monsch7a722ee2021-06-17 11:49:05 +0200978 Raises:
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200979 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400980 """
981 assert len(fill_data) == 4
982 assert size % 4 == 0
983 assert size % self.block_size == 0
984
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200985 if self._read_only:
986 raise OSError('ImageHandler is in read-only mode.')
987
David Zeuthena4fee8b2016-08-22 15:20:43 -0400988 if not self.is_sparse:
989 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100990 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400991 self._read_header()
992 return
993
994 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100995 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400996 self._update_chunks_and_blocks()
997
998 self._image.seek(self._sparse_end, os.SEEK_SET)
999 self._image.write(struct.pack(ImageChunk.FORMAT,
1000 ImageChunk.TYPE_FILL,
1001 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +01001002 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001003 4 + struct.calcsize(ImageChunk.FORMAT)))
1004 self._image.write(fill_data)
1005 self._read_header()
1006
1007 def seek(self, offset):
1008 """Sets the cursor position for reading from unsparsified file.
1009
1010 Arguments:
1011 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +01001012
1013 Raises:
1014 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001015 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07001016 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +02001017 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -04001018 self._file_pos = offset
1019
1020 def read(self, size):
1021 """Reads data from the unsparsified file.
1022
1023 This method may return fewer than |size| bytes of data if the end
1024 of the file was encountered.
1025
1026 The file cursor for reading is advanced by the number of bytes
1027 read.
1028
1029 Arguments:
1030 size: Number of bytes to read.
1031
1032 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +02001033 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001034 """
1035 if not self.is_sparse:
1036 self._image.seek(self._file_pos)
1037 data = self._image.read(size)
1038 self._file_pos += len(data)
1039 return data
1040
1041 # Iterate over all chunks.
1042 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1043 self._file_pos) - 1
1044 data = bytearray()
1045 to_go = size
1046 while to_go > 0:
1047 chunk = self._chunks[chunk_idx]
1048 chunk_pos_offset = self._file_pos - chunk.output_offset
1049 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1050
1051 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1052 self._image.seek(chunk.input_offset + chunk_pos_offset)
1053 data.extend(self._image.read(chunk_pos_to_go))
1054 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001055 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001056 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1057 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1058 else:
1059 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001060 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001061
1062 to_go -= chunk_pos_to_go
1063 self._file_pos += chunk_pos_to_go
1064 chunk_idx += 1
1065 # Generate partial read in case of EOF.
1066 if chunk_idx >= len(self._chunks):
1067 break
1068
Jan Monsch8347da92020-04-08 12:41:49 +02001069 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001070
1071 def tell(self):
1072 """Returns the file cursor position for reading from unsparsified file.
1073
1074 Returns:
1075 The file cursor position for reading.
1076 """
1077 return self._file_pos
1078
1079 def truncate(self, size):
1080 """Truncates the unsparsified file.
1081
1082 Arguments:
1083 size: Desired size of unsparsified file.
1084
1085 Raises:
1086 ValueError: If desired size isn't a multiple of the block size.
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001087 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001088 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001089 if self._read_only:
1090 raise OSError('ImageHandler is in read-only mode.')
1091
David Zeuthena4fee8b2016-08-22 15:20:43 -04001092 if not self.is_sparse:
1093 self._image.truncate(size)
1094 self._read_header()
1095 return
1096
1097 if size % self.block_size != 0:
1098 raise ValueError('Cannot truncate to a size which is not a multiple '
1099 'of the block size')
1100
1101 if size == self.image_size:
1102 # Trivial where there's nothing to do.
1103 return
Jan Monsch9c130122020-04-14 13:43:51 +02001104
1105 if size < self.image_size:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001106 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1107 chunk = self._chunks[chunk_idx]
1108 if chunk.output_offset != size:
1109 # Truncation in the middle of a trunk - need to keep the chunk
1110 # and modify it.
1111 chunk_idx_for_update = chunk_idx + 1
1112 num_to_keep = size - chunk.output_offset
1113 assert num_to_keep % self.block_size == 0
1114 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1115 truncate_at = (chunk.chunk_offset +
1116 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1117 data_sz = num_to_keep
1118 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1119 truncate_at = (chunk.chunk_offset +
1120 struct.calcsize(ImageChunk.FORMAT) + 4)
1121 data_sz = 4
1122 else:
1123 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1124 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1125 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001126 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001127 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1128 self._image.seek(chunk.chunk_offset)
1129 self._image.write(struct.pack(ImageChunk.FORMAT,
1130 chunk.chunk_type,
1131 0, # Reserved
1132 chunk_sz,
1133 total_sz))
1134 chunk.output_size = num_to_keep
1135 else:
1136 # Truncation at trunk boundary.
1137 truncate_at = chunk.chunk_offset
1138 chunk_idx_for_update = chunk_idx
1139
1140 self._num_total_chunks = chunk_idx_for_update
1141 self._num_total_blocks = 0
1142 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001143 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001144 self._update_chunks_and_blocks()
1145 self._image.truncate(truncate_at)
1146
1147 # We've modified the file so re-read all data.
1148 self._read_header()
1149 else:
1150 # Truncating to grow - just add a DONT_CARE section.
1151 self.append_dont_care(size - self.image_size)
1152
1153
David Zeuthen21e95262016-07-27 17:58:40 -04001154class AvbDescriptor(object):
1155 """Class for AVB descriptor.
1156
1157 See the |AvbDescriptor| C struct for more information.
1158
1159 Attributes:
1160 tag: The tag identifying what kind of descriptor this is.
1161 data: The data in the descriptor.
1162 """
1163
1164 SIZE = 16
1165 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1166
1167 def __init__(self, data):
1168 """Initializes a new property descriptor.
1169
1170 Arguments:
1171 data: If not None, must be a bytearray().
1172
1173 Raises:
1174 LookupError: If the given descriptor is malformed.
1175 """
1176 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1177
1178 if data:
1179 (self.tag, num_bytes_following) = (
1180 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1181 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1182 else:
1183 self.tag = None
1184 self.data = None
1185
1186 def print_desc(self, o):
1187 """Print the descriptor.
1188
1189 Arguments:
1190 o: The object to write the output to.
1191 """
1192 o.write(' Unknown descriptor:\n')
1193 o.write(' Tag: {}\n'.format(self.tag))
1194 if len(self.data) < 256:
1195 o.write(' Data: {} ({} bytes)\n'.format(
1196 repr(str(self.data)), len(self.data)))
1197 else:
1198 o.write(' Data: {} bytes\n'.format(len(self.data)))
1199
1200 def encode(self):
1201 """Serializes the descriptor.
1202
1203 Returns:
1204 A bytearray() with the descriptor data.
1205 """
1206 num_bytes_following = len(self.data)
1207 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1208 padding_size = nbf_with_padding - num_bytes_following
1209 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1210 padding = struct.pack(str(padding_size) + 'x')
1211 ret = desc + self.data + padding
1212 return bytearray(ret)
1213
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001214 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001215 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001216 """Verifies contents of the descriptor - used in verify_image sub-command.
1217
1218 Arguments:
1219 image_dir: The directory of the file being verified.
1220 image_ext: The extension of the file being verified (e.g. '.img').
1221 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001222 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001223 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001224 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1225 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001226
1227 Returns:
1228 True if the descriptor verifies, False otherwise.
1229 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001230 # Deletes unused parameters to prevent pylint warning unused-argument.
1231 del image_dir, image_ext, expected_chain_partitions_map
1232 del image_containing_descriptor, accept_zeroed_hashtree
1233
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001234 # Nothing to do.
1235 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001236
Jan Monscheeb28b62019-12-05 16:17:09 +01001237
David Zeuthen21e95262016-07-27 17:58:40 -04001238class AvbPropertyDescriptor(AvbDescriptor):
1239 """A class for property descriptors.
1240
1241 See the |AvbPropertyDescriptor| C struct for more information.
1242
1243 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001244 key: The key as string.
1245 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001246 """
1247
1248 TAG = 0
1249 SIZE = 32
1250 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001251 'Q' # key size (bytes)
1252 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001253
1254 def __init__(self, data=None):
1255 """Initializes a new property descriptor.
1256
1257 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001258 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001259
1260 Raises:
1261 LookupError: If the given descriptor is malformed.
1262 """
Jan Monsch7a722ee2021-06-17 11:49:05 +02001263 super().__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001264 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1265
1266 if data:
1267 (tag, num_bytes_following, key_size,
1268 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1269 expected_size = round_to_multiple(
1270 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1271 if tag != self.TAG or num_bytes_following != expected_size:
1272 raise LookupError('Given data does not look like a property '
1273 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001274 try:
1275 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1276 except UnicodeDecodeError as e:
Jan Monsch7a722ee2021-06-17 11:49:05 +02001277 raise LookupError('Key cannot be decoded as UTF-8: {}.'
1278 .format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04001279 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1280 value_size)]
1281 else:
1282 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001283 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001284
1285 def print_desc(self, o):
1286 """Print the descriptor.
1287
1288 Arguments:
1289 o: The object to write the output to.
1290 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001291 # Go forward with python 3, bytes are represented with the 'b' prefix,
1292 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1293 # the same between python 2 and python 3.
1294 printable_value = repr(self.value)
1295 if printable_value.startswith('b\''):
1296 printable_value = printable_value[1:]
1297
David Zeuthen21e95262016-07-27 17:58:40 -04001298 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001299 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001300 else:
1301 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1302
1303 def encode(self):
1304 """Serializes the descriptor.
1305
1306 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001307 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001308 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001309 key_encoded = self.key.encode('utf-8')
1310 num_bytes_following = (
1311 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001312 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1313 padding_size = nbf_with_padding - num_bytes_following
1314 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001315 len(key_encoded), len(self.value))
1316 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1317 padding_size * b'\0')
1318 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001319
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001320 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001321 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001322 """Verifies contents of the descriptor - used in verify_image sub-command.
1323
1324 Arguments:
1325 image_dir: The directory of the file being verified.
1326 image_ext: The extension of the file being verified (e.g. '.img').
1327 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001328 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001329 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001330 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1331 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001332
1333 Returns:
1334 True if the descriptor verifies, False otherwise.
1335 """
1336 # Nothing to do.
1337 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001338
Jan Monscheeb28b62019-12-05 16:17:09 +01001339
David Zeuthen21e95262016-07-27 17:58:40 -04001340class AvbHashtreeDescriptor(AvbDescriptor):
1341 """A class for hashtree descriptors.
1342
1343 See the |AvbHashtreeDescriptor| C struct for more information.
1344
1345 Attributes:
1346 dm_verity_version: dm-verity version used.
1347 image_size: Size of the image, after rounding up to |block_size|.
1348 tree_offset: Offset of the hash tree in the file.
1349 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001350 data_block_size: Data block size.
1351 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001352 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1353 fec_offset: Offset of FEC data (0 if FEC is not used).
1354 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001355 hash_algorithm: Hash algorithm used as string.
1356 partition_name: Partition name as string.
1357 salt: Salt used as bytes.
1358 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001359 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001360 """
1361
1362 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001363 RESERVED = 60
1364 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001365 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001366 'L' # dm-verity version used
1367 'Q' # image size (bytes)
1368 'Q' # tree offset (bytes)
1369 'Q' # tree size (bytes)
1370 'L' # data block size (bytes)
1371 'L' # hash block size (bytes)
1372 'L' # FEC number of roots
1373 'Q' # FEC offset (bytes)
1374 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001375 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001376 'L' # partition name (bytes)
1377 'L' # salt length (bytes)
1378 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001379 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001380 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001381
JeongHyeon Lee2998a352021-05-25 16:38:07 +09001382 FLAGS_DO_NOT_USE_AB = (1 << 0)
1383 FLAGS_CHECK_AT_MOST_ONCE = (1 << 1)
1384
David Zeuthen21e95262016-07-27 17:58:40 -04001385 def __init__(self, data=None):
1386 """Initializes a new hashtree descriptor.
1387
1388 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001389 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001390
1391 Raises:
1392 LookupError: If the given descriptor is malformed.
1393 """
Jan Monsch7a722ee2021-06-17 11:49:05 +02001394 super().__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001395 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1396
1397 if data:
1398 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1399 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001400 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1401 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001402 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1403 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001404 expected_size = round_to_multiple(
1405 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1406 if tag != self.TAG or num_bytes_following != expected_size:
1407 raise LookupError('Given data does not look like a hashtree '
1408 'descriptor.')
1409 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001410 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001411 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001412 try:
1413 self.partition_name = data[
1414 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1415 ].decode('utf-8')
1416 except UnicodeDecodeError as e:
1417 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
Jan Monsch7a722ee2021-06-17 11:49:05 +02001418 .format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04001419 o += partition_name_len
1420 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1421 o += salt_len
1422 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Tianjie62ad0222021-02-22 14:31:12 -08001423
1424 if root_digest_len != self._hashtree_digest_size():
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001425 if root_digest_len != 0:
1426 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001427
1428 else:
1429 self.dm_verity_version = 0
1430 self.image_size = 0
1431 self.tree_offset = 0
1432 self.tree_size = 0
1433 self.data_block_size = 0
1434 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001435 self.fec_num_roots = 0
1436 self.fec_offset = 0
1437 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001438 self.hash_algorithm = ''
1439 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001440 self.salt = b''
1441 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001442 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001443
Tianjie62ad0222021-02-22 14:31:12 -08001444 def _hashtree_digest_size(self):
1445 return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
1446
David Zeuthen21e95262016-07-27 17:58:40 -04001447 def print_desc(self, o):
1448 """Print the descriptor.
1449
1450 Arguments:
1451 o: The object to write the output to.
1452 """
1453 o.write(' Hashtree descriptor:\n')
1454 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1455 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1456 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1457 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1458 o.write(' Data Block Size: {} bytes\n'.format(
1459 self.data_block_size))
1460 o.write(' Hash Block Size: {} bytes\n'.format(
1461 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001462 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1463 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1464 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001465 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1466 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001467 o.write(' Salt: {}\n'.format(self.salt.hex()))
1468 o.write(' Root Digest: {}\n'.format(self.root_digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001469 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001470
1471 def encode(self):
1472 """Serializes the descriptor.
1473
1474 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001475 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001476 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001477 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1478 partition_name_encoded = self.partition_name.encode('utf-8')
1479 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1480 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001481 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1482 padding_size = nbf_with_padding - num_bytes_following
1483 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1484 self.dm_verity_version, self.image_size,
1485 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001486 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001487 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1488 len(partition_name_encoded), len(self.salt),
1489 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1490 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1491 padding_size * b'\0')
1492 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001493
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001494 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001495 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001496 """Verifies contents of the descriptor - used in verify_image sub-command.
1497
1498 Arguments:
1499 image_dir: The directory of the file being verified.
1500 image_ext: The extension of the file being verified (e.g. '.img').
1501 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001502 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001503 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001504 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1505 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001506
1507 Returns:
1508 True if the descriptor verifies, False otherwise.
1509 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001510 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001511 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001512 image = image_containing_descriptor
1513 else:
1514 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001515 image = ImageHandler(image_filename, read_only=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001516 # Generate the hashtree and checks that it matches what's in the file.
Tianjie62ad0222021-02-22 14:31:12 -08001517 digest_size = self._hashtree_digest_size()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001518 digest_padding = round_to_pow2(digest_size) - digest_size
1519 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001520 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001521 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1522 self.data_block_size,
1523 self.hash_algorithm, self.salt,
1524 digest_padding,
1525 hash_level_offsets,
1526 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001527 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001528 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001529 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1530 format(image_filename))
1531 return False
1532 # ... also check that the on-disk hashtree matches
1533 image.seek(self.tree_offset)
1534 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001535 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001536 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001537 print('{}: skipping verification since hashtree is zeroed and '
1538 '--accept_zeroed_hashtree was given'
1539 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001540 else:
1541 if hash_tree != hash_tree_ondisk:
1542 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001543 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001544 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001545 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1546 .format(self.partition_name, self.hash_algorithm, image.filename,
1547 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001548 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1549 # correct but this a) currently requires the 'fec' binary; and b) takes a
1550 # long time; and c) is not strictly needed for verification purposes as
1551 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001552 return True
1553
David Zeuthen21e95262016-07-27 17:58:40 -04001554
1555class AvbHashDescriptor(AvbDescriptor):
1556 """A class for hash descriptors.
1557
1558 See the |AvbHashDescriptor| C struct for more information.
1559
1560 Attributes:
1561 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001562 hash_algorithm: Hash algorithm used as string.
1563 partition_name: Partition name as string.
1564 salt: Salt used as bytes.
1565 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001566 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001567 """
1568
1569 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001570 RESERVED = 60
1571 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001572 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001573 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001574 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001575 'L' # partition name (bytes)
1576 'L' # salt length (bytes)
1577 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001578 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001579 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001580
1581 def __init__(self, data=None):
1582 """Initializes a new hash descriptor.
1583
1584 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001585 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001586
1587 Raises:
1588 LookupError: If the given descriptor is malformed.
1589 """
Jan Monsch7a722ee2021-06-17 11:49:05 +02001590 super().__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001591 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1592
1593 if data:
1594 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1595 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001596 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1597 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001598 expected_size = round_to_multiple(
1599 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1600 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001601 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001602 # Nuke NUL-bytes at the end.
Jan Monschee6fccd2020-04-09 19:36:13 +02001603 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001604 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001605 try:
1606 self.partition_name = data[
1607 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1608 ].decode('utf-8')
1609 except UnicodeDecodeError as e:
1610 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
Jan Monsch7a722ee2021-06-17 11:49:05 +02001611 .format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04001612 o += partition_name_len
1613 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1614 o += salt_len
1615 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001616 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001617 if digest_len != 0:
1618 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001619
1620 else:
1621 self.image_size = 0
1622 self.hash_algorithm = ''
1623 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001624 self.salt = b''
1625 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001626 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001627
1628 def print_desc(self, o):
1629 """Print the descriptor.
1630
1631 Arguments:
1632 o: The object to write the output to.
1633 """
1634 o.write(' Hash descriptor:\n')
1635 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1636 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1637 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001638 o.write(' Salt: {}\n'.format(self.salt.hex()))
1639 o.write(' Digest: {}\n'.format(self.digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001640 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001641
1642 def encode(self):
1643 """Serializes the descriptor.
1644
1645 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001646 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001647 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001648 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1649 partition_name_encoded = self.partition_name.encode('utf-8')
1650 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1651 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001652 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1653 padding_size = nbf_with_padding - num_bytes_following
1654 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001655 self.image_size, hash_algorithm_encoded,
1656 len(partition_name_encoded), len(self.salt),
1657 len(self.digest), self.flags, self.RESERVED * b'\0')
1658 ret = (desc + partition_name_encoded + self.salt + self.digest +
1659 padding_size * b'\0')
1660 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001661
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001662 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001663 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001664 """Verifies contents of the descriptor - used in verify_image sub-command.
1665
1666 Arguments:
1667 image_dir: The directory of the file being verified.
1668 image_ext: The extension of the file being verified (e.g. '.img').
1669 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001670 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001671 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001672 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1673 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001674
1675 Returns:
1676 True if the descriptor verifies, False otherwise.
1677 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001678 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001679 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001680 image = image_containing_descriptor
1681 else:
1682 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001683 image = ImageHandler(image_filename, read_only=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001684 data = image.read(self.image_size)
1685 ha = hashlib.new(self.hash_algorithm)
1686 ha.update(self.salt)
1687 ha.update(data)
1688 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001689 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001690 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001691 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1692 format(self.hash_algorithm, image_filename))
1693 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001694 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1695 .format(self.partition_name, self.hash_algorithm, image.filename,
1696 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001697 return True
1698
David Zeuthen21e95262016-07-27 17:58:40 -04001699
1700class AvbKernelCmdlineDescriptor(AvbDescriptor):
1701 """A class for kernel command-line descriptors.
1702
1703 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1704
1705 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001706 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001707 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001708 """
1709
1710 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001711 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001712 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001713 'L' # flags
1714 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001715
David Zeuthenfd41eb92016-11-17 12:24:47 -05001716 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1717 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1718
David Zeuthen21e95262016-07-27 17:58:40 -04001719 def __init__(self, data=None):
1720 """Initializes a new kernel cmdline descriptor.
1721
1722 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001723 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001724
1725 Raises:
1726 LookupError: If the given descriptor is malformed.
1727 """
Jan Monsch7a722ee2021-06-17 11:49:05 +02001728 super().__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001729 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1730
1731 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001732 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001733 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1734 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1735 8)
1736 if tag != self.TAG or num_bytes_following != expected_size:
1737 raise LookupError('Given data does not look like a kernel cmdline '
1738 'descriptor.')
1739 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001740 try:
1741 self.kernel_cmdline = data[
1742 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1743 except UnicodeDecodeError as e:
1744 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
Jan Monsch7a722ee2021-06-17 11:49:05 +02001745 .format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04001746 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001747 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001748 self.kernel_cmdline = ''
1749
1750 def print_desc(self, o):
1751 """Print the descriptor.
1752
1753 Arguments:
1754 o: The object to write the output to.
1755 """
1756 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001757 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001758 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001759
1760 def encode(self):
1761 """Serializes the descriptor.
1762
1763 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001764 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001765 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001766 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1767 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001768 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1769 padding_size = nbf_with_padding - num_bytes_following
1770 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001771 self.flags, len(kernel_cmd_encoded))
1772 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1773 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001774
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001775 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001776 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001777 """Verifies contents of the descriptor - used in verify_image sub-command.
1778
1779 Arguments:
1780 image_dir: The directory of the file being verified.
1781 image_ext: The extension of the file being verified (e.g. '.img').
1782 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001783 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001784 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001785 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1786 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001787
1788 Returns:
1789 True if the descriptor verifies, False otherwise.
1790 """
1791 # Nothing to verify.
1792 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001793
Jan Monscheeb28b62019-12-05 16:17:09 +01001794
David Zeuthen21e95262016-07-27 17:58:40 -04001795class AvbChainPartitionDescriptor(AvbDescriptor):
1796 """A class for chained partition descriptors.
1797
1798 See the |AvbChainPartitionDescriptor| C struct for more information.
1799
1800 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001801 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001802 partition_name: Partition name as string.
1803 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001804 """
1805
1806 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001807 RESERVED = 64
1808 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001809 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001810 'L' # rollback_index_location
1811 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001812 'L' + # public_key_size (bytes)
1813 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001814
1815 def __init__(self, data=None):
1816 """Initializes a new chain partition descriptor.
1817
1818 Arguments:
1819 data: If not None, must be a bytearray of size |SIZE|.
1820
1821 Raises:
1822 LookupError: If the given descriptor is malformed.
1823 """
1824 AvbDescriptor.__init__(self, None)
1825 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1826
1827 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001828 (tag, num_bytes_following, self.rollback_index_location,
1829 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001830 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001831 expected_size = round_to_multiple(
1832 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1833 if tag != self.TAG or num_bytes_following != expected_size:
1834 raise LookupError('Given data does not look like a chain partition '
1835 'descriptor.')
1836 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001837 try:
1838 self.partition_name = data[
1839 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1840 ].decode('utf-8')
1841 except UnicodeDecodeError as e:
1842 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
Jan Monsch7a722ee2021-06-17 11:49:05 +02001843 .format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04001844 o += partition_name_len
1845 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1846
1847 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001848 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001849 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001850 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001851
1852 def print_desc(self, o):
1853 """Print the descriptor.
1854
1855 Arguments:
1856 o: The object to write the output to.
1857 """
1858 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001859 o.write(' Partition Name: {}\n'.format(self.partition_name))
1860 o.write(' Rollback Index Location: {}\n'.format(
1861 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001862 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001863 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1864 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001865
1866 def encode(self):
1867 """Serializes the descriptor.
1868
1869 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001870 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001871 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001872 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001873 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001874 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001875 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1876 padding_size = nbf_with_padding - num_bytes_following
1877 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001878 self.rollback_index_location,
1879 len(partition_name_encoded), len(self.public_key),
1880 self.RESERVED * b'\0')
1881 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1882 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001883
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001884 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001885 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001886 """Verifies contents of the descriptor - used in verify_image sub-command.
1887
1888 Arguments:
1889 image_dir: The directory of the file being verified.
1890 image_ext: The extension of the file being verified (e.g. '.img').
1891 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001892 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001893 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001894 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1895 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001896
1897 Returns:
1898 True if the descriptor verifies, False otherwise.
1899 """
1900 value = expected_chain_partitions_map.get(self.partition_name)
1901 if not value:
1902 sys.stderr.write('No expected chain partition for partition {}. Use '
1903 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001904 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001905 format(self.partition_name))
1906 return False
1907 rollback_index_location, pk_blob = value
1908
1909 if self.rollback_index_location != rollback_index_location:
1910 sys.stderr.write('Expected rollback_index_location {} does not '
1911 'match {} in descriptor for partition {}\n'.
1912 format(rollback_index_location,
1913 self.rollback_index_location,
1914 self.partition_name))
1915 return False
1916
1917 if self.public_key != pk_blob:
1918 sys.stderr.write('Expected public key blob does not match public '
1919 'key blob in descriptor for partition {}\n'.
1920 format(self.partition_name))
1921 return False
1922
Jan Monsch23e0c622019-12-11 11:23:58 +01001923 print('{}: Successfully verified chain partition descriptor matches '
1924 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001925
1926 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001927
1928DESCRIPTOR_CLASSES = [
1929 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1930 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1931]
1932
1933
1934def parse_descriptors(data):
1935 """Parses a blob of data into descriptors.
1936
1937 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001938 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001939
1940 Returns:
1941 A list of instances of objects derived from AvbDescriptor. For
1942 unknown descriptors, the class AvbDescriptor is used.
1943 """
1944 o = 0
1945 ret = []
1946 while o < len(data):
1947 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1948 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001949 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001950 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001951 clazz = AvbDescriptor
1952 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001953 o += 16 + nb_following
1954 return ret
1955
1956
1957class AvbFooter(object):
1958 """A class for parsing and writing footers.
1959
1960 Footers are stored at the end of partitions and point to where the
1961 AvbVBMeta blob is located. They also contain the original size of
1962 the image before AVB information was added.
1963
1964 Attributes:
1965 magic: Magic for identifying the footer, see |MAGIC|.
1966 version_major: The major version of avbtool that wrote the footer.
1967 version_minor: The minor version of avbtool that wrote the footer.
1968 original_image_size: Original image size.
1969 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1970 vbmeta_size: Size of the AvbVBMeta blob.
1971 """
1972
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001973 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001974 SIZE = 64
1975 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001976 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1977 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001978 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001979 'Q' # Original image size.
1980 'Q' # Offset of VBMeta blob.
1981 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001982 str(RESERVED) + 'x') # padding for reserved bytes
1983
1984 def __init__(self, data=None):
1985 """Initializes a new footer object.
1986
1987 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001988 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001989
1990 Raises:
1991 LookupError: If the given footer is malformed.
1992 struct.error: If the given data has no footer.
1993 """
1994 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1995
1996 if data:
1997 (self.magic, self.version_major, self.version_minor,
1998 self.original_image_size, self.vbmeta_offset,
1999 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
2000 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002001 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04002002 else:
2003 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002004 self.version_major = self.FOOTER_VERSION_MAJOR
2005 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04002006 self.original_image_size = 0
2007 self.vbmeta_offset = 0
2008 self.vbmeta_size = 0
2009
David Zeuthena4fee8b2016-08-22 15:20:43 -04002010 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002011 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002012
David Zeuthena4fee8b2016-08-22 15:20:43 -04002013 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002014 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002015 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002016 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
2017 self.version_minor, self.original_image_size,
2018 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002019
2020
2021class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002022 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002023
Jan Monschfe00c0a2019-12-11 11:19:40 +01002024 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2025 avb_vbmeta_image.h.
2026
David Zeuthen21e95262016-07-27 17:58:40 -04002027 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002028 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2029 required_libavb_version_major: The major version of libavb required for this
2030 header.
2031 required_libavb_version_minor: The minor version of libavb required for this
2032 header.
2033 authentication_data_block_size: The size of the signature block.
2034 auxiliary_data_block_size: The size of the auxiliary data block.
2035 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2036 enum.
2037 hash_offset: Offset into the "Authentication data" block of hash data.
2038 hash_size: Length of the hash data.
2039 signature_offset: Offset into the "Authentication data" block of signature
2040 data.
2041 signature_size: Length of the signature data.
2042 public_key_offset: Offset into the "Auxiliary data" block of public key
2043 data.
2044 public_key_size: Length of the public key data.
2045 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2046 key metadata.
2047 public_key_metadata_size: Length of the public key metadata. Must be set to
2048 zero if there is no public key metadata.
2049 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2050 data.
2051 descriptors_size: Length of descriptor data.
2052 rollback_index: The rollback index which can be used to prevent rollback to
2053 older versions.
2054 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2055 zero if the vbmeta image is not a top-level image.
Varun Sharmade538272020-04-10 15:22:31 -07002056 rollback_index_location: The location of the rollback index defined in this
2057 header. Only valid for the main vbmeta. For chained partitions, the
2058 rollback index location must be specified in the
2059 AvbChainPartitionDescriptor and this value must be set to 0.
Jan Monschfe00c0a2019-12-11 11:19:40 +01002060 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2061 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2062 terminated. Applications must not make assumptions about how this
2063 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002064 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002065 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002066 SIZE = 256
2067
Varun Sharmade538272020-04-10 15:22:31 -07002068 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthene3cadca2017-02-22 21:25:46 -05002069 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002070
2071 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002072 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2073 '2Q' # 2 x block size
2074 'L' # algorithm type
2075 '2Q' # offset, size (hash)
2076 '2Q' # offset, size (signature)
2077 '2Q' # offset, size (public key)
2078 '2Q' # offset, size (public key metadata)
2079 '2Q' # offset, size (descriptors)
2080 'Q' # rollback_index
Varun Sharmade538272020-04-10 15:22:31 -07002081 'L' # flags
2082 'L' # rollback_index_location
David Zeuthene3cadca2017-02-22 21:25:46 -05002083 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002084 str(RESERVED) + 'x') # padding for reserved bytes
2085
2086 def __init__(self, data=None):
2087 """Initializes a new header object.
2088
2089 Arguments:
2090 data: If not None, must be a bytearray of size 8192.
2091
2092 Raises:
2093 Exception: If the given data is malformed.
2094 """
2095 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2096
2097 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002098 (self.magic, self.required_libavb_version_major,
2099 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002100 self.authentication_data_block_size, self.auxiliary_data_block_size,
2101 self.algorithm_type, self.hash_offset, self.hash_size,
2102 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002103 self.public_key_size, self.public_key_metadata_offset,
2104 self.public_key_metadata_size, self.descriptors_offset,
2105 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002106 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002107 self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002108 self.rollback_index_location,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002109 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002110 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002111 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002112 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002113 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002114 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002115 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002116 # Start by just requiring version 1.0. Code that adds features
2117 # in a future version can use bump_required_libavb_version_minor() to
2118 # bump the minor.
2119 self.required_libavb_version_major = AVB_VERSION_MAJOR
2120 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002121 self.authentication_data_block_size = 0
2122 self.auxiliary_data_block_size = 0
2123 self.algorithm_type = 0
2124 self.hash_offset = 0
2125 self.hash_size = 0
2126 self.signature_offset = 0
2127 self.signature_size = 0
2128 self.public_key_offset = 0
2129 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002130 self.public_key_metadata_offset = 0
2131 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002132 self.descriptors_offset = 0
2133 self.descriptors_size = 0
2134 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002135 self.flags = 0
Varun Sharmade538272020-04-10 15:22:31 -07002136 self.rollback_index_location = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002137 self.release_string = get_release_string()
2138
2139 def bump_required_libavb_version_minor(self, minor):
2140 """Function to bump required_libavb_version_minor.
2141
2142 Call this when writing data that requires a specific libavb
2143 version to parse it.
2144
2145 Arguments:
2146 minor: The minor version of libavb that has support for the feature.
2147 """
2148 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002149 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002150
David Zeuthen21e95262016-07-27 17:58:40 -04002151 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002152 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002153
2154 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002155 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002156 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002157 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002158 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002159 self.required_libavb_version_major,
2160 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002161 self.authentication_data_block_size,
2162 self.auxiliary_data_block_size, self.algorithm_type,
2163 self.hash_offset, self.hash_size, self.signature_offset,
2164 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002165 self.public_key_size, self.public_key_metadata_offset,
2166 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002167 self.descriptors_size, self.rollback_index, self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002168 self.rollback_index_location, release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002169
2170
2171class Avb(object):
2172 """Business logic for avbtool command-line tool."""
2173
David Zeuthen8b6973b2016-09-20 12:39:49 -04002174 # Keep in sync with avb_ab_flow.h.
2175 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002176 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002177 AB_MAJOR_VERSION = 1
2178 AB_MINOR_VERSION = 0
2179 AB_MISC_METADATA_OFFSET = 2048
2180
David Zeuthen09692692016-09-30 16:16:40 -04002181 # Constants for maximum metadata size. These are used to give
2182 # meaningful errors if the value passed in via --partition_size is
2183 # too small and when --calc_max_image_size is used. We use
2184 # conservative figures.
2185 MAX_VBMETA_SIZE = 64 * 1024
2186 MAX_FOOTER_SIZE = 4096
2187
Jan Monsch2c7be992020-04-03 14:37:13 +02002188 def generate_test_image(self, output, image_size, start_byte):
2189 """Generates a test image for testing avbtool with known content.
2190
2191 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2192
2193 Arguments:
2194 output: Write test image to this file.
2195 image_size: The size of the requested file in bytes.
2196 start_byte: The integer value of the start byte to use for pattern
2197 generation.
2198 """
2199 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2200 buf = bytearray()
2201 c = int(math.ceil(image_size / 256.0))
2202 for _ in range(0, c):
2203 buf.extend(pattern)
2204 output.write(buf[0:image_size])
2205
David Zeuthen49936b42018-08-07 17:38:58 -04002206 def extract_vbmeta_image(self, output, image_filename, padding_size):
2207 """Implements the 'extract_vbmeta_image' command.
2208
2209 Arguments:
2210 output: Write vbmeta struct to this file.
2211 image_filename: File to extract vbmeta data from (with a footer).
2212 padding_size: If not 0, pads output so size is a multiple of the number.
2213
2214 Raises:
2215 AvbError: If there's no footer in the image.
2216 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002217 image = ImageHandler(image_filename, read_only=True)
David Zeuthen49936b42018-08-07 17:38:58 -04002218 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002219 if not footer:
2220 raise AvbError('Given image does not have a footer.')
2221
2222 image.seek(footer.vbmeta_offset)
2223 vbmeta_blob = image.read(footer.vbmeta_size)
2224 output.write(vbmeta_blob)
2225
2226 if padding_size > 0:
2227 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2228 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002229 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002230
David Zeuthena4fee8b2016-08-22 15:20:43 -04002231 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002232 """Implements the 'erase_footer' command.
2233
2234 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002235 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002236 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002237
2238 Raises:
2239 AvbError: If there's no footer in the image.
2240 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002241 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002242 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002243 if not footer:
2244 raise AvbError('Given image does not have a footer.')
2245
2246 new_image_size = None
2247 if not keep_hashtree:
2248 new_image_size = footer.original_image_size
2249 else:
2250 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002251 # descriptor to figure out the location and size of the hashtree
2252 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002253 for desc in descriptors:
2254 if isinstance(desc, AvbHashtreeDescriptor):
2255 # The hashtree is always just following the main data so the
2256 # new size is easily derived.
2257 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002258 # If the image has FEC codes, also keep those.
2259 if desc.fec_offset > 0:
2260 fec_end = desc.fec_offset + desc.fec_size
2261 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002262 break
2263 if not new_image_size:
2264 raise AvbError('Requested to keep hashtree but no hashtree '
2265 'descriptor was found.')
2266
2267 # And cut...
2268 image.truncate(new_image_size)
2269
David Zeuthen1394f762019-04-30 10:20:11 -04002270 def zero_hashtree(self, image_filename):
2271 """Implements the 'zero_hashtree' command.
2272
2273 Arguments:
2274 image_filename: File to zero hashtree and FEC data from.
2275
2276 Raises:
2277 AvbError: If there's no footer in the image.
2278 """
David Zeuthen1394f762019-04-30 10:20:11 -04002279 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002280 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002281 if not footer:
2282 raise AvbError('Given image does not have a footer.')
2283
2284 # Search for a hashtree descriptor to figure out the location and
2285 # size of the hashtree and FEC.
2286 ht_desc = None
2287 for desc in descriptors:
2288 if isinstance(desc, AvbHashtreeDescriptor):
2289 ht_desc = desc
2290 break
2291
2292 if not ht_desc:
2293 raise AvbError('No hashtree descriptor was found.')
2294
2295 zero_ht_start_offset = ht_desc.tree_offset
2296 zero_ht_num_bytes = ht_desc.tree_size
2297 zero_fec_start_offset = None
2298 zero_fec_num_bytes = 0
2299 if ht_desc.fec_offset > 0:
2300 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2301 raise AvbError('Hash-tree and FEC data must be adjacent.')
2302 zero_fec_start_offset = ht_desc.fec_offset
2303 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002304 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2305 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002306 image.seek(zero_end_offset)
2307 data = image.read(image.image_size - zero_end_offset)
2308
2309 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2310 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2311 # beginning of both hashtree and FEC. (That way, in the future we can add
2312 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2313 #
2314 # Applications can use these markers to detect that the hashtree and/or
2315 # FEC needs to be recomputed.
2316 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002317 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002318 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002319 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002320 if zero_fec_start_offset:
2321 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002322 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002323 image.append_raw(data)
2324
David Zeuthen2bc232b2017-04-19 14:25:19 -04002325 def resize_image(self, image_filename, partition_size):
2326 """Implements the 'resize_image' command.
2327
2328 Arguments:
2329 image_filename: File with footer to resize.
2330 partition_size: The new size of the image.
2331
2332 Raises:
2333 AvbError: If there's no footer in the image.
2334 """
2335
2336 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002337 if partition_size % image.block_size != 0:
2338 raise AvbError('Partition size of {} is not a multiple of the image '
2339 'block size {}.'.format(partition_size,
2340 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002341 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002342 if not footer:
2343 raise AvbError('Given image does not have a footer.')
2344
2345 # The vbmeta blob is always at the end of the data so resizing an
2346 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002347 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2348 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002349 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2350 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002351
Jan Monschb1d920f2020-04-09 12:59:28 +02002352 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002353 raise AvbError('Requested size of {} is too small for an image '
2354 'of size {}.'
2355 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002356 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002357
2358 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2359 # with enough bytes such that the final Footer block is at the end
2360 # of partition_size.
2361 image.truncate(vbmeta_end_offset)
2362 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002363 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002364
2365 # Just reuse the same footer - only difference is that we're
2366 # writing it in a different place.
2367 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002368 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002369 footer_blob)
2370 image.append_raw(footer_blob_with_padding)
2371
David Zeuthen8b6973b2016-09-20 12:39:49 -04002372 def set_ab_metadata(self, misc_image, slot_data):
2373 """Implements the 'set_ab_metadata' command.
2374
2375 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2376 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2377
2378 Arguments:
2379 misc_image: The misc image to write to.
2380 slot_data: Slot data as a string
2381
2382 Raises:
2383 AvbError: If slot data is malformed.
2384 """
2385 tokens = slot_data.split(':')
2386 if len(tokens) != 6:
2387 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2388 a_priority = int(tokens[0])
2389 a_tries_remaining = int(tokens[1])
Jan Monsch9c130122020-04-14 13:43:51 +02002390 a_success = int(tokens[2]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002391 b_priority = int(tokens[3])
2392 b_tries_remaining = int(tokens[4])
Jan Monsch9c130122020-04-14 13:43:51 +02002393 b_success = int(tokens[5]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002394
2395 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2396 self.AB_MAGIC,
2397 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2398 a_priority, a_tries_remaining, a_success,
2399 b_priority, b_tries_remaining, b_success)
2400 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2401 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2402 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2403 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2404 misc_image.write(ab_data)
2405
Sen Jiang01553a22020-06-30 17:58:44 -07002406 def info_image(self, image_filename, output, atx):
David Zeuthen21e95262016-07-27 17:58:40 -04002407 """Implements the 'info_image' command.
2408
2409 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002410 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002411 output: Output file to write human-readable information to (file object).
Sen Jiang01553a22020-06-30 17:58:44 -07002412 atx: If True, show information about Android Things eXtension (ATX).
David Zeuthen21e95262016-07-27 17:58:40 -04002413 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002414 image = ImageHandler(image_filename, read_only=True)
David Zeuthen21e95262016-07-27 17:58:40 -04002415 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002416 (footer, header, descriptors, image_size) = self._parse_image(image)
2417
Bowgo Tsaid7145942020-03-20 17:03:51 +08002418 # To show the SHA1 of the public key.
2419 vbmeta_blob = self._load_vbmeta_blob(image)
2420 key_offset = (header.SIZE +
2421 header.authentication_data_block_size +
2422 header.public_key_offset)
2423 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2424
David Zeuthen21e95262016-07-27 17:58:40 -04002425 if footer:
2426 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2427 footer.version_minor))
2428 o.write('Image size: {} bytes\n'.format(image_size))
2429 o.write('Original image size: {} bytes\n'.format(
2430 footer.original_image_size))
2431 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2432 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2433 o.write('--\n')
2434
2435 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2436
David Zeuthene3cadca2017-02-22 21:25:46 -05002437 o.write('Minimum libavb version: {}.{}{}\n'.format(
2438 header.required_libavb_version_major,
2439 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002440 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002441 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2442 o.write('Authentication Block: {} bytes\n'.format(
2443 header.authentication_data_block_size))
2444 o.write('Auxiliary Block: {} bytes\n'.format(
2445 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002446 if key_blob:
2447 hexdig = hashlib.sha1(key_blob).hexdigest()
2448 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002449 o.write('Algorithm: {}\n'.format(alg_name))
2450 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002451 o.write('Flags: {}\n'.format(header.flags))
Jan Monscha18b2ec2020-04-22 11:43:35 +02002452 o.write('Rollback Index Location: {}\n'.format(
2453 header.rollback_index_location))
Jan Monschb1d920f2020-04-09 12:59:28 +02002454 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002455
2456 # Print descriptors.
2457 num_printed = 0
2458 o.write('Descriptors:\n')
2459 for desc in descriptors:
2460 desc.print_desc(o)
2461 num_printed += 1
2462 if num_printed == 0:
2463 o.write(' (none)\n')
2464
Sen Jiang01553a22020-06-30 17:58:44 -07002465 if atx and header.public_key_metadata_size:
2466 o.write('Android Things eXtension (ATX):\n')
2467 key_metadata_offset = (header.SIZE +
2468 header.authentication_data_block_size +
2469 header.public_key_metadata_offset)
2470 key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset
2471 + header.public_key_metadata_size]
2472 version, pik, psk = struct.unpack('<I1620s1620s', key_metadata_blob)
2473 o.write(' Metadata version: {}\n'.format(version))
2474
2475 def print_atx_certificate(cert):
Jan Monsch7a722ee2021-06-17 11:49:05 +02002476 version, public_key, subject, usage, key_version, _ = (
2477 struct.unpack('<I1032s32s32sQ512s', cert))
Sen Jiang01553a22020-06-30 17:58:44 -07002478 o.write(' Version: {}\n'.format(version))
2479 o.write(' Public key (sha1): {}\n'.format(
2480 hashlib.sha1(public_key).hexdigest()))
2481 o.write(' Subject: {}\n'.format(subject.hex()))
2482 o.write(' Usage: {}\n'.format(usage.hex()))
2483 o.write(' Key version: {}\n'.format(key_version))
2484
2485 o.write(' Product Intermediate Key:\n')
2486 print_atx_certificate(pik)
2487 o.write(' Product Signing Key:\n')
2488 print_atx_certificate(psk)
2489
Jan Monscheeb28b62019-12-05 16:17:09 +01002490 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2491 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002492 """Implements the 'verify_image' command.
2493
2494 Arguments:
2495 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002496 key_path: None or check that embedded public key matches key at given
2497 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002498 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002499 follow_chain_partitions:
2500 If True, will follows chain partitions even when not specified with
2501 the --expected_chain_partition option
2502 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2503 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002504
2505 Raises:
2506 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002507 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002508 expected_chain_partitions_map = {}
2509 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002510 for cp in expected_chain_partitions:
2511 cp_tokens = cp.split(':')
2512 if len(cp_tokens) != 3:
2513 raise AvbError('Malformed chained partition "{}".'.format(cp))
2514 partition_name = cp_tokens[0]
2515 rollback_index_location = int(cp_tokens[1])
2516 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002517 with open(file_path, 'rb') as f:
2518 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002519 expected_chain_partitions_map[partition_name] = (
2520 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002521
2522 image_dir = os.path.dirname(image_filename)
2523 image_ext = os.path.splitext(image_filename)[1]
2524
2525 key_blob = None
2526 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002527 print('Verifying image {} using key at {}'.format(image_filename,
2528 key_path))
Jan Monsch9c130122020-04-14 13:43:51 +02002529 key_blob = RSAPublicKey(key_path).encode()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002530 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002531 print('Verifying image {} using embedded public key'.format(
2532 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002533
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002534 image = ImageHandler(image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002535 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002536 offset = 0
2537 if footer:
2538 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002539
David Zeuthenb623d8b2017-04-04 16:05:53 -04002540 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002541 vbmeta_blob = image.read(header.SIZE
2542 + header.authentication_data_block_size
2543 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002544
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002545 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002546 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002547 raise AvbError('Signature check failed for {} vbmeta struct {}'
2548 .format(alg_name, image_filename))
2549
2550 if key_blob:
2551 # The embedded public key is in the auxiliary block at an offset.
2552 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002553 key_offset += header.authentication_data_block_size
2554 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002555 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2556 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002557 if key_blob != key_blob_in_vbmeta:
2558 raise AvbError('Embedded public key does not match given key.')
2559
2560 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002561 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2562 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002563 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002564 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2565 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002566
2567 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002568 if (isinstance(desc, AvbChainPartitionDescriptor)
2569 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002570 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002571 # In this case we're processing a chain descriptor but don't have a
2572 # --expect_chain_partition ... however --follow_chain_partitions was
2573 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002574 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2575 'and KEY (which has sha1 {}) not specified'
2576 .format(desc.partition_name, desc.rollback_index_location,
2577 hashlib.sha1(desc.public_key).hexdigest()))
2578 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002579 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002580 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002581 # Honor --follow_chain_partitions - add '--' to make the output more
2582 # readable.
2583 if (isinstance(desc, AvbChainPartitionDescriptor)
2584 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002585 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002586 chained_image_filename = os.path.join(image_dir,
2587 desc.partition_name + image_ext)
2588 self.verify_image(chained_image_filename, key_path, None, False,
2589 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002590
David Zeuthen34b6b492020-04-13 14:45:02 -04002591 def print_partition_digests(self, image_filename, output, as_json):
2592 """Implements the 'print_partition_digests' command.
2593
2594 Arguments:
2595 image_filename: Image file to get information from (file object).
2596 output: Output file to write human-readable information to (file object).
2597 as_json: If True, print information as JSON
2598
2599 Raises:
2600 AvbError: If getting the partition digests from the image fails.
2601 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002602 image_dir = os.path.dirname(image_filename)
2603 image_ext = os.path.splitext(image_filename)[1]
2604 json_partitions = None
2605 if as_json:
2606 json_partitions = []
Jan Monschcc9939a2020-04-16 09:15:20 +02002607 self._print_partition_digests(
2608 image_filename, output, json_partitions, image_dir, image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002609 if as_json:
2610 output.write(json.dumps({'partitions': json_partitions}, indent=2))
2611
Jan Monschcc9939a2020-04-16 09:15:20 +02002612 def _print_partition_digests(self, image_filename, output, json_partitions,
2613 image_dir, image_ext):
David Zeuthen34b6b492020-04-13 14:45:02 -04002614 """Helper for printing partitions.
2615
2616 Arguments:
2617 image_filename: Image file to get information from (file object).
2618 output: Output file to write human-readable information to (file object).
Jan Monschcc9939a2020-04-16 09:15:20 +02002619 json_partitions: If not None, don't print to output, instead add partition
2620 information to this list.
David Zeuthen34b6b492020-04-13 14:45:02 -04002621 image_dir: The directory to use when looking for chained partition files.
2622 image_ext: The extension to use for chained partition files.
2623
2624 Raises:
2625 AvbError: If getting the partition digests from the image fails.
2626 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002627 image = ImageHandler(image_filename, read_only=True)
David Zeuthen34b6b492020-04-13 14:45:02 -04002628 (_, _, descriptors, _) = self._parse_image(image)
2629
2630 for desc in descriptors:
2631 if isinstance(desc, AvbHashDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002632 digest = desc.digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002633 if json_partitions is not None:
2634 json_partitions.append({'name': desc.partition_name,
2635 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002636 else:
2637 output.write('{}: {}\n'.format(desc.partition_name, digest))
2638 elif isinstance(desc, AvbHashtreeDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002639 digest = desc.root_digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002640 if json_partitions is not None:
2641 json_partitions.append({'name': desc.partition_name,
2642 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002643 else:
2644 output.write('{}: {}\n'.format(desc.partition_name, digest))
2645 elif isinstance(desc, AvbChainPartitionDescriptor):
2646 chained_image_filename = os.path.join(image_dir,
2647 desc.partition_name + image_ext)
Jan Monschcc9939a2020-04-16 09:15:20 +02002648 self._print_partition_digests(
2649 chained_image_filename, output, json_partitions, image_dir,
2650 image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002651
David Zeuthenb8643c02018-05-17 17:21:18 -04002652 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2653 """Implements the 'calculate_vbmeta_digest' command.
2654
2655 Arguments:
2656 image_filename: Image file to get information from (file object).
2657 hash_algorithm: Hash algorithm used.
2658 output: Output file to write human-readable information to (file object).
2659 """
2660
2661 image_dir = os.path.dirname(image_filename)
2662 image_ext = os.path.splitext(image_filename)[1]
2663
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002664 image = ImageHandler(image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002665 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002666 offset = 0
2667 if footer:
2668 offset = footer.vbmeta_offset
2669 size = (header.SIZE + header.authentication_data_block_size +
2670 header.auxiliary_data_block_size)
2671 image.seek(offset)
2672 vbmeta_blob = image.read(size)
2673
Jan Monsch6f27bb12020-04-07 07:33:26 +02002674 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002675 hasher.update(vbmeta_blob)
2676
2677 for desc in descriptors:
2678 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002679 ch_image_filename = os.path.join(image_dir,
2680 desc.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002681 ch_image = ImageHandler(ch_image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002682 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002683 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002684 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2685 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002686 if ch_footer:
2687 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002688 ch_image.seek(ch_offset)
2689 ch_vbmeta_blob = ch_image.read(ch_size)
2690 hasher.update(ch_vbmeta_blob)
2691
2692 digest = hasher.digest()
Jan Monsch25040d92020-04-22 22:48:20 +02002693 output.write('{}\n'.format(digest.hex()))
David Zeuthenb8643c02018-05-17 17:21:18 -04002694
David Zeuthenf7d2e752018-09-20 13:30:41 -04002695 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2696 """Implements the 'calculate_kernel_cmdline' command.
2697
2698 Arguments:
2699 image_filename: Image file to get information from (file object).
2700 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2701 output: Output file to write human-readable information to (file object).
2702 """
2703
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002704 image = ImageHandler(image_filename, read_only=True)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002705 _, _, descriptors, _ = self._parse_image(image)
2706
2707 image_dir = os.path.dirname(image_filename)
2708 image_ext = os.path.splitext(image_filename)[1]
2709
2710 cmdline_descriptors = []
2711 for desc in descriptors:
2712 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002713 ch_image_filename = os.path.join(image_dir,
2714 desc.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002715 ch_image = ImageHandler(ch_image_filename, read_only=True)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002716 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2717 for ch_desc in ch_descriptors:
2718 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2719 cmdline_descriptors.append(ch_desc)
2720 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2721 cmdline_descriptors.append(desc)
2722
2723 kernel_cmdline_snippets = []
2724 for desc in cmdline_descriptors:
2725 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002726 if ((desc.flags &
2727 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2728 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002729 if hashtree_disabled:
2730 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002731 if (desc.flags &
2732 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002733 if not hashtree_disabled:
2734 use_cmdline = False
2735 if use_cmdline:
2736 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2737 output.write(' '.join(kernel_cmdline_snippets))
2738
David Zeuthen21e95262016-07-27 17:58:40 -04002739 def _parse_image(self, image):
2740 """Gets information about an image.
2741
2742 The image can either be a vbmeta or an image with a footer.
2743
2744 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002745 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002746
2747 Returns:
2748 A tuple where the first argument is a AvbFooter (None if there
2749 is no footer on the image), the second argument is a
2750 AvbVBMetaHeader, the third argument is a list of
2751 AvbDescriptor-derived instances, and the fourth argument is the
2752 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002753
2754 Raises:
2755 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002756 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002757 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002758 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002759 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002760 try:
2761 footer = AvbFooter(image.read(AvbFooter.SIZE))
2762 except (LookupError, struct.error):
2763 # Nope, just seek back to the start.
2764 image.seek(0)
2765
2766 vbmeta_offset = 0
2767 if footer:
2768 vbmeta_offset = footer.vbmeta_offset
2769
2770 image.seek(vbmeta_offset)
2771 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2772
2773 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2774 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2775 desc_start_offset = aux_block_offset + h.descriptors_offset
2776 image.seek(desc_start_offset)
2777 descriptors = parse_descriptors(image.read(h.descriptors_size))
2778
David Zeuthen09692692016-09-30 16:16:40 -04002779 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002780
David Zeuthenb1b994d2017-03-06 18:01:31 -05002781 def _load_vbmeta_blob(self, image):
2782 """Gets the vbmeta struct and associated sections.
2783
2784 The image can either be a vbmeta.img or an image with a footer.
2785
2786 Arguments:
2787 image: An ImageHandler (vbmeta or footer).
2788
2789 Returns:
2790 A blob with the vbmeta struct and other sections.
2791 """
2792 assert isinstance(image, ImageHandler)
2793 footer = None
2794 image.seek(image.image_size - AvbFooter.SIZE)
2795 try:
2796 footer = AvbFooter(image.read(AvbFooter.SIZE))
2797 except (LookupError, struct.error):
2798 # Nope, just seek back to the start.
2799 image.seek(0)
2800
2801 vbmeta_offset = 0
2802 if footer:
2803 vbmeta_offset = footer.vbmeta_offset
2804
2805 image.seek(vbmeta_offset)
2806 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2807
2808 image.seek(vbmeta_offset)
2809 data_size = AvbVBMetaHeader.SIZE
2810 data_size += h.authentication_data_block_size
2811 data_size += h.auxiliary_data_block_size
2812 return image.read(data_size)
2813
David Zeuthen73f2afa2017-05-17 16:54:11 -04002814 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002815 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002816
2817 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002818 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002819
2820 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002821 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2822 instructions. There is one for when hashtree is not disabled and one for
2823 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002824
David Zeuthen21e95262016-07-27 17:58:40 -04002825 """
David Zeuthen21e95262016-07-27 17:58:40 -04002826 c = 'dm="1 vroot none ro 1,'
Jan Monsch25040d92020-04-22 22:48:20 +02002827 c += '0' # start
2828 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
2829 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2830 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2831 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2832 c += ' {}'.format(ht.data_block_size) # data_block
2833 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002834 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2835 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
Jan Monsch25040d92020-04-22 22:48:20 +02002836 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2837 c += ' {}'.format(ht.root_digest.hex()) # root_digest
2838 c += ' {}'.format(ht.salt.hex()) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002839 if ht.fec_num_roots > 0:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09002840 if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE:
2841 c += ' 11' # number of optional args
2842 c += ' check_at_most_once'
2843 else:
2844 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002845 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002846 c += ' ignore_zero_blocks'
2847 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2848 c += ' fec_roots {}'.format(ht.fec_num_roots)
2849 # Note that fec_blocks is the size that FEC covers, *not* the
2850 # size of the FEC data. Since we use FEC for everything up until
2851 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002852 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2853 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002854 else:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09002855 if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE:
2856 c += ' 3' # number of optional args
2857 c += ' check_at_most_once'
2858 else:
2859 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002860 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002861 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002862 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002863
David Zeuthenfd41eb92016-11-17 12:24:47 -05002864 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002865 desc = AvbKernelCmdlineDescriptor()
2866 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002867 desc.flags = (
2868 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2869
2870 # The descriptor for when hashtree verification is disabled is a lot
2871 # simpler - we just set the root to the partition.
2872 desc_no_ht = AvbKernelCmdlineDescriptor()
2873 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2874 desc_no_ht.flags = (
2875 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2876
2877 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002878
David Zeuthen73f2afa2017-05-17 16:54:11 -04002879 def _get_cmdline_descriptors_for_dm_verity(self, image):
2880 """Generate kernel cmdline descriptors for dm-verity.
2881
2882 Arguments:
2883 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2884
2885 Returns:
2886 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2887 instructions. There is one for when hashtree is not disabled and one for
2888 when it is.
2889
2890 Raises:
2891 AvbError: If |image| doesn't have a hashtree descriptor.
2892
2893 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002894 (_, _, descriptors, _) = self._parse_image(image)
2895
2896 ht = None
2897 for desc in descriptors:
2898 if isinstance(desc, AvbHashtreeDescriptor):
2899 ht = desc
2900 break
2901
2902 if not ht:
2903 raise AvbError('No hashtree descriptor in given image')
2904
2905 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2906
David Zeuthen21e95262016-07-27 17:58:40 -04002907 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002908 key_path, public_key_metadata_path, rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07002909 flags, rollback_index_location,
2910 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002911 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002912 include_descriptors_from_image,
2913 signing_helper,
2914 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002915 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002916 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002917 print_required_libavb_version,
2918 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002919 """Implements the 'make_vbmeta_image' command.
2920
2921 Arguments:
2922 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002923 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002924 algorithm_name: Name of algorithm to use.
2925 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002926 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002927 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002928 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002929 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002930 props: Properties to insert (list of strings of the form 'key:value').
2931 props_from_file: Properties to insert (list of strings 'key:<path>').
2932 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002933 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002934 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002935 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002936 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002937 release_string: None or avbtool release string to use instead of default.
2938 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002939 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002940 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002941
2942 Raises:
2943 AvbError: If a chained partition is malformed.
2944 """
David Zeuthen1097a782017-05-31 15:53:17 -04002945 # If we're asked to calculate minimum required libavb version, we're done.
Varun Sharmade538272020-04-10 15:22:31 -07002946 tmp_header = AvbVBMetaHeader()
2947 if rollback_index_location > 0:
2948 tmp_header.bump_required_libavb_version_minor(2)
2949 if include_descriptors_from_image:
2950 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2951 # version of all included descriptors.
2952 for image in include_descriptors_from_image:
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002953 (_, image_header, _, _) = self._parse_image(ImageHandler(
2954 image.name, read_only=True))
Varun Sharmade538272020-04-10 15:22:31 -07002955 tmp_header.bump_required_libavb_version_minor(
2956 image_header.required_libavb_version_minor)
2957
David Zeuthen1097a782017-05-31 15:53:17 -04002958 if print_required_libavb_version:
Varun Sharmade538272020-04-10 15:22:31 -07002959 print('1.{}'.format(tmp_header.required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04002960 return
2961
2962 if not output:
2963 raise AvbError('No output file given')
2964
David Zeuthen21e95262016-07-27 17:58:40 -04002965 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002966 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002967 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002968 algorithm_name, key_path, public_key_metadata_path, descriptors,
Varun Sharmade538272020-04-10 15:22:31 -07002969 chain_partitions, rollback_index, flags, rollback_index_location,
2970 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002971 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002972 include_descriptors_from_image, signing_helper,
2973 signing_helper_with_files, release_string,
Varun Sharmade538272020-04-10 15:22:31 -07002974 append_to_release_string, tmp_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002975
2976 # Write entire vbmeta blob (header, authentication, auxiliary).
2977 output.seek(0)
2978 output.write(vbmeta_blob)
2979
David Zeuthen97cb5802017-06-01 16:14:05 -04002980 if padding_size > 0:
2981 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2982 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002983 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002984
David Zeuthen18666ab2016-11-15 11:18:05 -05002985 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2986 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002987 chain_partitions,
Varun Sharmade538272020-04-10 15:22:31 -07002988 rollback_index, flags, rollback_index_location,
2989 props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002990 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002991 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002992 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002993 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002994 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002995 release_string, append_to_release_string,
2996 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002997 """Generates a VBMeta blob.
2998
2999 This blob contains the header (struct AvbVBMetaHeader), the
3000 authentication data block (which contains the hash and signature
3001 for the header and auxiliary block), and the auxiliary block
3002 (which contains descriptors, the public key used, and other data).
3003
3004 The |key| parameter can |None| only if the |algorithm_name| is
3005 'NONE'.
3006
3007 Arguments:
3008 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3009 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003010 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003011 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003012 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003013 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003014 flags: Flags to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003015 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003016 props: Properties to insert (List of strings of the form 'key:value').
3017 props_from_file: Properties to insert (List of strings 'key:<path>').
3018 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003019 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003020 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003021 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3022 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003023 include_descriptors_from_image: List of file objects for which
3024 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003025 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003026 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003027 release_string: None or avbtool release string.
3028 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003029 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003030
3031 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003032 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003033
3034 Raises:
3035 Exception: If the |algorithm_name| is not found, if no key has
3036 been given and the given algorithm requires one, or the key is
3037 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04003038 """
3039 try:
3040 alg = ALGORITHMS[algorithm_name]
Jan Monsch7a722ee2021-06-17 11:49:05 +02003041 except KeyError as e:
3042 raise AvbError('Unknown algorithm with name {}'
3043 .format(algorithm_name)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04003044
David Zeuthena5fd3a42017-02-27 16:38:54 -05003045 if not descriptors:
3046 descriptors = []
3047
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003048 h = AvbVBMetaHeader()
3049 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3050
David Zeuthena5fd3a42017-02-27 16:38:54 -05003051 # Insert chained partition descriptors, if any
3052 if chain_partitions:
Varun Sharmade538272020-04-10 15:22:31 -07003053 used_locations = {rollback_index_location: True}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003054 for cp in chain_partitions:
3055 cp_tokens = cp.split(':')
3056 if len(cp_tokens) != 3:
3057 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003058 partition_name = cp_tokens[0]
Varun Sharmade538272020-04-10 15:22:31 -07003059 chained_rollback_index_location = int(cp_tokens[1])
David Zeuthend8e48582017-04-21 11:31:51 -04003060 file_path = cp_tokens[2]
3061 # Check that the same rollback location isn't being used by
3062 # multiple chained partitions.
Varun Sharmade538272020-04-10 15:22:31 -07003063 if used_locations.get(chained_rollback_index_location):
David Zeuthend8e48582017-04-21 11:31:51 -04003064 raise AvbError('Rollback Index Location {} is already in use.'.format(
Varun Sharmade538272020-04-10 15:22:31 -07003065 chained_rollback_index_location))
3066 used_locations[chained_rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003067 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003068 desc.partition_name = partition_name
Varun Sharmade538272020-04-10 15:22:31 -07003069 desc.rollback_index_location = chained_rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003070 if desc.rollback_index_location < 1:
3071 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003072 with open(file_path, 'rb') as f:
3073 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003074 descriptors.append(desc)
3075
David Zeuthen21e95262016-07-27 17:58:40 -04003076 # Descriptors.
3077 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003078 for desc in descriptors:
3079 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003080
3081 # Add properties.
3082 if props:
3083 for prop in props:
3084 idx = prop.find(':')
3085 if idx == -1:
3086 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003087 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003088 desc = AvbPropertyDescriptor()
3089 desc.key = prop[0:idx]
Jan Monschee6fccd2020-04-09 19:36:13 +02003090 desc.value = prop[(idx + 1):].encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04003091 encoded_descriptors.extend(desc.encode())
3092 if props_from_file:
3093 for prop in props_from_file:
3094 idx = prop.find(':')
3095 if idx == -1:
3096 raise AvbError('Malformed property "{}".'.format(prop))
3097 desc = AvbPropertyDescriptor()
3098 desc.key = prop[0:idx]
David Zeuthen21e95262016-07-27 17:58:40 -04003099 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02003100 with open(file_path, 'rb') as f:
Jan Monsch9c130122020-04-14 13:43:51 +02003101 # pylint: disable=attribute-defined-outside-init
Jan Monschb1d920f2020-04-09 12:59:28 +02003102 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04003103 encoded_descriptors.extend(desc.encode())
3104
David Zeuthen73f2afa2017-05-17 16:54:11 -04003105 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003106 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003107 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003108 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003109 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3110 encoded_descriptors.extend(cmdline_desc[0].encode())
3111 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003112
David Zeuthen73f2afa2017-05-17 16:54:11 -04003113 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3114 if ht_desc_to_setup:
3115 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3116 ht_desc_to_setup)
3117 encoded_descriptors.extend(cmdline_desc[0].encode())
3118 encoded_descriptors.extend(cmdline_desc[1].encode())
3119
David Zeuthen21e95262016-07-27 17:58:40 -04003120 # Add kernel command-lines.
3121 if kernel_cmdlines:
3122 for i in kernel_cmdlines:
3123 desc = AvbKernelCmdlineDescriptor()
3124 desc.kernel_cmdline = i
3125 encoded_descriptors.extend(desc.encode())
3126
3127 # Add descriptors from other images.
3128 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003129 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003130 for image in include_descriptors_from_image:
Alex Leggd6f4c392020-05-08 12:29:27 +10003131 image_handler = ImageHandler(image.name, read_only=True)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003132 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3133 image_handler)
3134 # Bump the required libavb version to support all included descriptors.
3135 h.bump_required_libavb_version_minor(
3136 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003137 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003138 # The --include_descriptors_from_image option is used in some setups
3139 # with images A and B where both A and B contain a descriptor
3140 # for a partition with the same name. Since it's not meaningful
3141 # to include both descriptors, only include the last seen descriptor.
3142 # See bug 76386656 for details.
3143 if hasattr(desc, 'partition_name'):
3144 key = type(desc).__name__ + '_' + desc.partition_name
3145 descriptors_dict[key] = desc.encode()
3146 else:
3147 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003148 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003149 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003150
David Zeuthen18666ab2016-11-15 11:18:05 -05003151 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003152 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05003153 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003154 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05003155 pkmd_blob = f.read()
3156
David Zeuthen21e95262016-07-27 17:58:40 -04003157 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02003158 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003159 if alg.public_key_num_bytes > 0:
3160 if not key_path:
3161 raise AvbError('Key is required for algorithm {}'.format(
3162 algorithm_name))
Jan Monsch9c130122020-04-14 13:43:51 +02003163 encoded_key = RSAPublicKey(key_path).encode()
David Zeuthen21e95262016-07-27 17:58:40 -04003164 if len(encoded_key) != alg.public_key_num_bytes:
3165 raise AvbError('Key is wrong size for algorithm {}'.format(
3166 algorithm_name))
3167
David Zeuthene3cadca2017-02-22 21:25:46 -05003168 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003169 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003170 h.release_string = release_string
3171
3172 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003173 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003174 h.release_string += ' ' + append_to_release_string
3175
David Zeuthen18666ab2016-11-15 11:18:05 -05003176 # For the Auxiliary data block, descriptors are stored at offset 0,
3177 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003178 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003179 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003180 h.descriptors_offset = 0
3181 h.descriptors_size = len(encoded_descriptors)
3182 h.public_key_offset = h.descriptors_size
3183 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003184 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3185 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003186
3187 # For the Authentication data block, the hash is first and then
3188 # the signature.
3189 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003190 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003191 h.algorithm_type = alg.algorithm_type
3192 h.hash_offset = 0
3193 h.hash_size = alg.hash_num_bytes
3194 # Signature offset and size - it's stored right after the hash
3195 # (in Authentication data block).
3196 h.signature_offset = alg.hash_num_bytes
3197 h.signature_size = alg.signature_num_bytes
3198
3199 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003200 h.flags = flags
Varun Sharmade538272020-04-10 15:22:31 -07003201 h.rollback_index_location = rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04003202
3203 # Generate Header data block.
3204 header_data_blob = h.encode()
3205
3206 # Generate Auxiliary data block.
3207 aux_data_blob = bytearray()
3208 aux_data_blob.extend(encoded_descriptors)
3209 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003210 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003211 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003212 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003213
3214 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003215 binary_hash = b''
3216 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003217 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003218 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003219 ha.update(header_data_blob)
3220 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003221 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003222
3223 # Calculate the signature.
Jan Monsch9c130122020-04-14 13:43:51 +02003224 rsa_key = RSAPublicKey(key_path)
Jan Monsche6dac432020-04-24 13:10:26 +02003225 data_to_sign = header_data_blob + bytes(aux_data_blob)
Jan Monsch9c130122020-04-14 13:43:51 +02003226 binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
3227 signing_helper, signing_helper_with_files)
David Zeuthen21e95262016-07-27 17:58:40 -04003228
3229 # Generate Authentication data block.
3230 auth_data_blob = bytearray()
3231 auth_data_blob.extend(binary_hash)
3232 auth_data_blob.extend(binary_signature)
3233 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003234 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003235
Jan Monschb1d920f2020-04-09 12:59:28 +02003236 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003237
3238 def extract_public_key(self, key_path, output):
3239 """Implements the 'extract_public_key' command.
3240
3241 Arguments:
3242 key_path: The path to a RSA private key file.
3243 output: The file to write to.
Jan Monsch9c130122020-04-14 13:43:51 +02003244
3245 Raises:
3246 AvbError: If the public key could not be extracted.
David Zeuthen21e95262016-07-27 17:58:40 -04003247 """
Jan Monsch9c130122020-04-14 13:43:51 +02003248 output.write(RSAPublicKey(key_path).encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003249
David Zeuthenb1b994d2017-03-06 18:01:31 -05003250 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3251 partition_size):
3252 """Implementation of the append_vbmeta_image command.
3253
3254 Arguments:
3255 image_filename: File to add the footer to.
3256 vbmeta_image_filename: File to get vbmeta struct from.
3257 partition_size: Size of partition.
3258
3259 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003260 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003261 """
3262 image = ImageHandler(image_filename)
3263
3264 if partition_size % image.block_size != 0:
3265 raise AvbError('Partition size of {} is not a multiple of the image '
3266 'block size {}.'.format(partition_size,
3267 image.block_size))
3268
3269 # If there's already a footer, truncate the image to its original
3270 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003271 if image.image_size >= AvbFooter.SIZE:
3272 image.seek(image.image_size - AvbFooter.SIZE)
3273 try:
3274 footer = AvbFooter(image.read(AvbFooter.SIZE))
3275 # Existing footer found. Just truncate.
3276 original_image_size = footer.original_image_size
3277 image.truncate(footer.original_image_size)
3278 except (LookupError, struct.error):
3279 original_image_size = image.image_size
3280 else:
3281 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003282 original_image_size = image.image_size
3283
3284 # If anything goes wrong from here-on, restore the image back to
3285 # its original size.
3286 try:
3287 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3288 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3289
3290 # If the image isn't sparse, its size might not be a multiple of
3291 # the block size. This will screw up padding later so just grow it.
3292 if image.image_size % image.block_size != 0:
3293 assert not image.is_sparse
3294 padding_needed = image.block_size - (image.image_size%image.block_size)
3295 image.truncate(image.image_size + padding_needed)
3296
3297 # The append_raw() method requires content with size being a
3298 # multiple of |block_size| so add padding as needed. Also record
3299 # where this is written to since we'll need to put that in the
3300 # footer.
3301 vbmeta_offset = image.image_size
3302 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3303 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003304 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003305
3306 # Append vbmeta blob and footer
3307 image.append_raw(vbmeta_blob_with_padding)
3308 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3309
3310 # Now insert a DONT_CARE chunk with enough bytes such that the
3311 # final Footer block is at the end of partition_size..
3312 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003313 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003314
3315 # Generate the Footer that tells where the VBMeta footer
3316 # is. Also put enough padding in the front of the footer since
3317 # we'll write out an entire block.
3318 footer = AvbFooter()
3319 footer.original_image_size = original_image_size
3320 footer.vbmeta_offset = vbmeta_offset
3321 footer.vbmeta_size = len(vbmeta_blob)
3322 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003323 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003324 footer_blob)
3325 image.append_raw(footer_blob_with_padding)
3326
Jan Monschb1d920f2020-04-09 12:59:28 +02003327 except Exception as e:
3328 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003329 image.truncate(original_image_size)
Jan Monsch7a722ee2021-06-17 11:49:05 +02003330 raise AvbError('Appending VBMeta image failed: {}.'.format(e)) from e
David Zeuthenb1b994d2017-03-06 18:01:31 -05003331
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003332 def add_hash_footer(self, image_filename, partition_size,
3333 dynamic_partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003334 hash_algorithm, salt, chain_partitions, algorithm_name,
3335 key_path,
Varun Sharmade538272020-04-10 15:22:31 -07003336 public_key_metadata_path, rollback_index, flags,
3337 rollback_index_location, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003338 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003339 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003340 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003341 signing_helper, signing_helper_with_files,
3342 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003343 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003344 print_required_libavb_version, use_persistent_digest,
3345 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003346 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003347
3348 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003349 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003350 partition_size: Size of partition.
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003351 dynamic_partition_size: Calculate partition size based on image size.
David Zeuthen21e95262016-07-27 17:58:40 -04003352 partition_name: Name of partition (without A/B suffix).
3353 hash_algorithm: Hash algorithm to use.
3354 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003355 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003356 algorithm_name: Name of algorithm to use.
3357 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003358 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003359 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003360 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003361 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003362 props: Properties to insert (List of strings of the form 'key:value').
3363 props_from_file: Properties to insert (List of strings 'key:<path>').
3364 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003365 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003366 dm-verity kernel cmdline from.
3367 include_descriptors_from_image: List of file objects for which
3368 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003369 calc_max_image_size: Don't store the footer - instead calculate the
3370 maximum image size leaving enough room for metadata with the
3371 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003372 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003373 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003374 release_string: None or avbtool release string.
3375 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003376 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3377 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003378 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003379 use_persistent_digest: Use a persistent digest on device.
3380 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003381
3382 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003383 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003384 """
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003385 if not partition_size and not dynamic_partition_size:
3386 raise AvbError('--dynamic_partition_size required when not specifying a '
3387 'partition size')
3388
3389 if dynamic_partition_size and calc_max_image_size:
3390 raise AvbError('--calc_max_image_size not supported with '
3391 '--dynamic_partition_size')
3392
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003393 required_libavb_version_minor = 0
3394 if use_persistent_digest or do_not_use_ab:
3395 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003396 if rollback_index_location > 0:
3397 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003398
David Zeuthen1097a782017-05-31 15:53:17 -04003399 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003400 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003401 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003402 return
3403
David Zeuthenbf562452017-05-17 18:04:43 -04003404 # First, calculate the maximum image size such that an image
3405 # this size + metadata (footer + vbmeta struct) fits in
3406 # |partition_size|.
3407 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003408 if not dynamic_partition_size and partition_size < max_metadata_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003409 raise AvbError('Parition size of {} is too small. '
3410 'Needs to be at least {}'.format(
3411 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003412
3413 # If we're asked to only calculate the maximum image size, we're done.
3414 if calc_max_image_size:
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003415 print('{}'.format(partition_size - max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003416 return
3417
David Zeuthena4fee8b2016-08-22 15:20:43 -04003418 image = ImageHandler(image_filename)
3419
David Zeuthen21e95262016-07-27 17:58:40 -04003420 # If there's already a footer, truncate the image to its original
3421 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3422 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003423 if image.image_size >= AvbFooter.SIZE:
3424 image.seek(image.image_size - AvbFooter.SIZE)
3425 try:
3426 footer = AvbFooter(image.read(AvbFooter.SIZE))
3427 # Existing footer found. Just truncate.
3428 original_image_size = footer.original_image_size
3429 image.truncate(footer.original_image_size)
3430 except (LookupError, struct.error):
3431 original_image_size = image.image_size
3432 else:
3433 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003434 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003435
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003436 if dynamic_partition_size:
Bowgo Tsaiafabdb32022-02-21 14:25:01 +08003437 partition_size = round_to_multiple(
3438 original_image_size + max_metadata_size, image.block_size)
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08003439
3440 max_image_size = partition_size - max_metadata_size
3441 if partition_size % image.block_size != 0:
3442 raise AvbError('Partition size of {} is not a multiple of the image '
3443 'block size {}.'.format(partition_size,
3444 image.block_size))
3445
David Zeuthen21e95262016-07-27 17:58:40 -04003446 # If anything goes wrong from here-on, restore the image back to
3447 # its original size.
3448 try:
David Zeuthen09692692016-09-30 16:16:40 -04003449 # If image size exceeds the maximum image size, fail.
3450 if image.image_size > max_image_size:
3451 raise AvbError('Image size of {} exceeds maximum image '
3452 'size of {} in order to fit in a partition '
3453 'size of {}.'.format(image.image_size, max_image_size,
3454 partition_size))
3455
Jan Monsch6f27bb12020-04-07 07:33:26 +02003456 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003457 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003458 salt = binascii.unhexlify(salt)
3459 elif salt is None and not use_persistent_digest:
3460 # If salt is not explicitly specified, choose a hash that's the same
3461 # size as the hash size. Don't populate a random salt if this
3462 # descriptor is being created to use a persistent digest on device.
3463 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003464 with open('/dev/urandom', 'rb') as f:
3465 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003466 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003467 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003468
Jan Monsch6f27bb12020-04-07 07:33:26 +02003469 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003470 # TODO(zeuthen): might want to read this in chunks to avoid
3471 # memory pressure, then again, this is only supposed to be used
3472 # on kernel/initramfs partitions. Possible optimization.
3473 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003474 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003475 digest = hasher.digest()
3476
3477 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003478 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003479 h_desc.hash_algorithm = hash_algorithm
3480 h_desc.partition_name = partition_name
3481 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003482 h_desc.flags = 0
3483 if do_not_use_ab:
3484 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3485 if not use_persistent_digest:
3486 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003487
3488 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003489 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003490 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003491 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003492 chain_partitions, rollback_index, flags, rollback_index_location,
3493 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003494 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003495 include_descriptors_from_image, signing_helper,
3496 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003497 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003498
David Zeuthend247fcb2017-02-16 12:09:27 -05003499 # Write vbmeta blob, if requested.
3500 if output_vbmeta_image:
3501 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003502
David Zeuthend247fcb2017-02-16 12:09:27 -05003503 # Append vbmeta blob and footer, unless requested not to.
3504 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003505 # If the image isn't sparse, its size might not be a multiple of
3506 # the block size. This will screw up padding later so just grow it.
3507 if image.image_size % image.block_size != 0:
3508 assert not image.is_sparse
3509 padding_needed = image.block_size - (
3510 image.image_size % image.block_size)
3511 image.truncate(image.image_size + padding_needed)
3512
3513 # The append_raw() method requires content with size being a
3514 # multiple of |block_size| so add padding as needed. Also record
3515 # where this is written to since we'll need to put that in the
3516 # footer.
3517 vbmeta_offset = image.image_size
3518 padding_needed = (
3519 round_to_multiple(len(vbmeta_blob), image.block_size) -
3520 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003521 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003522
David Zeuthend247fcb2017-02-16 12:09:27 -05003523 image.append_raw(vbmeta_blob_with_padding)
3524 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3525
3526 # Now insert a DONT_CARE chunk with enough bytes such that the
3527 # final Footer block is at the end of partition_size..
3528 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003529 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003530
3531 # Generate the Footer that tells where the VBMeta footer
3532 # is. Also put enough padding in the front of the footer since
3533 # we'll write out an entire block.
3534 footer = AvbFooter()
3535 footer.original_image_size = original_image_size
3536 footer.vbmeta_offset = vbmeta_offset
3537 footer.vbmeta_size = len(vbmeta_blob)
3538 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003539 footer_blob_with_padding = (
3540 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003541 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003542 except Exception as e:
3543 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003544 image.truncate(original_image_size)
Jan Monsch7a722ee2021-06-17 11:49:05 +02003545 raise AvbError('Adding hash_footer failed: {}.'.format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04003546
David Zeuthena4fee8b2016-08-22 15:20:43 -04003547 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003548 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003549 block_size, salt, chain_partitions, algorithm_name,
3550 key_path,
3551 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003552 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003553 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003554 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003555 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003556 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003557 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003558 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003559 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003560 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003561 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003562 use_persistent_root_digest, do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003563 no_hashtree, check_at_most_once):
David Zeuthen21e95262016-07-27 17:58:40 -04003564 """Implements the 'add_hashtree_footer' command.
3565
3566 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3567 more information about dm-verity and these hashes.
3568
3569 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003570 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003571 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003572 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003573 generate_fec: If True, generate FEC codes.
3574 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003575 hash_algorithm: Hash algorithm to use.
3576 block_size: Block size to use.
3577 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003578 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003579 algorithm_name: Name of algorithm to use.
3580 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003581 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003582 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003583 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003584 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003585 props: Properties to insert (List of strings of the form 'key:value').
3586 props_from_file: Properties to insert (List of strings 'key:<path>').
3587 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003588 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003589 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003590 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3591 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003592 include_descriptors_from_image: List of file objects for which
3593 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003594 calc_max_image_size: Don't store the hashtree or footer - instead
3595 calculate the maximum image size leaving enough room for hashtree
3596 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003597 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003598 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003599 release_string: None or avbtool release string.
3600 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003601 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3602 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003603 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003604 use_persistent_root_digest: Use a persistent root digest on device.
3605 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003606 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
Jan Monsch7a722ee2021-06-17 11:49:05 +02003607 check_at_most_once: Set to verify data blocks only the first time they
3608 are read from the data device.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003609
3610 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003611 AvbError: If an argument is incorrect or adding the hashtree footer
3612 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003613 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003614 required_libavb_version_minor = 0
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003615 if use_persistent_root_digest or do_not_use_ab or check_at_most_once:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003616 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003617 if rollback_index_location > 0:
3618 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003619
David Zeuthen1097a782017-05-31 15:53:17 -04003620 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003621 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003622 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003623 return
3624
Tianjie62ad0222021-02-22 14:31:12 -08003625 digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
3626 .digest())
David Zeuthen09692692016-09-30 16:16:40 -04003627 digest_padding = round_to_pow2(digest_size) - digest_size
3628
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003629 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3630 # size such that an image this size + the hashtree + metadata (footer +
3631 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3632 # for metadata.
3633 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003634 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003635 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003636 if not no_hashtree:
3637 (_, max_tree_size) = calc_hash_level_offsets(
3638 partition_size, block_size, digest_size + digest_padding)
3639 if generate_fec:
3640 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003641 max_metadata_size = (max_fec_size + max_tree_size +
3642 self.MAX_VBMETA_SIZE +
3643 self.MAX_FOOTER_SIZE)
3644 max_image_size = partition_size - max_metadata_size
3645 else:
3646 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003647
3648 # If we're asked to only calculate the maximum image size, we're done.
3649 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003650 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003651 return
3652
David Zeuthena4fee8b2016-08-22 15:20:43 -04003653 image = ImageHandler(image_filename)
3654
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003655 if partition_size > 0:
3656 if partition_size % image.block_size != 0:
3657 raise AvbError('Partition size of {} is not a multiple of the image '
3658 'block size {}.'.format(partition_size,
3659 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003660 elif image.image_size % image.block_size != 0:
3661 raise AvbError('File size of {} is not a multiple of the image '
3662 'block size {}.'.format(image.image_size,
3663 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003664
David Zeuthen21e95262016-07-27 17:58:40 -04003665 # If there's already a footer, truncate the image to its original
3666 # size. This way 'avbtool add_hashtree_footer' is idempotent
3667 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003668 if image.image_size >= AvbFooter.SIZE:
3669 image.seek(image.image_size - AvbFooter.SIZE)
3670 try:
3671 footer = AvbFooter(image.read(AvbFooter.SIZE))
3672 # Existing footer found. Just truncate.
3673 original_image_size = footer.original_image_size
3674 image.truncate(footer.original_image_size)
3675 except (LookupError, struct.error):
3676 original_image_size = image.image_size
3677 else:
3678 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003679 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003680
3681 # If anything goes wrong from here-on, restore the image back to
3682 # its original size.
3683 try:
3684 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003685 rounded_image_size = round_to_multiple(image.image_size, block_size)
3686 if rounded_image_size > image.image_size:
3687 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003688
David Zeuthen09692692016-09-30 16:16:40 -04003689 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003690 if partition_size > 0:
3691 if image.image_size > max_image_size:
3692 raise AvbError('Image size of {} exceeds maximum image '
3693 'size of {} in order to fit in a partition '
3694 'size of {}.'.format(image.image_size, max_image_size,
3695 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003696
3697 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003698 salt = binascii.unhexlify(salt)
3699 elif salt is None and not use_persistent_root_digest:
3700 # If salt is not explicitly specified, choose a hash that's the same
3701 # size as the hash size. Don't populate a random salt if this
3702 # descriptor is being created to use a persistent digest on device.
3703 hash_size = digest_size
Jan Monsch13efb5f2020-04-22 17:34:24 +02003704 with open('/dev/urandom', 'rb') as f:
Jan Monschb1d920f2020-04-09 12:59:28 +02003705 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003706 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003707 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003708
David Zeuthena4fee8b2016-08-22 15:20:43 -04003709 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003710 # offsets in advance.
3711 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003712 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003713
David Zeuthena4fee8b2016-08-22 15:20:43 -04003714 # If the image isn't sparse, its size might not be a multiple of
3715 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003716 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003717 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003718 padding_needed = image.block_size - (image.image_size%image.block_size)
3719 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003720
David Zeuthena4fee8b2016-08-22 15:20:43 -04003721 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003722 tree_offset = image.image_size
3723 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003724 block_size,
3725 hash_algorithm, salt,
3726 digest_padding,
3727 hash_level_offsets,
3728 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003729
3730 # Generate HashtreeDescriptor with details about the tree we
3731 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003732 if no_hashtree:
3733 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003734 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003735 ht_desc = AvbHashtreeDescriptor()
3736 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003737 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003738 ht_desc.tree_offset = tree_offset
3739 ht_desc.tree_size = tree_size
3740 ht_desc.data_block_size = block_size
3741 ht_desc.hash_block_size = block_size
3742 ht_desc.hash_algorithm = hash_algorithm
3743 ht_desc.partition_name = partition_name
3744 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003745 if do_not_use_ab:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003746 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003747 if not use_persistent_root_digest:
3748 ht_desc.root_digest = root_digest
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003749 if check_at_most_once:
3750 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE
David Zeuthen21e95262016-07-27 17:58:40 -04003751
David Zeuthen09692692016-09-30 16:16:40 -04003752 # Write the hash tree
3753 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3754 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003755 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003756 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003757 len_hashtree_and_fec = len(hash_tree_with_padding)
3758
3759 # Generate FEC codes, if requested.
3760 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003761 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003762 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003763 else:
3764 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003765 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3766 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003767 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003768 fec_offset = image.image_size
3769 image.append_raw(fec_data_with_padding)
3770 len_hashtree_and_fec += len(fec_data_with_padding)
3771 # Update the hashtree descriptor.
3772 ht_desc.fec_num_roots = fec_num_roots
3773 ht_desc.fec_offset = fec_offset
3774 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003775
David Zeuthen73f2afa2017-05-17 16:54:11 -04003776 ht_desc_to_setup = None
3777 if setup_as_rootfs_from_kernel:
3778 ht_desc_to_setup = ht_desc
3779
David Zeuthena4fee8b2016-08-22 15:20:43 -04003780 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003781 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003782 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003783 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003784 chain_partitions, rollback_index, flags, rollback_index_location,
3785 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003786 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003787 include_descriptors_from_image, signing_helper,
3788 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003789 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003790 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3791 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003792 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003793
David Zeuthend247fcb2017-02-16 12:09:27 -05003794 # Write vbmeta blob, if requested.
3795 if output_vbmeta_image:
3796 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003797
David Zeuthend247fcb2017-02-16 12:09:27 -05003798 # Append vbmeta blob and footer, unless requested not to.
3799 if not do_not_append_vbmeta_image:
3800 image.append_raw(vbmeta_blob_with_padding)
3801
3802 # Now insert a DONT_CARE chunk with enough bytes such that the
3803 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003804 if partition_size > 0:
3805 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003806 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003807
3808 # Generate the Footer that tells where the VBMeta footer
3809 # is. Also put enough padding in the front of the footer since
3810 # we'll write out an entire block.
3811 footer = AvbFooter()
3812 footer.original_image_size = original_image_size
3813 footer.vbmeta_offset = vbmeta_offset
3814 footer.vbmeta_size = len(vbmeta_blob)
3815 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003816 footer_blob_with_padding = (
3817 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003818 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003819
Jan Monschb1d920f2020-04-09 12:59:28 +02003820 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003821 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003822 image.truncate(original_image_size)
Jan Monsch7a722ee2021-06-17 11:49:05 +02003823 raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04003824
David Zeuthenc68f0822017-03-31 17:22:35 -04003825 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003826 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003827 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003828 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003829 """Implements the 'make_atx_certificate' command.
3830
3831 Android Things certificates are required for Android Things public key
3832 metadata. They chain the vbmeta signing key for a particular product back to
3833 a fused, permanent root key. These certificates are fixed-length and fixed-
3834 format with the explicit goal of not parsing ASN.1 in bootloader code.
3835
3836 Arguments:
3837 output: Certificate will be written to this file on success.
3838 authority_key_path: A PEM file path with the authority private key.
3839 If None, then a certificate will be created without a
3840 signature. The signature can be created out-of-band
3841 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003842 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003843 subject_key_version: A 64-bit version value. If this is None, the number
3844 of seconds since the epoch is used.
3845 subject: A subject identifier. For Product Signing Key certificates this
3846 should be the same Product ID found in the permanent attributes.
3847 is_intermediate_authority: True if the certificate is for an intermediate
3848 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003849 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003850 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003851 signing_helper_with_files: Same as signing_helper but uses files instead.
Jan Monsch9c130122020-04-14 13:43:51 +02003852
3853 Raises:
3854 AvbError: If there an error during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -08003855 """
3856 signed_data = bytearray()
3857 signed_data.extend(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003858 signed_data.extend(RSAPublicKey(subject_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003859 hasher = hashlib.sha256()
3860 hasher.update(subject)
3861 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003862 if not usage:
3863 usage = 'com.google.android.things.vboot'
3864 if is_intermediate_authority:
3865 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003866 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003867 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003868 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003869 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003870 subject_key_version = int(time.time())
3871 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003872 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003873 if authority_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003874 rsa_key = RSAPublicKey(authority_key_path)
Darren Krahn43e12d82017-02-24 16:26:31 -08003875 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003876 signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3877 signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08003878 output.write(signed_data)
3879 output.write(signature)
3880
David Zeuthenc68f0822017-03-31 17:22:35 -04003881 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003882 product_id):
3883 """Implements the 'make_atx_permanent_attributes' command.
3884
3885 Android Things permanent attributes are designed to be permanent for a
3886 particular product and a hash of these attributes should be fused into
3887 hardware to enforce this.
3888
3889 Arguments:
3890 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003891 root_authority_key_path: Path to a PEM or DER public key for
3892 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003893 product_id: A 16-byte Product ID.
3894
3895 Raises:
3896 AvbError: If an argument is incorrect.
3897 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003898 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003899 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003900 raise AvbError('Invalid Product ID length.')
3901 output.write(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003902 output.write(RSAPublicKey(root_authority_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003903 output.write(product_id)
3904
3905 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003906 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003907 """Implements the 'make_atx_metadata' command.
3908
3909 Android Things metadata are included in vbmeta images to facilitate
3910 verification. The output of this command can be used as the
3911 public_key_metadata argument to other commands.
3912
3913 Arguments:
3914 output: Metadata will be written to this file on success.
3915 intermediate_key_certificate: A certificate file as output by
3916 make_atx_certificate with
3917 is_intermediate_authority set to true.
3918 product_key_certificate: A certificate file as output by
3919 make_atx_certificate with
3920 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003921
3922 Raises:
3923 AvbError: If an argument is incorrect.
3924 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003925 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003926 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003927 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003928 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003929 raise AvbError('Invalid product key certificate length.')
3930 output.write(struct.pack('<I', 1)) # Format Version
3931 output.write(intermediate_key_certificate)
3932 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003933
Darren Krahnfccd64e2018-01-16 17:39:35 -08003934 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3935 unlock_key_certificate, challenge_path,
3936 unlock_key_path, signing_helper,
3937 signing_helper_with_files):
3938 """Implements the 'make_atx_unlock_credential' command.
3939
3940 Android Things unlock credentials can be used to authorize the unlock of AVB
3941 on a device. These credentials are presented to an Android Things bootloader
3942 via the fastboot interface in response to a 16-byte challenge. This method
3943 creates all fields of the credential except the challenge signature field
3944 (which is the last field) and can optionally create the challenge signature
3945 field as well if a challenge and the unlock_key_path is provided.
3946
3947 Arguments:
3948 output: The credential will be written to this file on success.
3949 intermediate_key_certificate: A certificate file as output by
3950 make_atx_certificate with
3951 is_intermediate_authority set to true.
3952 unlock_key_certificate: A certificate file as output by
3953 make_atx_certificate with
3954 is_intermediate_authority set to false and the
3955 usage set to
3956 'com.google.android.things.vboot.unlock'.
3957 challenge_path: [optional] A path to the challenge to sign.
3958 unlock_key_path: [optional] A PEM file path with the unlock private key.
3959 signing_helper: Program which signs a hash and returns the signature.
3960 signing_helper_with_files: Same as signing_helper but uses files instead.
3961
3962 Raises:
Jan Monsch9c130122020-04-14 13:43:51 +02003963 AvbError: If an argument is incorrect or an error occurs during signing.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003964 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003965 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3966 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003967 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3968 raise AvbError('Invalid intermediate key certificate length.')
3969 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3970 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003971 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003972 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003973 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003974 challenge = f.read()
3975 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3976 raise AvbError('Invalid unlock challenge length.')
3977 output.write(struct.pack('<I', 1)) # Format Version
3978 output.write(intermediate_key_certificate)
3979 output.write(unlock_key_certificate)
3980 if challenge_path and unlock_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003981 rsa_key = RSAPublicKey(unlock_key_path)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003982 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003983 signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3984 signing_helper_with_files)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003985 output.write(signature)
3986
David Zeuthen21e95262016-07-27 17:58:40 -04003987
3988def calc_hash_level_offsets(image_size, block_size, digest_size):
3989 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3990
3991 Arguments:
3992 image_size: The size of the image to calculate a Merkle-tree for.
3993 block_size: The block size, e.g. 4096.
3994 digest_size: The size of each hash, e.g. 32 for SHA-256.
3995
3996 Returns:
3997 A tuple where the first argument is an array of offsets and the
3998 second is size of the tree, in bytes.
3999 """
4000 level_offsets = []
4001 level_sizes = []
4002 tree_size = 0
4003
4004 num_levels = 0
4005 size = image_size
4006 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004007 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04004008 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4009
4010 level_sizes.append(level_size)
4011 tree_size += level_size
4012 num_levels += 1
4013
4014 size = level_size
4015
4016 for n in range(0, num_levels):
4017 offset = 0
4018 for m in range(n + 1, num_levels):
4019 offset += level_sizes[m]
4020 level_offsets.append(offset)
4021
David Zeuthena4fee8b2016-08-22 15:20:43 -04004022 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004023
4024
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004025# See system/extras/libfec/include/fec/io.h for these definitions.
4026FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4027FEC_MAGIC = 0xfecfecfe
4028
4029
4030def calc_fec_data_size(image_size, num_roots):
4031 """Calculates how much space FEC data will take.
4032
Jan Monschfe00c0a2019-12-11 11:19:40 +01004033 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004034 image_size: The size of the image.
4035 num_roots: Number of roots.
4036
4037 Returns:
4038 The number of bytes needed for FEC for an image of the given size
4039 and with the requested number of FEC roots.
4040
4041 Raises:
4042 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004043 """
4044 p = subprocess.Popen(
4045 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4046 stdout=subprocess.PIPE,
4047 stderr=subprocess.PIPE)
4048 (pout, perr) = p.communicate()
4049 retcode = p.wait()
4050 if retcode != 0:
4051 raise ValueError('Error invoking fec: {}'.format(perr))
4052 return int(pout)
4053
4054
4055def generate_fec_data(image_filename, num_roots):
4056 """Generate FEC codes for an image.
4057
Jan Monschfe00c0a2019-12-11 11:19:40 +01004058 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004059 image_filename: The filename of the image.
4060 num_roots: Number of roots.
4061
4062 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004063 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004064
4065 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02004066 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004067 """
Jan Monschb1d920f2020-04-09 12:59:28 +02004068 with tempfile.NamedTemporaryFile() as fec_tmpfile:
4069 try:
4070 subprocess.check_call(
4071 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4072 fec_tmpfile.name],
4073 stderr=open(os.devnull, 'wb'))
4074 except subprocess.CalledProcessError as e:
Jan Monsch7a722ee2021-06-17 11:49:05 +02004075 raise ValueError('Execution of \'fec\' tool failed: {}.'
4076 .format(e)) from e
Jan Monschb1d920f2020-04-09 12:59:28 +02004077 fec_data = fec_tmpfile.read()
4078
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004079 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4080 footer_data = fec_data[-footer_size:]
4081 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4082 footer_data)
4083 if magic != FEC_MAGIC:
4084 raise ValueError('Unexpected magic in FEC footer')
4085 return fec_data[0:fec_size]
4086
4087
David Zeuthen21e95262016-07-27 17:58:40 -04004088def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004089 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004090 """Generates a Merkle-tree for a file.
4091
Jan Monschfe00c0a2019-12-11 11:19:40 +01004092 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004093 image: The image, as a file.
4094 image_size: The size of the image.
4095 block_size: The block size, e.g. 4096.
4096 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4097 salt: The salt to use.
4098 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004099 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004100 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004101
4102 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004103 A tuple where the first element is the top-level hash as bytes and the
4104 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004105 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004106 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004107 hash_src_offset = 0
4108 hash_src_size = image_size
4109 level_num = 0
Bowgo Tsai35185662021-12-21 16:27:07 +08004110
4111 # If there is only one block, returns the top-level hash directly.
4112 if hash_src_size == block_size:
4113 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
4114 image.seek(0)
4115 hasher.update(image.read(block_size))
4116 return hasher.digest(), bytes(hash_ret)
4117
David Zeuthen21e95262016-07-27 17:58:40 -04004118 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08004119 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04004120 remaining = hash_src_size
4121 while remaining > 0:
Tianjie62ad0222021-02-22 14:31:12 -08004122 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004123 # Only read from the file for the first level - for subsequent
4124 # levels, access the array we're building.
4125 if level_num == 0:
4126 image.seek(hash_src_offset + hash_src_size - remaining)
4127 data = image.read(min(remaining, block_size))
4128 else:
4129 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4130 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004131 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004132
4133 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004134 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02004135 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08004136 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04004137 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02004138 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08004139
Jan Monschb1d920f2020-04-09 12:59:28 +02004140 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04004141
4142 padding_needed = (round_to_multiple(
4143 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02004144 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004145
David Zeuthena4fee8b2016-08-22 15:20:43 -04004146 # Copy level-output into resulting tree.
4147 offset = hash_level_offsets[level_num]
4148 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004149
David Zeuthena4fee8b2016-08-22 15:20:43 -04004150 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004151 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004152 level_num += 1
4153
Tianjie62ad0222021-02-22 14:31:12 -08004154 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04004155 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02004156 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04004157
4158
4159class AvbTool(object):
4160 """Object for avbtool command-line tool."""
4161
4162 def __init__(self):
4163 """Initializer method."""
4164 self.avb = Avb()
4165
4166 def _add_common_args(self, sub_parser):
4167 """Adds arguments used by several sub-commands.
4168
4169 Arguments:
4170 sub_parser: The parser to add arguments to.
4171 """
4172 sub_parser.add_argument('--algorithm',
4173 help='Algorithm to use (default: NONE)',
4174 metavar='ALGORITHM',
4175 default='NONE')
4176 sub_parser.add_argument('--key',
4177 help='Path to RSA private key file',
4178 metavar='KEY',
4179 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004180 sub_parser.add_argument('--signing_helper',
4181 help='Path to helper used for signing',
4182 metavar='APP',
4183 default=None,
4184 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004185 sub_parser.add_argument('--signing_helper_with_files',
4186 help='Path to helper used for signing using files',
4187 metavar='APP',
4188 default=None,
4189 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004190 sub_parser.add_argument('--public_key_metadata',
4191 help='Path to public key metadata file',
4192 metavar='KEY_METADATA',
4193 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004194 sub_parser.add_argument('--rollback_index',
4195 help='Rollback Index',
4196 type=parse_number,
4197 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004198 sub_parser.add_argument('--rollback_index_location',
4199 help='Location of main vbmeta Rollback Index',
4200 type=parse_number,
4201 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004202 # This is used internally for unit tests. Do not include in --help output.
4203 sub_parser.add_argument('--internal_release_string',
4204 help=argparse.SUPPRESS)
4205 sub_parser.add_argument('--append_to_release_string',
4206 help='Text to append to release string',
4207 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004208 sub_parser.add_argument('--prop',
4209 help='Add property',
4210 metavar='KEY:VALUE',
4211 action='append')
4212 sub_parser.add_argument('--prop_from_file',
4213 help='Add property from file',
4214 metavar='KEY:PATH',
4215 action='append')
4216 sub_parser.add_argument('--kernel_cmdline',
4217 help='Add kernel cmdline',
4218 metavar='CMDLINE',
4219 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004220 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4221 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4222 # at some future point.
4223 sub_parser.add_argument('--setup_rootfs_from_kernel',
4224 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004225 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004226 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004227 type=argparse.FileType('rb'))
4228 sub_parser.add_argument('--include_descriptors_from_image',
4229 help='Include descriptors from image',
4230 metavar='IMAGE',
4231 action='append',
4232 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004233 sub_parser.add_argument('--print_required_libavb_version',
4234 help=('Don\'t store the footer - '
4235 'instead calculate the required libavb '
4236 'version for the given options.'),
4237 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004238 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4239 sub_parser.add_argument('--chain_partition',
4240 help='Allow signed integrity-data for partition',
4241 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4242 action='append')
4243 sub_parser.add_argument('--flags',
4244 help='VBMeta flags',
4245 type=parse_number,
4246 default=0)
4247 sub_parser.add_argument('--set_hashtree_disabled_flag',
4248 help='Set the HASHTREE_DISABLED flag',
4249 action='store_true')
4250
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004251 def _add_common_footer_args(self, sub_parser):
4252 """Adds arguments used by add_*_footer sub-commands.
4253
4254 Arguments:
4255 sub_parser: The parser to add arguments to.
4256 """
4257 sub_parser.add_argument('--use_persistent_digest',
4258 help='Use a persistent digest on device instead of '
4259 'storing the digest in the descriptor. This '
4260 'cannot be used with A/B so must be combined '
4261 'with --do_not_use_ab when an A/B suffix is '
4262 'expected at runtime.',
4263 action='store_true')
4264 sub_parser.add_argument('--do_not_use_ab',
4265 help='The partition does not use A/B even when an '
4266 'A/B suffix is present. This must not be used '
4267 'for vbmeta or chained partitions.',
4268 action='store_true')
4269
David Zeuthena5fd3a42017-02-27 16:38:54 -05004270 def _fixup_common_args(self, args):
4271 """Common fixups needed by subcommands.
4272
4273 Arguments:
4274 args: Arguments to modify.
4275
4276 Returns:
4277 The modified arguments.
4278 """
4279 if args.set_hashtree_disabled_flag:
4280 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4281 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004282
4283 def run(self, argv):
4284 """Command-line processor.
4285
4286 Arguments:
4287 argv: Pass sys.argv from main.
4288 """
4289 parser = argparse.ArgumentParser()
4290 subparsers = parser.add_subparsers(title='subcommands')
4291
Jan Monsch2c7be992020-04-03 14:37:13 +02004292 sub_parser = subparsers.add_parser(
4293 'generate_test_image',
4294 help=('Generates a test image with a known pattern for testing: '
4295 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4296 sub_parser.add_argument('--image_size',
4297 help='Size of image to generate.',
4298 type=parse_number,
4299 required=True)
4300 sub_parser.add_argument('--start_byte',
4301 help='Integer for the start byte of the pattern.',
4302 type=parse_number,
4303 default=0)
4304 sub_parser.add_argument('--output',
4305 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004306 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004307 default=sys.stdout)
4308 sub_parser.set_defaults(func=self.generate_test_image)
4309
David Zeuthen21e95262016-07-27 17:58:40 -04004310 sub_parser = subparsers.add_parser('version',
4311 help='Prints version of avbtool.')
4312 sub_parser.set_defaults(func=self.version)
4313
4314 sub_parser = subparsers.add_parser('extract_public_key',
4315 help='Extract public key.')
4316 sub_parser.add_argument('--key',
4317 help='Path to RSA private key file',
4318 required=True)
4319 sub_parser.add_argument('--output',
4320 help='Output file name',
4321 type=argparse.FileType('wb'),
4322 required=True)
4323 sub_parser.set_defaults(func=self.extract_public_key)
4324
4325 sub_parser = subparsers.add_parser('make_vbmeta_image',
4326 help='Makes a vbmeta image.')
4327 sub_parser.add_argument('--output',
4328 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004329 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004330 sub_parser.add_argument('--padding_size',
4331 metavar='NUMBER',
4332 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004333 'its size is a multiple of NUMBER '
4334 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004335 type=parse_number,
4336 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004337 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004338 sub_parser.set_defaults(func=self.make_vbmeta_image)
4339
4340 sub_parser = subparsers.add_parser('add_hash_footer',
4341 help='Add hashes and footer to image.')
4342 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004343 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004344 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004345 sub_parser.add_argument('--partition_size',
4346 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004347 type=parse_number)
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08004348 sub_parser.add_argument('--dynamic_partition_size',
4349 help='Calculate partition size based on image size',
4350 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004351 sub_parser.add_argument('--partition_name',
4352 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004353 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004354 sub_parser.add_argument('--hash_algorithm',
4355 help='Hash algorithm to use (default: sha256)',
4356 default='sha256')
4357 sub_parser.add_argument('--salt',
4358 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004359 sub_parser.add_argument('--calc_max_image_size',
4360 help=('Don\'t store the footer - '
4361 'instead calculate the maximum image size '
4362 'leaving enough room for metadata with '
4363 'the given partition size.'),
4364 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004365 sub_parser.add_argument('--output_vbmeta_image',
4366 help='Also write vbmeta struct to file',
4367 type=argparse.FileType('wb'))
4368 sub_parser.add_argument('--do_not_append_vbmeta_image',
4369 help=('Do not append vbmeta struct or footer '
4370 'to the image'),
4371 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004372 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004373 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004374 sub_parser.set_defaults(func=self.add_hash_footer)
4375
David Zeuthenb1b994d2017-03-06 18:01:31 -05004376 sub_parser = subparsers.add_parser('append_vbmeta_image',
4377 help='Append vbmeta image to image.')
4378 sub_parser.add_argument('--image',
4379 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004380 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004381 sub_parser.add_argument('--partition_size',
4382 help='Partition size',
4383 type=parse_number,
4384 required=True)
4385 sub_parser.add_argument('--vbmeta_image',
4386 help='Image with vbmeta blob to append',
4387 type=argparse.FileType('rb'))
4388 sub_parser.set_defaults(func=self.append_vbmeta_image)
4389
Jan Monscheeb28b62019-12-05 16:17:09 +01004390 sub_parser = subparsers.add_parser(
4391 'add_hashtree_footer',
4392 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004393 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004394 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004395 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004396 sub_parser.add_argument('--partition_size',
4397 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004398 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004399 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004400 sub_parser.add_argument('--partition_name',
4401 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004402 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004403 sub_parser.add_argument('--hash_algorithm',
4404 help='Hash algorithm to use (default: sha1)',
4405 default='sha1')
4406 sub_parser.add_argument('--salt',
4407 help='Salt in hex (default: /dev/urandom)')
4408 sub_parser.add_argument('--block_size',
4409 help='Block size (default: 4096)',
4410 type=parse_number,
4411 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004412 # TODO(zeuthen): The --generate_fec option was removed when we
4413 # moved to generating FEC by default. To avoid breaking existing
4414 # users needing to transition we simply just print a warning below
4415 # in add_hashtree_footer(). Remove this option and the warning at
4416 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004417 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004418 help=argparse.SUPPRESS,
4419 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004420 sub_parser.add_argument(
4421 '--do_not_generate_fec',
4422 help='Do not generate forward-error-correction codes',
4423 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004424 sub_parser.add_argument('--fec_num_roots',
4425 help='Number of roots for FEC (default: 2)',
4426 type=parse_number,
4427 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004428 sub_parser.add_argument('--calc_max_image_size',
4429 help=('Don\'t store the hashtree or footer - '
4430 'instead calculate the maximum image size '
4431 'leaving enough room for hashtree '
4432 'and metadata with the given partition '
4433 'size.'),
4434 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004435 sub_parser.add_argument('--output_vbmeta_image',
4436 help='Also write vbmeta struct to file',
4437 type=argparse.FileType('wb'))
4438 sub_parser.add_argument('--do_not_append_vbmeta_image',
4439 help=('Do not append vbmeta struct or footer '
4440 'to the image'),
4441 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004442 # This is different from --setup_rootfs_from_kernel insofar that
4443 # it doesn't take an IMAGE, the generated cmdline will be for the
4444 # hashtree we're adding.
4445 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4446 action='store_true',
4447 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004448 sub_parser.add_argument('--no_hashtree',
4449 action='store_true',
4450 help='Do not append hashtree')
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004451 sub_parser.add_argument('--check_at_most_once',
4452 action='store_true',
4453 help='Set to verify data block only once')
David Zeuthen21e95262016-07-27 17:58:40 -04004454 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004455 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004456 sub_parser.set_defaults(func=self.add_hashtree_footer)
4457
4458 sub_parser = subparsers.add_parser('erase_footer',
4459 help='Erase footer from an image.')
4460 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004461 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004462 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004463 required=True)
4464 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004465 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004466 action='store_true')
4467 sub_parser.set_defaults(func=self.erase_footer)
4468
David Zeuthen1394f762019-04-30 10:20:11 -04004469 sub_parser = subparsers.add_parser('zero_hashtree',
4470 help='Zero out hashtree and FEC data.')
4471 sub_parser.add_argument('--image',
4472 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004473 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004474 required=True)
4475 sub_parser.set_defaults(func=self.zero_hashtree)
4476
Jan Monscheeb28b62019-12-05 16:17:09 +01004477 sub_parser = subparsers.add_parser(
4478 'extract_vbmeta_image',
4479 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004480 sub_parser.add_argument('--image',
4481 help='Image with footer',
4482 type=argparse.FileType('rb'),
4483 required=True)
4484 sub_parser.add_argument('--output',
4485 help='Output file name',
4486 type=argparse.FileType('wb'))
4487 sub_parser.add_argument('--padding_size',
4488 metavar='NUMBER',
4489 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004490 'its size is a multiple of NUMBER '
4491 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004492 type=parse_number,
4493 default=0)
4494 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4495
David Zeuthen2bc232b2017-04-19 14:25:19 -04004496 sub_parser = subparsers.add_parser('resize_image',
4497 help='Resize image with a footer.')
4498 sub_parser.add_argument('--image',
4499 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004500 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004501 required=True)
4502 sub_parser.add_argument('--partition_size',
4503 help='New partition size',
4504 type=parse_number)
4505 sub_parser.set_defaults(func=self.resize_image)
4506
David Zeuthen21e95262016-07-27 17:58:40 -04004507 sub_parser = subparsers.add_parser(
4508 'info_image',
4509 help='Show information about vbmeta or footer.')
4510 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004511 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004512 type=argparse.FileType('rb'),
4513 required=True)
4514 sub_parser.add_argument('--output',
4515 help='Write info to file',
4516 type=argparse.FileType('wt'),
4517 default=sys.stdout)
Sen Jiang01553a22020-06-30 17:58:44 -07004518 sub_parser.add_argument('--atx',
4519 help=('Show information about Android Things '
4520 'eXtension (ATX).'),
4521 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004522 sub_parser.set_defaults(func=self.info_image)
4523
David Zeuthenb623d8b2017-04-04 16:05:53 -04004524 sub_parser = subparsers.add_parser(
4525 'verify_image',
4526 help='Verify an image.')
4527 sub_parser.add_argument('--image',
4528 help='Image to verify',
4529 type=argparse.FileType('rb'),
4530 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004531 sub_parser.add_argument('--key',
4532 help='Check embedded public key matches KEY',
4533 metavar='KEY',
4534 required=False)
4535 sub_parser.add_argument('--expected_chain_partition',
4536 help='Expected chain partition',
4537 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4538 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004539 sub_parser.add_argument(
4540 '--follow_chain_partitions',
4541 help=('Follows chain partitions even when not '
4542 'specified with the --expected_chain_partition option'),
4543 action='store_true')
4544 sub_parser.add_argument(
4545 '--accept_zeroed_hashtree',
4546 help=('Accept images where the hashtree or FEC data is zeroed out'),
4547 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004548 sub_parser.set_defaults(func=self.verify_image)
4549
David Zeuthenb8643c02018-05-17 17:21:18 -04004550 sub_parser = subparsers.add_parser(
David Zeuthen34b6b492020-04-13 14:45:02 -04004551 'print_partition_digests',
4552 help='Prints partition digests.')
4553 sub_parser.add_argument('--image',
4554 help='Image to print partition digests from',
4555 type=argparse.FileType('rb'),
4556 required=True)
4557 sub_parser.add_argument('--output',
4558 help='Write info to file',
4559 type=argparse.FileType('wt'),
4560 default=sys.stdout)
4561 sub_parser.add_argument('--json',
4562 help=('Print output as JSON'),
4563 action='store_true')
4564 sub_parser.set_defaults(func=self.print_partition_digests)
4565
4566 sub_parser = subparsers.add_parser(
David Zeuthenb8643c02018-05-17 17:21:18 -04004567 'calculate_vbmeta_digest',
4568 help='Calculate vbmeta digest.')
4569 sub_parser.add_argument('--image',
4570 help='Image to calculate digest for',
4571 type=argparse.FileType('rb'),
4572 required=True)
4573 sub_parser.add_argument('--hash_algorithm',
4574 help='Hash algorithm to use (default: sha256)',
4575 default='sha256')
4576 sub_parser.add_argument('--output',
4577 help='Write hex digest to file (default: stdout)',
4578 type=argparse.FileType('wt'),
4579 default=sys.stdout)
4580 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4581
David Zeuthenf7d2e752018-09-20 13:30:41 -04004582 sub_parser = subparsers.add_parser(
4583 'calculate_kernel_cmdline',
4584 help='Calculate kernel cmdline.')
4585 sub_parser.add_argument('--image',
4586 help='Image to calculate kernel cmdline for',
4587 type=argparse.FileType('rb'),
4588 required=True)
4589 sub_parser.add_argument('--hashtree_disabled',
4590 help='Return the cmdline for hashtree disabled',
4591 action='store_true')
4592 sub_parser.add_argument('--output',
4593 help='Write cmdline to file (default: stdout)',
4594 type=argparse.FileType('wt'),
4595 default=sys.stdout)
4596 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4597
David Zeuthen8b6973b2016-09-20 12:39:49 -04004598 sub_parser = subparsers.add_parser('set_ab_metadata',
4599 help='Set A/B metadata.')
4600 sub_parser.add_argument('--misc_image',
4601 help=('The misc image to modify. If the image does '
4602 'not exist, it will be created.'),
4603 type=argparse.FileType('r+b'),
4604 required=True)
4605 sub_parser.add_argument('--slot_data',
4606 help=('Slot data of the form "priority", '
4607 '"tries_remaining", "sucessful_boot" for '
4608 'slot A followed by the same for slot B, '
4609 'separated by colons. The default value '
4610 'is 15:7:0:14:7:0.'),
4611 default='15:7:0:14:7:0')
4612 sub_parser.set_defaults(func=self.set_ab_metadata)
4613
Darren Krahn147b08d2016-12-20 16:38:29 -08004614 sub_parser = subparsers.add_parser(
4615 'make_atx_certificate',
4616 help='Create an Android Things eXtension (ATX) certificate.')
4617 sub_parser.add_argument('--output',
4618 help='Write certificate to file',
4619 type=argparse.FileType('wb'),
4620 default=sys.stdout)
4621 sub_parser.add_argument('--subject',
4622 help=('Path to subject file'),
4623 type=argparse.FileType('rb'),
4624 required=True)
4625 sub_parser.add_argument('--subject_key',
4626 help=('Path to subject RSA public key file'),
4627 type=argparse.FileType('rb'),
4628 required=True)
4629 sub_parser.add_argument('--subject_key_version',
4630 help=('Version of the subject key'),
4631 type=parse_number,
4632 required=False)
4633 sub_parser.add_argument('--subject_is_intermediate_authority',
4634 help=('Generate an intermediate authority '
4635 'certificate'),
4636 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004637 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004638 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004639 'string'),
4640 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004641 sub_parser.add_argument('--authority_key',
4642 help='Path to authority RSA private key file',
4643 required=False)
4644 sub_parser.add_argument('--signing_helper',
4645 help='Path to helper used for signing',
4646 metavar='APP',
4647 default=None,
4648 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004649 sub_parser.add_argument('--signing_helper_with_files',
4650 help='Path to helper used for signing using files',
4651 metavar='APP',
4652 default=None,
4653 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004654 sub_parser.set_defaults(func=self.make_atx_certificate)
4655
4656 sub_parser = subparsers.add_parser(
4657 'make_atx_permanent_attributes',
4658 help='Create Android Things eXtension (ATX) permanent attributes.')
4659 sub_parser.add_argument('--output',
4660 help='Write attributes to file',
4661 type=argparse.FileType('wb'),
4662 default=sys.stdout)
4663 sub_parser.add_argument('--root_authority_key',
4664 help='Path to authority RSA public key file',
4665 type=argparse.FileType('rb'),
4666 required=True)
4667 sub_parser.add_argument('--product_id',
4668 help=('Path to Product ID file'),
4669 type=argparse.FileType('rb'),
4670 required=True)
4671 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4672
4673 sub_parser = subparsers.add_parser(
4674 'make_atx_metadata',
4675 help='Create Android Things eXtension (ATX) metadata.')
4676 sub_parser.add_argument('--output',
4677 help='Write metadata to file',
4678 type=argparse.FileType('wb'),
4679 default=sys.stdout)
4680 sub_parser.add_argument('--intermediate_key_certificate',
4681 help='Path to intermediate key certificate file',
4682 type=argparse.FileType('rb'),
4683 required=True)
4684 sub_parser.add_argument('--product_key_certificate',
4685 help='Path to product key certificate file',
4686 type=argparse.FileType('rb'),
4687 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004688 sub_parser.set_defaults(func=self.make_atx_metadata)
4689
Darren Krahnfccd64e2018-01-16 17:39:35 -08004690 sub_parser = subparsers.add_parser(
4691 'make_atx_unlock_credential',
4692 help='Create an Android Things eXtension (ATX) unlock credential.')
4693 sub_parser.add_argument('--output',
4694 help='Write credential to file',
4695 type=argparse.FileType('wb'),
4696 default=sys.stdout)
4697 sub_parser.add_argument('--intermediate_key_certificate',
4698 help='Path to intermediate key certificate file',
4699 type=argparse.FileType('rb'),
4700 required=True)
4701 sub_parser.add_argument('--unlock_key_certificate',
4702 help='Path to unlock key certificate file',
4703 type=argparse.FileType('rb'),
4704 required=True)
4705 sub_parser.add_argument('--challenge',
4706 help='Path to the challenge to sign (optional). If '
4707 'this is not provided the challenge signature '
4708 'field is omitted and can be concatenated '
4709 'later.',
4710 required=False)
4711 sub_parser.add_argument('--unlock_key',
4712 help='Path to unlock key (optional). Must be '
4713 'provided if using --challenge.',
4714 required=False)
4715 sub_parser.add_argument('--signing_helper',
4716 help='Path to helper used for signing',
4717 metavar='APP',
4718 default=None,
4719 required=False)
4720 sub_parser.add_argument('--signing_helper_with_files',
4721 help='Path to helper used for signing using files',
4722 metavar='APP',
4723 default=None,
4724 required=False)
4725 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4726
David Zeuthen21e95262016-07-27 17:58:40 -04004727 args = parser.parse_args(argv[1:])
4728 try:
4729 args.func(args)
Jan Monschcc9939a2020-04-16 09:15:20 +02004730 except AttributeError:
4731 # This error gets raised when the command line tool is called without any
4732 # arguments. It mimics the original Python 2 behavior.
4733 parser.print_usage()
4734 print('avbtool: error: too few arguments')
4735 sys.exit(2)
David Zeuthen21e95262016-07-27 17:58:40 -04004736 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004737 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004738 sys.exit(1)
4739
4740 def version(self, _):
4741 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004742 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004743
Jan Monsch2c7be992020-04-03 14:37:13 +02004744 def generate_test_image(self, args):
4745 """Implements the 'generate_test_image' sub-command."""
4746 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4747
David Zeuthen21e95262016-07-27 17:58:40 -04004748 def extract_public_key(self, args):
4749 """Implements the 'extract_public_key' sub-command."""
4750 self.avb.extract_public_key(args.key, args.output)
4751
4752 def make_vbmeta_image(self, args):
4753 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004754 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004755 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004756 args.algorithm, args.key,
4757 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004758 args.flags, args.rollback_index_location,
4759 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004760 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004761 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004762 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004763 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004764 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004765 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004766 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004767 args.print_required_libavb_version,
4768 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004769
David Zeuthenb1b994d2017-03-06 18:01:31 -05004770 def append_vbmeta_image(self, args):
4771 """Implements the 'append_vbmeta_image' sub-command."""
4772 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4773 args.partition_size)
4774
David Zeuthen21e95262016-07-27 17:58:40 -04004775 def add_hash_footer(self, args):
4776 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004777 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004778 self.avb.add_hash_footer(args.image.name if args.image else None,
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08004779 args.partition_size, args.dynamic_partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004780 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004781 args.salt, args.chain_partition, args.algorithm,
4782 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004783 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004784 args.flags, args.rollback_index_location,
4785 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004786 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004787 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004788 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004789 args.calc_max_image_size,
4790 args.signing_helper,
4791 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004792 args.internal_release_string,
4793 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004794 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004795 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004796 args.print_required_libavb_version,
4797 args.use_persistent_digest,
4798 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004799
4800 def add_hashtree_footer(self, args):
4801 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004802 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004803 # TODO(zeuthen): Remove when removing support for the
4804 # '--generate_fec' option above.
4805 if args.generate_fec:
4806 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4807 'is now generated by default. Use the option '
4808 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004809 self.avb.add_hashtree_footer(
4810 args.image.name if args.image else None,
4811 args.partition_size,
4812 args.partition_name,
4813 not args.do_not_generate_fec, args.fec_num_roots,
4814 args.hash_algorithm, args.block_size,
4815 args.salt, args.chain_partition, args.algorithm,
4816 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004817 args.rollback_index, args.flags,
4818 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004819 args.prop_from_file,
4820 args.kernel_cmdline,
4821 args.setup_rootfs_from_kernel,
4822 args.setup_as_rootfs_from_kernel,
4823 args.include_descriptors_from_image,
4824 args.calc_max_image_size,
4825 args.signing_helper,
4826 args.signing_helper_with_files,
4827 args.internal_release_string,
4828 args.append_to_release_string,
4829 args.output_vbmeta_image,
4830 args.do_not_append_vbmeta_image,
4831 args.print_required_libavb_version,
4832 args.use_persistent_digest,
4833 args.do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004834 args.no_hashtree,
4835 args.check_at_most_once)
David Zeuthend247fcb2017-02-16 12:09:27 -05004836
David Zeuthen21e95262016-07-27 17:58:40 -04004837 def erase_footer(self, args):
4838 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004839 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004840
David Zeuthen1394f762019-04-30 10:20:11 -04004841 def zero_hashtree(self, args):
4842 """Implements the 'zero_hashtree' sub-command."""
4843 self.avb.zero_hashtree(args.image.name)
4844
David Zeuthen49936b42018-08-07 17:38:58 -04004845 def extract_vbmeta_image(self, args):
4846 """Implements the 'extract_vbmeta_image' sub-command."""
4847 self.avb.extract_vbmeta_image(args.output, args.image.name,
4848 args.padding_size)
4849
David Zeuthen2bc232b2017-04-19 14:25:19 -04004850 def resize_image(self, args):
4851 """Implements the 'resize_image' sub-command."""
4852 self.avb.resize_image(args.image.name, args.partition_size)
4853
David Zeuthen8b6973b2016-09-20 12:39:49 -04004854 def set_ab_metadata(self, args):
4855 """Implements the 'set_ab_metadata' sub-command."""
4856 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4857
David Zeuthen21e95262016-07-27 17:58:40 -04004858 def info_image(self, args):
4859 """Implements the 'info_image' sub-command."""
Sen Jiang01553a22020-06-30 17:58:44 -07004860 self.avb.info_image(args.image.name, args.output, args.atx)
David Zeuthen21e95262016-07-27 17:58:40 -04004861
David Zeuthenb623d8b2017-04-04 16:05:53 -04004862 def verify_image(self, args):
4863 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004864 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004865 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004866 args.follow_chain_partitions,
4867 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004868
David Zeuthen34b6b492020-04-13 14:45:02 -04004869 def print_partition_digests(self, args):
4870 """Implements the 'print_partition_digests' sub-command."""
4871 self.avb.print_partition_digests(args.image.name, args.output, args.json)
4872
David Zeuthenb8643c02018-05-17 17:21:18 -04004873 def calculate_vbmeta_digest(self, args):
4874 """Implements the 'calculate_vbmeta_digest' sub-command."""
4875 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4876 args.output)
4877
David Zeuthenf7d2e752018-09-20 13:30:41 -04004878 def calculate_kernel_cmdline(self, args):
4879 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004880 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4881 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004882
Darren Krahn147b08d2016-12-20 16:38:29 -08004883 def make_atx_certificate(self, args):
4884 """Implements the 'make_atx_certificate' sub-command."""
4885 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004886 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004887 args.subject_key_version,
4888 args.subject.read(),
4889 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004890 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004891 args.signing_helper,
4892 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004893
4894 def make_atx_permanent_attributes(self, args):
4895 """Implements the 'make_atx_permanent_attributes' sub-command."""
4896 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004897 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004898 args.product_id.read())
4899
4900 def make_atx_metadata(self, args):
4901 """Implements the 'make_atx_metadata' sub-command."""
4902 self.avb.make_atx_metadata(args.output,
4903 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004904 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004905
Darren Krahnfccd64e2018-01-16 17:39:35 -08004906 def make_atx_unlock_credential(self, args):
4907 """Implements the 'make_atx_unlock_credential' sub-command."""
4908 self.avb.make_atx_unlock_credential(
4909 args.output,
4910 args.intermediate_key_certificate.read(),
4911 args.unlock_key_certificate.read(),
4912 args.challenge,
4913 args.unlock_key,
4914 args.signing_helper,
4915 args.signing_helper_with_files)
4916
David Zeuthen21e95262016-07-27 17:58:40 -04004917
4918if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004919 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004920 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4921 log.write(' '.join(sys.argv))
4922 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004923
David Zeuthen21e95262016-07-27 17:58:40 -04004924 tool = AvbTool()
4925 tool.run(sys.argv)