blob: 5fb48b8e3520de530ac094fc95422205bf8400fd [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:
3437 partition_size = original_image_size + max_metadata_size
3438
3439 # Round up to block size.
3440 partition_size -= partition_size % -image.block_size
3441
3442 max_image_size = partition_size - max_metadata_size
3443 if partition_size % image.block_size != 0:
3444 raise AvbError('Partition size of {} is not a multiple of the image '
3445 'block size {}.'.format(partition_size,
3446 image.block_size))
3447
David Zeuthen21e95262016-07-27 17:58:40 -04003448 # If anything goes wrong from here-on, restore the image back to
3449 # its original size.
3450 try:
David Zeuthen09692692016-09-30 16:16:40 -04003451 # If image size exceeds the maximum image size, fail.
3452 if image.image_size > max_image_size:
3453 raise AvbError('Image size of {} exceeds maximum image '
3454 'size of {} in order to fit in a partition '
3455 'size of {}.'.format(image.image_size, max_image_size,
3456 partition_size))
3457
Jan Monsch6f27bb12020-04-07 07:33:26 +02003458 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003459 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003460 salt = binascii.unhexlify(salt)
3461 elif salt is None and not use_persistent_digest:
3462 # If salt is not explicitly specified, choose a hash that's the same
3463 # size as the hash size. Don't populate a random salt if this
3464 # descriptor is being created to use a persistent digest on device.
3465 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003466 with open('/dev/urandom', 'rb') as f:
3467 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003468 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003469 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003470
Jan Monsch6f27bb12020-04-07 07:33:26 +02003471 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003472 # TODO(zeuthen): might want to read this in chunks to avoid
3473 # memory pressure, then again, this is only supposed to be used
3474 # on kernel/initramfs partitions. Possible optimization.
3475 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003476 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003477 digest = hasher.digest()
3478
3479 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003480 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003481 h_desc.hash_algorithm = hash_algorithm
3482 h_desc.partition_name = partition_name
3483 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003484 h_desc.flags = 0
3485 if do_not_use_ab:
3486 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3487 if not use_persistent_digest:
3488 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003489
3490 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003491 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003492 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003493 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003494 chain_partitions, rollback_index, flags, rollback_index_location,
3495 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003496 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003497 include_descriptors_from_image, signing_helper,
3498 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003499 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003500
David Zeuthend247fcb2017-02-16 12:09:27 -05003501 # Write vbmeta blob, if requested.
3502 if output_vbmeta_image:
3503 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003504
David Zeuthend247fcb2017-02-16 12:09:27 -05003505 # Append vbmeta blob and footer, unless requested not to.
3506 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003507 # If the image isn't sparse, its size might not be a multiple of
3508 # the block size. This will screw up padding later so just grow it.
3509 if image.image_size % image.block_size != 0:
3510 assert not image.is_sparse
3511 padding_needed = image.block_size - (
3512 image.image_size % image.block_size)
3513 image.truncate(image.image_size + padding_needed)
3514
3515 # The append_raw() method requires content with size being a
3516 # multiple of |block_size| so add padding as needed. Also record
3517 # where this is written to since we'll need to put that in the
3518 # footer.
3519 vbmeta_offset = image.image_size
3520 padding_needed = (
3521 round_to_multiple(len(vbmeta_blob), image.block_size) -
3522 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003523 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003524
David Zeuthend247fcb2017-02-16 12:09:27 -05003525 image.append_raw(vbmeta_blob_with_padding)
3526 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3527
3528 # Now insert a DONT_CARE chunk with enough bytes such that the
3529 # final Footer block is at the end of partition_size..
3530 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003531 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003532
3533 # Generate the Footer that tells where the VBMeta footer
3534 # is. Also put enough padding in the front of the footer since
3535 # we'll write out an entire block.
3536 footer = AvbFooter()
3537 footer.original_image_size = original_image_size
3538 footer.vbmeta_offset = vbmeta_offset
3539 footer.vbmeta_size = len(vbmeta_blob)
3540 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003541 footer_blob_with_padding = (
3542 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003543 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003544 except Exception as e:
3545 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003546 image.truncate(original_image_size)
Jan Monsch7a722ee2021-06-17 11:49:05 +02003547 raise AvbError('Adding hash_footer failed: {}.'.format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04003548
David Zeuthena4fee8b2016-08-22 15:20:43 -04003549 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003550 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003551 block_size, salt, chain_partitions, algorithm_name,
3552 key_path,
3553 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003554 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003555 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003556 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003557 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003558 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003559 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003560 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003561 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003562 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003563 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003564 use_persistent_root_digest, do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003565 no_hashtree, check_at_most_once):
David Zeuthen21e95262016-07-27 17:58:40 -04003566 """Implements the 'add_hashtree_footer' command.
3567
3568 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3569 more information about dm-verity and these hashes.
3570
3571 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003572 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003573 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003574 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003575 generate_fec: If True, generate FEC codes.
3576 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003577 hash_algorithm: Hash algorithm to use.
3578 block_size: Block size to use.
3579 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003580 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003581 algorithm_name: Name of algorithm to use.
3582 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003583 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003584 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003585 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003586 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003587 props: Properties to insert (List of strings of the form 'key:value').
3588 props_from_file: Properties to insert (List of strings 'key:<path>').
3589 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003590 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003591 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003592 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3593 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003594 include_descriptors_from_image: List of file objects for which
3595 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003596 calc_max_image_size: Don't store the hashtree or footer - instead
3597 calculate the maximum image size leaving enough room for hashtree
3598 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003599 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003600 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003601 release_string: None or avbtool release string.
3602 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003603 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3604 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003605 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003606 use_persistent_root_digest: Use a persistent root digest on device.
3607 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003608 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
Jan Monsch7a722ee2021-06-17 11:49:05 +02003609 check_at_most_once: Set to verify data blocks only the first time they
3610 are read from the data device.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003611
3612 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003613 AvbError: If an argument is incorrect or adding the hashtree footer
3614 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003615 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003616 required_libavb_version_minor = 0
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003617 if use_persistent_root_digest or do_not_use_ab or check_at_most_once:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003618 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003619 if rollback_index_location > 0:
3620 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003621
David Zeuthen1097a782017-05-31 15:53:17 -04003622 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003623 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003624 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003625 return
3626
Tianjie62ad0222021-02-22 14:31:12 -08003627 digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
3628 .digest())
David Zeuthen09692692016-09-30 16:16:40 -04003629 digest_padding = round_to_pow2(digest_size) - digest_size
3630
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003631 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3632 # size such that an image this size + the hashtree + metadata (footer +
3633 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3634 # for metadata.
3635 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003636 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003637 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003638 if not no_hashtree:
3639 (_, max_tree_size) = calc_hash_level_offsets(
3640 partition_size, block_size, digest_size + digest_padding)
3641 if generate_fec:
3642 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003643 max_metadata_size = (max_fec_size + max_tree_size +
3644 self.MAX_VBMETA_SIZE +
3645 self.MAX_FOOTER_SIZE)
3646 max_image_size = partition_size - max_metadata_size
3647 else:
3648 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003649
3650 # If we're asked to only calculate the maximum image size, we're done.
3651 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003652 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003653 return
3654
David Zeuthena4fee8b2016-08-22 15:20:43 -04003655 image = ImageHandler(image_filename)
3656
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003657 if partition_size > 0:
3658 if partition_size % image.block_size != 0:
3659 raise AvbError('Partition size of {} is not a multiple of the image '
3660 'block size {}.'.format(partition_size,
3661 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003662 elif image.image_size % image.block_size != 0:
3663 raise AvbError('File size of {} is not a multiple of the image '
3664 'block size {}.'.format(image.image_size,
3665 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003666
David Zeuthen21e95262016-07-27 17:58:40 -04003667 # If there's already a footer, truncate the image to its original
3668 # size. This way 'avbtool add_hashtree_footer' is idempotent
3669 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003670 if image.image_size >= AvbFooter.SIZE:
3671 image.seek(image.image_size - AvbFooter.SIZE)
3672 try:
3673 footer = AvbFooter(image.read(AvbFooter.SIZE))
3674 # Existing footer found. Just truncate.
3675 original_image_size = footer.original_image_size
3676 image.truncate(footer.original_image_size)
3677 except (LookupError, struct.error):
3678 original_image_size = image.image_size
3679 else:
3680 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003681 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003682
3683 # If anything goes wrong from here-on, restore the image back to
3684 # its original size.
3685 try:
3686 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003687 rounded_image_size = round_to_multiple(image.image_size, block_size)
3688 if rounded_image_size > image.image_size:
3689 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003690
David Zeuthen09692692016-09-30 16:16:40 -04003691 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003692 if partition_size > 0:
3693 if image.image_size > max_image_size:
3694 raise AvbError('Image size of {} exceeds maximum image '
3695 'size of {} in order to fit in a partition '
3696 'size of {}.'.format(image.image_size, max_image_size,
3697 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003698
3699 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003700 salt = binascii.unhexlify(salt)
3701 elif salt is None and not use_persistent_root_digest:
3702 # If salt is not explicitly specified, choose a hash that's the same
3703 # size as the hash size. Don't populate a random salt if this
3704 # descriptor is being created to use a persistent digest on device.
3705 hash_size = digest_size
Jan Monsch13efb5f2020-04-22 17:34:24 +02003706 with open('/dev/urandom', 'rb') as f:
Jan Monschb1d920f2020-04-09 12:59:28 +02003707 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003708 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003709 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003710
David Zeuthena4fee8b2016-08-22 15:20:43 -04003711 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003712 # offsets in advance.
3713 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003714 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003715
David Zeuthena4fee8b2016-08-22 15:20:43 -04003716 # If the image isn't sparse, its size might not be a multiple of
3717 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003718 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003719 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003720 padding_needed = image.block_size - (image.image_size%image.block_size)
3721 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003722
David Zeuthena4fee8b2016-08-22 15:20:43 -04003723 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003724 tree_offset = image.image_size
3725 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003726 block_size,
3727 hash_algorithm, salt,
3728 digest_padding,
3729 hash_level_offsets,
3730 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003731
3732 # Generate HashtreeDescriptor with details about the tree we
3733 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003734 if no_hashtree:
3735 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003736 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003737 ht_desc = AvbHashtreeDescriptor()
3738 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003739 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003740 ht_desc.tree_offset = tree_offset
3741 ht_desc.tree_size = tree_size
3742 ht_desc.data_block_size = block_size
3743 ht_desc.hash_block_size = block_size
3744 ht_desc.hash_algorithm = hash_algorithm
3745 ht_desc.partition_name = partition_name
3746 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003747 if do_not_use_ab:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003748 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003749 if not use_persistent_root_digest:
3750 ht_desc.root_digest = root_digest
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003751 if check_at_most_once:
3752 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE
David Zeuthen21e95262016-07-27 17:58:40 -04003753
David Zeuthen09692692016-09-30 16:16:40 -04003754 # Write the hash tree
3755 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3756 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003757 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003758 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003759 len_hashtree_and_fec = len(hash_tree_with_padding)
3760
3761 # Generate FEC codes, if requested.
3762 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003763 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003764 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003765 else:
3766 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003767 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3768 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003769 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003770 fec_offset = image.image_size
3771 image.append_raw(fec_data_with_padding)
3772 len_hashtree_and_fec += len(fec_data_with_padding)
3773 # Update the hashtree descriptor.
3774 ht_desc.fec_num_roots = fec_num_roots
3775 ht_desc.fec_offset = fec_offset
3776 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003777
David Zeuthen73f2afa2017-05-17 16:54:11 -04003778 ht_desc_to_setup = None
3779 if setup_as_rootfs_from_kernel:
3780 ht_desc_to_setup = ht_desc
3781
David Zeuthena4fee8b2016-08-22 15:20:43 -04003782 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003783 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003784 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003785 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003786 chain_partitions, rollback_index, flags, rollback_index_location,
3787 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003788 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003789 include_descriptors_from_image, signing_helper,
3790 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003791 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003792 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3793 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003794 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003795
David Zeuthend247fcb2017-02-16 12:09:27 -05003796 # Write vbmeta blob, if requested.
3797 if output_vbmeta_image:
3798 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003799
David Zeuthend247fcb2017-02-16 12:09:27 -05003800 # Append vbmeta blob and footer, unless requested not to.
3801 if not do_not_append_vbmeta_image:
3802 image.append_raw(vbmeta_blob_with_padding)
3803
3804 # Now insert a DONT_CARE chunk with enough bytes such that the
3805 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003806 if partition_size > 0:
3807 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003808 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003809
3810 # Generate the Footer that tells where the VBMeta footer
3811 # is. Also put enough padding in the front of the footer since
3812 # we'll write out an entire block.
3813 footer = AvbFooter()
3814 footer.original_image_size = original_image_size
3815 footer.vbmeta_offset = vbmeta_offset
3816 footer.vbmeta_size = len(vbmeta_blob)
3817 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003818 footer_blob_with_padding = (
3819 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003820 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003821
Jan Monschb1d920f2020-04-09 12:59:28 +02003822 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003823 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003824 image.truncate(original_image_size)
Jan Monsch7a722ee2021-06-17 11:49:05 +02003825 raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e
David Zeuthen21e95262016-07-27 17:58:40 -04003826
David Zeuthenc68f0822017-03-31 17:22:35 -04003827 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003828 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003829 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003830 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003831 """Implements the 'make_atx_certificate' command.
3832
3833 Android Things certificates are required for Android Things public key
3834 metadata. They chain the vbmeta signing key for a particular product back to
3835 a fused, permanent root key. These certificates are fixed-length and fixed-
3836 format with the explicit goal of not parsing ASN.1 in bootloader code.
3837
3838 Arguments:
3839 output: Certificate will be written to this file on success.
3840 authority_key_path: A PEM file path with the authority private key.
3841 If None, then a certificate will be created without a
3842 signature. The signature can be created out-of-band
3843 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003844 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003845 subject_key_version: A 64-bit version value. If this is None, the number
3846 of seconds since the epoch is used.
3847 subject: A subject identifier. For Product Signing Key certificates this
3848 should be the same Product ID found in the permanent attributes.
3849 is_intermediate_authority: True if the certificate is for an intermediate
3850 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003851 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003852 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003853 signing_helper_with_files: Same as signing_helper but uses files instead.
Jan Monsch9c130122020-04-14 13:43:51 +02003854
3855 Raises:
3856 AvbError: If there an error during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -08003857 """
3858 signed_data = bytearray()
3859 signed_data.extend(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003860 signed_data.extend(RSAPublicKey(subject_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003861 hasher = hashlib.sha256()
3862 hasher.update(subject)
3863 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003864 if not usage:
3865 usage = 'com.google.android.things.vboot'
3866 if is_intermediate_authority:
3867 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003868 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003869 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003870 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003871 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003872 subject_key_version = int(time.time())
3873 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003874 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003875 if authority_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003876 rsa_key = RSAPublicKey(authority_key_path)
Darren Krahn43e12d82017-02-24 16:26:31 -08003877 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003878 signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3879 signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08003880 output.write(signed_data)
3881 output.write(signature)
3882
David Zeuthenc68f0822017-03-31 17:22:35 -04003883 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003884 product_id):
3885 """Implements the 'make_atx_permanent_attributes' command.
3886
3887 Android Things permanent attributes are designed to be permanent for a
3888 particular product and a hash of these attributes should be fused into
3889 hardware to enforce this.
3890
3891 Arguments:
3892 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003893 root_authority_key_path: Path to a PEM or DER public key for
3894 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003895 product_id: A 16-byte Product ID.
3896
3897 Raises:
3898 AvbError: If an argument is incorrect.
3899 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003900 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003901 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003902 raise AvbError('Invalid Product ID length.')
3903 output.write(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003904 output.write(RSAPublicKey(root_authority_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003905 output.write(product_id)
3906
3907 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003908 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003909 """Implements the 'make_atx_metadata' command.
3910
3911 Android Things metadata are included in vbmeta images to facilitate
3912 verification. The output of this command can be used as the
3913 public_key_metadata argument to other commands.
3914
3915 Arguments:
3916 output: Metadata will be written to this file on success.
3917 intermediate_key_certificate: A certificate file as output by
3918 make_atx_certificate with
3919 is_intermediate_authority set to true.
3920 product_key_certificate: A certificate file as output by
3921 make_atx_certificate with
3922 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003923
3924 Raises:
3925 AvbError: If an argument is incorrect.
3926 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003927 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003928 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003929 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003930 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003931 raise AvbError('Invalid product key certificate length.')
3932 output.write(struct.pack('<I', 1)) # Format Version
3933 output.write(intermediate_key_certificate)
3934 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003935
Darren Krahnfccd64e2018-01-16 17:39:35 -08003936 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3937 unlock_key_certificate, challenge_path,
3938 unlock_key_path, signing_helper,
3939 signing_helper_with_files):
3940 """Implements the 'make_atx_unlock_credential' command.
3941
3942 Android Things unlock credentials can be used to authorize the unlock of AVB
3943 on a device. These credentials are presented to an Android Things bootloader
3944 via the fastboot interface in response to a 16-byte challenge. This method
3945 creates all fields of the credential except the challenge signature field
3946 (which is the last field) and can optionally create the challenge signature
3947 field as well if a challenge and the unlock_key_path is provided.
3948
3949 Arguments:
3950 output: The credential will be written to this file on success.
3951 intermediate_key_certificate: A certificate file as output by
3952 make_atx_certificate with
3953 is_intermediate_authority set to true.
3954 unlock_key_certificate: A certificate file as output by
3955 make_atx_certificate with
3956 is_intermediate_authority set to false and the
3957 usage set to
3958 'com.google.android.things.vboot.unlock'.
3959 challenge_path: [optional] A path to the challenge to sign.
3960 unlock_key_path: [optional] A PEM file path with the unlock private key.
3961 signing_helper: Program which signs a hash and returns the signature.
3962 signing_helper_with_files: Same as signing_helper but uses files instead.
3963
3964 Raises:
Jan Monsch9c130122020-04-14 13:43:51 +02003965 AvbError: If an argument is incorrect or an error occurs during signing.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003966 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003967 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3968 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003969 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3970 raise AvbError('Invalid intermediate key certificate length.')
3971 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3972 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003973 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003974 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003975 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003976 challenge = f.read()
3977 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3978 raise AvbError('Invalid unlock challenge length.')
3979 output.write(struct.pack('<I', 1)) # Format Version
3980 output.write(intermediate_key_certificate)
3981 output.write(unlock_key_certificate)
3982 if challenge_path and unlock_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003983 rsa_key = RSAPublicKey(unlock_key_path)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003984 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003985 signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3986 signing_helper_with_files)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003987 output.write(signature)
3988
David Zeuthen21e95262016-07-27 17:58:40 -04003989
3990def calc_hash_level_offsets(image_size, block_size, digest_size):
3991 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3992
3993 Arguments:
3994 image_size: The size of the image to calculate a Merkle-tree for.
3995 block_size: The block size, e.g. 4096.
3996 digest_size: The size of each hash, e.g. 32 for SHA-256.
3997
3998 Returns:
3999 A tuple where the first argument is an array of offsets and the
4000 second is size of the tree, in bytes.
4001 """
4002 level_offsets = []
4003 level_sizes = []
4004 tree_size = 0
4005
4006 num_levels = 0
4007 size = image_size
4008 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004009 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04004010 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4011
4012 level_sizes.append(level_size)
4013 tree_size += level_size
4014 num_levels += 1
4015
4016 size = level_size
4017
4018 for n in range(0, num_levels):
4019 offset = 0
4020 for m in range(n + 1, num_levels):
4021 offset += level_sizes[m]
4022 level_offsets.append(offset)
4023
David Zeuthena4fee8b2016-08-22 15:20:43 -04004024 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004025
4026
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004027# See system/extras/libfec/include/fec/io.h for these definitions.
4028FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4029FEC_MAGIC = 0xfecfecfe
4030
4031
4032def calc_fec_data_size(image_size, num_roots):
4033 """Calculates how much space FEC data will take.
4034
Jan Monschfe00c0a2019-12-11 11:19:40 +01004035 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004036 image_size: The size of the image.
4037 num_roots: Number of roots.
4038
4039 Returns:
4040 The number of bytes needed for FEC for an image of the given size
4041 and with the requested number of FEC roots.
4042
4043 Raises:
4044 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004045 """
4046 p = subprocess.Popen(
4047 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4048 stdout=subprocess.PIPE,
4049 stderr=subprocess.PIPE)
4050 (pout, perr) = p.communicate()
4051 retcode = p.wait()
4052 if retcode != 0:
4053 raise ValueError('Error invoking fec: {}'.format(perr))
4054 return int(pout)
4055
4056
4057def generate_fec_data(image_filename, num_roots):
4058 """Generate FEC codes for an image.
4059
Jan Monschfe00c0a2019-12-11 11:19:40 +01004060 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004061 image_filename: The filename of the image.
4062 num_roots: Number of roots.
4063
4064 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004065 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004066
4067 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02004068 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004069 """
Jan Monschb1d920f2020-04-09 12:59:28 +02004070 with tempfile.NamedTemporaryFile() as fec_tmpfile:
4071 try:
4072 subprocess.check_call(
4073 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4074 fec_tmpfile.name],
4075 stderr=open(os.devnull, 'wb'))
4076 except subprocess.CalledProcessError as e:
Jan Monsch7a722ee2021-06-17 11:49:05 +02004077 raise ValueError('Execution of \'fec\' tool failed: {}.'
4078 .format(e)) from e
Jan Monschb1d920f2020-04-09 12:59:28 +02004079 fec_data = fec_tmpfile.read()
4080
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004081 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4082 footer_data = fec_data[-footer_size:]
4083 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4084 footer_data)
4085 if magic != FEC_MAGIC:
4086 raise ValueError('Unexpected magic in FEC footer')
4087 return fec_data[0:fec_size]
4088
4089
David Zeuthen21e95262016-07-27 17:58:40 -04004090def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004091 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004092 """Generates a Merkle-tree for a file.
4093
Jan Monschfe00c0a2019-12-11 11:19:40 +01004094 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004095 image: The image, as a file.
4096 image_size: The size of the image.
4097 block_size: The block size, e.g. 4096.
4098 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4099 salt: The salt to use.
4100 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004101 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004102 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004103
4104 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004105 A tuple where the first element is the top-level hash as bytes and the
4106 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004107 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004108 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004109 hash_src_offset = 0
4110 hash_src_size = image_size
4111 level_num = 0
Bowgo Tsai35185662021-12-21 16:27:07 +08004112
4113 # If there is only one block, returns the top-level hash directly.
4114 if hash_src_size == block_size:
4115 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
4116 image.seek(0)
4117 hasher.update(image.read(block_size))
4118 return hasher.digest(), bytes(hash_ret)
4119
David Zeuthen21e95262016-07-27 17:58:40 -04004120 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08004121 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04004122 remaining = hash_src_size
4123 while remaining > 0:
Tianjie62ad0222021-02-22 14:31:12 -08004124 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004125 # Only read from the file for the first level - for subsequent
4126 # levels, access the array we're building.
4127 if level_num == 0:
4128 image.seek(hash_src_offset + hash_src_size - remaining)
4129 data = image.read(min(remaining, block_size))
4130 else:
4131 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4132 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004133 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004134
4135 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004136 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02004137 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08004138 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04004139 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02004140 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08004141
Jan Monschb1d920f2020-04-09 12:59:28 +02004142 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04004143
4144 padding_needed = (round_to_multiple(
4145 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02004146 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004147
David Zeuthena4fee8b2016-08-22 15:20:43 -04004148 # Copy level-output into resulting tree.
4149 offset = hash_level_offsets[level_num]
4150 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004151
David Zeuthena4fee8b2016-08-22 15:20:43 -04004152 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004153 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004154 level_num += 1
4155
Tianjie62ad0222021-02-22 14:31:12 -08004156 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04004157 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02004158 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04004159
4160
4161class AvbTool(object):
4162 """Object for avbtool command-line tool."""
4163
4164 def __init__(self):
4165 """Initializer method."""
4166 self.avb = Avb()
4167
4168 def _add_common_args(self, sub_parser):
4169 """Adds arguments used by several sub-commands.
4170
4171 Arguments:
4172 sub_parser: The parser to add arguments to.
4173 """
4174 sub_parser.add_argument('--algorithm',
4175 help='Algorithm to use (default: NONE)',
4176 metavar='ALGORITHM',
4177 default='NONE')
4178 sub_parser.add_argument('--key',
4179 help='Path to RSA private key file',
4180 metavar='KEY',
4181 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004182 sub_parser.add_argument('--signing_helper',
4183 help='Path to helper used for signing',
4184 metavar='APP',
4185 default=None,
4186 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004187 sub_parser.add_argument('--signing_helper_with_files',
4188 help='Path to helper used for signing using files',
4189 metavar='APP',
4190 default=None,
4191 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004192 sub_parser.add_argument('--public_key_metadata',
4193 help='Path to public key metadata file',
4194 metavar='KEY_METADATA',
4195 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004196 sub_parser.add_argument('--rollback_index',
4197 help='Rollback Index',
4198 type=parse_number,
4199 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004200 sub_parser.add_argument('--rollback_index_location',
4201 help='Location of main vbmeta Rollback Index',
4202 type=parse_number,
4203 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004204 # This is used internally for unit tests. Do not include in --help output.
4205 sub_parser.add_argument('--internal_release_string',
4206 help=argparse.SUPPRESS)
4207 sub_parser.add_argument('--append_to_release_string',
4208 help='Text to append to release string',
4209 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004210 sub_parser.add_argument('--prop',
4211 help='Add property',
4212 metavar='KEY:VALUE',
4213 action='append')
4214 sub_parser.add_argument('--prop_from_file',
4215 help='Add property from file',
4216 metavar='KEY:PATH',
4217 action='append')
4218 sub_parser.add_argument('--kernel_cmdline',
4219 help='Add kernel cmdline',
4220 metavar='CMDLINE',
4221 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004222 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4223 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4224 # at some future point.
4225 sub_parser.add_argument('--setup_rootfs_from_kernel',
4226 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004227 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004228 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004229 type=argparse.FileType('rb'))
4230 sub_parser.add_argument('--include_descriptors_from_image',
4231 help='Include descriptors from image',
4232 metavar='IMAGE',
4233 action='append',
4234 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004235 sub_parser.add_argument('--print_required_libavb_version',
4236 help=('Don\'t store the footer - '
4237 'instead calculate the required libavb '
4238 'version for the given options.'),
4239 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004240 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4241 sub_parser.add_argument('--chain_partition',
4242 help='Allow signed integrity-data for partition',
4243 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4244 action='append')
4245 sub_parser.add_argument('--flags',
4246 help='VBMeta flags',
4247 type=parse_number,
4248 default=0)
4249 sub_parser.add_argument('--set_hashtree_disabled_flag',
4250 help='Set the HASHTREE_DISABLED flag',
4251 action='store_true')
4252
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004253 def _add_common_footer_args(self, sub_parser):
4254 """Adds arguments used by add_*_footer sub-commands.
4255
4256 Arguments:
4257 sub_parser: The parser to add arguments to.
4258 """
4259 sub_parser.add_argument('--use_persistent_digest',
4260 help='Use a persistent digest on device instead of '
4261 'storing the digest in the descriptor. This '
4262 'cannot be used with A/B so must be combined '
4263 'with --do_not_use_ab when an A/B suffix is '
4264 'expected at runtime.',
4265 action='store_true')
4266 sub_parser.add_argument('--do_not_use_ab',
4267 help='The partition does not use A/B even when an '
4268 'A/B suffix is present. This must not be used '
4269 'for vbmeta or chained partitions.',
4270 action='store_true')
4271
David Zeuthena5fd3a42017-02-27 16:38:54 -05004272 def _fixup_common_args(self, args):
4273 """Common fixups needed by subcommands.
4274
4275 Arguments:
4276 args: Arguments to modify.
4277
4278 Returns:
4279 The modified arguments.
4280 """
4281 if args.set_hashtree_disabled_flag:
4282 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4283 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004284
4285 def run(self, argv):
4286 """Command-line processor.
4287
4288 Arguments:
4289 argv: Pass sys.argv from main.
4290 """
4291 parser = argparse.ArgumentParser()
4292 subparsers = parser.add_subparsers(title='subcommands')
4293
Jan Monsch2c7be992020-04-03 14:37:13 +02004294 sub_parser = subparsers.add_parser(
4295 'generate_test_image',
4296 help=('Generates a test image with a known pattern for testing: '
4297 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4298 sub_parser.add_argument('--image_size',
4299 help='Size of image to generate.',
4300 type=parse_number,
4301 required=True)
4302 sub_parser.add_argument('--start_byte',
4303 help='Integer for the start byte of the pattern.',
4304 type=parse_number,
4305 default=0)
4306 sub_parser.add_argument('--output',
4307 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004308 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004309 default=sys.stdout)
4310 sub_parser.set_defaults(func=self.generate_test_image)
4311
David Zeuthen21e95262016-07-27 17:58:40 -04004312 sub_parser = subparsers.add_parser('version',
4313 help='Prints version of avbtool.')
4314 sub_parser.set_defaults(func=self.version)
4315
4316 sub_parser = subparsers.add_parser('extract_public_key',
4317 help='Extract public key.')
4318 sub_parser.add_argument('--key',
4319 help='Path to RSA private key file',
4320 required=True)
4321 sub_parser.add_argument('--output',
4322 help='Output file name',
4323 type=argparse.FileType('wb'),
4324 required=True)
4325 sub_parser.set_defaults(func=self.extract_public_key)
4326
4327 sub_parser = subparsers.add_parser('make_vbmeta_image',
4328 help='Makes a vbmeta image.')
4329 sub_parser.add_argument('--output',
4330 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004331 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004332 sub_parser.add_argument('--padding_size',
4333 metavar='NUMBER',
4334 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004335 'its size is a multiple of NUMBER '
4336 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004337 type=parse_number,
4338 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004339 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004340 sub_parser.set_defaults(func=self.make_vbmeta_image)
4341
4342 sub_parser = subparsers.add_parser('add_hash_footer',
4343 help='Add hashes and footer to image.')
4344 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004345 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004346 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004347 sub_parser.add_argument('--partition_size',
4348 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004349 type=parse_number)
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08004350 sub_parser.add_argument('--dynamic_partition_size',
4351 help='Calculate partition size based on image size',
4352 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004353 sub_parser.add_argument('--partition_name',
4354 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004355 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004356 sub_parser.add_argument('--hash_algorithm',
4357 help='Hash algorithm to use (default: sha256)',
4358 default='sha256')
4359 sub_parser.add_argument('--salt',
4360 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004361 sub_parser.add_argument('--calc_max_image_size',
4362 help=('Don\'t store the footer - '
4363 'instead calculate the maximum image size '
4364 'leaving enough room for metadata with '
4365 'the given partition size.'),
4366 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004367 sub_parser.add_argument('--output_vbmeta_image',
4368 help='Also write vbmeta struct to file',
4369 type=argparse.FileType('wb'))
4370 sub_parser.add_argument('--do_not_append_vbmeta_image',
4371 help=('Do not append vbmeta struct or footer '
4372 'to the image'),
4373 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004374 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004375 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004376 sub_parser.set_defaults(func=self.add_hash_footer)
4377
David Zeuthenb1b994d2017-03-06 18:01:31 -05004378 sub_parser = subparsers.add_parser('append_vbmeta_image',
4379 help='Append vbmeta image to image.')
4380 sub_parser.add_argument('--image',
4381 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004382 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004383 sub_parser.add_argument('--partition_size',
4384 help='Partition size',
4385 type=parse_number,
4386 required=True)
4387 sub_parser.add_argument('--vbmeta_image',
4388 help='Image with vbmeta blob to append',
4389 type=argparse.FileType('rb'))
4390 sub_parser.set_defaults(func=self.append_vbmeta_image)
4391
Jan Monscheeb28b62019-12-05 16:17:09 +01004392 sub_parser = subparsers.add_parser(
4393 'add_hashtree_footer',
4394 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004395 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004396 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004397 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004398 sub_parser.add_argument('--partition_size',
4399 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004400 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004401 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004402 sub_parser.add_argument('--partition_name',
4403 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004404 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004405 sub_parser.add_argument('--hash_algorithm',
4406 help='Hash algorithm to use (default: sha1)',
4407 default='sha1')
4408 sub_parser.add_argument('--salt',
4409 help='Salt in hex (default: /dev/urandom)')
4410 sub_parser.add_argument('--block_size',
4411 help='Block size (default: 4096)',
4412 type=parse_number,
4413 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004414 # TODO(zeuthen): The --generate_fec option was removed when we
4415 # moved to generating FEC by default. To avoid breaking existing
4416 # users needing to transition we simply just print a warning below
4417 # in add_hashtree_footer(). Remove this option and the warning at
4418 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004419 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004420 help=argparse.SUPPRESS,
4421 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004422 sub_parser.add_argument(
4423 '--do_not_generate_fec',
4424 help='Do not generate forward-error-correction codes',
4425 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004426 sub_parser.add_argument('--fec_num_roots',
4427 help='Number of roots for FEC (default: 2)',
4428 type=parse_number,
4429 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004430 sub_parser.add_argument('--calc_max_image_size',
4431 help=('Don\'t store the hashtree or footer - '
4432 'instead calculate the maximum image size '
4433 'leaving enough room for hashtree '
4434 'and metadata with the given partition '
4435 'size.'),
4436 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004437 sub_parser.add_argument('--output_vbmeta_image',
4438 help='Also write vbmeta struct to file',
4439 type=argparse.FileType('wb'))
4440 sub_parser.add_argument('--do_not_append_vbmeta_image',
4441 help=('Do not append vbmeta struct or footer '
4442 'to the image'),
4443 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004444 # This is different from --setup_rootfs_from_kernel insofar that
4445 # it doesn't take an IMAGE, the generated cmdline will be for the
4446 # hashtree we're adding.
4447 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4448 action='store_true',
4449 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004450 sub_parser.add_argument('--no_hashtree',
4451 action='store_true',
4452 help='Do not append hashtree')
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004453 sub_parser.add_argument('--check_at_most_once',
4454 action='store_true',
4455 help='Set to verify data block only once')
David Zeuthen21e95262016-07-27 17:58:40 -04004456 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004457 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004458 sub_parser.set_defaults(func=self.add_hashtree_footer)
4459
4460 sub_parser = subparsers.add_parser('erase_footer',
4461 help='Erase footer from an image.')
4462 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004463 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004464 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004465 required=True)
4466 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004467 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004468 action='store_true')
4469 sub_parser.set_defaults(func=self.erase_footer)
4470
David Zeuthen1394f762019-04-30 10:20:11 -04004471 sub_parser = subparsers.add_parser('zero_hashtree',
4472 help='Zero out hashtree and FEC data.')
4473 sub_parser.add_argument('--image',
4474 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004475 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004476 required=True)
4477 sub_parser.set_defaults(func=self.zero_hashtree)
4478
Jan Monscheeb28b62019-12-05 16:17:09 +01004479 sub_parser = subparsers.add_parser(
4480 'extract_vbmeta_image',
4481 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004482 sub_parser.add_argument('--image',
4483 help='Image with footer',
4484 type=argparse.FileType('rb'),
4485 required=True)
4486 sub_parser.add_argument('--output',
4487 help='Output file name',
4488 type=argparse.FileType('wb'))
4489 sub_parser.add_argument('--padding_size',
4490 metavar='NUMBER',
4491 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004492 'its size is a multiple of NUMBER '
4493 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004494 type=parse_number,
4495 default=0)
4496 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4497
David Zeuthen2bc232b2017-04-19 14:25:19 -04004498 sub_parser = subparsers.add_parser('resize_image',
4499 help='Resize image with a footer.')
4500 sub_parser.add_argument('--image',
4501 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004502 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004503 required=True)
4504 sub_parser.add_argument('--partition_size',
4505 help='New partition size',
4506 type=parse_number)
4507 sub_parser.set_defaults(func=self.resize_image)
4508
David Zeuthen21e95262016-07-27 17:58:40 -04004509 sub_parser = subparsers.add_parser(
4510 'info_image',
4511 help='Show information about vbmeta or footer.')
4512 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004513 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004514 type=argparse.FileType('rb'),
4515 required=True)
4516 sub_parser.add_argument('--output',
4517 help='Write info to file',
4518 type=argparse.FileType('wt'),
4519 default=sys.stdout)
Sen Jiang01553a22020-06-30 17:58:44 -07004520 sub_parser.add_argument('--atx',
4521 help=('Show information about Android Things '
4522 'eXtension (ATX).'),
4523 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004524 sub_parser.set_defaults(func=self.info_image)
4525
David Zeuthenb623d8b2017-04-04 16:05:53 -04004526 sub_parser = subparsers.add_parser(
4527 'verify_image',
4528 help='Verify an image.')
4529 sub_parser.add_argument('--image',
4530 help='Image to verify',
4531 type=argparse.FileType('rb'),
4532 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004533 sub_parser.add_argument('--key',
4534 help='Check embedded public key matches KEY',
4535 metavar='KEY',
4536 required=False)
4537 sub_parser.add_argument('--expected_chain_partition',
4538 help='Expected chain partition',
4539 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4540 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004541 sub_parser.add_argument(
4542 '--follow_chain_partitions',
4543 help=('Follows chain partitions even when not '
4544 'specified with the --expected_chain_partition option'),
4545 action='store_true')
4546 sub_parser.add_argument(
4547 '--accept_zeroed_hashtree',
4548 help=('Accept images where the hashtree or FEC data is zeroed out'),
4549 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004550 sub_parser.set_defaults(func=self.verify_image)
4551
David Zeuthenb8643c02018-05-17 17:21:18 -04004552 sub_parser = subparsers.add_parser(
David Zeuthen34b6b492020-04-13 14:45:02 -04004553 'print_partition_digests',
4554 help='Prints partition digests.')
4555 sub_parser.add_argument('--image',
4556 help='Image to print partition digests from',
4557 type=argparse.FileType('rb'),
4558 required=True)
4559 sub_parser.add_argument('--output',
4560 help='Write info to file',
4561 type=argparse.FileType('wt'),
4562 default=sys.stdout)
4563 sub_parser.add_argument('--json',
4564 help=('Print output as JSON'),
4565 action='store_true')
4566 sub_parser.set_defaults(func=self.print_partition_digests)
4567
4568 sub_parser = subparsers.add_parser(
David Zeuthenb8643c02018-05-17 17:21:18 -04004569 'calculate_vbmeta_digest',
4570 help='Calculate vbmeta digest.')
4571 sub_parser.add_argument('--image',
4572 help='Image to calculate digest for',
4573 type=argparse.FileType('rb'),
4574 required=True)
4575 sub_parser.add_argument('--hash_algorithm',
4576 help='Hash algorithm to use (default: sha256)',
4577 default='sha256')
4578 sub_parser.add_argument('--output',
4579 help='Write hex digest to file (default: stdout)',
4580 type=argparse.FileType('wt'),
4581 default=sys.stdout)
4582 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4583
David Zeuthenf7d2e752018-09-20 13:30:41 -04004584 sub_parser = subparsers.add_parser(
4585 'calculate_kernel_cmdline',
4586 help='Calculate kernel cmdline.')
4587 sub_parser.add_argument('--image',
4588 help='Image to calculate kernel cmdline for',
4589 type=argparse.FileType('rb'),
4590 required=True)
4591 sub_parser.add_argument('--hashtree_disabled',
4592 help='Return the cmdline for hashtree disabled',
4593 action='store_true')
4594 sub_parser.add_argument('--output',
4595 help='Write cmdline to file (default: stdout)',
4596 type=argparse.FileType('wt'),
4597 default=sys.stdout)
4598 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4599
David Zeuthen8b6973b2016-09-20 12:39:49 -04004600 sub_parser = subparsers.add_parser('set_ab_metadata',
4601 help='Set A/B metadata.')
4602 sub_parser.add_argument('--misc_image',
4603 help=('The misc image to modify. If the image does '
4604 'not exist, it will be created.'),
4605 type=argparse.FileType('r+b'),
4606 required=True)
4607 sub_parser.add_argument('--slot_data',
4608 help=('Slot data of the form "priority", '
4609 '"tries_remaining", "sucessful_boot" for '
4610 'slot A followed by the same for slot B, '
4611 'separated by colons. The default value '
4612 'is 15:7:0:14:7:0.'),
4613 default='15:7:0:14:7:0')
4614 sub_parser.set_defaults(func=self.set_ab_metadata)
4615
Darren Krahn147b08d2016-12-20 16:38:29 -08004616 sub_parser = subparsers.add_parser(
4617 'make_atx_certificate',
4618 help='Create an Android Things eXtension (ATX) certificate.')
4619 sub_parser.add_argument('--output',
4620 help='Write certificate to file',
4621 type=argparse.FileType('wb'),
4622 default=sys.stdout)
4623 sub_parser.add_argument('--subject',
4624 help=('Path to subject file'),
4625 type=argparse.FileType('rb'),
4626 required=True)
4627 sub_parser.add_argument('--subject_key',
4628 help=('Path to subject RSA public key file'),
4629 type=argparse.FileType('rb'),
4630 required=True)
4631 sub_parser.add_argument('--subject_key_version',
4632 help=('Version of the subject key'),
4633 type=parse_number,
4634 required=False)
4635 sub_parser.add_argument('--subject_is_intermediate_authority',
4636 help=('Generate an intermediate authority '
4637 'certificate'),
4638 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004639 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004640 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004641 'string'),
4642 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004643 sub_parser.add_argument('--authority_key',
4644 help='Path to authority RSA private key file',
4645 required=False)
4646 sub_parser.add_argument('--signing_helper',
4647 help='Path to helper used for signing',
4648 metavar='APP',
4649 default=None,
4650 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004651 sub_parser.add_argument('--signing_helper_with_files',
4652 help='Path to helper used for signing using files',
4653 metavar='APP',
4654 default=None,
4655 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004656 sub_parser.set_defaults(func=self.make_atx_certificate)
4657
4658 sub_parser = subparsers.add_parser(
4659 'make_atx_permanent_attributes',
4660 help='Create Android Things eXtension (ATX) permanent attributes.')
4661 sub_parser.add_argument('--output',
4662 help='Write attributes to file',
4663 type=argparse.FileType('wb'),
4664 default=sys.stdout)
4665 sub_parser.add_argument('--root_authority_key',
4666 help='Path to authority RSA public key file',
4667 type=argparse.FileType('rb'),
4668 required=True)
4669 sub_parser.add_argument('--product_id',
4670 help=('Path to Product ID file'),
4671 type=argparse.FileType('rb'),
4672 required=True)
4673 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4674
4675 sub_parser = subparsers.add_parser(
4676 'make_atx_metadata',
4677 help='Create Android Things eXtension (ATX) metadata.')
4678 sub_parser.add_argument('--output',
4679 help='Write metadata to file',
4680 type=argparse.FileType('wb'),
4681 default=sys.stdout)
4682 sub_parser.add_argument('--intermediate_key_certificate',
4683 help='Path to intermediate key certificate file',
4684 type=argparse.FileType('rb'),
4685 required=True)
4686 sub_parser.add_argument('--product_key_certificate',
4687 help='Path to product key certificate file',
4688 type=argparse.FileType('rb'),
4689 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004690 sub_parser.set_defaults(func=self.make_atx_metadata)
4691
Darren Krahnfccd64e2018-01-16 17:39:35 -08004692 sub_parser = subparsers.add_parser(
4693 'make_atx_unlock_credential',
4694 help='Create an Android Things eXtension (ATX) unlock credential.')
4695 sub_parser.add_argument('--output',
4696 help='Write credential to file',
4697 type=argparse.FileType('wb'),
4698 default=sys.stdout)
4699 sub_parser.add_argument('--intermediate_key_certificate',
4700 help='Path to intermediate key certificate file',
4701 type=argparse.FileType('rb'),
4702 required=True)
4703 sub_parser.add_argument('--unlock_key_certificate',
4704 help='Path to unlock key certificate file',
4705 type=argparse.FileType('rb'),
4706 required=True)
4707 sub_parser.add_argument('--challenge',
4708 help='Path to the challenge to sign (optional). If '
4709 'this is not provided the challenge signature '
4710 'field is omitted and can be concatenated '
4711 'later.',
4712 required=False)
4713 sub_parser.add_argument('--unlock_key',
4714 help='Path to unlock key (optional). Must be '
4715 'provided if using --challenge.',
4716 required=False)
4717 sub_parser.add_argument('--signing_helper',
4718 help='Path to helper used for signing',
4719 metavar='APP',
4720 default=None,
4721 required=False)
4722 sub_parser.add_argument('--signing_helper_with_files',
4723 help='Path to helper used for signing using files',
4724 metavar='APP',
4725 default=None,
4726 required=False)
4727 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4728
David Zeuthen21e95262016-07-27 17:58:40 -04004729 args = parser.parse_args(argv[1:])
4730 try:
4731 args.func(args)
Jan Monschcc9939a2020-04-16 09:15:20 +02004732 except AttributeError:
4733 # This error gets raised when the command line tool is called without any
4734 # arguments. It mimics the original Python 2 behavior.
4735 parser.print_usage()
4736 print('avbtool: error: too few arguments')
4737 sys.exit(2)
David Zeuthen21e95262016-07-27 17:58:40 -04004738 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004739 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004740 sys.exit(1)
4741
4742 def version(self, _):
4743 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004744 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004745
Jan Monsch2c7be992020-04-03 14:37:13 +02004746 def generate_test_image(self, args):
4747 """Implements the 'generate_test_image' sub-command."""
4748 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4749
David Zeuthen21e95262016-07-27 17:58:40 -04004750 def extract_public_key(self, args):
4751 """Implements the 'extract_public_key' sub-command."""
4752 self.avb.extract_public_key(args.key, args.output)
4753
4754 def make_vbmeta_image(self, args):
4755 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004756 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004757 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004758 args.algorithm, args.key,
4759 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004760 args.flags, args.rollback_index_location,
4761 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004762 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004763 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004764 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004765 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004766 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004767 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004768 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004769 args.print_required_libavb_version,
4770 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004771
David Zeuthenb1b994d2017-03-06 18:01:31 -05004772 def append_vbmeta_image(self, args):
4773 """Implements the 'append_vbmeta_image' sub-command."""
4774 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4775 args.partition_size)
4776
David Zeuthen21e95262016-07-27 17:58:40 -04004777 def add_hash_footer(self, args):
4778 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004779 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004780 self.avb.add_hash_footer(args.image.name if args.image else None,
Peter Collingbourne8b7b2bd2022-02-16 14:51:18 -08004781 args.partition_size, args.dynamic_partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004782 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004783 args.salt, args.chain_partition, args.algorithm,
4784 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004785 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004786 args.flags, args.rollback_index_location,
4787 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004788 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004789 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004790 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004791 args.calc_max_image_size,
4792 args.signing_helper,
4793 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004794 args.internal_release_string,
4795 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004796 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004797 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004798 args.print_required_libavb_version,
4799 args.use_persistent_digest,
4800 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004801
4802 def add_hashtree_footer(self, args):
4803 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004804 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004805 # TODO(zeuthen): Remove when removing support for the
4806 # '--generate_fec' option above.
4807 if args.generate_fec:
4808 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4809 'is now generated by default. Use the option '
4810 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004811 self.avb.add_hashtree_footer(
4812 args.image.name if args.image else None,
4813 args.partition_size,
4814 args.partition_name,
4815 not args.do_not_generate_fec, args.fec_num_roots,
4816 args.hash_algorithm, args.block_size,
4817 args.salt, args.chain_partition, args.algorithm,
4818 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004819 args.rollback_index, args.flags,
4820 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004821 args.prop_from_file,
4822 args.kernel_cmdline,
4823 args.setup_rootfs_from_kernel,
4824 args.setup_as_rootfs_from_kernel,
4825 args.include_descriptors_from_image,
4826 args.calc_max_image_size,
4827 args.signing_helper,
4828 args.signing_helper_with_files,
4829 args.internal_release_string,
4830 args.append_to_release_string,
4831 args.output_vbmeta_image,
4832 args.do_not_append_vbmeta_image,
4833 args.print_required_libavb_version,
4834 args.use_persistent_digest,
4835 args.do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004836 args.no_hashtree,
4837 args.check_at_most_once)
David Zeuthend247fcb2017-02-16 12:09:27 -05004838
David Zeuthen21e95262016-07-27 17:58:40 -04004839 def erase_footer(self, args):
4840 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004841 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004842
David Zeuthen1394f762019-04-30 10:20:11 -04004843 def zero_hashtree(self, args):
4844 """Implements the 'zero_hashtree' sub-command."""
4845 self.avb.zero_hashtree(args.image.name)
4846
David Zeuthen49936b42018-08-07 17:38:58 -04004847 def extract_vbmeta_image(self, args):
4848 """Implements the 'extract_vbmeta_image' sub-command."""
4849 self.avb.extract_vbmeta_image(args.output, args.image.name,
4850 args.padding_size)
4851
David Zeuthen2bc232b2017-04-19 14:25:19 -04004852 def resize_image(self, args):
4853 """Implements the 'resize_image' sub-command."""
4854 self.avb.resize_image(args.image.name, args.partition_size)
4855
David Zeuthen8b6973b2016-09-20 12:39:49 -04004856 def set_ab_metadata(self, args):
4857 """Implements the 'set_ab_metadata' sub-command."""
4858 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4859
David Zeuthen21e95262016-07-27 17:58:40 -04004860 def info_image(self, args):
4861 """Implements the 'info_image' sub-command."""
Sen Jiang01553a22020-06-30 17:58:44 -07004862 self.avb.info_image(args.image.name, args.output, args.atx)
David Zeuthen21e95262016-07-27 17:58:40 -04004863
David Zeuthenb623d8b2017-04-04 16:05:53 -04004864 def verify_image(self, args):
4865 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004866 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004867 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004868 args.follow_chain_partitions,
4869 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004870
David Zeuthen34b6b492020-04-13 14:45:02 -04004871 def print_partition_digests(self, args):
4872 """Implements the 'print_partition_digests' sub-command."""
4873 self.avb.print_partition_digests(args.image.name, args.output, args.json)
4874
David Zeuthenb8643c02018-05-17 17:21:18 -04004875 def calculate_vbmeta_digest(self, args):
4876 """Implements the 'calculate_vbmeta_digest' sub-command."""
4877 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4878 args.output)
4879
David Zeuthenf7d2e752018-09-20 13:30:41 -04004880 def calculate_kernel_cmdline(self, args):
4881 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004882 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4883 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004884
Darren Krahn147b08d2016-12-20 16:38:29 -08004885 def make_atx_certificate(self, args):
4886 """Implements the 'make_atx_certificate' sub-command."""
4887 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004888 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004889 args.subject_key_version,
4890 args.subject.read(),
4891 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004892 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004893 args.signing_helper,
4894 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004895
4896 def make_atx_permanent_attributes(self, args):
4897 """Implements the 'make_atx_permanent_attributes' sub-command."""
4898 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004899 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004900 args.product_id.read())
4901
4902 def make_atx_metadata(self, args):
4903 """Implements the 'make_atx_metadata' sub-command."""
4904 self.avb.make_atx_metadata(args.output,
4905 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004906 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004907
Darren Krahnfccd64e2018-01-16 17:39:35 -08004908 def make_atx_unlock_credential(self, args):
4909 """Implements the 'make_atx_unlock_credential' sub-command."""
4910 self.avb.make_atx_unlock_credential(
4911 args.output,
4912 args.intermediate_key_certificate.read(),
4913 args.unlock_key_certificate.read(),
4914 args.challenge,
4915 args.unlock_key,
4916 args.signing_helper,
4917 args.signing_helper_with_files)
4918
David Zeuthen21e95262016-07-27 17:58:40 -04004919
4920if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004921 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004922 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4923 log.write(' '.join(sys.argv))
4924 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004925
David Zeuthen21e95262016-07-27 17:58:40 -04004926 tool = AvbTool()
4927 tool.run(sys.argv)