blob: 715fb9fee52562db381ad8b2da4b4cba4a43e14b [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.
David Zeuthen21e95262016-07-27 17:58:40 -0400345 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400346
Jan Monsch38865f22020-04-08 09:32:58 +0200347 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400348
349 def __init__(self, key_path):
350 """Loads and parses an RSA key from either a private or public key file.
351
352 Arguments:
353 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100354
355 Raises:
356 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400357 """
358 # We used to have something as simple as this:
359 #
360 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
361 # self.exponent = key.e
362 # self.modulus = key.n
363 # self.num_bits = key.size() + 1
364 #
365 # but unfortunately PyCrypto is not available in the builder. So
366 # instead just parse openssl(1) output to get this
367 # information. It's ugly but...
368 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
369 p = subprocess.Popen(args,
370 stdin=subprocess.PIPE,
371 stdout=subprocess.PIPE,
372 stderr=subprocess.PIPE)
373 (pout, perr) = p.communicate()
374 if p.wait() != 0:
375 # Could be just a public key is passed, try that.
376 args.append('-pubin')
377 p = subprocess.Popen(args,
378 stdin=subprocess.PIPE,
379 stdout=subprocess.PIPE,
380 stderr=subprocess.PIPE)
381 (pout, perr) = p.communicate()
382 if p.wait() != 0:
383 raise AvbError('Error getting public key: {}'.format(perr))
384
385 if not pout.lower().startswith(self.MODULUS_PREFIX):
386 raise AvbError('Unexpected modulus output')
387
388 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
389
390 # The exponent is assumed to always be 65537 and the number of
391 # bits can be derived from the modulus by rounding up to the
392 # nearest power of 2.
Jan Monsch9c130122020-04-14 13:43:51 +0200393 self.key_path = key_path
David Zeuthenc68f0822017-03-31 17:22:35 -0400394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
Jan Monsch9c130122020-04-14 13:43:51 +0200398 def encode(self):
399 """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
David Zeuthen21e95262016-07-27 17:58:40 -0400400
Jan Monsch9c130122020-04-14 13:43:51 +0200401 This creates a |AvbRSAPublicKeyHeader| as well as the two large
402 numbers (|key_num_bits| bits long) following it.
David Zeuthen21e95262016-07-27 17:58:40 -0400403
Jan Monsch9c130122020-04-14 13:43:51 +0200404 Returns:
405 The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -0400406
Jan Monsch9c130122020-04-14 13:43:51 +0200407 Raises:
408 AvbError: If given RSA key exponent is not 65537.
409 """
410 if self.exponent != 65537:
411 raise AvbError('Only RSA keys with exponent 65537 are supported.')
412 ret = bytearray()
413 # Calculate n0inv = -1/n[0] (mod 2^32)
414 b = 2 ** 32
415 n0inv = b - modinv(self.modulus, b)
416 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
417 r = 2 ** self.modulus.bit_length()
418 rrmodn = r * r % self.modulus
419 ret.extend(struct.pack('!II', self.num_bits, n0inv))
420 ret.extend(encode_long(self.num_bits, self.modulus))
421 ret.extend(encode_long(self.num_bits, rrmodn))
422 return bytes(ret)
David Zeuthen21e95262016-07-27 17:58:40 -0400423
Jan Monsch9c130122020-04-14 13:43:51 +0200424 def sign(self, algorithm_name, data_to_sign, signing_helper=None,
425 signing_helper_with_files=None):
426 """Sign given data using |signing_helper| or openssl.
Jan Monsch77cd2022019-12-10 17:18:04 +0100427
Jan Monsch9c130122020-04-14 13:43:51 +0200428 openssl is used if neither the parameters signing_helper nor
429 signing_helper_with_files are given.
430
431 Arguments:
432 algorithm_name: The algorithm name as per the ALGORITHMS dict.
433 data_to_sign: Data to sign as bytes or bytearray.
434 signing_helper: Program which signs a hash and returns the signature.
435 signing_helper_with_files: Same as signing_helper but uses files instead.
436
437 Returns:
438 The signature as bytes.
439
440 Raises:
441 AvbError: If an error occurred during signing.
442 """
443 # Checks requested algorithm for validity.
444 algorithm = ALGORITHMS.get(algorithm_name)
445 if not algorithm:
446 raise AvbError('Algorithm with name {} is not supported.'
447 .format(algorithm_name))
448
449 if self.num_bits != (algorithm.signature_num_bytes * 8):
450 raise AvbError('Key size of key ({} bits) does not match key size '
451 '({} bits) of given algorithm {}.'
452 .format(self.num_bits, algorithm.signature_num_bytes * 8,
453 algorithm_name))
454
455 # Hashes the data.
456 hasher = hashlib.new(algorithm.hash_name)
457 hasher.update(data_to_sign)
458 digest = hasher.digest()
459
460 # Calculates the signature.
461 padding_and_hash = algorithm.padding + digest
462 p = None
463 if signing_helper_with_files is not None:
464 with tempfile.NamedTemporaryFile() as signing_file:
465 signing_file.write(padding_and_hash)
466 signing_file.flush()
467 p = subprocess.Popen([signing_helper_with_files, algorithm_name,
468 self.key_path, signing_file.name])
469 retcode = p.wait()
470 if retcode != 0:
471 raise AvbError('Error signing')
472 signing_file.seek(0)
473 signature = signing_file.read()
474 else:
475 if signing_helper is not None:
476 p = subprocess.Popen(
477 [signing_helper, algorithm_name, self.key_path],
478 stdin=subprocess.PIPE,
479 stdout=subprocess.PIPE,
480 stderr=subprocess.PIPE)
481 else:
482 p = subprocess.Popen(
483 ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
484 stdin=subprocess.PIPE,
485 stdout=subprocess.PIPE,
486 stderr=subprocess.PIPE)
487 (pout, perr) = p.communicate(padding_and_hash)
488 retcode = p.wait()
489 if retcode != 0:
490 raise AvbError('Error signing: {}'.format(perr))
491 signature = pout
492 if len(signature) != algorithm.signature_num_bytes:
493 raise AvbError('Error signing: Invalid length of signature')
494 return signature
David Zeuthen21e95262016-07-27 17:58:40 -0400495
496
497def lookup_algorithm_by_type(alg_type):
498 """Looks up algorithm by type.
499
500 Arguments:
501 alg_type: The integer representing the type.
502
503 Returns:
504 A tuple with the algorithm name and an |Algorithm| instance.
505
506 Raises:
507 Exception: If the algorithm cannot be found
508 """
509 for alg_name in ALGORITHMS:
510 alg_data = ALGORITHMS[alg_name]
511 if alg_data.algorithm_type == alg_type:
512 return (alg_name, alg_data)
513 raise AvbError('Unknown algorithm type {}'.format(alg_type))
514
Jan Monsch77cd2022019-12-10 17:18:04 +0100515
Dan Austina7bc4962019-12-02 13:26:08 -0800516def lookup_hash_size_by_type(alg_type):
517 """Looks up hash size by type.
518
519 Arguments:
520 alg_type: The integer representing the type.
521
522 Returns:
523 The corresponding hash size.
524
525 Raises:
526 AvbError: If the algorithm cannot be found.
527 """
528 for alg_name in ALGORITHMS:
529 alg_data = ALGORITHMS[alg_name]
530 if alg_data.algorithm_type == alg_type:
531 return alg_data.hash_num_bytes
532 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400533
Jan Monsch77cd2022019-12-10 17:18:04 +0100534
David Zeuthenb623d8b2017-04-04 16:05:53 -0400535def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100536 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537
538 Arguments:
539 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200540 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
541 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400542
543 Returns:
544 True if the signature is valid and corresponds to the embedded
545 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100546
547 Raises:
548 AvbError: If there errors calling out to openssl command during
549 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400550 """
551 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100552 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400553 return True
554 header_blob = vbmeta_blob[0:256]
555 auth_offset = 256
556 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
557 aux_size = vbmeta_header.auxiliary_data_block_size
558 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
559 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
560 pubkey_size = vbmeta_header.public_key_size
561 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
562
563 digest_offset = auth_offset + vbmeta_header.hash_offset
564 digest_size = vbmeta_header.hash_size
565 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
566
567 sig_offset = auth_offset + vbmeta_header.signature_offset
568 sig_size = vbmeta_header.signature_size
569 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
570
571 # Now that we've got the stored digest, public key, and signature
572 # all we need to do is to verify. This is the exactly the same
573 # steps as performed in the avb_vbmeta_image_verify() function in
574 # libavb/avb_vbmeta_image.c.
575
576 ha = hashlib.new(alg.hash_name)
577 ha.update(header_blob)
578 ha.update(aux_blob)
579 computed_digest = ha.digest()
580
581 if computed_digest != digest_blob:
582 return False
583
Jan Monsch9c130122020-04-14 13:43:51 +0200584 padding_and_digest = alg.padding + computed_digest
David Zeuthenb623d8b2017-04-04 16:05:53 -0400585
586 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100587 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400588 modulus = decode_long(modulus_blob)
589 exponent = 65537
590
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500591 # We used to have this:
592 #
593 # import Crypto.PublicKey.RSA
594 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
595 # if not key.verify(decode_long(padding_and_digest),
596 # (decode_long(sig_blob), None)):
597 # return False
598 # return True
599 #
600 # but since 'avbtool verify_image' is used on the builders we don't want
601 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
602 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
603 '\n'
604 '[pubkeyinfo]\n'
605 'algorithm=SEQUENCE:rsa_alg\n'
606 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
607 '\n'
608 '[rsa_alg]\n'
609 'algorithm=OID:rsaEncryption\n'
610 'parameter=NULL\n'
611 '\n'
612 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200613 'n=INTEGER:{}\n'
614 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
615 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500616
Jan Monsch38865f22020-04-08 09:32:58 +0200617 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
618 asn1_tmpfile.write(asn1_str.encode('ascii'))
619 asn1_tmpfile.flush()
620
621 with tempfile.NamedTemporaryFile() as der_tmpfile:
622 p = subprocess.Popen(
623 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
624 der_tmpfile.name, '-noout'])
625 retcode = p.wait()
626 if retcode != 0:
627 raise AvbError('Error generating DER file')
628
629 p = subprocess.Popen(
630 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
631 '-keyform', 'DER', '-raw'],
632 stdin=subprocess.PIPE,
633 stdout=subprocess.PIPE,
634 stderr=subprocess.PIPE)
635 (pout, perr) = p.communicate(sig_blob)
636 retcode = p.wait()
637 if retcode != 0:
638 raise AvbError('Error verifying data: {}'.format(perr))
639 if pout != padding_and_digest:
640 sys.stderr.write('Signature not correct\n')
641 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400642 return True
643
644
Tianjie62ad0222021-02-22 14:31:12 -0800645def create_avb_hashtree_hasher(algorithm, salt):
646 """Create the hasher for AVB hashtree based on the input algorithm."""
647
648 if algorithm.lower() == 'blake2b-256':
649 return hashlib.new('blake2b', salt, digest_size=32)
650
651 return hashlib.new(algorithm, salt)
652
653
David Zeuthena4fee8b2016-08-22 15:20:43 -0400654class ImageChunk(object):
655 """Data structure used for representing chunks in Android sparse files.
656
657 Attributes:
658 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
659 chunk_offset: Offset in the sparse file where this chunk begins.
660 output_offset: Offset in de-sparsified file where output begins.
661 output_size: Number of bytes in output.
662 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
663 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
664 """
665
666 FORMAT = '<2H2I'
667 TYPE_RAW = 0xcac1
668 TYPE_FILL = 0xcac2
669 TYPE_DONT_CARE = 0xcac3
670 TYPE_CRC32 = 0xcac4
671
672 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
673 input_offset, fill_data):
674 """Initializes an ImageChunk object.
675
676 Arguments:
677 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
678 chunk_offset: Offset in the sparse file where this chunk begins.
679 output_offset: Offset in de-sparsified file.
680 output_size: Number of bytes in output.
681 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200682 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400683
684 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200685 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400686 """
687 self.chunk_type = chunk_type
688 self.chunk_offset = chunk_offset
689 self.output_offset = output_offset
690 self.output_size = output_size
691 self.input_offset = input_offset
692 self.fill_data = fill_data
693 # Check invariants.
694 if self.chunk_type == self.TYPE_RAW:
695 if self.fill_data is not None:
696 raise ValueError('RAW chunk cannot have fill_data set.')
697 if not self.input_offset:
698 raise ValueError('RAW chunk must have input_offset set.')
699 elif self.chunk_type == self.TYPE_FILL:
700 if self.fill_data is None:
701 raise ValueError('FILL chunk must have fill_data set.')
702 if self.input_offset:
703 raise ValueError('FILL chunk cannot have input_offset set.')
704 elif self.chunk_type == self.TYPE_DONT_CARE:
705 if self.fill_data is not None:
706 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
707 if self.input_offset:
708 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
709 else:
710 raise ValueError('Invalid chunk type')
711
712
713class ImageHandler(object):
714 """Abstraction for image I/O with support for Android sparse images.
715
716 This class provides an interface for working with image files that
717 may be using the Android Sparse Image format. When an instance is
718 constructed, we test whether it's an Android sparse file. If so,
719 operations will be on the sparse file by interpreting the sparse
720 format, otherwise they will be directly on the file. Either way the
721 operations do the same.
722
723 For reading, this interface mimics a file object - it has seek(),
724 tell(), and read() methods. For writing, only truncation
725 (truncate()) and appending is supported (append_raw() and
726 append_dont_care()). Additionally, data can only be written in units
727 of the block size.
728
729 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400730 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400731 is_sparse: Whether the file being operated on is sparse.
732 block_size: The block size, typically 4096.
733 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400734 """
735 # See system/core/libsparse/sparse_format.h for details.
736 MAGIC = 0xed26ff3a
737 HEADER_FORMAT = '<I4H4I'
738
739 # These are formats and offset of just the |total_chunks| and
740 # |total_blocks| fields.
741 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
742 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
743
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200744 def __init__(self, image_filename, read_only=False):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400745 """Initializes an image handler.
746
747 Arguments:
748 image_filename: The name of the file to operate on.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200749 read_only: True if file is only opened for read-only operations.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400750
751 Raises:
752 ValueError: If data in the file is invalid.
753 """
David Zeuthen49936b42018-08-07 17:38:58 -0400754 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100755 self._num_total_blocks = 0
756 self._num_total_chunks = 0
757 self._file_pos = 0
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200758 self._read_only = read_only
David Zeuthena4fee8b2016-08-22 15:20:43 -0400759 self._read_header()
760
761 def _read_header(self):
762 """Initializes internal data structures used for reading file.
763
764 This may be called multiple times and is typically called after
765 modifying the file (e.g. appending, truncation).
766
767 Raises:
768 ValueError: If data in the file is invalid.
769 """
770 self.is_sparse = False
771 self.block_size = 4096
772 self._file_pos = 0
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200773 if self._read_only:
774 self._image = open(self.filename, 'rb')
775 else:
776 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400777 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400778 self.image_size = self._image.tell()
779
780 self._image.seek(0, os.SEEK_SET)
781 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
782 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
783 block_size, self._num_total_blocks, self._num_total_chunks,
784 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
785 if magic != self.MAGIC:
786 # Not a sparse image, our job here is done.
787 return
788 if not (major_version == 1 and minor_version == 0):
789 raise ValueError('Encountered sparse image format version {}.{} but '
790 'only 1.0 is supported'.format(major_version,
791 minor_version))
792 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
793 raise ValueError('Unexpected file_hdr_sz value {}.'.
794 format(file_hdr_sz))
795 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
796 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
797 format(chunk_hdr_sz))
798
799 self.block_size = block_size
800
801 # Build an list of chunks by parsing the file.
802 self._chunks = []
803
804 # Find the smallest offset where only "Don't care" chunks
805 # follow. This will be the size of the content in the sparse
806 # image.
807 offset = 0
808 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100809 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400810 chunk_offset = self._image.tell()
811
812 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
813 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
814 header_bin)
815 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
816
David Zeuthena4fee8b2016-08-22 15:20:43 -0400817 if chunk_type == ImageChunk.TYPE_RAW:
818 if data_sz != (chunk_sz * self.block_size):
819 raise ValueError('Raw chunk input size ({}) does not match output '
820 'size ({})'.
821 format(data_sz, chunk_sz*self.block_size))
822 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
823 chunk_offset,
824 output_offset,
825 chunk_sz*self.block_size,
826 self._image.tell(),
827 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700828 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400829
830 elif chunk_type == ImageChunk.TYPE_FILL:
831 if data_sz != 4:
832 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
833 'has {}'.format(data_sz))
834 fill_data = self._image.read(4)
835 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
836 chunk_offset,
837 output_offset,
838 chunk_sz*self.block_size,
839 None,
840 fill_data))
841 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
842 if data_sz != 0:
843 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
844 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400845 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
846 chunk_offset,
847 output_offset,
848 chunk_sz*self.block_size,
849 None,
850 None))
851 elif chunk_type == ImageChunk.TYPE_CRC32:
852 if data_sz != 4:
853 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
854 'this has {}'.format(data_sz))
855 self._image.read(4)
856 else:
857 raise ValueError('Unknown chunk type {}'.format(chunk_type))
858
859 offset += chunk_sz
860 output_offset += chunk_sz*self.block_size
861
862 # Record where sparse data end.
863 self._sparse_end = self._image.tell()
864
865 # Now that we've traversed all chunks, sanity check.
866 if self._num_total_blocks != offset:
867 raise ValueError('The header said we should have {} output blocks, '
868 'but we saw {}'.format(self._num_total_blocks, offset))
869 junk_len = len(self._image.read())
870 if junk_len > 0:
871 raise ValueError('There were {} bytes of extra data at the end of the '
872 'file.'.format(junk_len))
873
David Zeuthen09692692016-09-30 16:16:40 -0400874 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400875 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400876
877 # This is used when bisecting in read() to find the initial slice.
878 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
879
880 self.is_sparse = True
881
882 def _update_chunks_and_blocks(self):
883 """Helper function to update the image header.
884
885 The the |total_chunks| and |total_blocks| fields in the header
886 will be set to value of the |_num_total_blocks| and
887 |_num_total_chunks| attributes.
888
889 """
890 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
891 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
892 self._num_total_blocks,
893 self._num_total_chunks))
894
895 def append_dont_care(self, num_bytes):
896 """Appends a DONT_CARE chunk to the sparse file.
897
898 The given number of bytes must be a multiple of the block size.
899
900 Arguments:
901 num_bytes: Size in number of bytes of the DONT_CARE chunk.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200902
903 Raises
904 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400905 """
906 assert num_bytes % self.block_size == 0
907
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200908 if self._read_only:
909 raise OSError('ImageHandler is in read-only mode.')
910
David Zeuthena4fee8b2016-08-22 15:20:43 -0400911 if not self.is_sparse:
912 self._image.seek(0, os.SEEK_END)
913 # This is more efficient that writing NUL bytes since it'll add
914 # a hole on file systems that support sparse files (native
915 # sparse, not Android sparse).
916 self._image.truncate(self._image.tell() + num_bytes)
917 self._read_header()
918 return
919
920 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100921 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400922 self._update_chunks_and_blocks()
923
924 self._image.seek(self._sparse_end, os.SEEK_SET)
925 self._image.write(struct.pack(ImageChunk.FORMAT,
926 ImageChunk.TYPE_DONT_CARE,
927 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100928 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400929 struct.calcsize(ImageChunk.FORMAT)))
930 self._read_header()
931
932 def append_raw(self, data):
933 """Appends a RAW chunk to the sparse file.
934
935 The length of the given data must be a multiple of the block size.
936
937 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200938 data: Data to append as bytes.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200939
940 Raises
941 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400942 """
943 assert len(data) % self.block_size == 0
944
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200945 if self._read_only:
946 raise OSError('ImageHandler is in read-only mode.')
947
David Zeuthena4fee8b2016-08-22 15:20:43 -0400948 if not self.is_sparse:
949 self._image.seek(0, os.SEEK_END)
950 self._image.write(data)
951 self._read_header()
952 return
953
954 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100955 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400956 self._update_chunks_and_blocks()
957
958 self._image.seek(self._sparse_end, os.SEEK_SET)
959 self._image.write(struct.pack(ImageChunk.FORMAT,
960 ImageChunk.TYPE_RAW,
961 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100962 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400963 len(data) +
964 struct.calcsize(ImageChunk.FORMAT)))
965 self._image.write(data)
966 self._read_header()
967
968 def append_fill(self, fill_data, size):
969 """Appends a fill chunk to the sparse file.
970
971 The total length of the fill data must be a multiple of the block size.
972
973 Arguments:
974 fill_data: Fill data to append - must be four bytes.
975 size: Number of chunk - must be a multiple of four and the block size.
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200976
977 Raises
978 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400979 """
980 assert len(fill_data) == 4
981 assert size % 4 == 0
982 assert size % self.block_size == 0
983
Jan Monsch4e71bfd2020-04-27 22:44:37 +0200984 if self._read_only:
985 raise OSError('ImageHandler is in read-only mode.')
986
David Zeuthena4fee8b2016-08-22 15:20:43 -0400987 if not self.is_sparse:
988 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100989 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400990 self._read_header()
991 return
992
993 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100994 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400995 self._update_chunks_and_blocks()
996
997 self._image.seek(self._sparse_end, os.SEEK_SET)
998 self._image.write(struct.pack(ImageChunk.FORMAT,
999 ImageChunk.TYPE_FILL,
1000 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +01001001 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001002 4 + struct.calcsize(ImageChunk.FORMAT)))
1003 self._image.write(fill_data)
1004 self._read_header()
1005
1006 def seek(self, offset):
1007 """Sets the cursor position for reading from unsparsified file.
1008
1009 Arguments:
1010 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +01001011
1012 Raises:
1013 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001014 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07001015 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +02001016 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -04001017 self._file_pos = offset
1018
1019 def read(self, size):
1020 """Reads data from the unsparsified file.
1021
1022 This method may return fewer than |size| bytes of data if the end
1023 of the file was encountered.
1024
1025 The file cursor for reading is advanced by the number of bytes
1026 read.
1027
1028 Arguments:
1029 size: Number of bytes to read.
1030
1031 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +02001032 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001033 """
1034 if not self.is_sparse:
1035 self._image.seek(self._file_pos)
1036 data = self._image.read(size)
1037 self._file_pos += len(data)
1038 return data
1039
1040 # Iterate over all chunks.
1041 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1042 self._file_pos) - 1
1043 data = bytearray()
1044 to_go = size
1045 while to_go > 0:
1046 chunk = self._chunks[chunk_idx]
1047 chunk_pos_offset = self._file_pos - chunk.output_offset
1048 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1049
1050 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1051 self._image.seek(chunk.input_offset + chunk_pos_offset)
1052 data.extend(self._image.read(chunk_pos_to_go))
1053 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001054 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001055 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1056 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1057 else:
1058 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001059 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001060
1061 to_go -= chunk_pos_to_go
1062 self._file_pos += chunk_pos_to_go
1063 chunk_idx += 1
1064 # Generate partial read in case of EOF.
1065 if chunk_idx >= len(self._chunks):
1066 break
1067
Jan Monsch8347da92020-04-08 12:41:49 +02001068 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001069
1070 def tell(self):
1071 """Returns the file cursor position for reading from unsparsified file.
1072
1073 Returns:
1074 The file cursor position for reading.
1075 """
1076 return self._file_pos
1077
1078 def truncate(self, size):
1079 """Truncates the unsparsified file.
1080
1081 Arguments:
1082 size: Desired size of unsparsified file.
1083
1084 Raises:
1085 ValueError: If desired size isn't a multiple of the block size.
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001086 OSError: If ImageHandler was initialized in read-only mode.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001087 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001088 if self._read_only:
1089 raise OSError('ImageHandler is in read-only mode.')
1090
David Zeuthena4fee8b2016-08-22 15:20:43 -04001091 if not self.is_sparse:
1092 self._image.truncate(size)
1093 self._read_header()
1094 return
1095
1096 if size % self.block_size != 0:
1097 raise ValueError('Cannot truncate to a size which is not a multiple '
1098 'of the block size')
1099
1100 if size == self.image_size:
1101 # Trivial where there's nothing to do.
1102 return
Jan Monsch9c130122020-04-14 13:43:51 +02001103
1104 if size < self.image_size:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001105 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1106 chunk = self._chunks[chunk_idx]
1107 if chunk.output_offset != size:
1108 # Truncation in the middle of a trunk - need to keep the chunk
1109 # and modify it.
1110 chunk_idx_for_update = chunk_idx + 1
1111 num_to_keep = size - chunk.output_offset
1112 assert num_to_keep % self.block_size == 0
1113 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1114 truncate_at = (chunk.chunk_offset +
1115 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1116 data_sz = num_to_keep
1117 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1118 truncate_at = (chunk.chunk_offset +
1119 struct.calcsize(ImageChunk.FORMAT) + 4)
1120 data_sz = 4
1121 else:
1122 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1123 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1124 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001125 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001126 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1127 self._image.seek(chunk.chunk_offset)
1128 self._image.write(struct.pack(ImageChunk.FORMAT,
1129 chunk.chunk_type,
1130 0, # Reserved
1131 chunk_sz,
1132 total_sz))
1133 chunk.output_size = num_to_keep
1134 else:
1135 # Truncation at trunk boundary.
1136 truncate_at = chunk.chunk_offset
1137 chunk_idx_for_update = chunk_idx
1138
1139 self._num_total_chunks = chunk_idx_for_update
1140 self._num_total_blocks = 0
1141 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001142 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001143 self._update_chunks_and_blocks()
1144 self._image.truncate(truncate_at)
1145
1146 # We've modified the file so re-read all data.
1147 self._read_header()
1148 else:
1149 # Truncating to grow - just add a DONT_CARE section.
1150 self.append_dont_care(size - self.image_size)
1151
1152
David Zeuthen21e95262016-07-27 17:58:40 -04001153class AvbDescriptor(object):
1154 """Class for AVB descriptor.
1155
1156 See the |AvbDescriptor| C struct for more information.
1157
1158 Attributes:
1159 tag: The tag identifying what kind of descriptor this is.
1160 data: The data in the descriptor.
1161 """
1162
1163 SIZE = 16
1164 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1165
1166 def __init__(self, data):
1167 """Initializes a new property descriptor.
1168
1169 Arguments:
1170 data: If not None, must be a bytearray().
1171
1172 Raises:
1173 LookupError: If the given descriptor is malformed.
1174 """
1175 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1176
1177 if data:
1178 (self.tag, num_bytes_following) = (
1179 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1180 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1181 else:
1182 self.tag = None
1183 self.data = None
1184
1185 def print_desc(self, o):
1186 """Print the descriptor.
1187
1188 Arguments:
1189 o: The object to write the output to.
1190 """
1191 o.write(' Unknown descriptor:\n')
1192 o.write(' Tag: {}\n'.format(self.tag))
1193 if len(self.data) < 256:
1194 o.write(' Data: {} ({} bytes)\n'.format(
1195 repr(str(self.data)), len(self.data)))
1196 else:
1197 o.write(' Data: {} bytes\n'.format(len(self.data)))
1198
1199 def encode(self):
1200 """Serializes the descriptor.
1201
1202 Returns:
1203 A bytearray() with the descriptor data.
1204 """
1205 num_bytes_following = len(self.data)
1206 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1207 padding_size = nbf_with_padding - num_bytes_following
1208 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1209 padding = struct.pack(str(padding_size) + 'x')
1210 ret = desc + self.data + padding
1211 return bytearray(ret)
1212
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001213 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001214 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001215 """Verifies contents of the descriptor - used in verify_image sub-command.
1216
1217 Arguments:
1218 image_dir: The directory of the file being verified.
1219 image_ext: The extension of the file being verified (e.g. '.img').
1220 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001221 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001222 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001223 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1224 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001225
1226 Returns:
1227 True if the descriptor verifies, False otherwise.
1228 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001229 # Deletes unused parameters to prevent pylint warning unused-argument.
1230 del image_dir, image_ext, expected_chain_partitions_map
1231 del image_containing_descriptor, accept_zeroed_hashtree
1232
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001233 # Nothing to do.
1234 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001235
Jan Monscheeb28b62019-12-05 16:17:09 +01001236
David Zeuthen21e95262016-07-27 17:58:40 -04001237class AvbPropertyDescriptor(AvbDescriptor):
1238 """A class for property descriptors.
1239
1240 See the |AvbPropertyDescriptor| C struct for more information.
1241
1242 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001243 key: The key as string.
1244 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001245 """
1246
1247 TAG = 0
1248 SIZE = 32
1249 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001250 'Q' # key size (bytes)
1251 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001252
1253 def __init__(self, data=None):
1254 """Initializes a new property descriptor.
1255
1256 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001257 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001258
1259 Raises:
1260 LookupError: If the given descriptor is malformed.
1261 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001262 super(AvbPropertyDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001263 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1264
1265 if data:
1266 (tag, num_bytes_following, key_size,
1267 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1268 expected_size = round_to_multiple(
1269 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1270 if tag != self.TAG or num_bytes_following != expected_size:
1271 raise LookupError('Given data does not look like a property '
1272 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001273 try:
1274 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1275 except UnicodeDecodeError as e:
1276 raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001277 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1278 value_size)]
1279 else:
1280 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001281 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001282
1283 def print_desc(self, o):
1284 """Print the descriptor.
1285
1286 Arguments:
1287 o: The object to write the output to.
1288 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001289 # Go forward with python 3, bytes are represented with the 'b' prefix,
1290 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1291 # the same between python 2 and python 3.
1292 printable_value = repr(self.value)
1293 if printable_value.startswith('b\''):
1294 printable_value = printable_value[1:]
1295
David Zeuthen21e95262016-07-27 17:58:40 -04001296 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001297 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001298 else:
1299 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1300
1301 def encode(self):
1302 """Serializes the descriptor.
1303
1304 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001305 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001306 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001307 key_encoded = self.key.encode('utf-8')
1308 num_bytes_following = (
1309 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001310 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1311 padding_size = nbf_with_padding - num_bytes_following
1312 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001313 len(key_encoded), len(self.value))
1314 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1315 padding_size * b'\0')
1316 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001317
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001318 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001319 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001320 """Verifies contents of the descriptor - used in verify_image sub-command.
1321
1322 Arguments:
1323 image_dir: The directory of the file being verified.
1324 image_ext: The extension of the file being verified (e.g. '.img').
1325 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001326 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001327 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001328 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1329 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001330
1331 Returns:
1332 True if the descriptor verifies, False otherwise.
1333 """
1334 # Nothing to do.
1335 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001336
Jan Monscheeb28b62019-12-05 16:17:09 +01001337
David Zeuthen21e95262016-07-27 17:58:40 -04001338class AvbHashtreeDescriptor(AvbDescriptor):
1339 """A class for hashtree descriptors.
1340
1341 See the |AvbHashtreeDescriptor| C struct for more information.
1342
1343 Attributes:
1344 dm_verity_version: dm-verity version used.
1345 image_size: Size of the image, after rounding up to |block_size|.
1346 tree_offset: Offset of the hash tree in the file.
1347 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001348 data_block_size: Data block size.
1349 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001350 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1351 fec_offset: Offset of FEC data (0 if FEC is not used).
1352 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001353 hash_algorithm: Hash algorithm used as string.
1354 partition_name: Partition name as string.
1355 salt: Salt used as bytes.
1356 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001357 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001358 """
1359
1360 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001361 RESERVED = 60
1362 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001363 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001364 'L' # dm-verity version used
1365 'Q' # image size (bytes)
1366 'Q' # tree offset (bytes)
1367 'Q' # tree size (bytes)
1368 'L' # data block size (bytes)
1369 'L' # hash block size (bytes)
1370 'L' # FEC number of roots
1371 'Q' # FEC offset (bytes)
1372 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001373 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001374 'L' # partition name (bytes)
1375 'L' # salt length (bytes)
1376 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001377 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001378 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001379
JeongHyeon Lee2998a352021-05-25 16:38:07 +09001380 FLAGS_DO_NOT_USE_AB = (1 << 0)
1381 FLAGS_CHECK_AT_MOST_ONCE = (1 << 1)
1382
David Zeuthen21e95262016-07-27 17:58:40 -04001383 def __init__(self, data=None):
1384 """Initializes a new hashtree descriptor.
1385
1386 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001387 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001388
1389 Raises:
1390 LookupError: If the given descriptor is malformed.
1391 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001392 super(AvbHashtreeDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001393 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1394
1395 if data:
1396 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1397 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001398 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1399 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001400 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1401 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001402 expected_size = round_to_multiple(
1403 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1404 if tag != self.TAG or num_bytes_following != expected_size:
1405 raise LookupError('Given data does not look like a hashtree '
1406 'descriptor.')
1407 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001408 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001409 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001410 try:
1411 self.partition_name = data[
1412 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1413 ].decode('utf-8')
1414 except UnicodeDecodeError as e:
1415 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1416 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001417 o += partition_name_len
1418 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1419 o += salt_len
1420 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Tianjie62ad0222021-02-22 14:31:12 -08001421
1422 if root_digest_len != self._hashtree_digest_size():
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001423 if root_digest_len != 0:
1424 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001425
1426 else:
1427 self.dm_verity_version = 0
1428 self.image_size = 0
1429 self.tree_offset = 0
1430 self.tree_size = 0
1431 self.data_block_size = 0
1432 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001433 self.fec_num_roots = 0
1434 self.fec_offset = 0
1435 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001436 self.hash_algorithm = ''
1437 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001438 self.salt = b''
1439 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001440 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001441
Tianjie62ad0222021-02-22 14:31:12 -08001442 def _hashtree_digest_size(self):
1443 return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
1444
David Zeuthen21e95262016-07-27 17:58:40 -04001445 def print_desc(self, o):
1446 """Print the descriptor.
1447
1448 Arguments:
1449 o: The object to write the output to.
1450 """
1451 o.write(' Hashtree descriptor:\n')
1452 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1453 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1454 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1455 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1456 o.write(' Data Block Size: {} bytes\n'.format(
1457 self.data_block_size))
1458 o.write(' Hash Block Size: {} bytes\n'.format(
1459 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001460 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1461 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1462 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001463 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1464 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001465 o.write(' Salt: {}\n'.format(self.salt.hex()))
1466 o.write(' Root Digest: {}\n'.format(self.root_digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001467 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001468
1469 def encode(self):
1470 """Serializes the descriptor.
1471
1472 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001473 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001474 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001475 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1476 partition_name_encoded = self.partition_name.encode('utf-8')
1477 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1478 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001479 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1480 padding_size = nbf_with_padding - num_bytes_following
1481 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1482 self.dm_verity_version, self.image_size,
1483 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001484 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001485 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1486 len(partition_name_encoded), len(self.salt),
1487 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1488 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1489 padding_size * b'\0')
1490 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001491
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001492 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001493 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001494 """Verifies contents of the descriptor - used in verify_image sub-command.
1495
1496 Arguments:
1497 image_dir: The directory of the file being verified.
1498 image_ext: The extension of the file being verified (e.g. '.img').
1499 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001500 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001501 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001502 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1503 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001504
1505 Returns:
1506 True if the descriptor verifies, False otherwise.
1507 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001508 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001509 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001510 image = image_containing_descriptor
1511 else:
1512 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001513 image = ImageHandler(image_filename, read_only=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001514 # Generate the hashtree and checks that it matches what's in the file.
Tianjie62ad0222021-02-22 14:31:12 -08001515 digest_size = self._hashtree_digest_size()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001516 digest_padding = round_to_pow2(digest_size) - digest_size
1517 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001518 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001519 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1520 self.data_block_size,
1521 self.hash_algorithm, self.salt,
1522 digest_padding,
1523 hash_level_offsets,
1524 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001525 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001526 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001527 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1528 format(image_filename))
1529 return False
1530 # ... also check that the on-disk hashtree matches
1531 image.seek(self.tree_offset)
1532 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001533 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001534 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001535 print('{}: skipping verification since hashtree is zeroed and '
1536 '--accept_zeroed_hashtree was given'
1537 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001538 else:
1539 if hash_tree != hash_tree_ondisk:
1540 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001541 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001542 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001543 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1544 .format(self.partition_name, self.hash_algorithm, image.filename,
1545 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001546 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1547 # correct but this a) currently requires the 'fec' binary; and b) takes a
1548 # long time; and c) is not strictly needed for verification purposes as
1549 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001550 return True
1551
David Zeuthen21e95262016-07-27 17:58:40 -04001552
1553class AvbHashDescriptor(AvbDescriptor):
1554 """A class for hash descriptors.
1555
1556 See the |AvbHashDescriptor| C struct for more information.
1557
1558 Attributes:
1559 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001560 hash_algorithm: Hash algorithm used as string.
1561 partition_name: Partition name as string.
1562 salt: Salt used as bytes.
1563 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001564 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001565 """
1566
1567 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001568 RESERVED = 60
1569 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001570 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001571 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001572 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001573 'L' # partition name (bytes)
1574 'L' # salt length (bytes)
1575 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001576 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001577 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001578
1579 def __init__(self, data=None):
1580 """Initializes a new hash descriptor.
1581
1582 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001583 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001584
1585 Raises:
1586 LookupError: If the given descriptor is malformed.
1587 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001588 super(AvbHashDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001589 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1590
1591 if data:
1592 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1593 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001594 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1595 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001596 expected_size = round_to_multiple(
1597 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1598 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001599 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001600 # Nuke NUL-bytes at the end.
Jan Monschee6fccd2020-04-09 19:36:13 +02001601 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001602 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001603 try:
1604 self.partition_name = data[
1605 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1606 ].decode('utf-8')
1607 except UnicodeDecodeError as e:
1608 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1609 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001610 o += partition_name_len
1611 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1612 o += salt_len
1613 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001614 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001615 if digest_len != 0:
1616 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001617
1618 else:
1619 self.image_size = 0
1620 self.hash_algorithm = ''
1621 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001622 self.salt = b''
1623 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001624 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001625
1626 def print_desc(self, o):
1627 """Print the descriptor.
1628
1629 Arguments:
1630 o: The object to write the output to.
1631 """
1632 o.write(' Hash descriptor:\n')
1633 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1634 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1635 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001636 o.write(' Salt: {}\n'.format(self.salt.hex()))
1637 o.write(' Digest: {}\n'.format(self.digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001638 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001639
1640 def encode(self):
1641 """Serializes the descriptor.
1642
1643 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001644 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001645 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001646 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1647 partition_name_encoded = self.partition_name.encode('utf-8')
1648 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1649 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001650 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1651 padding_size = nbf_with_padding - num_bytes_following
1652 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001653 self.image_size, hash_algorithm_encoded,
1654 len(partition_name_encoded), len(self.salt),
1655 len(self.digest), self.flags, self.RESERVED * b'\0')
1656 ret = (desc + partition_name_encoded + self.salt + self.digest +
1657 padding_size * b'\0')
1658 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001659
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001660 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001661 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001662 """Verifies contents of the descriptor - used in verify_image sub-command.
1663
1664 Arguments:
1665 image_dir: The directory of the file being verified.
1666 image_ext: The extension of the file being verified (e.g. '.img').
1667 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001668 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001669 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001670 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1671 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001672
1673 Returns:
1674 True if the descriptor verifies, False otherwise.
1675 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001676 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001677 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001678 image = image_containing_descriptor
1679 else:
1680 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02001681 image = ImageHandler(image_filename, read_only=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001682 data = image.read(self.image_size)
1683 ha = hashlib.new(self.hash_algorithm)
1684 ha.update(self.salt)
1685 ha.update(data)
1686 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001687 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001688 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001689 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1690 format(self.hash_algorithm, image_filename))
1691 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001692 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1693 .format(self.partition_name, self.hash_algorithm, image.filename,
1694 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001695 return True
1696
David Zeuthen21e95262016-07-27 17:58:40 -04001697
1698class AvbKernelCmdlineDescriptor(AvbDescriptor):
1699 """A class for kernel command-line descriptors.
1700
1701 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1702
1703 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001704 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001705 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001706 """
1707
1708 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001709 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001710 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001711 'L' # flags
1712 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001713
David Zeuthenfd41eb92016-11-17 12:24:47 -05001714 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1715 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1716
David Zeuthen21e95262016-07-27 17:58:40 -04001717 def __init__(self, data=None):
1718 """Initializes a new kernel cmdline descriptor.
1719
1720 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001721 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001722
1723 Raises:
1724 LookupError: If the given descriptor is malformed.
1725 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001726 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001727 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1728
1729 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001730 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001731 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1732 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1733 8)
1734 if tag != self.TAG or num_bytes_following != expected_size:
1735 raise LookupError('Given data does not look like a kernel cmdline '
1736 'descriptor.')
1737 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001738 try:
1739 self.kernel_cmdline = data[
1740 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1741 except UnicodeDecodeError as e:
1742 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1743 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001744 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001745 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001746 self.kernel_cmdline = ''
1747
1748 def print_desc(self, o):
1749 """Print the descriptor.
1750
1751 Arguments:
1752 o: The object to write the output to.
1753 """
1754 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001755 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001756 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001757
1758 def encode(self):
1759 """Serializes the descriptor.
1760
1761 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001762 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001763 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001764 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1765 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001766 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1767 padding_size = nbf_with_padding - num_bytes_following
1768 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001769 self.flags, len(kernel_cmd_encoded))
1770 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1771 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001772
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001773 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001774 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001775 """Verifies contents of the descriptor - used in verify_image sub-command.
1776
1777 Arguments:
1778 image_dir: The directory of the file being verified.
1779 image_ext: The extension of the file being verified (e.g. '.img').
1780 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001781 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001782 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001783 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1784 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001785
1786 Returns:
1787 True if the descriptor verifies, False otherwise.
1788 """
1789 # Nothing to verify.
1790 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001791
Jan Monscheeb28b62019-12-05 16:17:09 +01001792
David Zeuthen21e95262016-07-27 17:58:40 -04001793class AvbChainPartitionDescriptor(AvbDescriptor):
1794 """A class for chained partition descriptors.
1795
1796 See the |AvbChainPartitionDescriptor| C struct for more information.
1797
1798 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001799 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001800 partition_name: Partition name as string.
1801 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001802 """
1803
1804 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001805 RESERVED = 64
1806 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001807 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001808 'L' # rollback_index_location
1809 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001810 'L' + # public_key_size (bytes)
1811 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001812
1813 def __init__(self, data=None):
1814 """Initializes a new chain partition descriptor.
1815
1816 Arguments:
1817 data: If not None, must be a bytearray of size |SIZE|.
1818
1819 Raises:
1820 LookupError: If the given descriptor is malformed.
1821 """
1822 AvbDescriptor.__init__(self, None)
1823 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1824
1825 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001826 (tag, num_bytes_following, self.rollback_index_location,
1827 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001828 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001829 expected_size = round_to_multiple(
1830 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1831 if tag != self.TAG or num_bytes_following != expected_size:
1832 raise LookupError('Given data does not look like a chain partition '
1833 'descriptor.')
1834 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001835 try:
1836 self.partition_name = data[
1837 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1838 ].decode('utf-8')
1839 except UnicodeDecodeError as e:
1840 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1841 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001842 o += partition_name_len
1843 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1844
1845 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001846 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001847 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001848 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001849
1850 def print_desc(self, o):
1851 """Print the descriptor.
1852
1853 Arguments:
1854 o: The object to write the output to.
1855 """
1856 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001857 o.write(' Partition Name: {}\n'.format(self.partition_name))
1858 o.write(' Rollback Index Location: {}\n'.format(
1859 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001860 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001861 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1862 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001863
1864 def encode(self):
1865 """Serializes the descriptor.
1866
1867 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001868 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001869 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001870 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001871 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001872 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001873 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1874 padding_size = nbf_with_padding - num_bytes_following
1875 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001876 self.rollback_index_location,
1877 len(partition_name_encoded), len(self.public_key),
1878 self.RESERVED * b'\0')
1879 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1880 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001881
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001882 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001883 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001884 """Verifies contents of the descriptor - used in verify_image sub-command.
1885
1886 Arguments:
1887 image_dir: The directory of the file being verified.
1888 image_ext: The extension of the file being verified (e.g. '.img').
1889 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001890 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001891 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001892 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1893 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001894
1895 Returns:
1896 True if the descriptor verifies, False otherwise.
1897 """
1898 value = expected_chain_partitions_map.get(self.partition_name)
1899 if not value:
1900 sys.stderr.write('No expected chain partition for partition {}. Use '
1901 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001902 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001903 format(self.partition_name))
1904 return False
1905 rollback_index_location, pk_blob = value
1906
1907 if self.rollback_index_location != rollback_index_location:
1908 sys.stderr.write('Expected rollback_index_location {} does not '
1909 'match {} in descriptor for partition {}\n'.
1910 format(rollback_index_location,
1911 self.rollback_index_location,
1912 self.partition_name))
1913 return False
1914
1915 if self.public_key != pk_blob:
1916 sys.stderr.write('Expected public key blob does not match public '
1917 'key blob in descriptor for partition {}\n'.
1918 format(self.partition_name))
1919 return False
1920
Jan Monsch23e0c622019-12-11 11:23:58 +01001921 print('{}: Successfully verified chain partition descriptor matches '
1922 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001923
1924 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001925
1926DESCRIPTOR_CLASSES = [
1927 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1928 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1929]
1930
1931
1932def parse_descriptors(data):
1933 """Parses a blob of data into descriptors.
1934
1935 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001936 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001937
1938 Returns:
1939 A list of instances of objects derived from AvbDescriptor. For
1940 unknown descriptors, the class AvbDescriptor is used.
1941 """
1942 o = 0
1943 ret = []
1944 while o < len(data):
1945 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1946 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001947 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001948 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001949 clazz = AvbDescriptor
1950 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001951 o += 16 + nb_following
1952 return ret
1953
1954
1955class AvbFooter(object):
1956 """A class for parsing and writing footers.
1957
1958 Footers are stored at the end of partitions and point to where the
1959 AvbVBMeta blob is located. They also contain the original size of
1960 the image before AVB information was added.
1961
1962 Attributes:
1963 magic: Magic for identifying the footer, see |MAGIC|.
1964 version_major: The major version of avbtool that wrote the footer.
1965 version_minor: The minor version of avbtool that wrote the footer.
1966 original_image_size: Original image size.
1967 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1968 vbmeta_size: Size of the AvbVBMeta blob.
1969 """
1970
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001971 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001972 SIZE = 64
1973 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001974 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1975 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001976 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001977 'Q' # Original image size.
1978 'Q' # Offset of VBMeta blob.
1979 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001980 str(RESERVED) + 'x') # padding for reserved bytes
1981
1982 def __init__(self, data=None):
1983 """Initializes a new footer object.
1984
1985 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001986 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001987
1988 Raises:
1989 LookupError: If the given footer is malformed.
1990 struct.error: If the given data has no footer.
1991 """
1992 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1993
1994 if data:
1995 (self.magic, self.version_major, self.version_minor,
1996 self.original_image_size, self.vbmeta_offset,
1997 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1998 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001999 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04002000 else:
2001 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002002 self.version_major = self.FOOTER_VERSION_MAJOR
2003 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04002004 self.original_image_size = 0
2005 self.vbmeta_offset = 0
2006 self.vbmeta_size = 0
2007
David Zeuthena4fee8b2016-08-22 15:20:43 -04002008 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002009 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002010
David Zeuthena4fee8b2016-08-22 15:20:43 -04002011 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002012 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002013 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002014 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
2015 self.version_minor, self.original_image_size,
2016 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002017
2018
2019class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002020 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002021
Jan Monschfe00c0a2019-12-11 11:19:40 +01002022 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2023 avb_vbmeta_image.h.
2024
David Zeuthen21e95262016-07-27 17:58:40 -04002025 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002026 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2027 required_libavb_version_major: The major version of libavb required for this
2028 header.
2029 required_libavb_version_minor: The minor version of libavb required for this
2030 header.
2031 authentication_data_block_size: The size of the signature block.
2032 auxiliary_data_block_size: The size of the auxiliary data block.
2033 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2034 enum.
2035 hash_offset: Offset into the "Authentication data" block of hash data.
2036 hash_size: Length of the hash data.
2037 signature_offset: Offset into the "Authentication data" block of signature
2038 data.
2039 signature_size: Length of the signature data.
2040 public_key_offset: Offset into the "Auxiliary data" block of public key
2041 data.
2042 public_key_size: Length of the public key data.
2043 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2044 key metadata.
2045 public_key_metadata_size: Length of the public key metadata. Must be set to
2046 zero if there is no public key metadata.
2047 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2048 data.
2049 descriptors_size: Length of descriptor data.
2050 rollback_index: The rollback index which can be used to prevent rollback to
2051 older versions.
2052 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2053 zero if the vbmeta image is not a top-level image.
Varun Sharmade538272020-04-10 15:22:31 -07002054 rollback_index_location: The location of the rollback index defined in this
2055 header. Only valid for the main vbmeta. For chained partitions, the
2056 rollback index location must be specified in the
2057 AvbChainPartitionDescriptor and this value must be set to 0.
Jan Monschfe00c0a2019-12-11 11:19:40 +01002058 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2059 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2060 terminated. Applications must not make assumptions about how this
2061 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002062 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002063 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002064 SIZE = 256
2065
Varun Sharmade538272020-04-10 15:22:31 -07002066 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthene3cadca2017-02-22 21:25:46 -05002067 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002068
2069 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002070 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2071 '2Q' # 2 x block size
2072 'L' # algorithm type
2073 '2Q' # offset, size (hash)
2074 '2Q' # offset, size (signature)
2075 '2Q' # offset, size (public key)
2076 '2Q' # offset, size (public key metadata)
2077 '2Q' # offset, size (descriptors)
2078 'Q' # rollback_index
Varun Sharmade538272020-04-10 15:22:31 -07002079 'L' # flags
2080 'L' # rollback_index_location
David Zeuthene3cadca2017-02-22 21:25:46 -05002081 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002082 str(RESERVED) + 'x') # padding for reserved bytes
2083
2084 def __init__(self, data=None):
2085 """Initializes a new header object.
2086
2087 Arguments:
2088 data: If not None, must be a bytearray of size 8192.
2089
2090 Raises:
2091 Exception: If the given data is malformed.
2092 """
2093 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2094
2095 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002096 (self.magic, self.required_libavb_version_major,
2097 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002098 self.authentication_data_block_size, self.auxiliary_data_block_size,
2099 self.algorithm_type, self.hash_offset, self.hash_size,
2100 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002101 self.public_key_size, self.public_key_metadata_offset,
2102 self.public_key_metadata_size, self.descriptors_offset,
2103 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002104 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002105 self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002106 self.rollback_index_location,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002107 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002108 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002109 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002110 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002111 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002112 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002113 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002114 # Start by just requiring version 1.0. Code that adds features
2115 # in a future version can use bump_required_libavb_version_minor() to
2116 # bump the minor.
2117 self.required_libavb_version_major = AVB_VERSION_MAJOR
2118 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002119 self.authentication_data_block_size = 0
2120 self.auxiliary_data_block_size = 0
2121 self.algorithm_type = 0
2122 self.hash_offset = 0
2123 self.hash_size = 0
2124 self.signature_offset = 0
2125 self.signature_size = 0
2126 self.public_key_offset = 0
2127 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002128 self.public_key_metadata_offset = 0
2129 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002130 self.descriptors_offset = 0
2131 self.descriptors_size = 0
2132 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002133 self.flags = 0
Varun Sharmade538272020-04-10 15:22:31 -07002134 self.rollback_index_location = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002135 self.release_string = get_release_string()
2136
2137 def bump_required_libavb_version_minor(self, minor):
2138 """Function to bump required_libavb_version_minor.
2139
2140 Call this when writing data that requires a specific libavb
2141 version to parse it.
2142
2143 Arguments:
2144 minor: The minor version of libavb that has support for the feature.
2145 """
2146 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002147 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002148
David Zeuthen21e95262016-07-27 17:58:40 -04002149 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002150 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002151
2152 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002153 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002154 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002155 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002156 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002157 self.required_libavb_version_major,
2158 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002159 self.authentication_data_block_size,
2160 self.auxiliary_data_block_size, self.algorithm_type,
2161 self.hash_offset, self.hash_size, self.signature_offset,
2162 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002163 self.public_key_size, self.public_key_metadata_offset,
2164 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002165 self.descriptors_size, self.rollback_index, self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002166 self.rollback_index_location, release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002167
2168
2169class Avb(object):
2170 """Business logic for avbtool command-line tool."""
2171
David Zeuthen8b6973b2016-09-20 12:39:49 -04002172 # Keep in sync with avb_ab_flow.h.
2173 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002174 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002175 AB_MAJOR_VERSION = 1
2176 AB_MINOR_VERSION = 0
2177 AB_MISC_METADATA_OFFSET = 2048
2178
David Zeuthen09692692016-09-30 16:16:40 -04002179 # Constants for maximum metadata size. These are used to give
2180 # meaningful errors if the value passed in via --partition_size is
2181 # too small and when --calc_max_image_size is used. We use
2182 # conservative figures.
2183 MAX_VBMETA_SIZE = 64 * 1024
2184 MAX_FOOTER_SIZE = 4096
2185
Jan Monsch2c7be992020-04-03 14:37:13 +02002186 def generate_test_image(self, output, image_size, start_byte):
2187 """Generates a test image for testing avbtool with known content.
2188
2189 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2190
2191 Arguments:
2192 output: Write test image to this file.
2193 image_size: The size of the requested file in bytes.
2194 start_byte: The integer value of the start byte to use for pattern
2195 generation.
2196 """
2197 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2198 buf = bytearray()
2199 c = int(math.ceil(image_size / 256.0))
2200 for _ in range(0, c):
2201 buf.extend(pattern)
2202 output.write(buf[0:image_size])
2203
David Zeuthen49936b42018-08-07 17:38:58 -04002204 def extract_vbmeta_image(self, output, image_filename, padding_size):
2205 """Implements the 'extract_vbmeta_image' command.
2206
2207 Arguments:
2208 output: Write vbmeta struct to this file.
2209 image_filename: File to extract vbmeta data from (with a footer).
2210 padding_size: If not 0, pads output so size is a multiple of the number.
2211
2212 Raises:
2213 AvbError: If there's no footer in the image.
2214 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002215 image = ImageHandler(image_filename, read_only=True)
David Zeuthen49936b42018-08-07 17:38:58 -04002216 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002217 if not footer:
2218 raise AvbError('Given image does not have a footer.')
2219
2220 image.seek(footer.vbmeta_offset)
2221 vbmeta_blob = image.read(footer.vbmeta_size)
2222 output.write(vbmeta_blob)
2223
2224 if padding_size > 0:
2225 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2226 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002227 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002228
David Zeuthena4fee8b2016-08-22 15:20:43 -04002229 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002230 """Implements the 'erase_footer' command.
2231
2232 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002233 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002234 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002235
2236 Raises:
2237 AvbError: If there's no footer in the image.
2238 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002239 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002240 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002241 if not footer:
2242 raise AvbError('Given image does not have a footer.')
2243
2244 new_image_size = None
2245 if not keep_hashtree:
2246 new_image_size = footer.original_image_size
2247 else:
2248 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002249 # descriptor to figure out the location and size of the hashtree
2250 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002251 for desc in descriptors:
2252 if isinstance(desc, AvbHashtreeDescriptor):
2253 # The hashtree is always just following the main data so the
2254 # new size is easily derived.
2255 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002256 # If the image has FEC codes, also keep those.
2257 if desc.fec_offset > 0:
2258 fec_end = desc.fec_offset + desc.fec_size
2259 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002260 break
2261 if not new_image_size:
2262 raise AvbError('Requested to keep hashtree but no hashtree '
2263 'descriptor was found.')
2264
2265 # And cut...
2266 image.truncate(new_image_size)
2267
David Zeuthen1394f762019-04-30 10:20:11 -04002268 def zero_hashtree(self, image_filename):
2269 """Implements the 'zero_hashtree' command.
2270
2271 Arguments:
2272 image_filename: File to zero hashtree and FEC data from.
2273
2274 Raises:
2275 AvbError: If there's no footer in the image.
2276 """
David Zeuthen1394f762019-04-30 10:20:11 -04002277 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002278 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002279 if not footer:
2280 raise AvbError('Given image does not have a footer.')
2281
2282 # Search for a hashtree descriptor to figure out the location and
2283 # size of the hashtree and FEC.
2284 ht_desc = None
2285 for desc in descriptors:
2286 if isinstance(desc, AvbHashtreeDescriptor):
2287 ht_desc = desc
2288 break
2289
2290 if not ht_desc:
2291 raise AvbError('No hashtree descriptor was found.')
2292
2293 zero_ht_start_offset = ht_desc.tree_offset
2294 zero_ht_num_bytes = ht_desc.tree_size
2295 zero_fec_start_offset = None
2296 zero_fec_num_bytes = 0
2297 if ht_desc.fec_offset > 0:
2298 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2299 raise AvbError('Hash-tree and FEC data must be adjacent.')
2300 zero_fec_start_offset = ht_desc.fec_offset
2301 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002302 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2303 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002304 image.seek(zero_end_offset)
2305 data = image.read(image.image_size - zero_end_offset)
2306
2307 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2308 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2309 # beginning of both hashtree and FEC. (That way, in the future we can add
2310 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2311 #
2312 # Applications can use these markers to detect that the hashtree and/or
2313 # FEC needs to be recomputed.
2314 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002315 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002316 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002317 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002318 if zero_fec_start_offset:
2319 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002320 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002321 image.append_raw(data)
2322
David Zeuthen2bc232b2017-04-19 14:25:19 -04002323 def resize_image(self, image_filename, partition_size):
2324 """Implements the 'resize_image' command.
2325
2326 Arguments:
2327 image_filename: File with footer to resize.
2328 partition_size: The new size of the image.
2329
2330 Raises:
2331 AvbError: If there's no footer in the image.
2332 """
2333
2334 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002335 if partition_size % image.block_size != 0:
2336 raise AvbError('Partition size of {} is not a multiple of the image '
2337 'block size {}.'.format(partition_size,
2338 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002339 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002340 if not footer:
2341 raise AvbError('Given image does not have a footer.')
2342
2343 # The vbmeta blob is always at the end of the data so resizing an
2344 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002345 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2346 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002347 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2348 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002349
Jan Monschb1d920f2020-04-09 12:59:28 +02002350 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002351 raise AvbError('Requested size of {} is too small for an image '
2352 'of size {}.'
2353 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002354 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002355
2356 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2357 # with enough bytes such that the final Footer block is at the end
2358 # of partition_size.
2359 image.truncate(vbmeta_end_offset)
2360 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002361 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002362
2363 # Just reuse the same footer - only difference is that we're
2364 # writing it in a different place.
2365 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002366 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002367 footer_blob)
2368 image.append_raw(footer_blob_with_padding)
2369
David Zeuthen8b6973b2016-09-20 12:39:49 -04002370 def set_ab_metadata(self, misc_image, slot_data):
2371 """Implements the 'set_ab_metadata' command.
2372
2373 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2374 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2375
2376 Arguments:
2377 misc_image: The misc image to write to.
2378 slot_data: Slot data as a string
2379
2380 Raises:
2381 AvbError: If slot data is malformed.
2382 """
2383 tokens = slot_data.split(':')
2384 if len(tokens) != 6:
2385 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2386 a_priority = int(tokens[0])
2387 a_tries_remaining = int(tokens[1])
Jan Monsch9c130122020-04-14 13:43:51 +02002388 a_success = int(tokens[2]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002389 b_priority = int(tokens[3])
2390 b_tries_remaining = int(tokens[4])
Jan Monsch9c130122020-04-14 13:43:51 +02002391 b_success = int(tokens[5]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002392
2393 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2394 self.AB_MAGIC,
2395 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2396 a_priority, a_tries_remaining, a_success,
2397 b_priority, b_tries_remaining, b_success)
2398 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2399 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2400 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2401 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2402 misc_image.write(ab_data)
2403
Sen Jiang01553a22020-06-30 17:58:44 -07002404 def info_image(self, image_filename, output, atx):
David Zeuthen21e95262016-07-27 17:58:40 -04002405 """Implements the 'info_image' command.
2406
2407 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002408 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002409 output: Output file to write human-readable information to (file object).
Sen Jiang01553a22020-06-30 17:58:44 -07002410 atx: If True, show information about Android Things eXtension (ATX).
David Zeuthen21e95262016-07-27 17:58:40 -04002411 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002412 image = ImageHandler(image_filename, read_only=True)
David Zeuthen21e95262016-07-27 17:58:40 -04002413 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002414 (footer, header, descriptors, image_size) = self._parse_image(image)
2415
Bowgo Tsaid7145942020-03-20 17:03:51 +08002416 # To show the SHA1 of the public key.
2417 vbmeta_blob = self._load_vbmeta_blob(image)
2418 key_offset = (header.SIZE +
2419 header.authentication_data_block_size +
2420 header.public_key_offset)
2421 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2422
David Zeuthen21e95262016-07-27 17:58:40 -04002423 if footer:
2424 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2425 footer.version_minor))
2426 o.write('Image size: {} bytes\n'.format(image_size))
2427 o.write('Original image size: {} bytes\n'.format(
2428 footer.original_image_size))
2429 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2430 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2431 o.write('--\n')
2432
2433 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2434
David Zeuthene3cadca2017-02-22 21:25:46 -05002435 o.write('Minimum libavb version: {}.{}{}\n'.format(
2436 header.required_libavb_version_major,
2437 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002438 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002439 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2440 o.write('Authentication Block: {} bytes\n'.format(
2441 header.authentication_data_block_size))
2442 o.write('Auxiliary Block: {} bytes\n'.format(
2443 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002444 if key_blob:
2445 hexdig = hashlib.sha1(key_blob).hexdigest()
2446 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002447 o.write('Algorithm: {}\n'.format(alg_name))
2448 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002449 o.write('Flags: {}\n'.format(header.flags))
Jan Monscha18b2ec2020-04-22 11:43:35 +02002450 o.write('Rollback Index Location: {}\n'.format(
2451 header.rollback_index_location))
Jan Monschb1d920f2020-04-09 12:59:28 +02002452 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002453
2454 # Print descriptors.
2455 num_printed = 0
2456 o.write('Descriptors:\n')
2457 for desc in descriptors:
2458 desc.print_desc(o)
2459 num_printed += 1
2460 if num_printed == 0:
2461 o.write(' (none)\n')
2462
Sen Jiang01553a22020-06-30 17:58:44 -07002463 if atx and header.public_key_metadata_size:
2464 o.write('Android Things eXtension (ATX):\n')
2465 key_metadata_offset = (header.SIZE +
2466 header.authentication_data_block_size +
2467 header.public_key_metadata_offset)
2468 key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset
2469 + header.public_key_metadata_size]
2470 version, pik, psk = struct.unpack('<I1620s1620s', key_metadata_blob)
2471 o.write(' Metadata version: {}\n'.format(version))
2472
2473 def print_atx_certificate(cert):
2474 version, public_key, subject, usage, key_version, _signature = \
2475 struct.unpack('<I1032s32s32sQ512s', cert)
2476 o.write(' Version: {}\n'.format(version))
2477 o.write(' Public key (sha1): {}\n'.format(
2478 hashlib.sha1(public_key).hexdigest()))
2479 o.write(' Subject: {}\n'.format(subject.hex()))
2480 o.write(' Usage: {}\n'.format(usage.hex()))
2481 o.write(' Key version: {}\n'.format(key_version))
2482
2483 o.write(' Product Intermediate Key:\n')
2484 print_atx_certificate(pik)
2485 o.write(' Product Signing Key:\n')
2486 print_atx_certificate(psk)
2487
Jan Monscheeb28b62019-12-05 16:17:09 +01002488 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2489 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002490 """Implements the 'verify_image' command.
2491
2492 Arguments:
2493 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002494 key_path: None or check that embedded public key matches key at given
2495 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002496 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002497 follow_chain_partitions:
2498 If True, will follows chain partitions even when not specified with
2499 the --expected_chain_partition option
2500 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2501 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002502
2503 Raises:
2504 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002505 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002506 expected_chain_partitions_map = {}
2507 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002508 for cp in expected_chain_partitions:
2509 cp_tokens = cp.split(':')
2510 if len(cp_tokens) != 3:
2511 raise AvbError('Malformed chained partition "{}".'.format(cp))
2512 partition_name = cp_tokens[0]
2513 rollback_index_location = int(cp_tokens[1])
2514 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002515 with open(file_path, 'rb') as f:
2516 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002517 expected_chain_partitions_map[partition_name] = (
2518 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002519
2520 image_dir = os.path.dirname(image_filename)
2521 image_ext = os.path.splitext(image_filename)[1]
2522
2523 key_blob = None
2524 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002525 print('Verifying image {} using key at {}'.format(image_filename,
2526 key_path))
Jan Monsch9c130122020-04-14 13:43:51 +02002527 key_blob = RSAPublicKey(key_path).encode()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002528 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002529 print('Verifying image {} using embedded public key'.format(
2530 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002531
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002532 image = ImageHandler(image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002533 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002534 offset = 0
2535 if footer:
2536 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002537
David Zeuthenb623d8b2017-04-04 16:05:53 -04002538 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002539 vbmeta_blob = image.read(header.SIZE
2540 + header.authentication_data_block_size
2541 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002542
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002543 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002544 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002545 raise AvbError('Signature check failed for {} vbmeta struct {}'
2546 .format(alg_name, image_filename))
2547
2548 if key_blob:
2549 # The embedded public key is in the auxiliary block at an offset.
2550 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002551 key_offset += header.authentication_data_block_size
2552 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002553 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2554 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002555 if key_blob != key_blob_in_vbmeta:
2556 raise AvbError('Embedded public key does not match given key.')
2557
2558 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002559 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2560 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002561 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002562 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2563 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002564
2565 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002566 if (isinstance(desc, AvbChainPartitionDescriptor)
2567 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002568 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002569 # In this case we're processing a chain descriptor but don't have a
2570 # --expect_chain_partition ... however --follow_chain_partitions was
2571 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002572 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2573 'and KEY (which has sha1 {}) not specified'
2574 .format(desc.partition_name, desc.rollback_index_location,
2575 hashlib.sha1(desc.public_key).hexdigest()))
2576 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002577 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002578 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002579 # Honor --follow_chain_partitions - add '--' to make the output more
2580 # readable.
2581 if (isinstance(desc, AvbChainPartitionDescriptor)
2582 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002583 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002584 chained_image_filename = os.path.join(image_dir,
2585 desc.partition_name + image_ext)
2586 self.verify_image(chained_image_filename, key_path, None, False,
2587 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002588
David Zeuthen34b6b492020-04-13 14:45:02 -04002589 def print_partition_digests(self, image_filename, output, as_json):
2590 """Implements the 'print_partition_digests' command.
2591
2592 Arguments:
2593 image_filename: Image file to get information from (file object).
2594 output: Output file to write human-readable information to (file object).
2595 as_json: If True, print information as JSON
2596
2597 Raises:
2598 AvbError: If getting the partition digests from the image fails.
2599 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002600 image_dir = os.path.dirname(image_filename)
2601 image_ext = os.path.splitext(image_filename)[1]
2602 json_partitions = None
2603 if as_json:
2604 json_partitions = []
Jan Monschcc9939a2020-04-16 09:15:20 +02002605 self._print_partition_digests(
2606 image_filename, output, json_partitions, image_dir, image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002607 if as_json:
2608 output.write(json.dumps({'partitions': json_partitions}, indent=2))
2609
Jan Monschcc9939a2020-04-16 09:15:20 +02002610 def _print_partition_digests(self, image_filename, output, json_partitions,
2611 image_dir, image_ext):
David Zeuthen34b6b492020-04-13 14:45:02 -04002612 """Helper for printing partitions.
2613
2614 Arguments:
2615 image_filename: Image file to get information from (file object).
2616 output: Output file to write human-readable information to (file object).
Jan Monschcc9939a2020-04-16 09:15:20 +02002617 json_partitions: If not None, don't print to output, instead add partition
2618 information to this list.
David Zeuthen34b6b492020-04-13 14:45:02 -04002619 image_dir: The directory to use when looking for chained partition files.
2620 image_ext: The extension to use for chained partition files.
2621
2622 Raises:
2623 AvbError: If getting the partition digests from the image fails.
2624 """
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002625 image = ImageHandler(image_filename, read_only=True)
David Zeuthen34b6b492020-04-13 14:45:02 -04002626 (_, _, descriptors, _) = self._parse_image(image)
2627
2628 for desc in descriptors:
2629 if isinstance(desc, AvbHashDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002630 digest = desc.digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002631 if json_partitions is not None:
2632 json_partitions.append({'name': desc.partition_name,
2633 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002634 else:
2635 output.write('{}: {}\n'.format(desc.partition_name, digest))
2636 elif isinstance(desc, AvbHashtreeDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002637 digest = desc.root_digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002638 if json_partitions is not None:
2639 json_partitions.append({'name': desc.partition_name,
2640 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002641 else:
2642 output.write('{}: {}\n'.format(desc.partition_name, digest))
2643 elif isinstance(desc, AvbChainPartitionDescriptor):
2644 chained_image_filename = os.path.join(image_dir,
2645 desc.partition_name + image_ext)
Jan Monschcc9939a2020-04-16 09:15:20 +02002646 self._print_partition_digests(
2647 chained_image_filename, output, json_partitions, image_dir,
2648 image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002649
David Zeuthenb8643c02018-05-17 17:21:18 -04002650 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2651 """Implements the 'calculate_vbmeta_digest' command.
2652
2653 Arguments:
2654 image_filename: Image file to get information from (file object).
2655 hash_algorithm: Hash algorithm used.
2656 output: Output file to write human-readable information to (file object).
2657 """
2658
2659 image_dir = os.path.dirname(image_filename)
2660 image_ext = os.path.splitext(image_filename)[1]
2661
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002662 image = ImageHandler(image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002663 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002664 offset = 0
2665 if footer:
2666 offset = footer.vbmeta_offset
2667 size = (header.SIZE + header.authentication_data_block_size +
2668 header.auxiliary_data_block_size)
2669 image.seek(offset)
2670 vbmeta_blob = image.read(size)
2671
Jan Monsch6f27bb12020-04-07 07:33:26 +02002672 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002673 hasher.update(vbmeta_blob)
2674
2675 for desc in descriptors:
2676 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002677 ch_image_filename = os.path.join(image_dir,
2678 desc.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002679 ch_image = ImageHandler(ch_image_filename, read_only=True)
Jan Monsch77cd2022019-12-10 17:18:04 +01002680 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002681 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002682 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2683 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002684 if ch_footer:
2685 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002686 ch_image.seek(ch_offset)
2687 ch_vbmeta_blob = ch_image.read(ch_size)
2688 hasher.update(ch_vbmeta_blob)
2689
2690 digest = hasher.digest()
Jan Monsch25040d92020-04-22 22:48:20 +02002691 output.write('{}\n'.format(digest.hex()))
David Zeuthenb8643c02018-05-17 17:21:18 -04002692
David Zeuthenf7d2e752018-09-20 13:30:41 -04002693 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2694 """Implements the 'calculate_kernel_cmdline' command.
2695
2696 Arguments:
2697 image_filename: Image file to get information from (file object).
2698 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2699 output: Output file to write human-readable information to (file object).
2700 """
2701
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002702 image = ImageHandler(image_filename, read_only=True)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002703 _, _, descriptors, _ = self._parse_image(image)
2704
2705 image_dir = os.path.dirname(image_filename)
2706 image_ext = os.path.splitext(image_filename)[1]
2707
2708 cmdline_descriptors = []
2709 for desc in descriptors:
2710 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002711 ch_image_filename = os.path.join(image_dir,
2712 desc.partition_name + image_ext)
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002713 ch_image = ImageHandler(ch_image_filename, read_only=True)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002714 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2715 for ch_desc in ch_descriptors:
2716 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2717 cmdline_descriptors.append(ch_desc)
2718 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2719 cmdline_descriptors.append(desc)
2720
2721 kernel_cmdline_snippets = []
2722 for desc in cmdline_descriptors:
2723 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002724 if ((desc.flags &
2725 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2726 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002727 if hashtree_disabled:
2728 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002729 if (desc.flags &
2730 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002731 if not hashtree_disabled:
2732 use_cmdline = False
2733 if use_cmdline:
2734 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2735 output.write(' '.join(kernel_cmdline_snippets))
2736
David Zeuthen21e95262016-07-27 17:58:40 -04002737 def _parse_image(self, image):
2738 """Gets information about an image.
2739
2740 The image can either be a vbmeta or an image with a footer.
2741
2742 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002743 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002744
2745 Returns:
2746 A tuple where the first argument is a AvbFooter (None if there
2747 is no footer on the image), the second argument is a
2748 AvbVBMetaHeader, the third argument is a list of
2749 AvbDescriptor-derived instances, and the fourth argument is the
2750 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002751
2752 Raises:
2753 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002754 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002755 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002756 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002757 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002758 try:
2759 footer = AvbFooter(image.read(AvbFooter.SIZE))
2760 except (LookupError, struct.error):
2761 # Nope, just seek back to the start.
2762 image.seek(0)
2763
2764 vbmeta_offset = 0
2765 if footer:
2766 vbmeta_offset = footer.vbmeta_offset
2767
2768 image.seek(vbmeta_offset)
2769 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2770
2771 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2772 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2773 desc_start_offset = aux_block_offset + h.descriptors_offset
2774 image.seek(desc_start_offset)
2775 descriptors = parse_descriptors(image.read(h.descriptors_size))
2776
David Zeuthen09692692016-09-30 16:16:40 -04002777 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002778
David Zeuthenb1b994d2017-03-06 18:01:31 -05002779 def _load_vbmeta_blob(self, image):
2780 """Gets the vbmeta struct and associated sections.
2781
2782 The image can either be a vbmeta.img or an image with a footer.
2783
2784 Arguments:
2785 image: An ImageHandler (vbmeta or footer).
2786
2787 Returns:
2788 A blob with the vbmeta struct and other sections.
2789 """
2790 assert isinstance(image, ImageHandler)
2791 footer = None
2792 image.seek(image.image_size - AvbFooter.SIZE)
2793 try:
2794 footer = AvbFooter(image.read(AvbFooter.SIZE))
2795 except (LookupError, struct.error):
2796 # Nope, just seek back to the start.
2797 image.seek(0)
2798
2799 vbmeta_offset = 0
2800 if footer:
2801 vbmeta_offset = footer.vbmeta_offset
2802
2803 image.seek(vbmeta_offset)
2804 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2805
2806 image.seek(vbmeta_offset)
2807 data_size = AvbVBMetaHeader.SIZE
2808 data_size += h.authentication_data_block_size
2809 data_size += h.auxiliary_data_block_size
2810 return image.read(data_size)
2811
David Zeuthen73f2afa2017-05-17 16:54:11 -04002812 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002813 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002814
2815 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002816 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002817
2818 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002819 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2820 instructions. There is one for when hashtree is not disabled and one for
2821 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002822
David Zeuthen21e95262016-07-27 17:58:40 -04002823 """
David Zeuthen21e95262016-07-27 17:58:40 -04002824 c = 'dm="1 vroot none ro 1,'
Jan Monsch25040d92020-04-22 22:48:20 +02002825 c += '0' # start
2826 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
2827 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2828 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2829 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2830 c += ' {}'.format(ht.data_block_size) # data_block
2831 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002832 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2833 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
Jan Monsch25040d92020-04-22 22:48:20 +02002834 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2835 c += ' {}'.format(ht.root_digest.hex()) # root_digest
2836 c += ' {}'.format(ht.salt.hex()) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002837 if ht.fec_num_roots > 0:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09002838 if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE:
2839 c += ' 11' # number of optional args
2840 c += ' check_at_most_once'
2841 else:
2842 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002843 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002844 c += ' ignore_zero_blocks'
2845 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2846 c += ' fec_roots {}'.format(ht.fec_num_roots)
2847 # Note that fec_blocks is the size that FEC covers, *not* the
2848 # size of the FEC data. Since we use FEC for everything up until
2849 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002850 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2851 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002852 else:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09002853 if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE:
2854 c += ' 3' # number of optional args
2855 c += ' check_at_most_once'
2856 else:
2857 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002858 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002859 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002860 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002861
David Zeuthenfd41eb92016-11-17 12:24:47 -05002862 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002863 desc = AvbKernelCmdlineDescriptor()
2864 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002865 desc.flags = (
2866 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2867
2868 # The descriptor for when hashtree verification is disabled is a lot
2869 # simpler - we just set the root to the partition.
2870 desc_no_ht = AvbKernelCmdlineDescriptor()
2871 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2872 desc_no_ht.flags = (
2873 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2874
2875 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002876
David Zeuthen73f2afa2017-05-17 16:54:11 -04002877 def _get_cmdline_descriptors_for_dm_verity(self, image):
2878 """Generate kernel cmdline descriptors for dm-verity.
2879
2880 Arguments:
2881 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2882
2883 Returns:
2884 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2885 instructions. There is one for when hashtree is not disabled and one for
2886 when it is.
2887
2888 Raises:
2889 AvbError: If |image| doesn't have a hashtree descriptor.
2890
2891 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002892 (_, _, descriptors, _) = self._parse_image(image)
2893
2894 ht = None
2895 for desc in descriptors:
2896 if isinstance(desc, AvbHashtreeDescriptor):
2897 ht = desc
2898 break
2899
2900 if not ht:
2901 raise AvbError('No hashtree descriptor in given image')
2902
2903 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2904
David Zeuthen21e95262016-07-27 17:58:40 -04002905 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002906 key_path, public_key_metadata_path, rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07002907 flags, rollback_index_location,
2908 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002909 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002910 include_descriptors_from_image,
2911 signing_helper,
2912 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002913 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002914 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002915 print_required_libavb_version,
2916 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002917 """Implements the 'make_vbmeta_image' command.
2918
2919 Arguments:
2920 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002921 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002922 algorithm_name: Name of algorithm to use.
2923 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002924 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002925 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002926 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002927 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002928 props: Properties to insert (list of strings of the form 'key:value').
2929 props_from_file: Properties to insert (list of strings 'key:<path>').
2930 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002931 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002932 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002933 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002934 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002935 release_string: None or avbtool release string to use instead of default.
2936 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002937 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002938 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002939
2940 Raises:
2941 AvbError: If a chained partition is malformed.
2942 """
David Zeuthen1097a782017-05-31 15:53:17 -04002943 # If we're asked to calculate minimum required libavb version, we're done.
Varun Sharmade538272020-04-10 15:22:31 -07002944 tmp_header = AvbVBMetaHeader()
2945 if rollback_index_location > 0:
2946 tmp_header.bump_required_libavb_version_minor(2)
2947 if include_descriptors_from_image:
2948 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2949 # version of all included descriptors.
2950 for image in include_descriptors_from_image:
Jan Monsch4e71bfd2020-04-27 22:44:37 +02002951 (_, image_header, _, _) = self._parse_image(ImageHandler(
2952 image.name, read_only=True))
Varun Sharmade538272020-04-10 15:22:31 -07002953 tmp_header.bump_required_libavb_version_minor(
2954 image_header.required_libavb_version_minor)
2955
David Zeuthen1097a782017-05-31 15:53:17 -04002956 if print_required_libavb_version:
Varun Sharmade538272020-04-10 15:22:31 -07002957 print('1.{}'.format(tmp_header.required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04002958 return
2959
2960 if not output:
2961 raise AvbError('No output file given')
2962
David Zeuthen21e95262016-07-27 17:58:40 -04002963 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002964 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002965 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002966 algorithm_name, key_path, public_key_metadata_path, descriptors,
Varun Sharmade538272020-04-10 15:22:31 -07002967 chain_partitions, rollback_index, flags, rollback_index_location,
2968 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002969 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002970 include_descriptors_from_image, signing_helper,
2971 signing_helper_with_files, release_string,
Varun Sharmade538272020-04-10 15:22:31 -07002972 append_to_release_string, tmp_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002973
2974 # Write entire vbmeta blob (header, authentication, auxiliary).
2975 output.seek(0)
2976 output.write(vbmeta_blob)
2977
David Zeuthen97cb5802017-06-01 16:14:05 -04002978 if padding_size > 0:
2979 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2980 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002981 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002982
David Zeuthen18666ab2016-11-15 11:18:05 -05002983 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2984 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002985 chain_partitions,
Varun Sharmade538272020-04-10 15:22:31 -07002986 rollback_index, flags, rollback_index_location,
2987 props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002988 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002989 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002990 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002991 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002992 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002993 release_string, append_to_release_string,
2994 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002995 """Generates a VBMeta blob.
2996
2997 This blob contains the header (struct AvbVBMetaHeader), the
2998 authentication data block (which contains the hash and signature
2999 for the header and auxiliary block), and the auxiliary block
3000 (which contains descriptors, the public key used, and other data).
3001
3002 The |key| parameter can |None| only if the |algorithm_name| is
3003 'NONE'.
3004
3005 Arguments:
3006 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3007 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003008 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003009 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003010 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003011 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003012 flags: Flags to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003013 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003014 props: Properties to insert (List of strings of the form 'key:value').
3015 props_from_file: Properties to insert (List of strings 'key:<path>').
3016 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003017 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003018 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003019 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3020 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003021 include_descriptors_from_image: List of file objects for which
3022 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003023 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003024 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003025 release_string: None or avbtool release string.
3026 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003027 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003028
3029 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003030 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003031
3032 Raises:
3033 Exception: If the |algorithm_name| is not found, if no key has
3034 been given and the given algorithm requires one, or the key is
3035 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04003036 """
3037 try:
3038 alg = ALGORITHMS[algorithm_name]
3039 except KeyError:
3040 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3041
David Zeuthena5fd3a42017-02-27 16:38:54 -05003042 if not descriptors:
3043 descriptors = []
3044
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003045 h = AvbVBMetaHeader()
3046 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3047
David Zeuthena5fd3a42017-02-27 16:38:54 -05003048 # Insert chained partition descriptors, if any
3049 if chain_partitions:
Varun Sharmade538272020-04-10 15:22:31 -07003050 used_locations = {rollback_index_location: True}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003051 for cp in chain_partitions:
3052 cp_tokens = cp.split(':')
3053 if len(cp_tokens) != 3:
3054 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003055 partition_name = cp_tokens[0]
Varun Sharmade538272020-04-10 15:22:31 -07003056 chained_rollback_index_location = int(cp_tokens[1])
David Zeuthend8e48582017-04-21 11:31:51 -04003057 file_path = cp_tokens[2]
3058 # Check that the same rollback location isn't being used by
3059 # multiple chained partitions.
Varun Sharmade538272020-04-10 15:22:31 -07003060 if used_locations.get(chained_rollback_index_location):
David Zeuthend8e48582017-04-21 11:31:51 -04003061 raise AvbError('Rollback Index Location {} is already in use.'.format(
Varun Sharmade538272020-04-10 15:22:31 -07003062 chained_rollback_index_location))
3063 used_locations[chained_rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003064 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003065 desc.partition_name = partition_name
Varun Sharmade538272020-04-10 15:22:31 -07003066 desc.rollback_index_location = chained_rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003067 if desc.rollback_index_location < 1:
3068 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003069 with open(file_path, 'rb') as f:
3070 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003071 descriptors.append(desc)
3072
David Zeuthen21e95262016-07-27 17:58:40 -04003073 # Descriptors.
3074 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003075 for desc in descriptors:
3076 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003077
3078 # Add properties.
3079 if props:
3080 for prop in props:
3081 idx = prop.find(':')
3082 if idx == -1:
3083 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003084 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003085 desc = AvbPropertyDescriptor()
3086 desc.key = prop[0:idx]
Jan Monschee6fccd2020-04-09 19:36:13 +02003087 desc.value = prop[(idx + 1):].encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04003088 encoded_descriptors.extend(desc.encode())
3089 if props_from_file:
3090 for prop in props_from_file:
3091 idx = prop.find(':')
3092 if idx == -1:
3093 raise AvbError('Malformed property "{}".'.format(prop))
3094 desc = AvbPropertyDescriptor()
3095 desc.key = prop[0:idx]
David Zeuthen21e95262016-07-27 17:58:40 -04003096 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02003097 with open(file_path, 'rb') as f:
Jan Monsch9c130122020-04-14 13:43:51 +02003098 # pylint: disable=attribute-defined-outside-init
Jan Monschb1d920f2020-04-09 12:59:28 +02003099 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04003100 encoded_descriptors.extend(desc.encode())
3101
David Zeuthen73f2afa2017-05-17 16:54:11 -04003102 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003103 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003104 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003105 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003106 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3107 encoded_descriptors.extend(cmdline_desc[0].encode())
3108 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003109
David Zeuthen73f2afa2017-05-17 16:54:11 -04003110 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3111 if ht_desc_to_setup:
3112 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3113 ht_desc_to_setup)
3114 encoded_descriptors.extend(cmdline_desc[0].encode())
3115 encoded_descriptors.extend(cmdline_desc[1].encode())
3116
David Zeuthen21e95262016-07-27 17:58:40 -04003117 # Add kernel command-lines.
3118 if kernel_cmdlines:
3119 for i in kernel_cmdlines:
3120 desc = AvbKernelCmdlineDescriptor()
3121 desc.kernel_cmdline = i
3122 encoded_descriptors.extend(desc.encode())
3123
3124 # Add descriptors from other images.
3125 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003126 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003127 for image in include_descriptors_from_image:
Alex Leggd6f4c392020-05-08 12:29:27 +10003128 image_handler = ImageHandler(image.name, read_only=True)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003129 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3130 image_handler)
3131 # Bump the required libavb version to support all included descriptors.
3132 h.bump_required_libavb_version_minor(
3133 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003134 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003135 # The --include_descriptors_from_image option is used in some setups
3136 # with images A and B where both A and B contain a descriptor
3137 # for a partition with the same name. Since it's not meaningful
3138 # to include both descriptors, only include the last seen descriptor.
3139 # See bug 76386656 for details.
3140 if hasattr(desc, 'partition_name'):
3141 key = type(desc).__name__ + '_' + desc.partition_name
3142 descriptors_dict[key] = desc.encode()
3143 else:
3144 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003145 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003146 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003147
David Zeuthen18666ab2016-11-15 11:18:05 -05003148 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003149 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05003150 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003151 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05003152 pkmd_blob = f.read()
3153
David Zeuthen21e95262016-07-27 17:58:40 -04003154 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02003155 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003156 if alg.public_key_num_bytes > 0:
3157 if not key_path:
3158 raise AvbError('Key is required for algorithm {}'.format(
3159 algorithm_name))
Jan Monsch9c130122020-04-14 13:43:51 +02003160 encoded_key = RSAPublicKey(key_path).encode()
David Zeuthen21e95262016-07-27 17:58:40 -04003161 if len(encoded_key) != alg.public_key_num_bytes:
3162 raise AvbError('Key is wrong size for algorithm {}'.format(
3163 algorithm_name))
3164
David Zeuthene3cadca2017-02-22 21:25:46 -05003165 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003166 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003167 h.release_string = release_string
3168
3169 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003170 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003171 h.release_string += ' ' + append_to_release_string
3172
David Zeuthen18666ab2016-11-15 11:18:05 -05003173 # For the Auxiliary data block, descriptors are stored at offset 0,
3174 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003175 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003176 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003177 h.descriptors_offset = 0
3178 h.descriptors_size = len(encoded_descriptors)
3179 h.public_key_offset = h.descriptors_size
3180 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003181 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3182 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003183
3184 # For the Authentication data block, the hash is first and then
3185 # the signature.
3186 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003187 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003188 h.algorithm_type = alg.algorithm_type
3189 h.hash_offset = 0
3190 h.hash_size = alg.hash_num_bytes
3191 # Signature offset and size - it's stored right after the hash
3192 # (in Authentication data block).
3193 h.signature_offset = alg.hash_num_bytes
3194 h.signature_size = alg.signature_num_bytes
3195
3196 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003197 h.flags = flags
Varun Sharmade538272020-04-10 15:22:31 -07003198 h.rollback_index_location = rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04003199
3200 # Generate Header data block.
3201 header_data_blob = h.encode()
3202
3203 # Generate Auxiliary data block.
3204 aux_data_blob = bytearray()
3205 aux_data_blob.extend(encoded_descriptors)
3206 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003207 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003208 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003209 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003210
3211 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003212 binary_hash = b''
3213 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003214 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003215 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003216 ha.update(header_data_blob)
3217 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003218 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003219
3220 # Calculate the signature.
Jan Monsch9c130122020-04-14 13:43:51 +02003221 rsa_key = RSAPublicKey(key_path)
Jan Monsche6dac432020-04-24 13:10:26 +02003222 data_to_sign = header_data_blob + bytes(aux_data_blob)
Jan Monsch9c130122020-04-14 13:43:51 +02003223 binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
3224 signing_helper, signing_helper_with_files)
David Zeuthen21e95262016-07-27 17:58:40 -04003225
3226 # Generate Authentication data block.
3227 auth_data_blob = bytearray()
3228 auth_data_blob.extend(binary_hash)
3229 auth_data_blob.extend(binary_signature)
3230 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003231 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003232
Jan Monschb1d920f2020-04-09 12:59:28 +02003233 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003234
3235 def extract_public_key(self, key_path, output):
3236 """Implements the 'extract_public_key' command.
3237
3238 Arguments:
3239 key_path: The path to a RSA private key file.
3240 output: The file to write to.
Jan Monsch9c130122020-04-14 13:43:51 +02003241
3242 Raises:
3243 AvbError: If the public key could not be extracted.
David Zeuthen21e95262016-07-27 17:58:40 -04003244 """
Jan Monsch9c130122020-04-14 13:43:51 +02003245 output.write(RSAPublicKey(key_path).encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003246
David Zeuthenb1b994d2017-03-06 18:01:31 -05003247 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3248 partition_size):
3249 """Implementation of the append_vbmeta_image command.
3250
3251 Arguments:
3252 image_filename: File to add the footer to.
3253 vbmeta_image_filename: File to get vbmeta struct from.
3254 partition_size: Size of partition.
3255
3256 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003257 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003258 """
3259 image = ImageHandler(image_filename)
3260
3261 if partition_size % image.block_size != 0:
3262 raise AvbError('Partition size of {} is not a multiple of the image '
3263 'block size {}.'.format(partition_size,
3264 image.block_size))
3265
3266 # If there's already a footer, truncate the image to its original
3267 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003268 if image.image_size >= AvbFooter.SIZE:
3269 image.seek(image.image_size - AvbFooter.SIZE)
3270 try:
3271 footer = AvbFooter(image.read(AvbFooter.SIZE))
3272 # Existing footer found. Just truncate.
3273 original_image_size = footer.original_image_size
3274 image.truncate(footer.original_image_size)
3275 except (LookupError, struct.error):
3276 original_image_size = image.image_size
3277 else:
3278 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003279 original_image_size = image.image_size
3280
3281 # If anything goes wrong from here-on, restore the image back to
3282 # its original size.
3283 try:
3284 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3285 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3286
3287 # If the image isn't sparse, its size might not be a multiple of
3288 # the block size. This will screw up padding later so just grow it.
3289 if image.image_size % image.block_size != 0:
3290 assert not image.is_sparse
3291 padding_needed = image.block_size - (image.image_size%image.block_size)
3292 image.truncate(image.image_size + padding_needed)
3293
3294 # The append_raw() method requires content with size being a
3295 # multiple of |block_size| so add padding as needed. Also record
3296 # where this is written to since we'll need to put that in the
3297 # footer.
3298 vbmeta_offset = image.image_size
3299 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3300 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003301 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003302
3303 # Append vbmeta blob and footer
3304 image.append_raw(vbmeta_blob_with_padding)
3305 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3306
3307 # Now insert a DONT_CARE chunk with enough bytes such that the
3308 # final Footer block is at the end of partition_size..
3309 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003310 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003311
3312 # Generate the Footer that tells where the VBMeta footer
3313 # is. Also put enough padding in the front of the footer since
3314 # we'll write out an entire block.
3315 footer = AvbFooter()
3316 footer.original_image_size = original_image_size
3317 footer.vbmeta_offset = vbmeta_offset
3318 footer.vbmeta_size = len(vbmeta_blob)
3319 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003320 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003321 footer_blob)
3322 image.append_raw(footer_blob_with_padding)
3323
Jan Monschb1d920f2020-04-09 12:59:28 +02003324 except Exception as e:
3325 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003326 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003327 raise AvbError('Appending VBMeta image failed: {}.'.format(e))
David Zeuthenb1b994d2017-03-06 18:01:31 -05003328
David Zeuthena4fee8b2016-08-22 15:20:43 -04003329 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003330 hash_algorithm, salt, chain_partitions, algorithm_name,
3331 key_path,
Varun Sharmade538272020-04-10 15:22:31 -07003332 public_key_metadata_path, rollback_index, flags,
3333 rollback_index_location, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003334 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003335 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003336 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003337 signing_helper, signing_helper_with_files,
3338 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003339 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003340 print_required_libavb_version, use_persistent_digest,
3341 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003342 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003343
3344 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003345 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003346 partition_size: Size of partition.
3347 partition_name: Name of partition (without A/B suffix).
3348 hash_algorithm: Hash algorithm to use.
3349 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003350 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003351 algorithm_name: Name of algorithm to use.
3352 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003353 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003354 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003355 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003356 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003357 props: Properties to insert (List of strings of the form 'key:value').
3358 props_from_file: Properties to insert (List of strings 'key:<path>').
3359 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003360 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003361 dm-verity kernel cmdline from.
3362 include_descriptors_from_image: List of file objects for which
3363 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003364 calc_max_image_size: Don't store the footer - instead calculate the
3365 maximum image size leaving enough room for metadata with the
3366 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003367 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003368 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003369 release_string: None or avbtool release string.
3370 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003371 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3372 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003373 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003374 use_persistent_digest: Use a persistent digest on device.
3375 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003376
3377 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003378 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003379 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003380 required_libavb_version_minor = 0
3381 if use_persistent_digest or do_not_use_ab:
3382 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003383 if rollback_index_location > 0:
3384 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003385
David Zeuthen1097a782017-05-31 15:53:17 -04003386 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003387 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003388 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003389 return
3390
David Zeuthenbf562452017-05-17 18:04:43 -04003391 # First, calculate the maximum image size such that an image
3392 # this size + metadata (footer + vbmeta struct) fits in
3393 # |partition_size|.
3394 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003395 if partition_size < max_metadata_size:
3396 raise AvbError('Parition size of {} is too small. '
3397 'Needs to be at least {}'.format(
3398 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003399 max_image_size = partition_size - max_metadata_size
3400
3401 # If we're asked to only calculate the maximum image size, we're done.
3402 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003403 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003404 return
3405
David Zeuthena4fee8b2016-08-22 15:20:43 -04003406 image = ImageHandler(image_filename)
3407
3408 if partition_size % image.block_size != 0:
3409 raise AvbError('Partition size of {} is not a multiple of the image '
3410 'block size {}.'.format(partition_size,
3411 image.block_size))
3412
David Zeuthen21e95262016-07-27 17:58:40 -04003413 # If there's already a footer, truncate the image to its original
3414 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3415 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003416 if image.image_size >= AvbFooter.SIZE:
3417 image.seek(image.image_size - AvbFooter.SIZE)
3418 try:
3419 footer = AvbFooter(image.read(AvbFooter.SIZE))
3420 # Existing footer found. Just truncate.
3421 original_image_size = footer.original_image_size
3422 image.truncate(footer.original_image_size)
3423 except (LookupError, struct.error):
3424 original_image_size = image.image_size
3425 else:
3426 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003427 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003428
3429 # If anything goes wrong from here-on, restore the image back to
3430 # its original size.
3431 try:
David Zeuthen09692692016-09-30 16:16:40 -04003432 # If image size exceeds the maximum image size, fail.
3433 if image.image_size > max_image_size:
3434 raise AvbError('Image size of {} exceeds maximum image '
3435 'size of {} in order to fit in a partition '
3436 'size of {}.'.format(image.image_size, max_image_size,
3437 partition_size))
3438
Jan Monsch6f27bb12020-04-07 07:33:26 +02003439 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003440 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003441 salt = binascii.unhexlify(salt)
3442 elif salt is None and not use_persistent_digest:
3443 # If salt is not explicitly specified, choose a hash that's the same
3444 # size as the hash size. Don't populate a random salt if this
3445 # descriptor is being created to use a persistent digest on device.
3446 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003447 with open('/dev/urandom', 'rb') as f:
3448 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003449 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003450 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003451
Jan Monsch6f27bb12020-04-07 07:33:26 +02003452 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003453 # TODO(zeuthen): might want to read this in chunks to avoid
3454 # memory pressure, then again, this is only supposed to be used
3455 # on kernel/initramfs partitions. Possible optimization.
3456 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003457 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003458 digest = hasher.digest()
3459
3460 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003461 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003462 h_desc.hash_algorithm = hash_algorithm
3463 h_desc.partition_name = partition_name
3464 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003465 h_desc.flags = 0
3466 if do_not_use_ab:
3467 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3468 if not use_persistent_digest:
3469 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003470
3471 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003472 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003473 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003474 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003475 chain_partitions, rollback_index, flags, rollback_index_location,
3476 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003477 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003478 include_descriptors_from_image, signing_helper,
3479 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003480 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003481
David Zeuthend247fcb2017-02-16 12:09:27 -05003482 # Write vbmeta blob, if requested.
3483 if output_vbmeta_image:
3484 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003485
David Zeuthend247fcb2017-02-16 12:09:27 -05003486 # Append vbmeta blob and footer, unless requested not to.
3487 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003488 # If the image isn't sparse, its size might not be a multiple of
3489 # the block size. This will screw up padding later so just grow it.
3490 if image.image_size % image.block_size != 0:
3491 assert not image.is_sparse
3492 padding_needed = image.block_size - (
3493 image.image_size % image.block_size)
3494 image.truncate(image.image_size + padding_needed)
3495
3496 # The append_raw() method requires content with size being a
3497 # multiple of |block_size| so add padding as needed. Also record
3498 # where this is written to since we'll need to put that in the
3499 # footer.
3500 vbmeta_offset = image.image_size
3501 padding_needed = (
3502 round_to_multiple(len(vbmeta_blob), image.block_size) -
3503 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003504 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003505
David Zeuthend247fcb2017-02-16 12:09:27 -05003506 image.append_raw(vbmeta_blob_with_padding)
3507 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3508
3509 # Now insert a DONT_CARE chunk with enough bytes such that the
3510 # final Footer block is at the end of partition_size..
3511 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003512 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003513
3514 # Generate the Footer that tells where the VBMeta footer
3515 # is. Also put enough padding in the front of the footer since
3516 # we'll write out an entire block.
3517 footer = AvbFooter()
3518 footer.original_image_size = original_image_size
3519 footer.vbmeta_offset = vbmeta_offset
3520 footer.vbmeta_size = len(vbmeta_blob)
3521 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003522 footer_blob_with_padding = (
3523 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003524 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003525 except Exception as e:
3526 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003527 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003528 raise AvbError('Adding hash_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003529
David Zeuthena4fee8b2016-08-22 15:20:43 -04003530 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003531 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003532 block_size, salt, chain_partitions, algorithm_name,
3533 key_path,
3534 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003535 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003536 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003537 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003538 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003539 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003540 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003541 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003542 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003543 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003544 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003545 use_persistent_root_digest, do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003546 no_hashtree, check_at_most_once):
David Zeuthen21e95262016-07-27 17:58:40 -04003547 """Implements the 'add_hashtree_footer' command.
3548
3549 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3550 more information about dm-verity and these hashes.
3551
3552 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003553 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003554 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003555 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003556 generate_fec: If True, generate FEC codes.
3557 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003558 hash_algorithm: Hash algorithm to use.
3559 block_size: Block size to use.
3560 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003561 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003562 algorithm_name: Name of algorithm to use.
3563 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003564 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003565 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003566 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003567 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003568 props: Properties to insert (List of strings of the form 'key:value').
3569 props_from_file: Properties to insert (List of strings 'key:<path>').
3570 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003571 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003572 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003573 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3574 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003575 include_descriptors_from_image: List of file objects for which
3576 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003577 calc_max_image_size: Don't store the hashtree or footer - instead
3578 calculate the maximum image size leaving enough room for hashtree
3579 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003580 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003581 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003582 release_string: None or avbtool release string.
3583 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003584 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3585 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003586 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003587 use_persistent_root_digest: Use a persistent root digest on device.
3588 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003589 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003590 check_at_most_once: Set to verify data blocks only the first time they are read
3591 from the data device.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003592
3593 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003594 AvbError: If an argument is incorrect or adding the hashtree footer
3595 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003596 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003597 required_libavb_version_minor = 0
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003598 if use_persistent_root_digest or do_not_use_ab or check_at_most_once:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003599 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003600 if rollback_index_location > 0:
3601 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003602
David Zeuthen1097a782017-05-31 15:53:17 -04003603 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003604 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003605 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003606 return
3607
Tianjie62ad0222021-02-22 14:31:12 -08003608 digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
3609 .digest())
David Zeuthen09692692016-09-30 16:16:40 -04003610 digest_padding = round_to_pow2(digest_size) - digest_size
3611
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003612 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3613 # size such that an image this size + the hashtree + metadata (footer +
3614 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3615 # for metadata.
3616 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003617 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003618 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003619 if not no_hashtree:
3620 (_, max_tree_size) = calc_hash_level_offsets(
3621 partition_size, block_size, digest_size + digest_padding)
3622 if generate_fec:
3623 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003624 max_metadata_size = (max_fec_size + max_tree_size +
3625 self.MAX_VBMETA_SIZE +
3626 self.MAX_FOOTER_SIZE)
3627 max_image_size = partition_size - max_metadata_size
3628 else:
3629 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003630
3631 # If we're asked to only calculate the maximum image size, we're done.
3632 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003633 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003634 return
3635
David Zeuthena4fee8b2016-08-22 15:20:43 -04003636 image = ImageHandler(image_filename)
3637
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003638 if partition_size > 0:
3639 if partition_size % image.block_size != 0:
3640 raise AvbError('Partition size of {} is not a multiple of the image '
3641 'block size {}.'.format(partition_size,
3642 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003643 elif image.image_size % image.block_size != 0:
3644 raise AvbError('File size of {} is not a multiple of the image '
3645 'block size {}.'.format(image.image_size,
3646 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003647
David Zeuthen21e95262016-07-27 17:58:40 -04003648 # If there's already a footer, truncate the image to its original
3649 # size. This way 'avbtool add_hashtree_footer' is idempotent
3650 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003651 if image.image_size >= AvbFooter.SIZE:
3652 image.seek(image.image_size - AvbFooter.SIZE)
3653 try:
3654 footer = AvbFooter(image.read(AvbFooter.SIZE))
3655 # Existing footer found. Just truncate.
3656 original_image_size = footer.original_image_size
3657 image.truncate(footer.original_image_size)
3658 except (LookupError, struct.error):
3659 original_image_size = image.image_size
3660 else:
3661 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003662 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003663
3664 # If anything goes wrong from here-on, restore the image back to
3665 # its original size.
3666 try:
3667 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003668 rounded_image_size = round_to_multiple(image.image_size, block_size)
3669 if rounded_image_size > image.image_size:
3670 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003671
David Zeuthen09692692016-09-30 16:16:40 -04003672 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003673 if partition_size > 0:
3674 if image.image_size > max_image_size:
3675 raise AvbError('Image size of {} exceeds maximum image '
3676 'size of {} in order to fit in a partition '
3677 'size of {}.'.format(image.image_size, max_image_size,
3678 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003679
3680 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003681 salt = binascii.unhexlify(salt)
3682 elif salt is None and not use_persistent_root_digest:
3683 # If salt is not explicitly specified, choose a hash that's the same
3684 # size as the hash size. Don't populate a random salt if this
3685 # descriptor is being created to use a persistent digest on device.
3686 hash_size = digest_size
Jan Monsch13efb5f2020-04-22 17:34:24 +02003687 with open('/dev/urandom', 'rb') as f:
Jan Monschb1d920f2020-04-09 12:59:28 +02003688 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003689 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003690 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003691
David Zeuthena4fee8b2016-08-22 15:20:43 -04003692 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003693 # offsets in advance.
3694 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003695 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003696
David Zeuthena4fee8b2016-08-22 15:20:43 -04003697 # If the image isn't sparse, its size might not be a multiple of
3698 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003699 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003700 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003701 padding_needed = image.block_size - (image.image_size%image.block_size)
3702 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003703
David Zeuthena4fee8b2016-08-22 15:20:43 -04003704 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003705 tree_offset = image.image_size
3706 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003707 block_size,
3708 hash_algorithm, salt,
3709 digest_padding,
3710 hash_level_offsets,
3711 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003712
3713 # Generate HashtreeDescriptor with details about the tree we
3714 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003715 if no_hashtree:
3716 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003717 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003718 ht_desc = AvbHashtreeDescriptor()
3719 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003720 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003721 ht_desc.tree_offset = tree_offset
3722 ht_desc.tree_size = tree_size
3723 ht_desc.data_block_size = block_size
3724 ht_desc.hash_block_size = block_size
3725 ht_desc.hash_algorithm = hash_algorithm
3726 ht_desc.partition_name = partition_name
3727 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003728 if do_not_use_ab:
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003729 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003730 if not use_persistent_root_digest:
3731 ht_desc.root_digest = root_digest
JeongHyeon Lee2998a352021-05-25 16:38:07 +09003732 if check_at_most_once:
3733 ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE
David Zeuthen21e95262016-07-27 17:58:40 -04003734
David Zeuthen09692692016-09-30 16:16:40 -04003735 # Write the hash tree
3736 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3737 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003738 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003739 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003740 len_hashtree_and_fec = len(hash_tree_with_padding)
3741
3742 # Generate FEC codes, if requested.
3743 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003744 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003745 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003746 else:
3747 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003748 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3749 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003750 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003751 fec_offset = image.image_size
3752 image.append_raw(fec_data_with_padding)
3753 len_hashtree_and_fec += len(fec_data_with_padding)
3754 # Update the hashtree descriptor.
3755 ht_desc.fec_num_roots = fec_num_roots
3756 ht_desc.fec_offset = fec_offset
3757 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003758
David Zeuthen73f2afa2017-05-17 16:54:11 -04003759 ht_desc_to_setup = None
3760 if setup_as_rootfs_from_kernel:
3761 ht_desc_to_setup = ht_desc
3762
David Zeuthena4fee8b2016-08-22 15:20:43 -04003763 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003764 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003765 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003766 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003767 chain_partitions, rollback_index, flags, rollback_index_location,
3768 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003769 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003770 include_descriptors_from_image, signing_helper,
3771 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003772 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003773 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3774 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003775 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003776
David Zeuthend247fcb2017-02-16 12:09:27 -05003777 # Write vbmeta blob, if requested.
3778 if output_vbmeta_image:
3779 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003780
David Zeuthend247fcb2017-02-16 12:09:27 -05003781 # Append vbmeta blob and footer, unless requested not to.
3782 if not do_not_append_vbmeta_image:
3783 image.append_raw(vbmeta_blob_with_padding)
3784
3785 # Now insert a DONT_CARE chunk with enough bytes such that the
3786 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003787 if partition_size > 0:
3788 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003789 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003790
3791 # Generate the Footer that tells where the VBMeta footer
3792 # is. Also put enough padding in the front of the footer since
3793 # we'll write out an entire block.
3794 footer = AvbFooter()
3795 footer.original_image_size = original_image_size
3796 footer.vbmeta_offset = vbmeta_offset
3797 footer.vbmeta_size = len(vbmeta_blob)
3798 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003799 footer_blob_with_padding = (
3800 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003801 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003802
Jan Monschb1d920f2020-04-09 12:59:28 +02003803 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003804 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003805 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003806 raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003807
David Zeuthenc68f0822017-03-31 17:22:35 -04003808 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003809 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003810 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003811 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003812 """Implements the 'make_atx_certificate' command.
3813
3814 Android Things certificates are required for Android Things public key
3815 metadata. They chain the vbmeta signing key for a particular product back to
3816 a fused, permanent root key. These certificates are fixed-length and fixed-
3817 format with the explicit goal of not parsing ASN.1 in bootloader code.
3818
3819 Arguments:
3820 output: Certificate will be written to this file on success.
3821 authority_key_path: A PEM file path with the authority private key.
3822 If None, then a certificate will be created without a
3823 signature. The signature can be created out-of-band
3824 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003825 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003826 subject_key_version: A 64-bit version value. If this is None, the number
3827 of seconds since the epoch is used.
3828 subject: A subject identifier. For Product Signing Key certificates this
3829 should be the same Product ID found in the permanent attributes.
3830 is_intermediate_authority: True if the certificate is for an intermediate
3831 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003832 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003833 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003834 signing_helper_with_files: Same as signing_helper but uses files instead.
Jan Monsch9c130122020-04-14 13:43:51 +02003835
3836 Raises:
3837 AvbError: If there an error during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -08003838 """
3839 signed_data = bytearray()
3840 signed_data.extend(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003841 signed_data.extend(RSAPublicKey(subject_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003842 hasher = hashlib.sha256()
3843 hasher.update(subject)
3844 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003845 if not usage:
3846 usage = 'com.google.android.things.vboot'
3847 if is_intermediate_authority:
3848 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003849 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003850 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003851 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003852 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003853 subject_key_version = int(time.time())
3854 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003855 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003856 if authority_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003857 rsa_key = RSAPublicKey(authority_key_path)
Darren Krahn43e12d82017-02-24 16:26:31 -08003858 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003859 signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3860 signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08003861 output.write(signed_data)
3862 output.write(signature)
3863
David Zeuthenc68f0822017-03-31 17:22:35 -04003864 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003865 product_id):
3866 """Implements the 'make_atx_permanent_attributes' command.
3867
3868 Android Things permanent attributes are designed to be permanent for a
3869 particular product and a hash of these attributes should be fused into
3870 hardware to enforce this.
3871
3872 Arguments:
3873 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003874 root_authority_key_path: Path to a PEM or DER public key for
3875 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003876 product_id: A 16-byte Product ID.
3877
3878 Raises:
3879 AvbError: If an argument is incorrect.
3880 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003881 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003882 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003883 raise AvbError('Invalid Product ID length.')
3884 output.write(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003885 output.write(RSAPublicKey(root_authority_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003886 output.write(product_id)
3887
3888 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003889 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003890 """Implements the 'make_atx_metadata' command.
3891
3892 Android Things metadata are included in vbmeta images to facilitate
3893 verification. The output of this command can be used as the
3894 public_key_metadata argument to other commands.
3895
3896 Arguments:
3897 output: Metadata will be written to this file on success.
3898 intermediate_key_certificate: A certificate file as output by
3899 make_atx_certificate with
3900 is_intermediate_authority set to true.
3901 product_key_certificate: A certificate file as output by
3902 make_atx_certificate with
3903 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003904
3905 Raises:
3906 AvbError: If an argument is incorrect.
3907 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003908 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003909 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003910 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003911 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003912 raise AvbError('Invalid product key certificate length.')
3913 output.write(struct.pack('<I', 1)) # Format Version
3914 output.write(intermediate_key_certificate)
3915 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003916
Darren Krahnfccd64e2018-01-16 17:39:35 -08003917 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3918 unlock_key_certificate, challenge_path,
3919 unlock_key_path, signing_helper,
3920 signing_helper_with_files):
3921 """Implements the 'make_atx_unlock_credential' command.
3922
3923 Android Things unlock credentials can be used to authorize the unlock of AVB
3924 on a device. These credentials are presented to an Android Things bootloader
3925 via the fastboot interface in response to a 16-byte challenge. This method
3926 creates all fields of the credential except the challenge signature field
3927 (which is the last field) and can optionally create the challenge signature
3928 field as well if a challenge and the unlock_key_path is provided.
3929
3930 Arguments:
3931 output: The credential will be written to this file on success.
3932 intermediate_key_certificate: A certificate file as output by
3933 make_atx_certificate with
3934 is_intermediate_authority set to true.
3935 unlock_key_certificate: A certificate file as output by
3936 make_atx_certificate with
3937 is_intermediate_authority set to false and the
3938 usage set to
3939 'com.google.android.things.vboot.unlock'.
3940 challenge_path: [optional] A path to the challenge to sign.
3941 unlock_key_path: [optional] A PEM file path with the unlock private key.
3942 signing_helper: Program which signs a hash and returns the signature.
3943 signing_helper_with_files: Same as signing_helper but uses files instead.
3944
3945 Raises:
Jan Monsch9c130122020-04-14 13:43:51 +02003946 AvbError: If an argument is incorrect or an error occurs during signing.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003947 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003948 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3949 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003950 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3951 raise AvbError('Invalid intermediate key certificate length.')
3952 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3953 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003954 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003955 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003956 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003957 challenge = f.read()
3958 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3959 raise AvbError('Invalid unlock challenge length.')
3960 output.write(struct.pack('<I', 1)) # Format Version
3961 output.write(intermediate_key_certificate)
3962 output.write(unlock_key_certificate)
3963 if challenge_path and unlock_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003964 rsa_key = RSAPublicKey(unlock_key_path)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003965 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003966 signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3967 signing_helper_with_files)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003968 output.write(signature)
3969
David Zeuthen21e95262016-07-27 17:58:40 -04003970
3971def calc_hash_level_offsets(image_size, block_size, digest_size):
3972 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3973
3974 Arguments:
3975 image_size: The size of the image to calculate a Merkle-tree for.
3976 block_size: The block size, e.g. 4096.
3977 digest_size: The size of each hash, e.g. 32 for SHA-256.
3978
3979 Returns:
3980 A tuple where the first argument is an array of offsets and the
3981 second is size of the tree, in bytes.
3982 """
3983 level_offsets = []
3984 level_sizes = []
3985 tree_size = 0
3986
3987 num_levels = 0
3988 size = image_size
3989 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003990 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003991 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3992
3993 level_sizes.append(level_size)
3994 tree_size += level_size
3995 num_levels += 1
3996
3997 size = level_size
3998
3999 for n in range(0, num_levels):
4000 offset = 0
4001 for m in range(n + 1, num_levels):
4002 offset += level_sizes[m]
4003 level_offsets.append(offset)
4004
David Zeuthena4fee8b2016-08-22 15:20:43 -04004005 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004006
4007
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004008# See system/extras/libfec/include/fec/io.h for these definitions.
4009FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4010FEC_MAGIC = 0xfecfecfe
4011
4012
4013def calc_fec_data_size(image_size, num_roots):
4014 """Calculates how much space FEC data will take.
4015
Jan Monschfe00c0a2019-12-11 11:19:40 +01004016 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004017 image_size: The size of the image.
4018 num_roots: Number of roots.
4019
4020 Returns:
4021 The number of bytes needed for FEC for an image of the given size
4022 and with the requested number of FEC roots.
4023
4024 Raises:
4025 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004026 """
4027 p = subprocess.Popen(
4028 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4029 stdout=subprocess.PIPE,
4030 stderr=subprocess.PIPE)
4031 (pout, perr) = p.communicate()
4032 retcode = p.wait()
4033 if retcode != 0:
4034 raise ValueError('Error invoking fec: {}'.format(perr))
4035 return int(pout)
4036
4037
4038def generate_fec_data(image_filename, num_roots):
4039 """Generate FEC codes for an image.
4040
Jan Monschfe00c0a2019-12-11 11:19:40 +01004041 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004042 image_filename: The filename of the image.
4043 num_roots: Number of roots.
4044
4045 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004046 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004047
4048 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02004049 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004050 """
Jan Monschb1d920f2020-04-09 12:59:28 +02004051 with tempfile.NamedTemporaryFile() as fec_tmpfile:
4052 try:
4053 subprocess.check_call(
4054 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4055 fec_tmpfile.name],
4056 stderr=open(os.devnull, 'wb'))
4057 except subprocess.CalledProcessError as e:
4058 raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
4059 fec_data = fec_tmpfile.read()
4060
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004061 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4062 footer_data = fec_data[-footer_size:]
4063 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4064 footer_data)
4065 if magic != FEC_MAGIC:
4066 raise ValueError('Unexpected magic in FEC footer')
4067 return fec_data[0:fec_size]
4068
4069
David Zeuthen21e95262016-07-27 17:58:40 -04004070def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004071 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004072 """Generates a Merkle-tree for a file.
4073
Jan Monschfe00c0a2019-12-11 11:19:40 +01004074 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004075 image: The image, as a file.
4076 image_size: The size of the image.
4077 block_size: The block size, e.g. 4096.
4078 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4079 salt: The salt to use.
4080 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004081 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004082 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004083
4084 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004085 A tuple where the first element is the top-level hash as bytes and the
4086 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004087 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004088 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004089 hash_src_offset = 0
4090 hash_src_size = image_size
4091 level_num = 0
4092 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08004093 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04004094 remaining = hash_src_size
4095 while remaining > 0:
Tianjie62ad0222021-02-22 14:31:12 -08004096 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004097 # Only read from the file for the first level - for subsequent
4098 # levels, access the array we're building.
4099 if level_num == 0:
4100 image.seek(hash_src_offset + hash_src_size - remaining)
4101 data = image.read(min(remaining, block_size))
4102 else:
4103 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4104 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004105 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004106
4107 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004108 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02004109 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08004110 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04004111 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02004112 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08004113
Jan Monschb1d920f2020-04-09 12:59:28 +02004114 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04004115
4116 padding_needed = (round_to_multiple(
4117 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02004118 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004119
David Zeuthena4fee8b2016-08-22 15:20:43 -04004120 # Copy level-output into resulting tree.
4121 offset = hash_level_offsets[level_num]
4122 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004123
David Zeuthena4fee8b2016-08-22 15:20:43 -04004124 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004125 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004126 level_num += 1
4127
Tianjie62ad0222021-02-22 14:31:12 -08004128 hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04004129 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02004130 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04004131
4132
4133class AvbTool(object):
4134 """Object for avbtool command-line tool."""
4135
4136 def __init__(self):
4137 """Initializer method."""
4138 self.avb = Avb()
4139
4140 def _add_common_args(self, sub_parser):
4141 """Adds arguments used by several sub-commands.
4142
4143 Arguments:
4144 sub_parser: The parser to add arguments to.
4145 """
4146 sub_parser.add_argument('--algorithm',
4147 help='Algorithm to use (default: NONE)',
4148 metavar='ALGORITHM',
4149 default='NONE')
4150 sub_parser.add_argument('--key',
4151 help='Path to RSA private key file',
4152 metavar='KEY',
4153 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004154 sub_parser.add_argument('--signing_helper',
4155 help='Path to helper used for signing',
4156 metavar='APP',
4157 default=None,
4158 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004159 sub_parser.add_argument('--signing_helper_with_files',
4160 help='Path to helper used for signing using files',
4161 metavar='APP',
4162 default=None,
4163 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004164 sub_parser.add_argument('--public_key_metadata',
4165 help='Path to public key metadata file',
4166 metavar='KEY_METADATA',
4167 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004168 sub_parser.add_argument('--rollback_index',
4169 help='Rollback Index',
4170 type=parse_number,
4171 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004172 sub_parser.add_argument('--rollback_index_location',
4173 help='Location of main vbmeta Rollback Index',
4174 type=parse_number,
4175 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004176 # This is used internally for unit tests. Do not include in --help output.
4177 sub_parser.add_argument('--internal_release_string',
4178 help=argparse.SUPPRESS)
4179 sub_parser.add_argument('--append_to_release_string',
4180 help='Text to append to release string',
4181 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004182 sub_parser.add_argument('--prop',
4183 help='Add property',
4184 metavar='KEY:VALUE',
4185 action='append')
4186 sub_parser.add_argument('--prop_from_file',
4187 help='Add property from file',
4188 metavar='KEY:PATH',
4189 action='append')
4190 sub_parser.add_argument('--kernel_cmdline',
4191 help='Add kernel cmdline',
4192 metavar='CMDLINE',
4193 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004194 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4195 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4196 # at some future point.
4197 sub_parser.add_argument('--setup_rootfs_from_kernel',
4198 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004199 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004200 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004201 type=argparse.FileType('rb'))
4202 sub_parser.add_argument('--include_descriptors_from_image',
4203 help='Include descriptors from image',
4204 metavar='IMAGE',
4205 action='append',
4206 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004207 sub_parser.add_argument('--print_required_libavb_version',
4208 help=('Don\'t store the footer - '
4209 'instead calculate the required libavb '
4210 'version for the given options.'),
4211 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004212 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4213 sub_parser.add_argument('--chain_partition',
4214 help='Allow signed integrity-data for partition',
4215 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4216 action='append')
4217 sub_parser.add_argument('--flags',
4218 help='VBMeta flags',
4219 type=parse_number,
4220 default=0)
4221 sub_parser.add_argument('--set_hashtree_disabled_flag',
4222 help='Set the HASHTREE_DISABLED flag',
4223 action='store_true')
4224
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004225 def _add_common_footer_args(self, sub_parser):
4226 """Adds arguments used by add_*_footer sub-commands.
4227
4228 Arguments:
4229 sub_parser: The parser to add arguments to.
4230 """
4231 sub_parser.add_argument('--use_persistent_digest',
4232 help='Use a persistent digest on device instead of '
4233 'storing the digest in the descriptor. This '
4234 'cannot be used with A/B so must be combined '
4235 'with --do_not_use_ab when an A/B suffix is '
4236 'expected at runtime.',
4237 action='store_true')
4238 sub_parser.add_argument('--do_not_use_ab',
4239 help='The partition does not use A/B even when an '
4240 'A/B suffix is present. This must not be used '
4241 'for vbmeta or chained partitions.',
4242 action='store_true')
4243
David Zeuthena5fd3a42017-02-27 16:38:54 -05004244 def _fixup_common_args(self, args):
4245 """Common fixups needed by subcommands.
4246
4247 Arguments:
4248 args: Arguments to modify.
4249
4250 Returns:
4251 The modified arguments.
4252 """
4253 if args.set_hashtree_disabled_flag:
4254 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4255 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004256
4257 def run(self, argv):
4258 """Command-line processor.
4259
4260 Arguments:
4261 argv: Pass sys.argv from main.
4262 """
4263 parser = argparse.ArgumentParser()
4264 subparsers = parser.add_subparsers(title='subcommands')
4265
Jan Monsch2c7be992020-04-03 14:37:13 +02004266 sub_parser = subparsers.add_parser(
4267 'generate_test_image',
4268 help=('Generates a test image with a known pattern for testing: '
4269 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4270 sub_parser.add_argument('--image_size',
4271 help='Size of image to generate.',
4272 type=parse_number,
4273 required=True)
4274 sub_parser.add_argument('--start_byte',
4275 help='Integer for the start byte of the pattern.',
4276 type=parse_number,
4277 default=0)
4278 sub_parser.add_argument('--output',
4279 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004280 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004281 default=sys.stdout)
4282 sub_parser.set_defaults(func=self.generate_test_image)
4283
David Zeuthen21e95262016-07-27 17:58:40 -04004284 sub_parser = subparsers.add_parser('version',
4285 help='Prints version of avbtool.')
4286 sub_parser.set_defaults(func=self.version)
4287
4288 sub_parser = subparsers.add_parser('extract_public_key',
4289 help='Extract public key.')
4290 sub_parser.add_argument('--key',
4291 help='Path to RSA private key file',
4292 required=True)
4293 sub_parser.add_argument('--output',
4294 help='Output file name',
4295 type=argparse.FileType('wb'),
4296 required=True)
4297 sub_parser.set_defaults(func=self.extract_public_key)
4298
4299 sub_parser = subparsers.add_parser('make_vbmeta_image',
4300 help='Makes a vbmeta image.')
4301 sub_parser.add_argument('--output',
4302 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004303 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004304 sub_parser.add_argument('--padding_size',
4305 metavar='NUMBER',
4306 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004307 'its size is a multiple of NUMBER '
4308 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004309 type=parse_number,
4310 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004311 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004312 sub_parser.set_defaults(func=self.make_vbmeta_image)
4313
4314 sub_parser = subparsers.add_parser('add_hash_footer',
4315 help='Add hashes and footer to image.')
4316 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004317 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004318 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004319 sub_parser.add_argument('--partition_size',
4320 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004321 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004322 sub_parser.add_argument('--partition_name',
4323 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004324 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004325 sub_parser.add_argument('--hash_algorithm',
4326 help='Hash algorithm to use (default: sha256)',
4327 default='sha256')
4328 sub_parser.add_argument('--salt',
4329 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004330 sub_parser.add_argument('--calc_max_image_size',
4331 help=('Don\'t store the footer - '
4332 'instead calculate the maximum image size '
4333 'leaving enough room for metadata with '
4334 'the given partition size.'),
4335 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004336 sub_parser.add_argument('--output_vbmeta_image',
4337 help='Also write vbmeta struct to file',
4338 type=argparse.FileType('wb'))
4339 sub_parser.add_argument('--do_not_append_vbmeta_image',
4340 help=('Do not append vbmeta struct or footer '
4341 'to the image'),
4342 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004343 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004344 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004345 sub_parser.set_defaults(func=self.add_hash_footer)
4346
David Zeuthenb1b994d2017-03-06 18:01:31 -05004347 sub_parser = subparsers.add_parser('append_vbmeta_image',
4348 help='Append vbmeta image to image.')
4349 sub_parser.add_argument('--image',
4350 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004351 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004352 sub_parser.add_argument('--partition_size',
4353 help='Partition size',
4354 type=parse_number,
4355 required=True)
4356 sub_parser.add_argument('--vbmeta_image',
4357 help='Image with vbmeta blob to append',
4358 type=argparse.FileType('rb'))
4359 sub_parser.set_defaults(func=self.append_vbmeta_image)
4360
Jan Monscheeb28b62019-12-05 16:17:09 +01004361 sub_parser = subparsers.add_parser(
4362 'add_hashtree_footer',
4363 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004364 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004365 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004366 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004367 sub_parser.add_argument('--partition_size',
4368 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004369 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004370 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004371 sub_parser.add_argument('--partition_name',
4372 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004373 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004374 sub_parser.add_argument('--hash_algorithm',
4375 help='Hash algorithm to use (default: sha1)',
4376 default='sha1')
4377 sub_parser.add_argument('--salt',
4378 help='Salt in hex (default: /dev/urandom)')
4379 sub_parser.add_argument('--block_size',
4380 help='Block size (default: 4096)',
4381 type=parse_number,
4382 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004383 # TODO(zeuthen): The --generate_fec option was removed when we
4384 # moved to generating FEC by default. To avoid breaking existing
4385 # users needing to transition we simply just print a warning below
4386 # in add_hashtree_footer(). Remove this option and the warning at
4387 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004388 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004389 help=argparse.SUPPRESS,
4390 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004391 sub_parser.add_argument(
4392 '--do_not_generate_fec',
4393 help='Do not generate forward-error-correction codes',
4394 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004395 sub_parser.add_argument('--fec_num_roots',
4396 help='Number of roots for FEC (default: 2)',
4397 type=parse_number,
4398 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004399 sub_parser.add_argument('--calc_max_image_size',
4400 help=('Don\'t store the hashtree or footer - '
4401 'instead calculate the maximum image size '
4402 'leaving enough room for hashtree '
4403 'and metadata with the given partition '
4404 'size.'),
4405 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004406 sub_parser.add_argument('--output_vbmeta_image',
4407 help='Also write vbmeta struct to file',
4408 type=argparse.FileType('wb'))
4409 sub_parser.add_argument('--do_not_append_vbmeta_image',
4410 help=('Do not append vbmeta struct or footer '
4411 'to the image'),
4412 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004413 # This is different from --setup_rootfs_from_kernel insofar that
4414 # it doesn't take an IMAGE, the generated cmdline will be for the
4415 # hashtree we're adding.
4416 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4417 action='store_true',
4418 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004419 sub_parser.add_argument('--no_hashtree',
4420 action='store_true',
4421 help='Do not append hashtree')
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004422 sub_parser.add_argument('--check_at_most_once',
4423 action='store_true',
4424 help='Set to verify data block only once')
David Zeuthen21e95262016-07-27 17:58:40 -04004425 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004426 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004427 sub_parser.set_defaults(func=self.add_hashtree_footer)
4428
4429 sub_parser = subparsers.add_parser('erase_footer',
4430 help='Erase footer from an image.')
4431 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004432 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004433 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004434 required=True)
4435 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004436 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004437 action='store_true')
4438 sub_parser.set_defaults(func=self.erase_footer)
4439
David Zeuthen1394f762019-04-30 10:20:11 -04004440 sub_parser = subparsers.add_parser('zero_hashtree',
4441 help='Zero out hashtree and FEC data.')
4442 sub_parser.add_argument('--image',
4443 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004444 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004445 required=True)
4446 sub_parser.set_defaults(func=self.zero_hashtree)
4447
Jan Monscheeb28b62019-12-05 16:17:09 +01004448 sub_parser = subparsers.add_parser(
4449 'extract_vbmeta_image',
4450 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004451 sub_parser.add_argument('--image',
4452 help='Image with footer',
4453 type=argparse.FileType('rb'),
4454 required=True)
4455 sub_parser.add_argument('--output',
4456 help='Output file name',
4457 type=argparse.FileType('wb'))
4458 sub_parser.add_argument('--padding_size',
4459 metavar='NUMBER',
4460 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004461 'its size is a multiple of NUMBER '
4462 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004463 type=parse_number,
4464 default=0)
4465 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4466
David Zeuthen2bc232b2017-04-19 14:25:19 -04004467 sub_parser = subparsers.add_parser('resize_image',
4468 help='Resize image with a footer.')
4469 sub_parser.add_argument('--image',
4470 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004471 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004472 required=True)
4473 sub_parser.add_argument('--partition_size',
4474 help='New partition size',
4475 type=parse_number)
4476 sub_parser.set_defaults(func=self.resize_image)
4477
David Zeuthen21e95262016-07-27 17:58:40 -04004478 sub_parser = subparsers.add_parser(
4479 'info_image',
4480 help='Show information about vbmeta or footer.')
4481 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004482 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004483 type=argparse.FileType('rb'),
4484 required=True)
4485 sub_parser.add_argument('--output',
4486 help='Write info to file',
4487 type=argparse.FileType('wt'),
4488 default=sys.stdout)
Sen Jiang01553a22020-06-30 17:58:44 -07004489 sub_parser.add_argument('--atx',
4490 help=('Show information about Android Things '
4491 'eXtension (ATX).'),
4492 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004493 sub_parser.set_defaults(func=self.info_image)
4494
David Zeuthenb623d8b2017-04-04 16:05:53 -04004495 sub_parser = subparsers.add_parser(
4496 'verify_image',
4497 help='Verify an image.')
4498 sub_parser.add_argument('--image',
4499 help='Image to verify',
4500 type=argparse.FileType('rb'),
4501 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004502 sub_parser.add_argument('--key',
4503 help='Check embedded public key matches KEY',
4504 metavar='KEY',
4505 required=False)
4506 sub_parser.add_argument('--expected_chain_partition',
4507 help='Expected chain partition',
4508 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4509 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004510 sub_parser.add_argument(
4511 '--follow_chain_partitions',
4512 help=('Follows chain partitions even when not '
4513 'specified with the --expected_chain_partition option'),
4514 action='store_true')
4515 sub_parser.add_argument(
4516 '--accept_zeroed_hashtree',
4517 help=('Accept images where the hashtree or FEC data is zeroed out'),
4518 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004519 sub_parser.set_defaults(func=self.verify_image)
4520
David Zeuthenb8643c02018-05-17 17:21:18 -04004521 sub_parser = subparsers.add_parser(
David Zeuthen34b6b492020-04-13 14:45:02 -04004522 'print_partition_digests',
4523 help='Prints partition digests.')
4524 sub_parser.add_argument('--image',
4525 help='Image to print partition digests from',
4526 type=argparse.FileType('rb'),
4527 required=True)
4528 sub_parser.add_argument('--output',
4529 help='Write info to file',
4530 type=argparse.FileType('wt'),
4531 default=sys.stdout)
4532 sub_parser.add_argument('--json',
4533 help=('Print output as JSON'),
4534 action='store_true')
4535 sub_parser.set_defaults(func=self.print_partition_digests)
4536
4537 sub_parser = subparsers.add_parser(
David Zeuthenb8643c02018-05-17 17:21:18 -04004538 'calculate_vbmeta_digest',
4539 help='Calculate vbmeta digest.')
4540 sub_parser.add_argument('--image',
4541 help='Image to calculate digest for',
4542 type=argparse.FileType('rb'),
4543 required=True)
4544 sub_parser.add_argument('--hash_algorithm',
4545 help='Hash algorithm to use (default: sha256)',
4546 default='sha256')
4547 sub_parser.add_argument('--output',
4548 help='Write hex digest to file (default: stdout)',
4549 type=argparse.FileType('wt'),
4550 default=sys.stdout)
4551 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4552
David Zeuthenf7d2e752018-09-20 13:30:41 -04004553 sub_parser = subparsers.add_parser(
4554 'calculate_kernel_cmdline',
4555 help='Calculate kernel cmdline.')
4556 sub_parser.add_argument('--image',
4557 help='Image to calculate kernel cmdline for',
4558 type=argparse.FileType('rb'),
4559 required=True)
4560 sub_parser.add_argument('--hashtree_disabled',
4561 help='Return the cmdline for hashtree disabled',
4562 action='store_true')
4563 sub_parser.add_argument('--output',
4564 help='Write cmdline to file (default: stdout)',
4565 type=argparse.FileType('wt'),
4566 default=sys.stdout)
4567 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4568
David Zeuthen8b6973b2016-09-20 12:39:49 -04004569 sub_parser = subparsers.add_parser('set_ab_metadata',
4570 help='Set A/B metadata.')
4571 sub_parser.add_argument('--misc_image',
4572 help=('The misc image to modify. If the image does '
4573 'not exist, it will be created.'),
4574 type=argparse.FileType('r+b'),
4575 required=True)
4576 sub_parser.add_argument('--slot_data',
4577 help=('Slot data of the form "priority", '
4578 '"tries_remaining", "sucessful_boot" for '
4579 'slot A followed by the same for slot B, '
4580 'separated by colons. The default value '
4581 'is 15:7:0:14:7:0.'),
4582 default='15:7:0:14:7:0')
4583 sub_parser.set_defaults(func=self.set_ab_metadata)
4584
Darren Krahn147b08d2016-12-20 16:38:29 -08004585 sub_parser = subparsers.add_parser(
4586 'make_atx_certificate',
4587 help='Create an Android Things eXtension (ATX) certificate.')
4588 sub_parser.add_argument('--output',
4589 help='Write certificate to file',
4590 type=argparse.FileType('wb'),
4591 default=sys.stdout)
4592 sub_parser.add_argument('--subject',
4593 help=('Path to subject file'),
4594 type=argparse.FileType('rb'),
4595 required=True)
4596 sub_parser.add_argument('--subject_key',
4597 help=('Path to subject RSA public key file'),
4598 type=argparse.FileType('rb'),
4599 required=True)
4600 sub_parser.add_argument('--subject_key_version',
4601 help=('Version of the subject key'),
4602 type=parse_number,
4603 required=False)
4604 sub_parser.add_argument('--subject_is_intermediate_authority',
4605 help=('Generate an intermediate authority '
4606 'certificate'),
4607 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004608 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004609 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004610 'string'),
4611 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004612 sub_parser.add_argument('--authority_key',
4613 help='Path to authority RSA private key file',
4614 required=False)
4615 sub_parser.add_argument('--signing_helper',
4616 help='Path to helper used for signing',
4617 metavar='APP',
4618 default=None,
4619 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004620 sub_parser.add_argument('--signing_helper_with_files',
4621 help='Path to helper used for signing using files',
4622 metavar='APP',
4623 default=None,
4624 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004625 sub_parser.set_defaults(func=self.make_atx_certificate)
4626
4627 sub_parser = subparsers.add_parser(
4628 'make_atx_permanent_attributes',
4629 help='Create Android Things eXtension (ATX) permanent attributes.')
4630 sub_parser.add_argument('--output',
4631 help='Write attributes to file',
4632 type=argparse.FileType('wb'),
4633 default=sys.stdout)
4634 sub_parser.add_argument('--root_authority_key',
4635 help='Path to authority RSA public key file',
4636 type=argparse.FileType('rb'),
4637 required=True)
4638 sub_parser.add_argument('--product_id',
4639 help=('Path to Product ID file'),
4640 type=argparse.FileType('rb'),
4641 required=True)
4642 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4643
4644 sub_parser = subparsers.add_parser(
4645 'make_atx_metadata',
4646 help='Create Android Things eXtension (ATX) metadata.')
4647 sub_parser.add_argument('--output',
4648 help='Write metadata to file',
4649 type=argparse.FileType('wb'),
4650 default=sys.stdout)
4651 sub_parser.add_argument('--intermediate_key_certificate',
4652 help='Path to intermediate key certificate file',
4653 type=argparse.FileType('rb'),
4654 required=True)
4655 sub_parser.add_argument('--product_key_certificate',
4656 help='Path to product key certificate file',
4657 type=argparse.FileType('rb'),
4658 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004659 sub_parser.set_defaults(func=self.make_atx_metadata)
4660
Darren Krahnfccd64e2018-01-16 17:39:35 -08004661 sub_parser = subparsers.add_parser(
4662 'make_atx_unlock_credential',
4663 help='Create an Android Things eXtension (ATX) unlock credential.')
4664 sub_parser.add_argument('--output',
4665 help='Write credential to file',
4666 type=argparse.FileType('wb'),
4667 default=sys.stdout)
4668 sub_parser.add_argument('--intermediate_key_certificate',
4669 help='Path to intermediate key certificate file',
4670 type=argparse.FileType('rb'),
4671 required=True)
4672 sub_parser.add_argument('--unlock_key_certificate',
4673 help='Path to unlock key certificate file',
4674 type=argparse.FileType('rb'),
4675 required=True)
4676 sub_parser.add_argument('--challenge',
4677 help='Path to the challenge to sign (optional). If '
4678 'this is not provided the challenge signature '
4679 'field is omitted and can be concatenated '
4680 'later.',
4681 required=False)
4682 sub_parser.add_argument('--unlock_key',
4683 help='Path to unlock key (optional). Must be '
4684 'provided if using --challenge.',
4685 required=False)
4686 sub_parser.add_argument('--signing_helper',
4687 help='Path to helper used for signing',
4688 metavar='APP',
4689 default=None,
4690 required=False)
4691 sub_parser.add_argument('--signing_helper_with_files',
4692 help='Path to helper used for signing using files',
4693 metavar='APP',
4694 default=None,
4695 required=False)
4696 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4697
David Zeuthen21e95262016-07-27 17:58:40 -04004698 args = parser.parse_args(argv[1:])
4699 try:
4700 args.func(args)
Jan Monschcc9939a2020-04-16 09:15:20 +02004701 except AttributeError:
4702 # This error gets raised when the command line tool is called without any
4703 # arguments. It mimics the original Python 2 behavior.
4704 parser.print_usage()
4705 print('avbtool: error: too few arguments')
4706 sys.exit(2)
David Zeuthen21e95262016-07-27 17:58:40 -04004707 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004708 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004709 sys.exit(1)
4710
4711 def version(self, _):
4712 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004713 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004714
Jan Monsch2c7be992020-04-03 14:37:13 +02004715 def generate_test_image(self, args):
4716 """Implements the 'generate_test_image' sub-command."""
4717 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4718
David Zeuthen21e95262016-07-27 17:58:40 -04004719 def extract_public_key(self, args):
4720 """Implements the 'extract_public_key' sub-command."""
4721 self.avb.extract_public_key(args.key, args.output)
4722
4723 def make_vbmeta_image(self, args):
4724 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004725 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004726 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004727 args.algorithm, args.key,
4728 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004729 args.flags, args.rollback_index_location,
4730 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004731 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004732 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004733 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004734 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004735 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004736 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004737 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004738 args.print_required_libavb_version,
4739 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004740
David Zeuthenb1b994d2017-03-06 18:01:31 -05004741 def append_vbmeta_image(self, args):
4742 """Implements the 'append_vbmeta_image' sub-command."""
4743 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4744 args.partition_size)
4745
David Zeuthen21e95262016-07-27 17:58:40 -04004746 def add_hash_footer(self, args):
4747 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004748 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004749 self.avb.add_hash_footer(args.image.name if args.image else None,
4750 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004751 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004752 args.salt, args.chain_partition, args.algorithm,
4753 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004754 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004755 args.flags, args.rollback_index_location,
4756 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004757 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004758 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004759 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004760 args.calc_max_image_size,
4761 args.signing_helper,
4762 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004763 args.internal_release_string,
4764 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004765 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004766 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004767 args.print_required_libavb_version,
4768 args.use_persistent_digest,
4769 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004770
4771 def add_hashtree_footer(self, args):
4772 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004773 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004774 # TODO(zeuthen): Remove when removing support for the
4775 # '--generate_fec' option above.
4776 if args.generate_fec:
4777 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4778 'is now generated by default. Use the option '
4779 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004780 self.avb.add_hashtree_footer(
4781 args.image.name if args.image else None,
4782 args.partition_size,
4783 args.partition_name,
4784 not args.do_not_generate_fec, args.fec_num_roots,
4785 args.hash_algorithm, args.block_size,
4786 args.salt, args.chain_partition, args.algorithm,
4787 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004788 args.rollback_index, args.flags,
4789 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004790 args.prop_from_file,
4791 args.kernel_cmdline,
4792 args.setup_rootfs_from_kernel,
4793 args.setup_as_rootfs_from_kernel,
4794 args.include_descriptors_from_image,
4795 args.calc_max_image_size,
4796 args.signing_helper,
4797 args.signing_helper_with_files,
4798 args.internal_release_string,
4799 args.append_to_release_string,
4800 args.output_vbmeta_image,
4801 args.do_not_append_vbmeta_image,
4802 args.print_required_libavb_version,
4803 args.use_persistent_digest,
4804 args.do_not_use_ab,
JeongHyeon Lee2998a352021-05-25 16:38:07 +09004805 args.no_hashtree,
4806 args.check_at_most_once)
David Zeuthend247fcb2017-02-16 12:09:27 -05004807
David Zeuthen21e95262016-07-27 17:58:40 -04004808 def erase_footer(self, args):
4809 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004810 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004811
David Zeuthen1394f762019-04-30 10:20:11 -04004812 def zero_hashtree(self, args):
4813 """Implements the 'zero_hashtree' sub-command."""
4814 self.avb.zero_hashtree(args.image.name)
4815
David Zeuthen49936b42018-08-07 17:38:58 -04004816 def extract_vbmeta_image(self, args):
4817 """Implements the 'extract_vbmeta_image' sub-command."""
4818 self.avb.extract_vbmeta_image(args.output, args.image.name,
4819 args.padding_size)
4820
David Zeuthen2bc232b2017-04-19 14:25:19 -04004821 def resize_image(self, args):
4822 """Implements the 'resize_image' sub-command."""
4823 self.avb.resize_image(args.image.name, args.partition_size)
4824
David Zeuthen8b6973b2016-09-20 12:39:49 -04004825 def set_ab_metadata(self, args):
4826 """Implements the 'set_ab_metadata' sub-command."""
4827 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4828
David Zeuthen21e95262016-07-27 17:58:40 -04004829 def info_image(self, args):
4830 """Implements the 'info_image' sub-command."""
Sen Jiang01553a22020-06-30 17:58:44 -07004831 self.avb.info_image(args.image.name, args.output, args.atx)
David Zeuthen21e95262016-07-27 17:58:40 -04004832
David Zeuthenb623d8b2017-04-04 16:05:53 -04004833 def verify_image(self, args):
4834 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004835 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004836 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004837 args.follow_chain_partitions,
4838 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004839
David Zeuthen34b6b492020-04-13 14:45:02 -04004840 def print_partition_digests(self, args):
4841 """Implements the 'print_partition_digests' sub-command."""
4842 self.avb.print_partition_digests(args.image.name, args.output, args.json)
4843
David Zeuthenb8643c02018-05-17 17:21:18 -04004844 def calculate_vbmeta_digest(self, args):
4845 """Implements the 'calculate_vbmeta_digest' sub-command."""
4846 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4847 args.output)
4848
David Zeuthenf7d2e752018-09-20 13:30:41 -04004849 def calculate_kernel_cmdline(self, args):
4850 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004851 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4852 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004853
Darren Krahn147b08d2016-12-20 16:38:29 -08004854 def make_atx_certificate(self, args):
4855 """Implements the 'make_atx_certificate' sub-command."""
4856 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004857 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004858 args.subject_key_version,
4859 args.subject.read(),
4860 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004861 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004862 args.signing_helper,
4863 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004864
4865 def make_atx_permanent_attributes(self, args):
4866 """Implements the 'make_atx_permanent_attributes' sub-command."""
4867 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004868 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004869 args.product_id.read())
4870
4871 def make_atx_metadata(self, args):
4872 """Implements the 'make_atx_metadata' sub-command."""
4873 self.avb.make_atx_metadata(args.output,
4874 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004875 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004876
Darren Krahnfccd64e2018-01-16 17:39:35 -08004877 def make_atx_unlock_credential(self, args):
4878 """Implements the 'make_atx_unlock_credential' sub-command."""
4879 self.avb.make_atx_unlock_credential(
4880 args.output,
4881 args.intermediate_key_certificate.read(),
4882 args.unlock_key_certificate.read(),
4883 args.challenge,
4884 args.unlock_key,
4885 args.signing_helper,
4886 args.signing_helper_with_files)
4887
David Zeuthen21e95262016-07-27 17:58:40 -04004888
4889if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004890 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004891 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4892 log.write(' '.join(sys.argv))
4893 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004894
David Zeuthen21e95262016-07-27 17:58:40 -04004895 tool = AvbTool()
4896 tool.run(sys.argv)