blob: 0e89f3337e2ce3d0d1a2fe0a5b2c687b84f73e4b [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040033import math
David Zeuthen21e95262016-07-27 17:58:40 -040034import os
35import struct
36import subprocess
37import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040038import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080039import time
David Zeuthen21e95262016-07-27 17:58:40 -040040
David Zeuthene3cadca2017-02-22 21:25:46 -050041# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040042AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080043AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
Jan Monsch2c7be992020-04-03 14:37:13 +020052# Configuration for enabling logging of calls to avbtool.
53AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
54
David Zeuthene3cadca2017-02-22 21:25:46 -050055
David Zeuthen21e95262016-07-27 17:58:40 -040056class AvbError(Exception):
57 """Application-specific errors.
58
59 These errors represent issues for which a stack-trace should not be
60 presented.
61
62 Attributes:
63 message: Error message.
64 """
65
66 def __init__(self, message):
67 Exception.__init__(self, message)
68
69
70class Algorithm(object):
71 """Contains details about an algorithm.
72
Tao Bao80418a52018-07-20 11:41:22 -070073 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040074
75 The constant |ALGORITHMS| is a dictionary from human-readable
76 names (e.g 'SHA256_RSA2048') to instances of this class.
77
78 Attributes:
79 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040080 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040081 hash_num_bytes: Number of bytes used to store the hash.
82 signature_num_bytes: Number of bytes used to store the signature.
83 public_key_num_bytes: Number of bytes used to store the public key.
84 padding: Padding used for signature, if any.
85 """
86
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
88 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040089 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040090 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040091 self.hash_num_bytes = hash_num_bytes
92 self.signature_num_bytes = signature_num_bytes
93 self.public_key_num_bytes = public_key_num_bytes
94 self.padding = padding
95
David Zeuthenb623d8b2017-04-04 16:05:53 -040096
David Zeuthen21e95262016-07-27 17:58:40 -040097# This must be kept in sync with the avb_crypto.h file.
98#
99# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
100# obtained from section 5.2.2 of RFC 4880.
101ALGORITHMS = {
102 'NONE': Algorithm(
103 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400104 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400105 hash_num_bytes=0,
106 signature_num_bytes=0,
107 public_key_num_bytes=0,
108 padding=[]),
109 'SHA256_RSA2048': Algorithm(
110 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400111 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400112 hash_num_bytes=32,
113 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100114 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*202 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA4096': Algorithm(
124 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400125 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400126 hash_num_bytes=32,
127 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100128 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*458 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA256_RSA8192': Algorithm(
138 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400139 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400140 hash_num_bytes=32,
141 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100142 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400143 padding=[
144 # PKCS1-v1_5 padding
145 0x00, 0x01] + [0xff]*970 + [0x00] + [
146 # ASN.1 header
147 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
148 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
149 0x00, 0x04, 0x20,
150 ]),
151 'SHA512_RSA2048': Algorithm(
152 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400153 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400154 hash_num_bytes=64,
155 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100156 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400157 padding=[
158 # PKCS1-v1_5 padding
159 0x00, 0x01] + [0xff]*170 + [0x00] + [
160 # ASN.1 header
161 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
162 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
163 0x00, 0x04, 0x40
164 ]),
165 'SHA512_RSA4096': Algorithm(
166 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400167 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400168 hash_num_bytes=64,
169 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100170 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400171 padding=[
172 # PKCS1-v1_5 padding
173 0x00, 0x01] + [0xff]*426 + [0x00] + [
174 # ASN.1 header
175 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
176 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
177 0x00, 0x04, 0x40
178 ]),
179 'SHA512_RSA8192': Algorithm(
180 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400181 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400182 hash_num_bytes=64,
183 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100184 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400185 padding=[
186 # PKCS1-v1_5 padding
187 0x00, 0x01] + [0xff]*938 + [0x00] + [
188 # ASN.1 header
189 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
190 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
191 0x00, 0x04, 0x40
192 ]),
193}
194
195
David Zeuthene3cadca2017-02-22 21:25:46 -0500196def get_release_string():
197 """Calculates the release string to use in the VBMeta struct."""
198 # Keep in sync with libavb/avb_version.c:avb_version_string().
199 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
200 AVB_VERSION_MINOR,
201 AVB_VERSION_SUB)
202
203
David Zeuthen21e95262016-07-27 17:58:40 -0400204def round_to_multiple(number, size):
205 """Rounds a number up to nearest multiple of another number.
206
Jan Monschfe00c0a2019-12-11 11:19:40 +0100207 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400208 number: The number to round up.
209 size: The multiple to round up to.
210
211 Returns:
212 If |number| is a multiple of |size|, returns |number|, otherwise
213 returns |number| + |size|.
214 """
215 remainder = number % size
216 if remainder == 0:
217 return number
218 return number + size - remainder
219
220
221def round_to_pow2(number):
222 """Rounds a number up to the next power of 2.
223
Jan Monschfe00c0a2019-12-11 11:19:40 +0100224 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400225 number: The number to round up.
226
227 Returns:
228 If |number| is already a power of 2 then |number| is
229 returned. Otherwise the smallest power of 2 greater than |number|
230 is returned.
231 """
232 return 2**((number - 1).bit_length())
233
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235def encode_long(num_bits, value):
236 """Encodes a long to a bytearray() using a given amount of bits.
237
238 This number is written big-endian, e.g. with the most significant
239 bit first.
240
David Zeuthenb623d8b2017-04-04 16:05:53 -0400241 This is the reverse of decode_long().
242
David Zeuthen21e95262016-07-27 17:58:40 -0400243 Arguments:
244 num_bits: The number of bits to write, e.g. 2048.
245 value: The value to write.
246
247 Returns:
248 A bytearray() with the encoded long.
249 """
250 ret = bytearray()
251 for bit_pos in range(num_bits, 0, -8):
252 octet = (value >> (bit_pos - 8)) & 0xff
253 ret.extend(struct.pack('!B', octet))
254 return ret
255
256
David Zeuthenb623d8b2017-04-04 16:05:53 -0400257def decode_long(blob):
258 """Decodes a long from a bytearray() using a given amount of bits.
259
260 This number is expected to be in big-endian, e.g. with the most
261 significant bit first.
262
263 This is the reverse of encode_long().
264
265 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100266 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400267
268 Returns:
269 The decoded value.
270 """
271 ret = 0
272 for b in bytearray(blob):
273 ret *= 256
274 ret += b
275 return ret
276
277
David Zeuthen21e95262016-07-27 17:58:40 -0400278def egcd(a, b):
279 """Calculate greatest common divisor of two numbers.
280
281 This implementation uses a recursive version of the extended
282 Euclidian algorithm.
283
284 Arguments:
285 a: First number.
286 b: Second number.
287
288 Returns:
289 A tuple (gcd, x, y) that where |gcd| is the greatest common
290 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
291 """
292 if a == 0:
293 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100294 g, y, x = egcd(b % a, a)
295 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400296
297
298def modinv(a, m):
299 """Calculate modular multiplicative inverse of |a| modulo |m|.
300
301 This calculates the number |x| such that |a| * |x| == 1 (modulo
302 |m|). This number only exists if |a| and |m| are co-prime - |None|
303 is returned if this isn't true.
304
305 Arguments:
306 a: The number to calculate a modular inverse of.
307 m: The modulo to use.
308
309 Returns:
310 The modular multiplicative inverse of |a| and |m| or |None| if
311 these numbers are not co-prime.
312 """
313 gcd, x, _ = egcd(a, m)
314 if gcd != 1:
315 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100316 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400317
318
319def parse_number(string):
320 """Parse a string as a number.
321
322 This is just a short-hand for int(string, 0) suitable for use in the
323 |type| parameter of |ArgumentParser|'s add_argument() function. An
324 improvement to just using type=int is that this function supports
325 numbers in other bases, e.g. "0x1234".
326
327 Arguments:
328 string: The string to parse.
329
330 Returns:
331 The parsed integer.
332
333 Raises:
334 ValueError: If the number could not be parsed.
335 """
336 return int(string, 0)
337
338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339class RSAPublicKey(object):
340 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400341
David Zeuthenc68f0822017-03-31 17:22:35 -0400342 Attributes:
343 exponent: The key exponent.
344 modulus: The key modulus.
345 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400346 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400347
348 MODULUS_PREFIX = 'modulus='
349
350 def __init__(self, key_path):
351 """Loads and parses an RSA key from either a private or public key file.
352
353 Arguments:
354 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100355
356 Raises:
357 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400358 """
359 # We used to have something as simple as this:
360 #
361 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
362 # self.exponent = key.e
363 # self.modulus = key.n
364 # self.num_bits = key.size() + 1
365 #
366 # but unfortunately PyCrypto is not available in the builder. So
367 # instead just parse openssl(1) output to get this
368 # information. It's ugly but...
369 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
370 p = subprocess.Popen(args,
371 stdin=subprocess.PIPE,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE)
374 (pout, perr) = p.communicate()
375 if p.wait() != 0:
376 # Could be just a public key is passed, try that.
377 args.append('-pubin')
378 p = subprocess.Popen(args,
379 stdin=subprocess.PIPE,
380 stdout=subprocess.PIPE,
381 stderr=subprocess.PIPE)
382 (pout, perr) = p.communicate()
383 if p.wait() != 0:
384 raise AvbError('Error getting public key: {}'.format(perr))
385
386 if not pout.lower().startswith(self.MODULUS_PREFIX):
387 raise AvbError('Unexpected modulus output')
388
389 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
390
391 # The exponent is assumed to always be 65537 and the number of
392 # bits can be derived from the modulus by rounding up to the
393 # nearest power of 2.
394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
398
David Zeuthenc68f0822017-03-31 17:22:35 -0400399def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400400 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
401
402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
404
405 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
408 Returns:
409 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100410
411 Raises:
412 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400413 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 key = RSAPublicKey(key_path)
415 if key.exponent != 65537:
416 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400417 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monsch23e0c622019-12-11 11:23:58 +0100419 b = 2L**32 # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400421 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monsch23e0c622019-12-11 11:23:58 +0100422 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400423 rrmodn = r * r % key.modulus
424 ret.extend(struct.pack('!II', key.num_bits, n0inv))
425 ret.extend(encode_long(key.num_bits, key.modulus))
426 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400427 return ret
428
429
430def lookup_algorithm_by_type(alg_type):
431 """Looks up algorithm by type.
432
433 Arguments:
434 alg_type: The integer representing the type.
435
436 Returns:
437 A tuple with the algorithm name and an |Algorithm| instance.
438
439 Raises:
440 Exception: If the algorithm cannot be found
441 """
442 for alg_name in ALGORITHMS:
443 alg_data = ALGORITHMS[alg_name]
444 if alg_data.algorithm_type == alg_type:
445 return (alg_name, alg_data)
446 raise AvbError('Unknown algorithm type {}'.format(alg_type))
447
Jan Monsch77cd2022019-12-10 17:18:04 +0100448
Dan Austina7bc4962019-12-02 13:26:08 -0800449def lookup_hash_size_by_type(alg_type):
450 """Looks up hash size by type.
451
452 Arguments:
453 alg_type: The integer representing the type.
454
455 Returns:
456 The corresponding hash size.
457
458 Raises:
459 AvbError: If the algorithm cannot be found.
460 """
461 for alg_name in ALGORITHMS:
462 alg_data = ALGORITHMS[alg_name]
463 if alg_data.algorithm_type == alg_type:
464 return alg_data.hash_num_bytes
465 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400466
Jan Monsch77cd2022019-12-10 17:18:04 +0100467
David Zeuthena156d3d2017-06-01 12:08:09 -0400468def raw_sign(signing_helper, signing_helper_with_files,
469 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900470 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 """Computes a raw RSA signature using |signing_helper| or openssl.
472
473 Arguments:
474 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400475 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800476 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900477 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800478 key_path: Path to the private key file. Must be PEM format.
479 raw_data_to_sign: Data to sign (bytearray or str expected).
480
481 Returns:
482 A bytearray containing the signature.
483
484 Raises:
485 Exception: If an error occurs.
486 """
487 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400488 if signing_helper_with_files is not None:
489 signing_file = tempfile.NamedTemporaryFile()
490 signing_file.write(str(raw_data_to_sign))
491 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100492 p = subprocess.Popen([
493 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400494 retcode = p.wait()
495 if retcode != 0:
496 raise AvbError('Error signing')
497 signing_file.seek(0)
498 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800499 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400500 if signing_helper is not None:
501 p = subprocess.Popen(
502 [signing_helper, algorithm_name, key_path],
503 stdin=subprocess.PIPE,
504 stdout=subprocess.PIPE,
505 stderr=subprocess.PIPE)
506 else:
507 p = subprocess.Popen(
508 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
509 stdin=subprocess.PIPE,
510 stdout=subprocess.PIPE,
511 stderr=subprocess.PIPE)
512 (pout, perr) = p.communicate(str(raw_data_to_sign))
513 retcode = p.wait()
514 if retcode != 0:
515 raise AvbError('Error signing: {}'.format(perr))
516 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900517 if len(signature) != signature_num_bytes:
518 raise AvbError('Error signing: Invalid length of signature')
519 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800520
521
David Zeuthenb623d8b2017-04-04 16:05:53 -0400522def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100523 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400524
525 Arguments:
526 vbmeta_header: A AvbVBMetaHeader.
527 vbmeta_blob: The whole vbmeta blob, including the header.
528
529 Returns:
530 True if the signature is valid and corresponds to the embedded
531 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100532
533 Raises:
534 AvbError: If there errors calling out to openssl command during
535 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536 """
537 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100538 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400539 return True
540 header_blob = vbmeta_blob[0:256]
541 auth_offset = 256
542 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
543 aux_size = vbmeta_header.auxiliary_data_block_size
544 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
545 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
546 pubkey_size = vbmeta_header.public_key_size
547 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
548
549 digest_offset = auth_offset + vbmeta_header.hash_offset
550 digest_size = vbmeta_header.hash_size
551 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
552
553 sig_offset = auth_offset + vbmeta_header.signature_offset
554 sig_size = vbmeta_header.signature_size
555 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
556
557 # Now that we've got the stored digest, public key, and signature
558 # all we need to do is to verify. This is the exactly the same
559 # steps as performed in the avb_vbmeta_image_verify() function in
560 # libavb/avb_vbmeta_image.c.
561
562 ha = hashlib.new(alg.hash_name)
563 ha.update(header_blob)
564 ha.update(aux_blob)
565 computed_digest = ha.digest()
566
567 if computed_digest != digest_blob:
568 return False
569
570 padding_and_digest = bytearray(alg.padding)
571 padding_and_digest.extend(computed_digest)
572
573 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100574 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400575 modulus = decode_long(modulus_blob)
576 exponent = 65537
577
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500578 # We used to have this:
579 #
580 # import Crypto.PublicKey.RSA
581 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
582 # if not key.verify(decode_long(padding_and_digest),
583 # (decode_long(sig_blob), None)):
584 # return False
585 # return True
586 #
587 # but since 'avbtool verify_image' is used on the builders we don't want
588 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
589 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
590 '\n'
591 '[pubkeyinfo]\n'
592 'algorithm=SEQUENCE:rsa_alg\n'
593 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
594 '\n'
595 '[rsa_alg]\n'
596 'algorithm=OID:rsaEncryption\n'
597 'parameter=NULL\n'
598 '\n'
599 '[rsapubkey]\n'
600 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100601 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
602 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500603 asn1_tmpfile = tempfile.NamedTemporaryFile()
604 asn1_tmpfile.write(asn1_str)
605 asn1_tmpfile.flush()
606 der_tmpfile = tempfile.NamedTemporaryFile()
607 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100608 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
609 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500610 retcode = p.wait()
611 if retcode != 0:
612 raise AvbError('Error generating DER file')
613
614 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100615 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
616 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500617 stdin=subprocess.PIPE,
618 stdout=subprocess.PIPE,
619 stderr=subprocess.PIPE)
620 (pout, perr) = p.communicate(str(sig_blob))
621 retcode = p.wait()
622 if retcode != 0:
623 raise AvbError('Error verifying data: {}'.format(perr))
624 recovered_data = bytearray(pout)
625 if recovered_data != padding_and_digest:
626 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400627 return False
628 return True
629
630
David Zeuthena4fee8b2016-08-22 15:20:43 -0400631class ImageChunk(object):
632 """Data structure used for representing chunks in Android sparse files.
633
634 Attributes:
635 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
636 chunk_offset: Offset in the sparse file where this chunk begins.
637 output_offset: Offset in de-sparsified file where output begins.
638 output_size: Number of bytes in output.
639 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
640 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
641 """
642
643 FORMAT = '<2H2I'
644 TYPE_RAW = 0xcac1
645 TYPE_FILL = 0xcac2
646 TYPE_DONT_CARE = 0xcac3
647 TYPE_CRC32 = 0xcac4
648
649 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
650 input_offset, fill_data):
651 """Initializes an ImageChunk object.
652
653 Arguments:
654 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
655 chunk_offset: Offset in the sparse file where this chunk begins.
656 output_offset: Offset in de-sparsified file.
657 output_size: Number of bytes in output.
658 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
659 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
660
661 Raises:
662 ValueError: If data is not well-formed.
663 """
664 self.chunk_type = chunk_type
665 self.chunk_offset = chunk_offset
666 self.output_offset = output_offset
667 self.output_size = output_size
668 self.input_offset = input_offset
669 self.fill_data = fill_data
670 # Check invariants.
671 if self.chunk_type == self.TYPE_RAW:
672 if self.fill_data is not None:
673 raise ValueError('RAW chunk cannot have fill_data set.')
674 if not self.input_offset:
675 raise ValueError('RAW chunk must have input_offset set.')
676 elif self.chunk_type == self.TYPE_FILL:
677 if self.fill_data is None:
678 raise ValueError('FILL chunk must have fill_data set.')
679 if self.input_offset:
680 raise ValueError('FILL chunk cannot have input_offset set.')
681 elif self.chunk_type == self.TYPE_DONT_CARE:
682 if self.fill_data is not None:
683 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
684 if self.input_offset:
685 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
686 else:
687 raise ValueError('Invalid chunk type')
688
689
690class ImageHandler(object):
691 """Abstraction for image I/O with support for Android sparse images.
692
693 This class provides an interface for working with image files that
694 may be using the Android Sparse Image format. When an instance is
695 constructed, we test whether it's an Android sparse file. If so,
696 operations will be on the sparse file by interpreting the sparse
697 format, otherwise they will be directly on the file. Either way the
698 operations do the same.
699
700 For reading, this interface mimics a file object - it has seek(),
701 tell(), and read() methods. For writing, only truncation
702 (truncate()) and appending is supported (append_raw() and
703 append_dont_care()). Additionally, data can only be written in units
704 of the block size.
705
706 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400707 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400708 is_sparse: Whether the file being operated on is sparse.
709 block_size: The block size, typically 4096.
710 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400711 """
712 # See system/core/libsparse/sparse_format.h for details.
713 MAGIC = 0xed26ff3a
714 HEADER_FORMAT = '<I4H4I'
715
716 # These are formats and offset of just the |total_chunks| and
717 # |total_blocks| fields.
718 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
719 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
720
721 def __init__(self, image_filename):
722 """Initializes an image handler.
723
724 Arguments:
725 image_filename: The name of the file to operate on.
726
727 Raises:
728 ValueError: If data in the file is invalid.
729 """
David Zeuthen49936b42018-08-07 17:38:58 -0400730 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100731 self._num_total_blocks = 0
732 self._num_total_chunks = 0
733 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400734 self._read_header()
735
736 def _read_header(self):
737 """Initializes internal data structures used for reading file.
738
739 This may be called multiple times and is typically called after
740 modifying the file (e.g. appending, truncation).
741
742 Raises:
743 ValueError: If data in the file is invalid.
744 """
745 self.is_sparse = False
746 self.block_size = 4096
747 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400748 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400749 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400750 self.image_size = self._image.tell()
751
752 self._image.seek(0, os.SEEK_SET)
753 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
754 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
755 block_size, self._num_total_blocks, self._num_total_chunks,
756 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
757 if magic != self.MAGIC:
758 # Not a sparse image, our job here is done.
759 return
760 if not (major_version == 1 and minor_version == 0):
761 raise ValueError('Encountered sparse image format version {}.{} but '
762 'only 1.0 is supported'.format(major_version,
763 minor_version))
764 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
765 raise ValueError('Unexpected file_hdr_sz value {}.'.
766 format(file_hdr_sz))
767 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
768 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
769 format(chunk_hdr_sz))
770
771 self.block_size = block_size
772
773 # Build an list of chunks by parsing the file.
774 self._chunks = []
775
776 # Find the smallest offset where only "Don't care" chunks
777 # follow. This will be the size of the content in the sparse
778 # image.
779 offset = 0
780 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100781 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400782 chunk_offset = self._image.tell()
783
784 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
785 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
786 header_bin)
787 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
788
David Zeuthena4fee8b2016-08-22 15:20:43 -0400789 if chunk_type == ImageChunk.TYPE_RAW:
790 if data_sz != (chunk_sz * self.block_size):
791 raise ValueError('Raw chunk input size ({}) does not match output '
792 'size ({})'.
793 format(data_sz, chunk_sz*self.block_size))
794 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
795 chunk_offset,
796 output_offset,
797 chunk_sz*self.block_size,
798 self._image.tell(),
799 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700800 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400801
802 elif chunk_type == ImageChunk.TYPE_FILL:
803 if data_sz != 4:
804 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
805 'has {}'.format(data_sz))
806 fill_data = self._image.read(4)
807 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
808 chunk_offset,
809 output_offset,
810 chunk_sz*self.block_size,
811 None,
812 fill_data))
813 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
814 if data_sz != 0:
815 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
816 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400817 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
818 chunk_offset,
819 output_offset,
820 chunk_sz*self.block_size,
821 None,
822 None))
823 elif chunk_type == ImageChunk.TYPE_CRC32:
824 if data_sz != 4:
825 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
826 'this has {}'.format(data_sz))
827 self._image.read(4)
828 else:
829 raise ValueError('Unknown chunk type {}'.format(chunk_type))
830
831 offset += chunk_sz
832 output_offset += chunk_sz*self.block_size
833
834 # Record where sparse data end.
835 self._sparse_end = self._image.tell()
836
837 # Now that we've traversed all chunks, sanity check.
838 if self._num_total_blocks != offset:
839 raise ValueError('The header said we should have {} output blocks, '
840 'but we saw {}'.format(self._num_total_blocks, offset))
841 junk_len = len(self._image.read())
842 if junk_len > 0:
843 raise ValueError('There were {} bytes of extra data at the end of the '
844 'file.'.format(junk_len))
845
David Zeuthen09692692016-09-30 16:16:40 -0400846 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400847 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400848
849 # This is used when bisecting in read() to find the initial slice.
850 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
851
852 self.is_sparse = True
853
854 def _update_chunks_and_blocks(self):
855 """Helper function to update the image header.
856
857 The the |total_chunks| and |total_blocks| fields in the header
858 will be set to value of the |_num_total_blocks| and
859 |_num_total_chunks| attributes.
860
861 """
862 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
863 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
864 self._num_total_blocks,
865 self._num_total_chunks))
866
867 def append_dont_care(self, num_bytes):
868 """Appends a DONT_CARE chunk to the sparse file.
869
870 The given number of bytes must be a multiple of the block size.
871
872 Arguments:
873 num_bytes: Size in number of bytes of the DONT_CARE chunk.
874 """
875 assert num_bytes % self.block_size == 0
876
877 if not self.is_sparse:
878 self._image.seek(0, os.SEEK_END)
879 # This is more efficient that writing NUL bytes since it'll add
880 # a hole on file systems that support sparse files (native
881 # sparse, not Android sparse).
882 self._image.truncate(self._image.tell() + num_bytes)
883 self._read_header()
884 return
885
886 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100887 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400888 self._update_chunks_and_blocks()
889
890 self._image.seek(self._sparse_end, os.SEEK_SET)
891 self._image.write(struct.pack(ImageChunk.FORMAT,
892 ImageChunk.TYPE_DONT_CARE,
893 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100894 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400895 struct.calcsize(ImageChunk.FORMAT)))
896 self._read_header()
897
898 def append_raw(self, data):
899 """Appends a RAW chunk to the sparse file.
900
901 The length of the given data must be a multiple of the block size.
902
903 Arguments:
904 data: Data to append.
905 """
906 assert len(data) % self.block_size == 0
907
908 if not self.is_sparse:
909 self._image.seek(0, os.SEEK_END)
910 self._image.write(data)
911 self._read_header()
912 return
913
914 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100915 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400916 self._update_chunks_and_blocks()
917
918 self._image.seek(self._sparse_end, os.SEEK_SET)
919 self._image.write(struct.pack(ImageChunk.FORMAT,
920 ImageChunk.TYPE_RAW,
921 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100922 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400923 len(data) +
924 struct.calcsize(ImageChunk.FORMAT)))
925 self._image.write(data)
926 self._read_header()
927
928 def append_fill(self, fill_data, size):
929 """Appends a fill chunk to the sparse file.
930
931 The total length of the fill data must be a multiple of the block size.
932
933 Arguments:
934 fill_data: Fill data to append - must be four bytes.
935 size: Number of chunk - must be a multiple of four and the block size.
936 """
937 assert len(fill_data) == 4
938 assert size % 4 == 0
939 assert size % self.block_size == 0
940
941 if not self.is_sparse:
942 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100943 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400944 self._read_header()
945 return
946
947 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100948 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400949 self._update_chunks_and_blocks()
950
951 self._image.seek(self._sparse_end, os.SEEK_SET)
952 self._image.write(struct.pack(ImageChunk.FORMAT,
953 ImageChunk.TYPE_FILL,
954 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100955 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400956 4 + struct.calcsize(ImageChunk.FORMAT)))
957 self._image.write(fill_data)
958 self._read_header()
959
960 def seek(self, offset):
961 """Sets the cursor position for reading from unsparsified file.
962
963 Arguments:
964 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100965
966 Raises:
967 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400968 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700969 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100970 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400971 self._file_pos = offset
972
973 def read(self, size):
974 """Reads data from the unsparsified file.
975
976 This method may return fewer than |size| bytes of data if the end
977 of the file was encountered.
978
979 The file cursor for reading is advanced by the number of bytes
980 read.
981
982 Arguments:
983 size: Number of bytes to read.
984
985 Returns:
986 The data.
987
988 """
989 if not self.is_sparse:
990 self._image.seek(self._file_pos)
991 data = self._image.read(size)
992 self._file_pos += len(data)
993 return data
994
995 # Iterate over all chunks.
996 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
997 self._file_pos) - 1
998 data = bytearray()
999 to_go = size
1000 while to_go > 0:
1001 chunk = self._chunks[chunk_idx]
1002 chunk_pos_offset = self._file_pos - chunk.output_offset
1003 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1004
1005 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1006 self._image.seek(chunk.input_offset + chunk_pos_offset)
1007 data.extend(self._image.read(chunk_pos_to_go))
1008 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001009 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001010 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1011 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1012 else:
1013 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1014 data.extend('\0' * chunk_pos_to_go)
1015
1016 to_go -= chunk_pos_to_go
1017 self._file_pos += chunk_pos_to_go
1018 chunk_idx += 1
1019 # Generate partial read in case of EOF.
1020 if chunk_idx >= len(self._chunks):
1021 break
1022
1023 return data
1024
1025 def tell(self):
1026 """Returns the file cursor position for reading from unsparsified file.
1027
1028 Returns:
1029 The file cursor position for reading.
1030 """
1031 return self._file_pos
1032
1033 def truncate(self, size):
1034 """Truncates the unsparsified file.
1035
1036 Arguments:
1037 size: Desired size of unsparsified file.
1038
1039 Raises:
1040 ValueError: If desired size isn't a multiple of the block size.
1041 """
1042 if not self.is_sparse:
1043 self._image.truncate(size)
1044 self._read_header()
1045 return
1046
1047 if size % self.block_size != 0:
1048 raise ValueError('Cannot truncate to a size which is not a multiple '
1049 'of the block size')
1050
1051 if size == self.image_size:
1052 # Trivial where there's nothing to do.
1053 return
1054 elif size < self.image_size:
1055 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1056 chunk = self._chunks[chunk_idx]
1057 if chunk.output_offset != size:
1058 # Truncation in the middle of a trunk - need to keep the chunk
1059 # and modify it.
1060 chunk_idx_for_update = chunk_idx + 1
1061 num_to_keep = size - chunk.output_offset
1062 assert num_to_keep % self.block_size == 0
1063 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1064 truncate_at = (chunk.chunk_offset +
1065 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1066 data_sz = num_to_keep
1067 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1068 truncate_at = (chunk.chunk_offset +
1069 struct.calcsize(ImageChunk.FORMAT) + 4)
1070 data_sz = 4
1071 else:
1072 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1073 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1074 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001075 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001076 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1077 self._image.seek(chunk.chunk_offset)
1078 self._image.write(struct.pack(ImageChunk.FORMAT,
1079 chunk.chunk_type,
1080 0, # Reserved
1081 chunk_sz,
1082 total_sz))
1083 chunk.output_size = num_to_keep
1084 else:
1085 # Truncation at trunk boundary.
1086 truncate_at = chunk.chunk_offset
1087 chunk_idx_for_update = chunk_idx
1088
1089 self._num_total_chunks = chunk_idx_for_update
1090 self._num_total_blocks = 0
1091 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001092 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001093 self._update_chunks_and_blocks()
1094 self._image.truncate(truncate_at)
1095
1096 # We've modified the file so re-read all data.
1097 self._read_header()
1098 else:
1099 # Truncating to grow - just add a DONT_CARE section.
1100 self.append_dont_care(size - self.image_size)
1101
1102
David Zeuthen21e95262016-07-27 17:58:40 -04001103class AvbDescriptor(object):
1104 """Class for AVB descriptor.
1105
1106 See the |AvbDescriptor| C struct for more information.
1107
1108 Attributes:
1109 tag: The tag identifying what kind of descriptor this is.
1110 data: The data in the descriptor.
1111 """
1112
1113 SIZE = 16
1114 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1115
1116 def __init__(self, data):
1117 """Initializes a new property descriptor.
1118
1119 Arguments:
1120 data: If not None, must be a bytearray().
1121
1122 Raises:
1123 LookupError: If the given descriptor is malformed.
1124 """
1125 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1126
1127 if data:
1128 (self.tag, num_bytes_following) = (
1129 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1130 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1131 else:
1132 self.tag = None
1133 self.data = None
1134
1135 def print_desc(self, o):
1136 """Print the descriptor.
1137
1138 Arguments:
1139 o: The object to write the output to.
1140 """
1141 o.write(' Unknown descriptor:\n')
1142 o.write(' Tag: {}\n'.format(self.tag))
1143 if len(self.data) < 256:
1144 o.write(' Data: {} ({} bytes)\n'.format(
1145 repr(str(self.data)), len(self.data)))
1146 else:
1147 o.write(' Data: {} bytes\n'.format(len(self.data)))
1148
1149 def encode(self):
1150 """Serializes the descriptor.
1151
1152 Returns:
1153 A bytearray() with the descriptor data.
1154 """
1155 num_bytes_following = len(self.data)
1156 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1157 padding_size = nbf_with_padding - num_bytes_following
1158 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1159 padding = struct.pack(str(padding_size) + 'x')
1160 ret = desc + self.data + padding
1161 return bytearray(ret)
1162
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001163 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001164 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001165 """Verifies contents of the descriptor - used in verify_image sub-command.
1166
1167 Arguments:
1168 image_dir: The directory of the file being verified.
1169 image_ext: The extension of the file being verified (e.g. '.img').
1170 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001171 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001172 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001173 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1174 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001175
1176 Returns:
1177 True if the descriptor verifies, False otherwise.
1178 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001179 # Deletes unused parameters to prevent pylint warning unused-argument.
1180 del image_dir, image_ext, expected_chain_partitions_map
1181 del image_containing_descriptor, accept_zeroed_hashtree
1182
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001183 # Nothing to do.
1184 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001185
Jan Monscheeb28b62019-12-05 16:17:09 +01001186
David Zeuthen21e95262016-07-27 17:58:40 -04001187class AvbPropertyDescriptor(AvbDescriptor):
1188 """A class for property descriptors.
1189
1190 See the |AvbPropertyDescriptor| C struct for more information.
1191
1192 Attributes:
1193 key: The key.
1194 value: The key.
1195 """
1196
1197 TAG = 0
1198 SIZE = 32
1199 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1200 'Q' # key size (bytes)
1201 'Q') # value size (bytes)
1202
1203 def __init__(self, data=None):
1204 """Initializes a new property descriptor.
1205
1206 Arguments:
1207 data: If not None, must be a bytearray of size |SIZE|.
1208
1209 Raises:
1210 LookupError: If the given descriptor is malformed.
1211 """
1212 AvbDescriptor.__init__(self, None)
1213 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1214
1215 if data:
1216 (tag, num_bytes_following, key_size,
1217 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1218 expected_size = round_to_multiple(
1219 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1220 if tag != self.TAG or num_bytes_following != expected_size:
1221 raise LookupError('Given data does not look like a property '
1222 'descriptor.')
1223 self.key = data[self.SIZE:(self.SIZE + key_size)]
1224 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1225 value_size)]
1226 else:
1227 self.key = ''
1228 self.value = ''
1229
1230 def print_desc(self, o):
1231 """Print the descriptor.
1232
1233 Arguments:
1234 o: The object to write the output to.
1235 """
1236 if len(self.value) < 256:
1237 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1238 else:
1239 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1240
1241 def encode(self):
1242 """Serializes the descriptor.
1243
1244 Returns:
1245 A bytearray() with the descriptor data.
1246 """
1247 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1248 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1249 padding_size = nbf_with_padding - num_bytes_following
1250 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1251 len(self.key), len(self.value))
1252 padding = struct.pack(str(padding_size) + 'x')
1253 ret = desc + self.key + '\0' + self.value + '\0' + padding
1254 return bytearray(ret)
1255
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001256 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001257 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001258 """Verifies contents of the descriptor - used in verify_image sub-command.
1259
1260 Arguments:
1261 image_dir: The directory of the file being verified.
1262 image_ext: The extension of the file being verified (e.g. '.img').
1263 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001264 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001265 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001266 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1267 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001268
1269 Returns:
1270 True if the descriptor verifies, False otherwise.
1271 """
1272 # Nothing to do.
1273 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001274
Jan Monscheeb28b62019-12-05 16:17:09 +01001275
David Zeuthen21e95262016-07-27 17:58:40 -04001276class AvbHashtreeDescriptor(AvbDescriptor):
1277 """A class for hashtree descriptors.
1278
1279 See the |AvbHashtreeDescriptor| C struct for more information.
1280
1281 Attributes:
1282 dm_verity_version: dm-verity version used.
1283 image_size: Size of the image, after rounding up to |block_size|.
1284 tree_offset: Offset of the hash tree in the file.
1285 tree_size: Size of the tree.
1286 data_block_size: Data block size
1287 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001288 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1289 fec_offset: Offset of FEC data (0 if FEC is not used).
1290 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001291 hash_algorithm: Hash algorithm used.
1292 partition_name: Partition name.
1293 salt: Salt used.
1294 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001295 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001296 """
1297
1298 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001299 RESERVED = 60
1300 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001301 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1302 'L' # dm-verity version used
1303 'Q' # image size (bytes)
1304 'Q' # tree offset (bytes)
1305 'Q' # tree size (bytes)
1306 'L' # data block size (bytes)
1307 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001308 'L' # FEC number of roots
1309 'Q' # FEC offset (bytes)
1310 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001311 '32s' # hash algorithm used
1312 'L' # partition name (bytes)
1313 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001314 'L' # root digest length (bytes)
1315 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001316 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001317
1318 def __init__(self, data=None):
1319 """Initializes a new hashtree descriptor.
1320
1321 Arguments:
1322 data: If not None, must be a bytearray of size |SIZE|.
1323
1324 Raises:
1325 LookupError: If the given descriptor is malformed.
1326 """
1327 AvbDescriptor.__init__(self, None)
1328 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1329
1330 if data:
1331 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1332 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001333 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1334 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001335 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1336 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001337 expected_size = round_to_multiple(
1338 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1339 if tag != self.TAG or num_bytes_following != expected_size:
1340 raise LookupError('Given data does not look like a hashtree '
1341 'descriptor.')
1342 # Nuke NUL-bytes at the end.
1343 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1344 o = 0
1345 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1346 partition_name_len)])
1347 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1348 self.partition_name.decode('utf-8')
1349 o += partition_name_len
1350 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1351 o += salt_len
1352 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1353 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001354 if root_digest_len != 0:
1355 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001356
1357 else:
1358 self.dm_verity_version = 0
1359 self.image_size = 0
1360 self.tree_offset = 0
1361 self.tree_size = 0
1362 self.data_block_size = 0
1363 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001364 self.fec_num_roots = 0
1365 self.fec_offset = 0
1366 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001367 self.hash_algorithm = ''
1368 self.partition_name = ''
1369 self.salt = bytearray()
1370 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001371 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001372
1373 def print_desc(self, o):
1374 """Print the descriptor.
1375
1376 Arguments:
1377 o: The object to write the output to.
1378 """
1379 o.write(' Hashtree descriptor:\n')
1380 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1381 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1382 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1383 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1384 o.write(' Data Block Size: {} bytes\n'.format(
1385 self.data_block_size))
1386 o.write(' Hash Block Size: {} bytes\n'.format(
1387 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001388 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1389 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1390 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001391 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1392 o.write(' Partition Name: {}\n'.format(self.partition_name))
1393 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1394 'hex')))
1395 o.write(' Root Digest: {}\n'.format(str(
1396 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001397 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001398
1399 def encode(self):
1400 """Serializes the descriptor.
1401
1402 Returns:
1403 A bytearray() with the descriptor data.
1404 """
1405 encoded_name = self.partition_name.encode('utf-8')
1406 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1407 len(self.root_digest) - 16)
1408 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1409 padding_size = nbf_with_padding - num_bytes_following
1410 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1411 self.dm_verity_version, self.image_size,
1412 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001413 self.hash_block_size, self.fec_num_roots,
1414 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001415 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001416 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001417 padding = struct.pack(str(padding_size) + 'x')
1418 ret = desc + encoded_name + self.salt + self.root_digest + padding
1419 return bytearray(ret)
1420
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001421 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001422 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001423 """Verifies contents of the descriptor - used in verify_image sub-command.
1424
1425 Arguments:
1426 image_dir: The directory of the file being verified.
1427 image_ext: The extension of the file being verified (e.g. '.img').
1428 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001429 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001430 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001431 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1432 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001433
1434 Returns:
1435 True if the descriptor verifies, False otherwise.
1436 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001437 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001438 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001439 image = image_containing_descriptor
1440 else:
1441 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1442 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001443 # Generate the hashtree and checks that it matches what's in the file.
1444 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1445 digest_padding = round_to_pow2(digest_size) - digest_size
1446 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001447 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001448 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1449 self.data_block_size,
1450 self.hash_algorithm, self.salt,
1451 digest_padding,
1452 hash_level_offsets,
1453 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001454 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001455 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001456 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1457 format(image_filename))
1458 return False
1459 # ... also check that the on-disk hashtree matches
1460 image.seek(self.tree_offset)
1461 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001462 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001463 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001464 print('{}: skipping verification since hashtree is zeroed and '
1465 '--accept_zeroed_hashtree was given'
1466 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001467 else:
1468 if hash_tree != hash_tree_ondisk:
1469 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001470 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001471 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001472 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1473 .format(self.partition_name, self.hash_algorithm, image.filename,
1474 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001475 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1476 # correct but this a) currently requires the 'fec' binary; and b) takes a
1477 # long time; and c) is not strictly needed for verification purposes as
1478 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001479 return True
1480
David Zeuthen21e95262016-07-27 17:58:40 -04001481
1482class AvbHashDescriptor(AvbDescriptor):
1483 """A class for hash descriptors.
1484
1485 See the |AvbHashDescriptor| C struct for more information.
1486
1487 Attributes:
1488 image_size: Image size, in bytes.
1489 hash_algorithm: Hash algorithm used.
1490 partition_name: Partition name.
1491 salt: Salt used.
1492 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001493 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001494 """
1495
1496 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001497 RESERVED = 60
1498 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001499 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1500 'Q' # image size (bytes)
1501 '32s' # hash algorithm used
1502 'L' # partition name (bytes)
1503 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001504 'L' # digest length (bytes)
1505 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001506 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001507
1508 def __init__(self, data=None):
1509 """Initializes a new hash descriptor.
1510
1511 Arguments:
1512 data: If not None, must be a bytearray of size |SIZE|.
1513
1514 Raises:
1515 LookupError: If the given descriptor is malformed.
1516 """
1517 AvbDescriptor.__init__(self, None)
1518 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1519
1520 if data:
1521 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1522 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001523 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1524 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001525 expected_size = round_to_multiple(
1526 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1527 if tag != self.TAG or num_bytes_following != expected_size:
1528 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1529 # Nuke NUL-bytes at the end.
1530 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1531 o = 0
1532 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1533 partition_name_len)])
1534 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1535 self.partition_name.decode('utf-8')
1536 o += partition_name_len
1537 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1538 o += salt_len
1539 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1540 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001541 if digest_len != 0:
1542 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001543
1544 else:
1545 self.image_size = 0
1546 self.hash_algorithm = ''
1547 self.partition_name = ''
1548 self.salt = bytearray()
1549 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001550 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001551
1552 def print_desc(self, o):
1553 """Print the descriptor.
1554
1555 Arguments:
1556 o: The object to write the output to.
1557 """
1558 o.write(' Hash descriptor:\n')
1559 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1560 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1561 o.write(' Partition Name: {}\n'.format(self.partition_name))
1562 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1563 'hex')))
1564 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1565 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001566 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001567
1568 def encode(self):
1569 """Serializes the descriptor.
1570
1571 Returns:
1572 A bytearray() with the descriptor data.
1573 """
1574 encoded_name = self.partition_name.encode('utf-8')
1575 num_bytes_following = (
1576 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1577 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1578 padding_size = nbf_with_padding - num_bytes_following
1579 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1580 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001581 len(self.salt), len(self.digest), self.flags,
1582 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001583 padding = struct.pack(str(padding_size) + 'x')
1584 ret = desc + encoded_name + self.salt + self.digest + padding
1585 return bytearray(ret)
1586
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001587 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001588 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001589 """Verifies contents of the descriptor - used in verify_image sub-command.
1590
1591 Arguments:
1592 image_dir: The directory of the file being verified.
1593 image_ext: The extension of the file being verified (e.g. '.img').
1594 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001595 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001596 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001597 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1598 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001599
1600 Returns:
1601 True if the descriptor verifies, False otherwise.
1602 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001603 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001604 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001605 image = image_containing_descriptor
1606 else:
1607 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1608 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001609 data = image.read(self.image_size)
1610 ha = hashlib.new(self.hash_algorithm)
1611 ha.update(self.salt)
1612 ha.update(data)
1613 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001614 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001615 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001616 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1617 format(self.hash_algorithm, image_filename))
1618 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001619 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1620 .format(self.partition_name, self.hash_algorithm, image.filename,
1621 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001622 return True
1623
David Zeuthen21e95262016-07-27 17:58:40 -04001624
1625class AvbKernelCmdlineDescriptor(AvbDescriptor):
1626 """A class for kernel command-line descriptors.
1627
1628 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1629
1630 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001631 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001632 kernel_cmdline: The kernel command-line.
1633 """
1634
1635 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001636 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001637 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001638 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001639 'L') # cmdline length (bytes)
1640
David Zeuthenfd41eb92016-11-17 12:24:47 -05001641 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1642 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1643
David Zeuthen21e95262016-07-27 17:58:40 -04001644 def __init__(self, data=None):
1645 """Initializes a new kernel cmdline descriptor.
1646
1647 Arguments:
1648 data: If not None, must be a bytearray of size |SIZE|.
1649
1650 Raises:
1651 LookupError: If the given descriptor is malformed.
1652 """
1653 AvbDescriptor.__init__(self, None)
1654 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1655
1656 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001657 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001658 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1659 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1660 8)
1661 if tag != self.TAG or num_bytes_following != expected_size:
1662 raise LookupError('Given data does not look like a kernel cmdline '
1663 'descriptor.')
1664 # Nuke NUL-bytes at the end.
1665 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1666 kernel_cmdline_length)])
1667 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1668 self.kernel_cmdline.decode('utf-8')
1669 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001670 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001671 self.kernel_cmdline = ''
1672
1673 def print_desc(self, o):
1674 """Print the descriptor.
1675
1676 Arguments:
1677 o: The object to write the output to.
1678 """
1679 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001680 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001681 o.write(' Kernel Cmdline: {}\n'.format(repr(
1682 self.kernel_cmdline)))
1683
1684 def encode(self):
1685 """Serializes the descriptor.
1686
1687 Returns:
1688 A bytearray() with the descriptor data.
1689 """
1690 encoded_str = self.kernel_cmdline.encode('utf-8')
1691 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1692 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1693 padding_size = nbf_with_padding - num_bytes_following
1694 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001695 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001696 padding = struct.pack(str(padding_size) + 'x')
1697 ret = desc + encoded_str + padding
1698 return bytearray(ret)
1699
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001700 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001701 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001702 """Verifies contents of the descriptor - used in verify_image sub-command.
1703
1704 Arguments:
1705 image_dir: The directory of the file being verified.
1706 image_ext: The extension of the file being verified (e.g. '.img').
1707 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001708 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001709 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001710 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1711 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001712
1713 Returns:
1714 True if the descriptor verifies, False otherwise.
1715 """
1716 # Nothing to verify.
1717 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001718
Jan Monscheeb28b62019-12-05 16:17:09 +01001719
David Zeuthen21e95262016-07-27 17:58:40 -04001720class AvbChainPartitionDescriptor(AvbDescriptor):
1721 """A class for chained partition descriptors.
1722
1723 See the |AvbChainPartitionDescriptor| C struct for more information.
1724
1725 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001726 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001727 partition_name: Partition name.
1728 public_key: Bytes for the public key.
1729 """
1730
1731 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001732 RESERVED = 64
1733 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001734 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001735 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001736 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001737 'L' + # public_key_size (bytes)
1738 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001739
1740 def __init__(self, data=None):
1741 """Initializes a new chain partition descriptor.
1742
1743 Arguments:
1744 data: If not None, must be a bytearray of size |SIZE|.
1745
1746 Raises:
1747 LookupError: If the given descriptor is malformed.
1748 """
1749 AvbDescriptor.__init__(self, None)
1750 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1751
1752 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001753 (tag, num_bytes_following, self.rollback_index_location,
1754 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001755 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001756 expected_size = round_to_multiple(
1757 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1758 if tag != self.TAG or num_bytes_following != expected_size:
1759 raise LookupError('Given data does not look like a chain partition '
1760 'descriptor.')
1761 o = 0
1762 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1763 partition_name_len)])
1764 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1765 self.partition_name.decode('utf-8')
1766 o += partition_name_len
1767 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1768
1769 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001770 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001771 self.partition_name = ''
1772 self.public_key = bytearray()
1773
1774 def print_desc(self, o):
1775 """Print the descriptor.
1776
1777 Arguments:
1778 o: The object to write the output to.
1779 """
1780 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001781 o.write(' Partition Name: {}\n'.format(self.partition_name))
1782 o.write(' Rollback Index Location: {}\n'.format(
1783 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001784 # Just show the SHA1 of the key, for size reasons.
1785 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001786 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001787
1788 def encode(self):
1789 """Serializes the descriptor.
1790
1791 Returns:
1792 A bytearray() with the descriptor data.
1793 """
1794 encoded_name = self.partition_name.encode('utf-8')
1795 num_bytes_following = (
1796 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1797 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1798 padding_size = nbf_with_padding - num_bytes_following
1799 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001800 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001801 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001802 padding = struct.pack(str(padding_size) + 'x')
1803 ret = desc + encoded_name + self.public_key + padding
1804 return bytearray(ret)
1805
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001806 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001807 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001808 """Verifies contents of the descriptor - used in verify_image sub-command.
1809
1810 Arguments:
1811 image_dir: The directory of the file being verified.
1812 image_ext: The extension of the file being verified (e.g. '.img').
1813 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001814 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001815 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001816 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1817 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001818
1819 Returns:
1820 True if the descriptor verifies, False otherwise.
1821 """
1822 value = expected_chain_partitions_map.get(self.partition_name)
1823 if not value:
1824 sys.stderr.write('No expected chain partition for partition {}. Use '
1825 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001826 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001827 format(self.partition_name))
1828 return False
1829 rollback_index_location, pk_blob = value
1830
1831 if self.rollback_index_location != rollback_index_location:
1832 sys.stderr.write('Expected rollback_index_location {} does not '
1833 'match {} in descriptor for partition {}\n'.
1834 format(rollback_index_location,
1835 self.rollback_index_location,
1836 self.partition_name))
1837 return False
1838
1839 if self.public_key != pk_blob:
1840 sys.stderr.write('Expected public key blob does not match public '
1841 'key blob in descriptor for partition {}\n'.
1842 format(self.partition_name))
1843 return False
1844
Jan Monsch23e0c622019-12-11 11:23:58 +01001845 print('{}: Successfully verified chain partition descriptor matches '
1846 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001847
1848 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001849
1850DESCRIPTOR_CLASSES = [
1851 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1852 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1853]
1854
1855
1856def parse_descriptors(data):
1857 """Parses a blob of data into descriptors.
1858
1859 Arguments:
1860 data: A bytearray() with encoded descriptors.
1861
1862 Returns:
1863 A list of instances of objects derived from AvbDescriptor. For
1864 unknown descriptors, the class AvbDescriptor is used.
1865 """
1866 o = 0
1867 ret = []
1868 while o < len(data):
1869 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1870 if tag < len(DESCRIPTOR_CLASSES):
1871 c = DESCRIPTOR_CLASSES[tag]
1872 else:
1873 c = AvbDescriptor
1874 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1875 o += 16 + nb_following
1876 return ret
1877
1878
1879class AvbFooter(object):
1880 """A class for parsing and writing footers.
1881
1882 Footers are stored at the end of partitions and point to where the
1883 AvbVBMeta blob is located. They also contain the original size of
1884 the image before AVB information was added.
1885
1886 Attributes:
1887 magic: Magic for identifying the footer, see |MAGIC|.
1888 version_major: The major version of avbtool that wrote the footer.
1889 version_minor: The minor version of avbtool that wrote the footer.
1890 original_image_size: Original image size.
1891 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1892 vbmeta_size: Size of the AvbVBMeta blob.
1893 """
1894
1895 MAGIC = 'AVBf'
1896 SIZE = 64
1897 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001898 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1899 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001900 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1901 'Q' # Original image size.
1902 'Q' # Offset of VBMeta blob.
1903 'Q' + # Size of VBMeta blob.
1904 str(RESERVED) + 'x') # padding for reserved bytes
1905
1906 def __init__(self, data=None):
1907 """Initializes a new footer object.
1908
1909 Arguments:
1910 data: If not None, must be a bytearray of size 4096.
1911
1912 Raises:
1913 LookupError: If the given footer is malformed.
1914 struct.error: If the given data has no footer.
1915 """
1916 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1917
1918 if data:
1919 (self.magic, self.version_major, self.version_minor,
1920 self.original_image_size, self.vbmeta_offset,
1921 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1922 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001923 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001924 else:
1925 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001926 self.version_major = self.FOOTER_VERSION_MAJOR
1927 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001928 self.original_image_size = 0
1929 self.vbmeta_offset = 0
1930 self.vbmeta_size = 0
1931
David Zeuthena4fee8b2016-08-22 15:20:43 -04001932 def encode(self):
1933 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001934
David Zeuthena4fee8b2016-08-22 15:20:43 -04001935 Returns:
1936 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001937 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001938 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1939 self.version_minor, self.original_image_size,
1940 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001941
1942
1943class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001944 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001945
Jan Monschfe00c0a2019-12-11 11:19:40 +01001946 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1947 avb_vbmeta_image.h.
1948
David Zeuthen21e95262016-07-27 17:58:40 -04001949 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001950 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1951 required_libavb_version_major: The major version of libavb required for this
1952 header.
1953 required_libavb_version_minor: The minor version of libavb required for this
1954 header.
1955 authentication_data_block_size: The size of the signature block.
1956 auxiliary_data_block_size: The size of the auxiliary data block.
1957 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1958 enum.
1959 hash_offset: Offset into the "Authentication data" block of hash data.
1960 hash_size: Length of the hash data.
1961 signature_offset: Offset into the "Authentication data" block of signature
1962 data.
1963 signature_size: Length of the signature data.
1964 public_key_offset: Offset into the "Auxiliary data" block of public key
1965 data.
1966 public_key_size: Length of the public key data.
1967 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1968 key metadata.
1969 public_key_metadata_size: Length of the public key metadata. Must be set to
1970 zero if there is no public key metadata.
1971 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1972 data.
1973 descriptors_size: Length of descriptor data.
1974 rollback_index: The rollback index which can be used to prevent rollback to
1975 older versions.
1976 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
1977 zero if the vbmeta image is not a top-level image.
1978 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
1979 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
1980 terminated. Applications must not make assumptions about how this
1981 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04001982 """
1983
1984 SIZE = 256
1985
David Zeuthene3cadca2017-02-22 21:25:46 -05001986 # Keep in sync with |reserved0| and |reserved| field of
1987 # |AvbVBMetaImageHeader|.
1988 RESERVED0 = 4
1989 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001990
1991 # Keep in sync with |AvbVBMetaImageHeader|.
1992 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1993 '2Q' # 2 x block size
1994 'L' # algorithm type
1995 '2Q' # offset, size (hash)
1996 '2Q' # offset, size (signature)
1997 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001998 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001999 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002000 'Q' # rollback_index
2001 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002002 str(RESERVED0) + 'x' + # padding for reserved bytes
2003 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002004 str(RESERVED) + 'x') # padding for reserved bytes
2005
2006 def __init__(self, data=None):
2007 """Initializes a new header object.
2008
2009 Arguments:
2010 data: If not None, must be a bytearray of size 8192.
2011
2012 Raises:
2013 Exception: If the given data is malformed.
2014 """
2015 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2016
2017 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002018 (self.magic, self.required_libavb_version_major,
2019 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002020 self.authentication_data_block_size, self.auxiliary_data_block_size,
2021 self.algorithm_type, self.hash_offset, self.hash_size,
2022 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002023 self.public_key_size, self.public_key_metadata_offset,
2024 self.public_key_metadata_size, self.descriptors_offset,
2025 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002026 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002027 self.flags,
2028 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002029 # Nuke NUL-bytes at the end of the string.
2030 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002031 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002032 else:
2033 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002034 # Start by just requiring version 1.0. Code that adds features
2035 # in a future version can use bump_required_libavb_version_minor() to
2036 # bump the minor.
2037 self.required_libavb_version_major = AVB_VERSION_MAJOR
2038 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002039 self.authentication_data_block_size = 0
2040 self.auxiliary_data_block_size = 0
2041 self.algorithm_type = 0
2042 self.hash_offset = 0
2043 self.hash_size = 0
2044 self.signature_offset = 0
2045 self.signature_size = 0
2046 self.public_key_offset = 0
2047 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002048 self.public_key_metadata_offset = 0
2049 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002050 self.descriptors_offset = 0
2051 self.descriptors_size = 0
2052 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002053 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002054 self.release_string = get_release_string()
2055
2056 def bump_required_libavb_version_minor(self, minor):
2057 """Function to bump required_libavb_version_minor.
2058
2059 Call this when writing data that requires a specific libavb
2060 version to parse it.
2061
2062 Arguments:
2063 minor: The minor version of libavb that has support for the feature.
2064 """
2065 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002066 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002067
David Zeuthen21e95262016-07-27 17:58:40 -04002068 def encode(self):
2069 """Serializes the header (256) to a bytearray().
2070
2071 Returns:
2072 A bytearray() with the encoded header.
2073 """
2074 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002075 self.required_libavb_version_major,
2076 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002077 self.authentication_data_block_size,
2078 self.auxiliary_data_block_size, self.algorithm_type,
2079 self.hash_offset, self.hash_size, self.signature_offset,
2080 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002081 self.public_key_size, self.public_key_metadata_offset,
2082 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002083 self.descriptors_size, self.rollback_index, self.flags,
2084 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002085
2086
2087class Avb(object):
2088 """Business logic for avbtool command-line tool."""
2089
David Zeuthen8b6973b2016-09-20 12:39:49 -04002090 # Keep in sync with avb_ab_flow.h.
2091 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2092 AB_MAGIC = '\0AB0'
2093 AB_MAJOR_VERSION = 1
2094 AB_MINOR_VERSION = 0
2095 AB_MISC_METADATA_OFFSET = 2048
2096
David Zeuthen09692692016-09-30 16:16:40 -04002097 # Constants for maximum metadata size. These are used to give
2098 # meaningful errors if the value passed in via --partition_size is
2099 # too small and when --calc_max_image_size is used. We use
2100 # conservative figures.
2101 MAX_VBMETA_SIZE = 64 * 1024
2102 MAX_FOOTER_SIZE = 4096
2103
Jan Monsch2c7be992020-04-03 14:37:13 +02002104 def generate_test_image(self, output, image_size, start_byte):
2105 """Generates a test image for testing avbtool with known content.
2106
2107 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2108
2109 Arguments:
2110 output: Write test image to this file.
2111 image_size: The size of the requested file in bytes.
2112 start_byte: The integer value of the start byte to use for pattern
2113 generation.
2114 """
2115 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2116 buf = bytearray()
2117 c = int(math.ceil(image_size / 256.0))
2118 for _ in range(0, c):
2119 buf.extend(pattern)
2120 output.write(buf[0:image_size])
2121
David Zeuthen49936b42018-08-07 17:38:58 -04002122 def extract_vbmeta_image(self, output, image_filename, padding_size):
2123 """Implements the 'extract_vbmeta_image' command.
2124
2125 Arguments:
2126 output: Write vbmeta struct to this file.
2127 image_filename: File to extract vbmeta data from (with a footer).
2128 padding_size: If not 0, pads output so size is a multiple of the number.
2129
2130 Raises:
2131 AvbError: If there's no footer in the image.
2132 """
2133 image = ImageHandler(image_filename)
2134
2135 (footer, _, _, _) = self._parse_image(image)
2136
2137 if not footer:
2138 raise AvbError('Given image does not have a footer.')
2139
2140 image.seek(footer.vbmeta_offset)
2141 vbmeta_blob = image.read(footer.vbmeta_size)
2142 output.write(vbmeta_blob)
2143
2144 if padding_size > 0:
2145 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2146 padding_needed = padded_size - len(vbmeta_blob)
2147 output.write('\0' * padding_needed)
2148
David Zeuthena4fee8b2016-08-22 15:20:43 -04002149 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002150 """Implements the 'erase_footer' command.
2151
2152 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002153 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002154 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002155
2156 Raises:
2157 AvbError: If there's no footer in the image.
2158 """
2159
David Zeuthena4fee8b2016-08-22 15:20:43 -04002160 image = ImageHandler(image_filename)
2161
David Zeuthen21e95262016-07-27 17:58:40 -04002162 (footer, _, descriptors, _) = self._parse_image(image)
2163
2164 if not footer:
2165 raise AvbError('Given image does not have a footer.')
2166
2167 new_image_size = None
2168 if not keep_hashtree:
2169 new_image_size = footer.original_image_size
2170 else:
2171 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002172 # descriptor to figure out the location and size of the hashtree
2173 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002174 for desc in descriptors:
2175 if isinstance(desc, AvbHashtreeDescriptor):
2176 # The hashtree is always just following the main data so the
2177 # new size is easily derived.
2178 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002179 # If the image has FEC codes, also keep those.
2180 if desc.fec_offset > 0:
2181 fec_end = desc.fec_offset + desc.fec_size
2182 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002183 break
2184 if not new_image_size:
2185 raise AvbError('Requested to keep hashtree but no hashtree '
2186 'descriptor was found.')
2187
2188 # And cut...
2189 image.truncate(new_image_size)
2190
David Zeuthen1394f762019-04-30 10:20:11 -04002191 def zero_hashtree(self, image_filename):
2192 """Implements the 'zero_hashtree' command.
2193
2194 Arguments:
2195 image_filename: File to zero hashtree and FEC data from.
2196
2197 Raises:
2198 AvbError: If there's no footer in the image.
2199 """
2200
2201 image = ImageHandler(image_filename)
2202
2203 (footer, _, descriptors, _) = self._parse_image(image)
2204
2205 if not footer:
2206 raise AvbError('Given image does not have a footer.')
2207
2208 # Search for a hashtree descriptor to figure out the location and
2209 # size of the hashtree and FEC.
2210 ht_desc = None
2211 for desc in descriptors:
2212 if isinstance(desc, AvbHashtreeDescriptor):
2213 ht_desc = desc
2214 break
2215
2216 if not ht_desc:
2217 raise AvbError('No hashtree descriptor was found.')
2218
2219 zero_ht_start_offset = ht_desc.tree_offset
2220 zero_ht_num_bytes = ht_desc.tree_size
2221 zero_fec_start_offset = None
2222 zero_fec_num_bytes = 0
2223 if ht_desc.fec_offset > 0:
2224 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2225 raise AvbError('Hash-tree and FEC data must be adjacent.')
2226 zero_fec_start_offset = ht_desc.fec_offset
2227 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002228 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2229 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002230 image.seek(zero_end_offset)
2231 data = image.read(image.image_size - zero_end_offset)
2232
2233 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2234 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2235 # beginning of both hashtree and FEC. (That way, in the future we can add
2236 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2237 #
2238 # Applications can use these markers to detect that the hashtree and/or
2239 # FEC needs to be recomputed.
2240 image.truncate(zero_ht_start_offset)
2241 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2242 image.append_raw(data_zeroed_firstblock)
2243 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2244 if zero_fec_start_offset:
2245 image.append_raw(data_zeroed_firstblock)
2246 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2247 image.append_raw(data)
2248
David Zeuthen2bc232b2017-04-19 14:25:19 -04002249 def resize_image(self, image_filename, partition_size):
2250 """Implements the 'resize_image' command.
2251
2252 Arguments:
2253 image_filename: File with footer to resize.
2254 partition_size: The new size of the image.
2255
2256 Raises:
2257 AvbError: If there's no footer in the image.
2258 """
2259
2260 image = ImageHandler(image_filename)
2261
2262 if partition_size % image.block_size != 0:
2263 raise AvbError('Partition size of {} is not a multiple of the image '
2264 'block size {}.'.format(partition_size,
2265 image.block_size))
2266
Jan Monsch77cd2022019-12-10 17:18:04 +01002267 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002268
2269 if not footer:
2270 raise AvbError('Given image does not have a footer.')
2271
2272 # The vbmeta blob is always at the end of the data so resizing an
2273 # image amounts to just moving the footer around.
2274
2275 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2276 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002277 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2278 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002279
2280 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002281 raise AvbError('Requested size of {} is too small for an image '
2282 'of size {}.'
2283 .format(partition_size,
2284 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002285
2286 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2287 # with enough bytes such that the final Footer block is at the end
2288 # of partition_size.
2289 image.truncate(vbmeta_end_offset)
2290 image.append_dont_care(partition_size - vbmeta_end_offset -
2291 1*image.block_size)
2292
2293 # Just reuse the same footer - only difference is that we're
2294 # writing it in a different place.
2295 footer_blob = footer.encode()
2296 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2297 footer_blob)
2298 image.append_raw(footer_blob_with_padding)
2299
David Zeuthen8b6973b2016-09-20 12:39:49 -04002300 def set_ab_metadata(self, misc_image, slot_data):
2301 """Implements the 'set_ab_metadata' command.
2302
2303 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2304 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2305
2306 Arguments:
2307 misc_image: The misc image to write to.
2308 slot_data: Slot data as a string
2309
2310 Raises:
2311 AvbError: If slot data is malformed.
2312 """
2313 tokens = slot_data.split(':')
2314 if len(tokens) != 6:
2315 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2316 a_priority = int(tokens[0])
2317 a_tries_remaining = int(tokens[1])
2318 a_success = True if int(tokens[2]) != 0 else False
2319 b_priority = int(tokens[3])
2320 b_tries_remaining = int(tokens[4])
2321 b_success = True if int(tokens[5]) != 0 else False
2322
2323 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2324 self.AB_MAGIC,
2325 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2326 a_priority, a_tries_remaining, a_success,
2327 b_priority, b_tries_remaining, b_success)
2328 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2329 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2330 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2331 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2332 misc_image.write(ab_data)
2333
David Zeuthena4fee8b2016-08-22 15:20:43 -04002334 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002335 """Implements the 'info_image' command.
2336
2337 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002338 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002339 output: Output file to write human-readable information to (file object).
2340 """
2341
David Zeuthena4fee8b2016-08-22 15:20:43 -04002342 image = ImageHandler(image_filename)
2343
David Zeuthen21e95262016-07-27 17:58:40 -04002344 o = output
2345
2346 (footer, header, descriptors, image_size) = self._parse_image(image)
2347
Bowgo Tsaid7145942020-03-20 17:03:51 +08002348 # To show the SHA1 of the public key.
2349 vbmeta_blob = self._load_vbmeta_blob(image)
2350 key_offset = (header.SIZE +
2351 header.authentication_data_block_size +
2352 header.public_key_offset)
2353 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2354
David Zeuthen21e95262016-07-27 17:58:40 -04002355 if footer:
2356 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2357 footer.version_minor))
2358 o.write('Image size: {} bytes\n'.format(image_size))
2359 o.write('Original image size: {} bytes\n'.format(
2360 footer.original_image_size))
2361 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2362 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2363 o.write('--\n')
2364
2365 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2366
David Zeuthene3cadca2017-02-22 21:25:46 -05002367 o.write('Minimum libavb version: {}.{}{}\n'.format(
2368 header.required_libavb_version_major,
2369 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002370 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002371 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2372 o.write('Authentication Block: {} bytes\n'.format(
2373 header.authentication_data_block_size))
2374 o.write('Auxiliary Block: {} bytes\n'.format(
2375 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002376 if key_blob:
2377 hexdig = hashlib.sha1(key_blob).hexdigest()
2378 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002379 o.write('Algorithm: {}\n'.format(alg_name))
2380 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002381 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002382 o.write('Release String: \'{}\'\n'.format(
2383 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002384
2385 # Print descriptors.
2386 num_printed = 0
2387 o.write('Descriptors:\n')
2388 for desc in descriptors:
2389 desc.print_desc(o)
2390 num_printed += 1
2391 if num_printed == 0:
2392 o.write(' (none)\n')
2393
Jan Monscheeb28b62019-12-05 16:17:09 +01002394 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2395 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002396 """Implements the 'verify_image' command.
2397
2398 Arguments:
2399 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002400 key_path: None or check that embedded public key matches key at given
2401 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002402 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002403 follow_chain_partitions:
2404 If True, will follows chain partitions even when not specified with
2405 the --expected_chain_partition option
2406 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2407 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002408
2409 Raises:
2410 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002411 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002412 expected_chain_partitions_map = {}
2413 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002414 for cp in expected_chain_partitions:
2415 cp_tokens = cp.split(':')
2416 if len(cp_tokens) != 3:
2417 raise AvbError('Malformed chained partition "{}".'.format(cp))
2418 partition_name = cp_tokens[0]
2419 rollback_index_location = int(cp_tokens[1])
2420 file_path = cp_tokens[2]
2421 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002422 expected_chain_partitions_map[partition_name] = (
2423 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002424
2425 image_dir = os.path.dirname(image_filename)
2426 image_ext = os.path.splitext(image_filename)[1]
2427
2428 key_blob = None
2429 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002430 print('Verifying image {} using key at {}'.format(image_filename,
2431 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002432 key_blob = encode_rsa_key(key_path)
2433 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002434 print('Verifying image {} using embedded public key'.format(
2435 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002436
David Zeuthenb623d8b2017-04-04 16:05:53 -04002437 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002438 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002439 offset = 0
2440 if footer:
2441 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002442
David Zeuthenb623d8b2017-04-04 16:05:53 -04002443 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002444 vbmeta_blob = image.read(header.SIZE
2445 + header.authentication_data_block_size
2446 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002447
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002448 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002449 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002450 raise AvbError('Signature check failed for {} vbmeta struct {}'
2451 .format(alg_name, image_filename))
2452
2453 if key_blob:
2454 # The embedded public key is in the auxiliary block at an offset.
2455 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002456 key_offset += header.authentication_data_block_size
2457 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002458 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2459 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002460 if key_blob != key_blob_in_vbmeta:
2461 raise AvbError('Embedded public key does not match given key.')
2462
2463 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002464 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2465 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002466 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002467 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2468 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002469
2470 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002471 if (isinstance(desc, AvbChainPartitionDescriptor)
2472 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002473 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002474 # In this case we're processing a chain descriptor but don't have a
2475 # --expect_chain_partition ... however --follow_chain_partitions was
2476 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002477 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2478 'and KEY (which has sha1 {}) not specified'
2479 .format(desc.partition_name, desc.rollback_index_location,
2480 hashlib.sha1(desc.public_key).hexdigest()))
2481 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002482 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002483 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002484 # Honor --follow_chain_partitions - add '--' to make the output more
2485 # readable.
2486 if (isinstance(desc, AvbChainPartitionDescriptor)
2487 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002488 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002489 chained_image_filename = os.path.join(image_dir,
2490 desc.partition_name + image_ext)
2491 self.verify_image(chained_image_filename, key_path, None, False,
2492 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002493
David Zeuthenb8643c02018-05-17 17:21:18 -04002494 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2495 """Implements the 'calculate_vbmeta_digest' command.
2496
2497 Arguments:
2498 image_filename: Image file to get information from (file object).
2499 hash_algorithm: Hash algorithm used.
2500 output: Output file to write human-readable information to (file object).
2501 """
2502
2503 image_dir = os.path.dirname(image_filename)
2504 image_ext = os.path.splitext(image_filename)[1]
2505
2506 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002507 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002508 offset = 0
2509 if footer:
2510 offset = footer.vbmeta_offset
2511 size = (header.SIZE + header.authentication_data_block_size +
2512 header.auxiliary_data_block_size)
2513 image.seek(offset)
2514 vbmeta_blob = image.read(size)
2515
2516 hasher = hashlib.new(name=hash_algorithm)
2517 hasher.update(vbmeta_blob)
2518
2519 for desc in descriptors:
2520 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002521 ch_image_filename = os.path.join(image_dir,
2522 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002523 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002524 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002525 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002526 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2527 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002528 if ch_footer:
2529 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002530 ch_image.seek(ch_offset)
2531 ch_vbmeta_blob = ch_image.read(ch_size)
2532 hasher.update(ch_vbmeta_blob)
2533
2534 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002535 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002536
David Zeuthenf7d2e752018-09-20 13:30:41 -04002537 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2538 """Implements the 'calculate_kernel_cmdline' command.
2539
2540 Arguments:
2541 image_filename: Image file to get information from (file object).
2542 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2543 output: Output file to write human-readable information to (file object).
2544 """
2545
2546 image = ImageHandler(image_filename)
2547 _, _, descriptors, _ = self._parse_image(image)
2548
2549 image_dir = os.path.dirname(image_filename)
2550 image_ext = os.path.splitext(image_filename)[1]
2551
2552 cmdline_descriptors = []
2553 for desc in descriptors:
2554 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002555 ch_image_filename = os.path.join(image_dir,
2556 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002557 ch_image = ImageHandler(ch_image_filename)
2558 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2559 for ch_desc in ch_descriptors:
2560 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2561 cmdline_descriptors.append(ch_desc)
2562 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2563 cmdline_descriptors.append(desc)
2564
2565 kernel_cmdline_snippets = []
2566 for desc in cmdline_descriptors:
2567 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002568 if ((desc.flags &
2569 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2570 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002571 if hashtree_disabled:
2572 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002573 if (desc.flags &
2574 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002575 if not hashtree_disabled:
2576 use_cmdline = False
2577 if use_cmdline:
2578 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2579 output.write(' '.join(kernel_cmdline_snippets))
2580
David Zeuthen21e95262016-07-27 17:58:40 -04002581 def _parse_image(self, image):
2582 """Gets information about an image.
2583
2584 The image can either be a vbmeta or an image with a footer.
2585
2586 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002587 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002588
2589 Returns:
2590 A tuple where the first argument is a AvbFooter (None if there
2591 is no footer on the image), the second argument is a
2592 AvbVBMetaHeader, the third argument is a list of
2593 AvbDescriptor-derived instances, and the fourth argument is the
2594 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002595
2596 Raises:
2597 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002598 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002599 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002600 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002601 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002602 try:
2603 footer = AvbFooter(image.read(AvbFooter.SIZE))
2604 except (LookupError, struct.error):
2605 # Nope, just seek back to the start.
2606 image.seek(0)
2607
2608 vbmeta_offset = 0
2609 if footer:
2610 vbmeta_offset = footer.vbmeta_offset
2611
2612 image.seek(vbmeta_offset)
2613 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2614
2615 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2616 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2617 desc_start_offset = aux_block_offset + h.descriptors_offset
2618 image.seek(desc_start_offset)
2619 descriptors = parse_descriptors(image.read(h.descriptors_size))
2620
David Zeuthen09692692016-09-30 16:16:40 -04002621 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002622
David Zeuthenb1b994d2017-03-06 18:01:31 -05002623 def _load_vbmeta_blob(self, image):
2624 """Gets the vbmeta struct and associated sections.
2625
2626 The image can either be a vbmeta.img or an image with a footer.
2627
2628 Arguments:
2629 image: An ImageHandler (vbmeta or footer).
2630
2631 Returns:
2632 A blob with the vbmeta struct and other sections.
2633 """
2634 assert isinstance(image, ImageHandler)
2635 footer = None
2636 image.seek(image.image_size - AvbFooter.SIZE)
2637 try:
2638 footer = AvbFooter(image.read(AvbFooter.SIZE))
2639 except (LookupError, struct.error):
2640 # Nope, just seek back to the start.
2641 image.seek(0)
2642
2643 vbmeta_offset = 0
2644 if footer:
2645 vbmeta_offset = footer.vbmeta_offset
2646
2647 image.seek(vbmeta_offset)
2648 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2649
2650 image.seek(vbmeta_offset)
2651 data_size = AvbVBMetaHeader.SIZE
2652 data_size += h.authentication_data_block_size
2653 data_size += h.auxiliary_data_block_size
2654 return image.read(data_size)
2655
David Zeuthen73f2afa2017-05-17 16:54:11 -04002656 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002657 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002658
2659 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002660 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002661
2662 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002663 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2664 instructions. There is one for when hashtree is not disabled and one for
2665 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002666
David Zeuthen21e95262016-07-27 17:58:40 -04002667 """
2668
David Zeuthen21e95262016-07-27 17:58:40 -04002669 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002670 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002671 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002672 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2673 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2674 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2675 c += ' {}'.format(ht.data_block_size) # data_block
2676 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002677 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2678 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002679 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2680 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2681 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2682 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002683 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002684 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002685 c += ' ignore_zero_blocks'
2686 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2687 c += ' fec_roots {}'.format(ht.fec_num_roots)
2688 # Note that fec_blocks is the size that FEC covers, *not* the
2689 # size of the FEC data. Since we use FEC for everything up until
2690 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002691 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2692 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002693 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002694 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002695 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002696 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002697 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002698
David Zeuthenfd41eb92016-11-17 12:24:47 -05002699 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002700 desc = AvbKernelCmdlineDescriptor()
2701 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002702 desc.flags = (
2703 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2704
2705 # The descriptor for when hashtree verification is disabled is a lot
2706 # simpler - we just set the root to the partition.
2707 desc_no_ht = AvbKernelCmdlineDescriptor()
2708 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2709 desc_no_ht.flags = (
2710 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2711
2712 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002713
David Zeuthen73f2afa2017-05-17 16:54:11 -04002714 def _get_cmdline_descriptors_for_dm_verity(self, image):
2715 """Generate kernel cmdline descriptors for dm-verity.
2716
2717 Arguments:
2718 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2719
2720 Returns:
2721 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2722 instructions. There is one for when hashtree is not disabled and one for
2723 when it is.
2724
2725 Raises:
2726 AvbError: If |image| doesn't have a hashtree descriptor.
2727
2728 """
2729
2730 (_, _, descriptors, _) = self._parse_image(image)
2731
2732 ht = None
2733 for desc in descriptors:
2734 if isinstance(desc, AvbHashtreeDescriptor):
2735 ht = desc
2736 break
2737
2738 if not ht:
2739 raise AvbError('No hashtree descriptor in given image')
2740
2741 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2742
David Zeuthen21e95262016-07-27 17:58:40 -04002743 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002744 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002745 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002746 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002747 include_descriptors_from_image,
2748 signing_helper,
2749 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002750 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002751 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002752 print_required_libavb_version,
2753 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002754 """Implements the 'make_vbmeta_image' command.
2755
2756 Arguments:
2757 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002758 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002759 algorithm_name: Name of algorithm to use.
2760 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002761 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002762 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002763 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002764 props: Properties to insert (list of strings of the form 'key:value').
2765 props_from_file: Properties to insert (list of strings 'key:<path>').
2766 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002767 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002768 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002769 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002770 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002771 release_string: None or avbtool release string to use instead of default.
2772 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002773 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002774 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002775
2776 Raises:
2777 AvbError: If a chained partition is malformed.
2778 """
2779
David Zeuthen1097a782017-05-31 15:53:17 -04002780 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002781 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002782 if include_descriptors_from_image:
2783 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2784 # version of all included descriptors.
2785 tmp_header = AvbVBMetaHeader()
2786 for image in include_descriptors_from_image:
2787 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2788 tmp_header.bump_required_libavb_version_minor(
2789 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002790 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002791 else:
2792 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002793 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002794 return
2795
2796 if not output:
2797 raise AvbError('No output file given')
2798
David Zeuthen21e95262016-07-27 17:58:40 -04002799 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002800 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002801 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002802 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002803 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002804 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002805 include_descriptors_from_image, signing_helper,
2806 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002807 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002808
2809 # Write entire vbmeta blob (header, authentication, auxiliary).
2810 output.seek(0)
2811 output.write(vbmeta_blob)
2812
David Zeuthen97cb5802017-06-01 16:14:05 -04002813 if padding_size > 0:
2814 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2815 padding_needed = padded_size - len(vbmeta_blob)
2816 output.write('\0' * padding_needed)
2817
David Zeuthen18666ab2016-11-15 11:18:05 -05002818 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2819 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002820 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002821 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002822 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002823 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002824 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002825 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002826 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002827 release_string, append_to_release_string,
2828 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002829 """Generates a VBMeta blob.
2830
2831 This blob contains the header (struct AvbVBMetaHeader), the
2832 authentication data block (which contains the hash and signature
2833 for the header and auxiliary block), and the auxiliary block
2834 (which contains descriptors, the public key used, and other data).
2835
2836 The |key| parameter can |None| only if the |algorithm_name| is
2837 'NONE'.
2838
2839 Arguments:
2840 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2841 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002842 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002843 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002844 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002845 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002846 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002847 props: Properties to insert (List of strings of the form 'key:value').
2848 props_from_file: Properties to insert (List of strings 'key:<path>').
2849 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002850 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002851 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002852 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2853 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002854 include_descriptors_from_image: List of file objects for which
2855 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002856 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002857 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002858 release_string: None or avbtool release string.
2859 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002860 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002861
2862 Returns:
2863 A bytearray() with the VBMeta blob.
2864
2865 Raises:
2866 Exception: If the |algorithm_name| is not found, if no key has
2867 been given and the given algorithm requires one, or the key is
2868 of the wrong size.
2869
2870 """
2871 try:
2872 alg = ALGORITHMS[algorithm_name]
2873 except KeyError:
2874 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2875
David Zeuthena5fd3a42017-02-27 16:38:54 -05002876 if not descriptors:
2877 descriptors = []
2878
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002879 h = AvbVBMetaHeader()
2880 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2881
David Zeuthena5fd3a42017-02-27 16:38:54 -05002882 # Insert chained partition descriptors, if any
2883 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002884 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002885 for cp in chain_partitions:
2886 cp_tokens = cp.split(':')
2887 if len(cp_tokens) != 3:
2888 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002889 partition_name = cp_tokens[0]
2890 rollback_index_location = int(cp_tokens[1])
2891 file_path = cp_tokens[2]
2892 # Check that the same rollback location isn't being used by
2893 # multiple chained partitions.
2894 if used_locations.get(rollback_index_location):
2895 raise AvbError('Rollback Index Location {} is already in use.'.format(
2896 rollback_index_location))
2897 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002898 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002899 desc.partition_name = partition_name
2900 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002901 if desc.rollback_index_location < 1:
2902 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002903 desc.public_key = open(file_path, 'rb').read()
2904 descriptors.append(desc)
2905
David Zeuthen21e95262016-07-27 17:58:40 -04002906 # Descriptors.
2907 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002908 for desc in descriptors:
2909 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002910
2911 # Add properties.
2912 if props:
2913 for prop in props:
2914 idx = prop.find(':')
2915 if idx == -1:
2916 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002917 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002918 desc = AvbPropertyDescriptor()
2919 desc.key = prop[0:idx]
2920 desc.value = prop[(idx + 1):]
2921 encoded_descriptors.extend(desc.encode())
2922 if props_from_file:
2923 for prop in props_from_file:
2924 idx = prop.find(':')
2925 if idx == -1:
2926 raise AvbError('Malformed property "{}".'.format(prop))
2927 desc = AvbPropertyDescriptor()
2928 desc.key = prop[0:idx]
2929 desc.value = prop[(idx + 1):]
2930 file_path = prop[(idx + 1):]
2931 desc.value = open(file_path, 'rb').read()
2932 encoded_descriptors.extend(desc.encode())
2933
David Zeuthen73f2afa2017-05-17 16:54:11 -04002934 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002935 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002936 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002937 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002938 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2939 encoded_descriptors.extend(cmdline_desc[0].encode())
2940 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002941
David Zeuthen73f2afa2017-05-17 16:54:11 -04002942 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2943 if ht_desc_to_setup:
2944 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2945 ht_desc_to_setup)
2946 encoded_descriptors.extend(cmdline_desc[0].encode())
2947 encoded_descriptors.extend(cmdline_desc[1].encode())
2948
David Zeuthen21e95262016-07-27 17:58:40 -04002949 # Add kernel command-lines.
2950 if kernel_cmdlines:
2951 for i in kernel_cmdlines:
2952 desc = AvbKernelCmdlineDescriptor()
2953 desc.kernel_cmdline = i
2954 encoded_descriptors.extend(desc.encode())
2955
2956 # Add descriptors from other images.
2957 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002958 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002959 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002960 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002961 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2962 image_handler)
2963 # Bump the required libavb version to support all included descriptors.
2964 h.bump_required_libavb_version_minor(
2965 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002966 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002967 # The --include_descriptors_from_image option is used in some setups
2968 # with images A and B where both A and B contain a descriptor
2969 # for a partition with the same name. Since it's not meaningful
2970 # to include both descriptors, only include the last seen descriptor.
2971 # See bug 76386656 for details.
2972 if hasattr(desc, 'partition_name'):
2973 key = type(desc).__name__ + '_' + desc.partition_name
2974 descriptors_dict[key] = desc.encode()
2975 else:
2976 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002977 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002978 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002979
David Zeuthen18666ab2016-11-15 11:18:05 -05002980 # Load public key metadata blob, if requested.
2981 pkmd_blob = []
2982 if public_key_metadata_path:
2983 with open(public_key_metadata_path) as f:
2984 pkmd_blob = f.read()
2985
David Zeuthen21e95262016-07-27 17:58:40 -04002986 key = None
2987 encoded_key = bytearray()
2988 if alg.public_key_num_bytes > 0:
2989 if not key_path:
2990 raise AvbError('Key is required for algorithm {}'.format(
2991 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002992 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002993 if len(encoded_key) != alg.public_key_num_bytes:
2994 raise AvbError('Key is wrong size for algorithm {}'.format(
2995 algorithm_name))
2996
David Zeuthene3cadca2017-02-22 21:25:46 -05002997 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01002998 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05002999 if isinstance(release_string, (str, unicode)):
3000 h.release_string = release_string
3001
3002 # Append to release string, if requested. Also insert a space before.
3003 if isinstance(append_to_release_string, (str, unicode)):
3004 h.release_string += ' ' + append_to_release_string
3005
David Zeuthen18666ab2016-11-15 11:18:05 -05003006 # For the Auxiliary data block, descriptors are stored at offset 0,
3007 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003008 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003009 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003010 h.descriptors_offset = 0
3011 h.descriptors_size = len(encoded_descriptors)
3012 h.public_key_offset = h.descriptors_size
3013 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003014 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3015 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003016
3017 # For the Authentication data block, the hash is first and then
3018 # the signature.
3019 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003020 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003021 h.algorithm_type = alg.algorithm_type
3022 h.hash_offset = 0
3023 h.hash_size = alg.hash_num_bytes
3024 # Signature offset and size - it's stored right after the hash
3025 # (in Authentication data block).
3026 h.signature_offset = alg.hash_num_bytes
3027 h.signature_size = alg.signature_num_bytes
3028
3029 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003030 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003031
3032 # Generate Header data block.
3033 header_data_blob = h.encode()
3034
3035 # Generate Auxiliary data block.
3036 aux_data_blob = bytearray()
3037 aux_data_blob.extend(encoded_descriptors)
3038 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003039 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003040 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3041 aux_data_blob.extend('\0' * padding_bytes)
3042
3043 # Calculate the hash.
3044 binary_hash = bytearray()
3045 binary_signature = bytearray()
3046 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003047 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003048 ha.update(header_data_blob)
3049 ha.update(aux_data_blob)
3050 binary_hash.extend(ha.digest())
3051
3052 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003053 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003054 binary_signature.extend(raw_sign(signing_helper,
3055 signing_helper_with_files,
3056 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003057 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003058 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003059
3060 # Generate Authentication data block.
3061 auth_data_blob = bytearray()
3062 auth_data_blob.extend(binary_hash)
3063 auth_data_blob.extend(binary_signature)
3064 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3065 auth_data_blob.extend('\0' * padding_bytes)
3066
3067 return header_data_blob + auth_data_blob + aux_data_blob
3068
3069 def extract_public_key(self, key_path, output):
3070 """Implements the 'extract_public_key' command.
3071
3072 Arguments:
3073 key_path: The path to a RSA private key file.
3074 output: The file to write to.
3075 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003076 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003077
David Zeuthenb1b994d2017-03-06 18:01:31 -05003078 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3079 partition_size):
3080 """Implementation of the append_vbmeta_image command.
3081
3082 Arguments:
3083 image_filename: File to add the footer to.
3084 vbmeta_image_filename: File to get vbmeta struct from.
3085 partition_size: Size of partition.
3086
3087 Raises:
3088 AvbError: If an argument is incorrect.
3089 """
3090 image = ImageHandler(image_filename)
3091
3092 if partition_size % image.block_size != 0:
3093 raise AvbError('Partition size of {} is not a multiple of the image '
3094 'block size {}.'.format(partition_size,
3095 image.block_size))
3096
3097 # If there's already a footer, truncate the image to its original
3098 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003099 if image.image_size >= AvbFooter.SIZE:
3100 image.seek(image.image_size - AvbFooter.SIZE)
3101 try:
3102 footer = AvbFooter(image.read(AvbFooter.SIZE))
3103 # Existing footer found. Just truncate.
3104 original_image_size = footer.original_image_size
3105 image.truncate(footer.original_image_size)
3106 except (LookupError, struct.error):
3107 original_image_size = image.image_size
3108 else:
3109 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003110 original_image_size = image.image_size
3111
3112 # If anything goes wrong from here-on, restore the image back to
3113 # its original size.
3114 try:
3115 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3116 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3117
3118 # If the image isn't sparse, its size might not be a multiple of
3119 # the block size. This will screw up padding later so just grow it.
3120 if image.image_size % image.block_size != 0:
3121 assert not image.is_sparse
3122 padding_needed = image.block_size - (image.image_size%image.block_size)
3123 image.truncate(image.image_size + padding_needed)
3124
3125 # The append_raw() method requires content with size being a
3126 # multiple of |block_size| so add padding as needed. Also record
3127 # where this is written to since we'll need to put that in the
3128 # footer.
3129 vbmeta_offset = image.image_size
3130 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3131 len(vbmeta_blob))
3132 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3133
3134 # Append vbmeta blob and footer
3135 image.append_raw(vbmeta_blob_with_padding)
3136 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3137
3138 # Now insert a DONT_CARE chunk with enough bytes such that the
3139 # final Footer block is at the end of partition_size..
3140 image.append_dont_care(partition_size - vbmeta_end_offset -
3141 1*image.block_size)
3142
3143 # Generate the Footer that tells where the VBMeta footer
3144 # is. Also put enough padding in the front of the footer since
3145 # we'll write out an entire block.
3146 footer = AvbFooter()
3147 footer.original_image_size = original_image_size
3148 footer.vbmeta_offset = vbmeta_offset
3149 footer.vbmeta_size = len(vbmeta_blob)
3150 footer_blob = footer.encode()
3151 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3152 footer_blob)
3153 image.append_raw(footer_blob_with_padding)
3154
3155 except:
3156 # Truncate back to original size, then re-raise
3157 image.truncate(original_image_size)
3158 raise
3159
David Zeuthena4fee8b2016-08-22 15:20:43 -04003160 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003161 hash_algorithm, salt, chain_partitions, algorithm_name,
3162 key_path,
3163 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003164 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003165 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003166 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003167 signing_helper, signing_helper_with_files,
3168 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003169 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003170 print_required_libavb_version, use_persistent_digest,
3171 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003172 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003173
3174 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003175 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003176 partition_size: Size of partition.
3177 partition_name: Name of partition (without A/B suffix).
3178 hash_algorithm: Hash algorithm to use.
3179 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003180 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003181 algorithm_name: Name of algorithm to use.
3182 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003183 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003184 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003185 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003186 props: Properties to insert (List of strings of the form 'key:value').
3187 props_from_file: Properties to insert (List of strings 'key:<path>').
3188 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003189 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003190 dm-verity kernel cmdline from.
3191 include_descriptors_from_image: List of file objects for which
3192 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003193 calc_max_image_size: Don't store the footer - instead calculate the
3194 maximum image size leaving enough room for metadata with the
3195 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003196 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003197 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003198 release_string: None or avbtool release string.
3199 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003200 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3201 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003202 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003203 use_persistent_digest: Use a persistent digest on device.
3204 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003205
3206 Raises:
3207 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003208 """
David Zeuthen1097a782017-05-31 15:53:17 -04003209
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003210 required_libavb_version_minor = 0
3211 if use_persistent_digest or do_not_use_ab:
3212 required_libavb_version_minor = 1
3213
David Zeuthen1097a782017-05-31 15:53:17 -04003214 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003215 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003216 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003217 return
3218
David Zeuthenbf562452017-05-17 18:04:43 -04003219 # First, calculate the maximum image size such that an image
3220 # this size + metadata (footer + vbmeta struct) fits in
3221 # |partition_size|.
3222 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003223 if partition_size < max_metadata_size:
3224 raise AvbError('Parition size of {} is too small. '
3225 'Needs to be at least {}'.format(
3226 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003227 max_image_size = partition_size - max_metadata_size
3228
3229 # If we're asked to only calculate the maximum image size, we're done.
3230 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003231 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003232 return
3233
David Zeuthena4fee8b2016-08-22 15:20:43 -04003234 image = ImageHandler(image_filename)
3235
3236 if partition_size % image.block_size != 0:
3237 raise AvbError('Partition size of {} is not a multiple of the image '
3238 'block size {}.'.format(partition_size,
3239 image.block_size))
3240
David Zeuthen21e95262016-07-27 17:58:40 -04003241 # If there's already a footer, truncate the image to its original
3242 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3243 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003244 if image.image_size >= AvbFooter.SIZE:
3245 image.seek(image.image_size - AvbFooter.SIZE)
3246 try:
3247 footer = AvbFooter(image.read(AvbFooter.SIZE))
3248 # Existing footer found. Just truncate.
3249 original_image_size = footer.original_image_size
3250 image.truncate(footer.original_image_size)
3251 except (LookupError, struct.error):
3252 original_image_size = image.image_size
3253 else:
3254 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003255 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003256
3257 # If anything goes wrong from here-on, restore the image back to
3258 # its original size.
3259 try:
David Zeuthen09692692016-09-30 16:16:40 -04003260 # If image size exceeds the maximum image size, fail.
3261 if image.image_size > max_image_size:
3262 raise AvbError('Image size of {} exceeds maximum image '
3263 'size of {} in order to fit in a partition '
3264 'size of {}.'.format(image.image_size, max_image_size,
3265 partition_size))
3266
David Zeuthen21e95262016-07-27 17:58:40 -04003267 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3268 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003269 salt = binascii.unhexlify(salt)
3270 elif salt is None and not use_persistent_digest:
3271 # If salt is not explicitly specified, choose a hash that's the same
3272 # size as the hash size. Don't populate a random salt if this
3273 # descriptor is being created to use a persistent digest on device.
3274 hash_size = digest_size
3275 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003276 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003277 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003278
3279 hasher = hashlib.new(name=hash_algorithm, string=salt)
3280 # TODO(zeuthen): might want to read this in chunks to avoid
3281 # memory pressure, then again, this is only supposed to be used
3282 # on kernel/initramfs partitions. Possible optimization.
3283 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003284 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003285 digest = hasher.digest()
3286
3287 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003288 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003289 h_desc.hash_algorithm = hash_algorithm
3290 h_desc.partition_name = partition_name
3291 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003292 h_desc.flags = 0
3293 if do_not_use_ab:
3294 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3295 if not use_persistent_digest:
3296 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003297
3298 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003299 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003300 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003301 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003302 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003303 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003304 include_descriptors_from_image, signing_helper,
3305 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003306 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003307
David Zeuthend247fcb2017-02-16 12:09:27 -05003308 # Write vbmeta blob, if requested.
3309 if output_vbmeta_image:
3310 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003311
David Zeuthend247fcb2017-02-16 12:09:27 -05003312 # Append vbmeta blob and footer, unless requested not to.
3313 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003314 # If the image isn't sparse, its size might not be a multiple of
3315 # the block size. This will screw up padding later so just grow it.
3316 if image.image_size % image.block_size != 0:
3317 assert not image.is_sparse
3318 padding_needed = image.block_size - (
3319 image.image_size % image.block_size)
3320 image.truncate(image.image_size + padding_needed)
3321
3322 # The append_raw() method requires content with size being a
3323 # multiple of |block_size| so add padding as needed. Also record
3324 # where this is written to since we'll need to put that in the
3325 # footer.
3326 vbmeta_offset = image.image_size
3327 padding_needed = (
3328 round_to_multiple(len(vbmeta_blob), image.block_size) -
3329 len(vbmeta_blob))
3330 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3331
David Zeuthend247fcb2017-02-16 12:09:27 -05003332 image.append_raw(vbmeta_blob_with_padding)
3333 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3334
3335 # Now insert a DONT_CARE chunk with enough bytes such that the
3336 # final Footer block is at the end of partition_size..
3337 image.append_dont_care(partition_size - vbmeta_end_offset -
3338 1*image.block_size)
3339
3340 # Generate the Footer that tells where the VBMeta footer
3341 # is. Also put enough padding in the front of the footer since
3342 # we'll write out an entire block.
3343 footer = AvbFooter()
3344 footer.original_image_size = original_image_size
3345 footer.vbmeta_offset = vbmeta_offset
3346 footer.vbmeta_size = len(vbmeta_blob)
3347 footer_blob = footer.encode()
3348 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3349 footer_blob)
3350 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003351
David Zeuthen21e95262016-07-27 17:58:40 -04003352 except:
3353 # Truncate back to original size, then re-raise
3354 image.truncate(original_image_size)
3355 raise
3356
David Zeuthena4fee8b2016-08-22 15:20:43 -04003357 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003358 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003359 block_size, salt, chain_partitions, algorithm_name,
3360 key_path,
3361 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003362 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003363 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003364 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003365 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003366 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003367 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003368 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003369 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003370 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003371 use_persistent_root_digest, do_not_use_ab,
3372 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003373 """Implements the 'add_hashtree_footer' command.
3374
3375 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3376 more information about dm-verity and these hashes.
3377
3378 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003379 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003380 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003381 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003382 generate_fec: If True, generate FEC codes.
3383 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003384 hash_algorithm: Hash algorithm to use.
3385 block_size: Block size to use.
3386 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003387 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003388 algorithm_name: Name of algorithm to use.
3389 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003390 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003391 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003392 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003393 props: Properties to insert (List of strings of the form 'key:value').
3394 props_from_file: Properties to insert (List of strings 'key:<path>').
3395 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003396 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003397 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003398 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3399 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003400 include_descriptors_from_image: List of file objects for which
3401 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003402 calc_max_image_size: Don't store the hashtree or footer - instead
3403 calculate the maximum image size leaving enough room for hashtree
3404 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003405 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003406 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003407 release_string: None or avbtool release string.
3408 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003409 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3410 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003411 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003412 use_persistent_root_digest: Use a persistent root digest on device.
3413 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003414 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003415
3416 Raises:
3417 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003418 """
David Zeuthen1097a782017-05-31 15:53:17 -04003419
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003420 required_libavb_version_minor = 0
3421 if use_persistent_root_digest or do_not_use_ab:
3422 required_libavb_version_minor = 1
3423
David Zeuthen1097a782017-05-31 15:53:17 -04003424 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003425 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003426 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003427 return
3428
David Zeuthen09692692016-09-30 16:16:40 -04003429 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3430 digest_padding = round_to_pow2(digest_size) - digest_size
3431
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003432 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3433 # size such that an image this size + the hashtree + metadata (footer +
3434 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3435 # for metadata.
3436 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003437 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003438 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003439 if not no_hashtree:
3440 (_, max_tree_size) = calc_hash_level_offsets(
3441 partition_size, block_size, digest_size + digest_padding)
3442 if generate_fec:
3443 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003444 max_metadata_size = (max_fec_size + max_tree_size +
3445 self.MAX_VBMETA_SIZE +
3446 self.MAX_FOOTER_SIZE)
3447 max_image_size = partition_size - max_metadata_size
3448 else:
3449 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003450
3451 # If we're asked to only calculate the maximum image size, we're done.
3452 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003453 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003454 return
3455
David Zeuthena4fee8b2016-08-22 15:20:43 -04003456 image = ImageHandler(image_filename)
3457
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003458 if partition_size > 0:
3459 if partition_size % image.block_size != 0:
3460 raise AvbError('Partition size of {} is not a multiple of the image '
3461 'block size {}.'.format(partition_size,
3462 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003463 elif image.image_size % image.block_size != 0:
3464 raise AvbError('File size of {} is not a multiple of the image '
3465 'block size {}.'.format(image.image_size,
3466 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003467
David Zeuthen21e95262016-07-27 17:58:40 -04003468 # If there's already a footer, truncate the image to its original
3469 # size. This way 'avbtool add_hashtree_footer' is idempotent
3470 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003471 if image.image_size >= AvbFooter.SIZE:
3472 image.seek(image.image_size - AvbFooter.SIZE)
3473 try:
3474 footer = AvbFooter(image.read(AvbFooter.SIZE))
3475 # Existing footer found. Just truncate.
3476 original_image_size = footer.original_image_size
3477 image.truncate(footer.original_image_size)
3478 except (LookupError, struct.error):
3479 original_image_size = image.image_size
3480 else:
3481 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003482 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003483
3484 # If anything goes wrong from here-on, restore the image back to
3485 # its original size.
3486 try:
3487 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003488 rounded_image_size = round_to_multiple(image.image_size, block_size)
3489 if rounded_image_size > image.image_size:
3490 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003491
David Zeuthen09692692016-09-30 16:16:40 -04003492 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003493 if partition_size > 0:
3494 if image.image_size > max_image_size:
3495 raise AvbError('Image size of {} exceeds maximum image '
3496 'size of {} in order to fit in a partition '
3497 'size of {}.'.format(image.image_size, max_image_size,
3498 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003499
3500 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003501 salt = binascii.unhexlify(salt)
3502 elif salt is None and not use_persistent_root_digest:
3503 # If salt is not explicitly specified, choose a hash that's the same
3504 # size as the hash size. Don't populate a random salt if this
3505 # descriptor is being created to use a persistent digest on device.
3506 hash_size = digest_size
3507 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003508 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003509 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003510
David Zeuthena4fee8b2016-08-22 15:20:43 -04003511 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003512 # offsets in advance.
3513 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003514 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003515
David Zeuthena4fee8b2016-08-22 15:20:43 -04003516 # If the image isn't sparse, its size might not be a multiple of
3517 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003518 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003519 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003520 padding_needed = image.block_size - (image.image_size%image.block_size)
3521 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003522
David Zeuthena4fee8b2016-08-22 15:20:43 -04003523 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003524 tree_offset = image.image_size
3525 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003526 block_size,
3527 hash_algorithm, salt,
3528 digest_padding,
3529 hash_level_offsets,
3530 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003531
3532 # Generate HashtreeDescriptor with details about the tree we
3533 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003534 if no_hashtree:
3535 tree_size = 0
3536 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003537 ht_desc = AvbHashtreeDescriptor()
3538 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003539 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003540 ht_desc.tree_offset = tree_offset
3541 ht_desc.tree_size = tree_size
3542 ht_desc.data_block_size = block_size
3543 ht_desc.hash_block_size = block_size
3544 ht_desc.hash_algorithm = hash_algorithm
3545 ht_desc.partition_name = partition_name
3546 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003547 if do_not_use_ab:
3548 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3549 if not use_persistent_root_digest:
3550 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003551
David Zeuthen09692692016-09-30 16:16:40 -04003552 # Write the hash tree
3553 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3554 len(hash_tree))
3555 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3556 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003557 len_hashtree_and_fec = len(hash_tree_with_padding)
3558
3559 # Generate FEC codes, if requested.
3560 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003561 if no_hashtree:
3562 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003563 else:
3564 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003565 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3566 len(fec_data))
3567 fec_data_with_padding = fec_data + '\0'*padding_needed
3568 fec_offset = image.image_size
3569 image.append_raw(fec_data_with_padding)
3570 len_hashtree_and_fec += len(fec_data_with_padding)
3571 # Update the hashtree descriptor.
3572 ht_desc.fec_num_roots = fec_num_roots
3573 ht_desc.fec_offset = fec_offset
3574 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003575
David Zeuthen73f2afa2017-05-17 16:54:11 -04003576 ht_desc_to_setup = None
3577 if setup_as_rootfs_from_kernel:
3578 ht_desc_to_setup = ht_desc
3579
David Zeuthena4fee8b2016-08-22 15:20:43 -04003580 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003581 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003582 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003583 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003584 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003585 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003586 include_descriptors_from_image, signing_helper,
3587 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003588 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003589 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3590 len(vbmeta_blob))
3591 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003592
David Zeuthend247fcb2017-02-16 12:09:27 -05003593 # Write vbmeta blob, if requested.
3594 if output_vbmeta_image:
3595 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003596
David Zeuthend247fcb2017-02-16 12:09:27 -05003597 # Append vbmeta blob and footer, unless requested not to.
3598 if not do_not_append_vbmeta_image:
3599 image.append_raw(vbmeta_blob_with_padding)
3600
3601 # Now insert a DONT_CARE chunk with enough bytes such that the
3602 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003603 if partition_size > 0:
3604 image.append_dont_care(partition_size - image.image_size -
3605 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003606
3607 # Generate the Footer that tells where the VBMeta footer
3608 # is. Also put enough padding in the front of the footer since
3609 # we'll write out an entire block.
3610 footer = AvbFooter()
3611 footer.original_image_size = original_image_size
3612 footer.vbmeta_offset = vbmeta_offset
3613 footer.vbmeta_size = len(vbmeta_blob)
3614 footer_blob = footer.encode()
3615 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3616 footer_blob)
3617 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003618
David Zeuthen21e95262016-07-27 17:58:40 -04003619 except:
David Zeuthen09692692016-09-30 16:16:40 -04003620 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003621 image.truncate(original_image_size)
3622 raise
3623
David Zeuthenc68f0822017-03-31 17:22:35 -04003624 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003625 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003626 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003627 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003628 """Implements the 'make_atx_certificate' command.
3629
3630 Android Things certificates are required for Android Things public key
3631 metadata. They chain the vbmeta signing key for a particular product back to
3632 a fused, permanent root key. These certificates are fixed-length and fixed-
3633 format with the explicit goal of not parsing ASN.1 in bootloader code.
3634
3635 Arguments:
3636 output: Certificate will be written to this file on success.
3637 authority_key_path: A PEM file path with the authority private key.
3638 If None, then a certificate will be created without a
3639 signature. The signature can be created out-of-band
3640 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003641 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003642 subject_key_version: A 64-bit version value. If this is None, the number
3643 of seconds since the epoch is used.
3644 subject: A subject identifier. For Product Signing Key certificates this
3645 should be the same Product ID found in the permanent attributes.
3646 is_intermediate_authority: True if the certificate is for an intermediate
3647 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003648 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003649 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003650 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003651 """
3652 signed_data = bytearray()
3653 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003654 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003655 hasher = hashlib.sha256()
3656 hasher.update(subject)
3657 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003658 if not usage:
3659 usage = 'com.google.android.things.vboot'
3660 if is_intermediate_authority:
3661 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003662 hasher = hashlib.sha256()
3663 hasher.update(usage)
3664 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003665 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003666 subject_key_version = int(time.time())
3667 signed_data.extend(struct.pack('<Q', subject_key_version))
3668 signature = bytearray()
3669 if authority_key_path:
3670 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003671 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003672 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003673 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003674 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003675 hasher.update(signed_data)
3676 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003677 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3678 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003679 alg.signature_num_bytes, authority_key_path,
3680 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003681 output.write(signed_data)
3682 output.write(signature)
3683
David Zeuthenc68f0822017-03-31 17:22:35 -04003684 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003685 product_id):
3686 """Implements the 'make_atx_permanent_attributes' command.
3687
3688 Android Things permanent attributes are designed to be permanent for a
3689 particular product and a hash of these attributes should be fused into
3690 hardware to enforce this.
3691
3692 Arguments:
3693 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003694 root_authority_key_path: Path to a PEM or DER public key for
3695 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003696 product_id: A 16-byte Product ID.
3697
3698 Raises:
3699 AvbError: If an argument is incorrect.
3700 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003701 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003702 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003703 raise AvbError('Invalid Product ID length.')
3704 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003705 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003706 output.write(product_id)
3707
3708 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003709 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003710 """Implements the 'make_atx_metadata' command.
3711
3712 Android Things metadata are included in vbmeta images to facilitate
3713 verification. The output of this command can be used as the
3714 public_key_metadata argument to other commands.
3715
3716 Arguments:
3717 output: Metadata will be written to this file on success.
3718 intermediate_key_certificate: A certificate file as output by
3719 make_atx_certificate with
3720 is_intermediate_authority set to true.
3721 product_key_certificate: A certificate file as output by
3722 make_atx_certificate with
3723 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003724
3725 Raises:
3726 AvbError: If an argument is incorrect.
3727 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003728 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003729 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003730 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003731 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003732 raise AvbError('Invalid product key certificate length.')
3733 output.write(struct.pack('<I', 1)) # Format Version
3734 output.write(intermediate_key_certificate)
3735 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003736
Darren Krahnfccd64e2018-01-16 17:39:35 -08003737 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3738 unlock_key_certificate, challenge_path,
3739 unlock_key_path, signing_helper,
3740 signing_helper_with_files):
3741 """Implements the 'make_atx_unlock_credential' command.
3742
3743 Android Things unlock credentials can be used to authorize the unlock of AVB
3744 on a device. These credentials are presented to an Android Things bootloader
3745 via the fastboot interface in response to a 16-byte challenge. This method
3746 creates all fields of the credential except the challenge signature field
3747 (which is the last field) and can optionally create the challenge signature
3748 field as well if a challenge and the unlock_key_path is provided.
3749
3750 Arguments:
3751 output: The credential will be written to this file on success.
3752 intermediate_key_certificate: A certificate file as output by
3753 make_atx_certificate with
3754 is_intermediate_authority set to true.
3755 unlock_key_certificate: A certificate file as output by
3756 make_atx_certificate with
3757 is_intermediate_authority set to false and the
3758 usage set to
3759 'com.google.android.things.vboot.unlock'.
3760 challenge_path: [optional] A path to the challenge to sign.
3761 unlock_key_path: [optional] A PEM file path with the unlock private key.
3762 signing_helper: Program which signs a hash and returns the signature.
3763 signing_helper_with_files: Same as signing_helper but uses files instead.
3764
3765 Raises:
3766 AvbError: If an argument is incorrect.
3767 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003768 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3769 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003770 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3771 raise AvbError('Invalid intermediate key certificate length.')
3772 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3773 raise AvbError('Invalid product key certificate length.')
3774 challenge = bytearray()
3775 if challenge_path:
3776 with open(challenge_path, 'r') as f:
3777 challenge = f.read()
3778 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3779 raise AvbError('Invalid unlock challenge length.')
3780 output.write(struct.pack('<I', 1)) # Format Version
3781 output.write(intermediate_key_certificate)
3782 output.write(unlock_key_certificate)
3783 if challenge_path and unlock_key_path:
3784 signature = bytearray()
3785 padding_and_hash = bytearray()
3786 algorithm_name = 'SHA512_RSA4096'
3787 alg = ALGORITHMS[algorithm_name]
3788 hasher = hashlib.sha512()
3789 padding_and_hash.extend(alg.padding)
3790 hasher.update(challenge)
3791 padding_and_hash.extend(hasher.digest())
3792 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3793 algorithm_name,
3794 alg.signature_num_bytes, unlock_key_path,
3795 padding_and_hash))
3796 output.write(signature)
3797
David Zeuthen21e95262016-07-27 17:58:40 -04003798
3799def calc_hash_level_offsets(image_size, block_size, digest_size):
3800 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3801
3802 Arguments:
3803 image_size: The size of the image to calculate a Merkle-tree for.
3804 block_size: The block size, e.g. 4096.
3805 digest_size: The size of each hash, e.g. 32 for SHA-256.
3806
3807 Returns:
3808 A tuple where the first argument is an array of offsets and the
3809 second is size of the tree, in bytes.
3810 """
3811 level_offsets = []
3812 level_sizes = []
3813 tree_size = 0
3814
3815 num_levels = 0
3816 size = image_size
3817 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003818 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003819 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3820
3821 level_sizes.append(level_size)
3822 tree_size += level_size
3823 num_levels += 1
3824
3825 size = level_size
3826
3827 for n in range(0, num_levels):
3828 offset = 0
3829 for m in range(n + 1, num_levels):
3830 offset += level_sizes[m]
3831 level_offsets.append(offset)
3832
David Zeuthena4fee8b2016-08-22 15:20:43 -04003833 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003834
3835
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003836# See system/extras/libfec/include/fec/io.h for these definitions.
3837FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3838FEC_MAGIC = 0xfecfecfe
3839
3840
3841def calc_fec_data_size(image_size, num_roots):
3842 """Calculates how much space FEC data will take.
3843
Jan Monschfe00c0a2019-12-11 11:19:40 +01003844 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003845 image_size: The size of the image.
3846 num_roots: Number of roots.
3847
3848 Returns:
3849 The number of bytes needed for FEC for an image of the given size
3850 and with the requested number of FEC roots.
3851
3852 Raises:
3853 ValueError: If output from the 'fec' tool is invalid.
3854
3855 """
3856 p = subprocess.Popen(
3857 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3858 stdout=subprocess.PIPE,
3859 stderr=subprocess.PIPE)
3860 (pout, perr) = p.communicate()
3861 retcode = p.wait()
3862 if retcode != 0:
3863 raise ValueError('Error invoking fec: {}'.format(perr))
3864 return int(pout)
3865
3866
3867def generate_fec_data(image_filename, num_roots):
3868 """Generate FEC codes for an image.
3869
Jan Monschfe00c0a2019-12-11 11:19:40 +01003870 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003871 image_filename: The filename of the image.
3872 num_roots: Number of roots.
3873
3874 Returns:
3875 The FEC data blob.
3876
3877 Raises:
3878 ValueError: If output from the 'fec' tool is invalid.
3879 """
3880 fec_tmpfile = tempfile.NamedTemporaryFile()
3881 subprocess.check_call(
3882 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3883 fec_tmpfile.name],
3884 stderr=open(os.devnull))
3885 fec_data = fec_tmpfile.read()
3886 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3887 footer_data = fec_data[-footer_size:]
3888 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3889 footer_data)
3890 if magic != FEC_MAGIC:
3891 raise ValueError('Unexpected magic in FEC footer')
3892 return fec_data[0:fec_size]
3893
3894
David Zeuthen21e95262016-07-27 17:58:40 -04003895def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003896 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003897 """Generates a Merkle-tree for a file.
3898
Jan Monschfe00c0a2019-12-11 11:19:40 +01003899 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003900 image: The image, as a file.
3901 image_size: The size of the image.
3902 block_size: The block size, e.g. 4096.
3903 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3904 salt: The salt to use.
3905 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003906 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003907 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003908
3909 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003910 A tuple where the first element is the top-level hash and the
3911 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003912 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003913 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003914 hash_src_offset = 0
3915 hash_src_size = image_size
3916 level_num = 0
3917 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003918 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003919 remaining = hash_src_size
3920 while remaining > 0:
3921 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003922 # Only read from the file for the first level - for subsequent
3923 # levels, access the array we're building.
3924 if level_num == 0:
3925 image.seek(hash_src_offset + hash_src_size - remaining)
3926 data = image.read(min(remaining, block_size))
3927 else:
3928 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3929 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003930 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003931
3932 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003933 if len(data) < block_size:
3934 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003935 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003936 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003937 level_output_list.append('\0' * digest_padding)
3938
3939 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003940
3941 padding_needed = (round_to_multiple(
3942 len(level_output), block_size) - len(level_output))
3943 level_output += '\0' * padding_needed
3944
David Zeuthena4fee8b2016-08-22 15:20:43 -04003945 # Copy level-output into resulting tree.
3946 offset = hash_level_offsets[level_num]
3947 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003948
David Zeuthena4fee8b2016-08-22 15:20:43 -04003949 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003950 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003951 level_num += 1
3952
3953 hasher = hashlib.new(name=hash_alg_name, string=salt)
3954 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003955 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003956
3957
3958class AvbTool(object):
3959 """Object for avbtool command-line tool."""
3960
3961 def __init__(self):
3962 """Initializer method."""
3963 self.avb = Avb()
3964
3965 def _add_common_args(self, sub_parser):
3966 """Adds arguments used by several sub-commands.
3967
3968 Arguments:
3969 sub_parser: The parser to add arguments to.
3970 """
3971 sub_parser.add_argument('--algorithm',
3972 help='Algorithm to use (default: NONE)',
3973 metavar='ALGORITHM',
3974 default='NONE')
3975 sub_parser.add_argument('--key',
3976 help='Path to RSA private key file',
3977 metavar='KEY',
3978 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003979 sub_parser.add_argument('--signing_helper',
3980 help='Path to helper used for signing',
3981 metavar='APP',
3982 default=None,
3983 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003984 sub_parser.add_argument('--signing_helper_with_files',
3985 help='Path to helper used for signing using files',
3986 metavar='APP',
3987 default=None,
3988 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003989 sub_parser.add_argument('--public_key_metadata',
3990 help='Path to public key metadata file',
3991 metavar='KEY_METADATA',
3992 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003993 sub_parser.add_argument('--rollback_index',
3994 help='Rollback Index',
3995 type=parse_number,
3996 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003997 # This is used internally for unit tests. Do not include in --help output.
3998 sub_parser.add_argument('--internal_release_string',
3999 help=argparse.SUPPRESS)
4000 sub_parser.add_argument('--append_to_release_string',
4001 help='Text to append to release string',
4002 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004003 sub_parser.add_argument('--prop',
4004 help='Add property',
4005 metavar='KEY:VALUE',
4006 action='append')
4007 sub_parser.add_argument('--prop_from_file',
4008 help='Add property from file',
4009 metavar='KEY:PATH',
4010 action='append')
4011 sub_parser.add_argument('--kernel_cmdline',
4012 help='Add kernel cmdline',
4013 metavar='CMDLINE',
4014 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004015 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4016 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4017 # at some future point.
4018 sub_parser.add_argument('--setup_rootfs_from_kernel',
4019 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004020 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004021 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004022 type=argparse.FileType('rb'))
4023 sub_parser.add_argument('--include_descriptors_from_image',
4024 help='Include descriptors from image',
4025 metavar='IMAGE',
4026 action='append',
4027 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004028 sub_parser.add_argument('--print_required_libavb_version',
4029 help=('Don\'t store the footer - '
4030 'instead calculate the required libavb '
4031 'version for the given options.'),
4032 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004033 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4034 sub_parser.add_argument('--chain_partition',
4035 help='Allow signed integrity-data for partition',
4036 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4037 action='append')
4038 sub_parser.add_argument('--flags',
4039 help='VBMeta flags',
4040 type=parse_number,
4041 default=0)
4042 sub_parser.add_argument('--set_hashtree_disabled_flag',
4043 help='Set the HASHTREE_DISABLED flag',
4044 action='store_true')
4045
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004046 def _add_common_footer_args(self, sub_parser):
4047 """Adds arguments used by add_*_footer sub-commands.
4048
4049 Arguments:
4050 sub_parser: The parser to add arguments to.
4051 """
4052 sub_parser.add_argument('--use_persistent_digest',
4053 help='Use a persistent digest on device instead of '
4054 'storing the digest in the descriptor. This '
4055 'cannot be used with A/B so must be combined '
4056 'with --do_not_use_ab when an A/B suffix is '
4057 'expected at runtime.',
4058 action='store_true')
4059 sub_parser.add_argument('--do_not_use_ab',
4060 help='The partition does not use A/B even when an '
4061 'A/B suffix is present. This must not be used '
4062 'for vbmeta or chained partitions.',
4063 action='store_true')
4064
David Zeuthena5fd3a42017-02-27 16:38:54 -05004065 def _fixup_common_args(self, args):
4066 """Common fixups needed by subcommands.
4067
4068 Arguments:
4069 args: Arguments to modify.
4070
4071 Returns:
4072 The modified arguments.
4073 """
4074 if args.set_hashtree_disabled_flag:
4075 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4076 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004077
4078 def run(self, argv):
4079 """Command-line processor.
4080
4081 Arguments:
4082 argv: Pass sys.argv from main.
4083 """
4084 parser = argparse.ArgumentParser()
4085 subparsers = parser.add_subparsers(title='subcommands')
4086
Jan Monsch2c7be992020-04-03 14:37:13 +02004087 sub_parser = subparsers.add_parser(
4088 'generate_test_image',
4089 help=('Generates a test image with a known pattern for testing: '
4090 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4091 sub_parser.add_argument('--image_size',
4092 help='Size of image to generate.',
4093 type=parse_number,
4094 required=True)
4095 sub_parser.add_argument('--start_byte',
4096 help='Integer for the start byte of the pattern.',
4097 type=parse_number,
4098 default=0)
4099 sub_parser.add_argument('--output',
4100 help='Output file name.',
4101 type=argparse.FileType('wt'),
4102 default=sys.stdout)
4103 sub_parser.set_defaults(func=self.generate_test_image)
4104
David Zeuthen21e95262016-07-27 17:58:40 -04004105 sub_parser = subparsers.add_parser('version',
4106 help='Prints version of avbtool.')
4107 sub_parser.set_defaults(func=self.version)
4108
4109 sub_parser = subparsers.add_parser('extract_public_key',
4110 help='Extract public key.')
4111 sub_parser.add_argument('--key',
4112 help='Path to RSA private key file',
4113 required=True)
4114 sub_parser.add_argument('--output',
4115 help='Output file name',
4116 type=argparse.FileType('wb'),
4117 required=True)
4118 sub_parser.set_defaults(func=self.extract_public_key)
4119
4120 sub_parser = subparsers.add_parser('make_vbmeta_image',
4121 help='Makes a vbmeta image.')
4122 sub_parser.add_argument('--output',
4123 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004124 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004125 sub_parser.add_argument('--padding_size',
4126 metavar='NUMBER',
4127 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004128 'its size is a multiple of NUMBER '
4129 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004130 type=parse_number,
4131 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004132 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004133 sub_parser.set_defaults(func=self.make_vbmeta_image)
4134
4135 sub_parser = subparsers.add_parser('add_hash_footer',
4136 help='Add hashes and footer to image.')
4137 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004138 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004139 type=argparse.FileType('rab+'))
4140 sub_parser.add_argument('--partition_size',
4141 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004142 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004143 sub_parser.add_argument('--partition_name',
4144 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004145 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004146 sub_parser.add_argument('--hash_algorithm',
4147 help='Hash algorithm to use (default: sha256)',
4148 default='sha256')
4149 sub_parser.add_argument('--salt',
4150 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004151 sub_parser.add_argument('--calc_max_image_size',
4152 help=('Don\'t store the footer - '
4153 'instead calculate the maximum image size '
4154 'leaving enough room for metadata with '
4155 'the given partition size.'),
4156 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004157 sub_parser.add_argument('--output_vbmeta_image',
4158 help='Also write vbmeta struct to file',
4159 type=argparse.FileType('wb'))
4160 sub_parser.add_argument('--do_not_append_vbmeta_image',
4161 help=('Do not append vbmeta struct or footer '
4162 'to the image'),
4163 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004164 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004165 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004166 sub_parser.set_defaults(func=self.add_hash_footer)
4167
David Zeuthenb1b994d2017-03-06 18:01:31 -05004168 sub_parser = subparsers.add_parser('append_vbmeta_image',
4169 help='Append vbmeta image to image.')
4170 sub_parser.add_argument('--image',
4171 help='Image to append vbmeta blob to',
4172 type=argparse.FileType('rab+'))
4173 sub_parser.add_argument('--partition_size',
4174 help='Partition size',
4175 type=parse_number,
4176 required=True)
4177 sub_parser.add_argument('--vbmeta_image',
4178 help='Image with vbmeta blob to append',
4179 type=argparse.FileType('rb'))
4180 sub_parser.set_defaults(func=self.append_vbmeta_image)
4181
Jan Monscheeb28b62019-12-05 16:17:09 +01004182 sub_parser = subparsers.add_parser(
4183 'add_hashtree_footer',
4184 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004185 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004186 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004187 type=argparse.FileType('rab+'))
4188 sub_parser.add_argument('--partition_size',
4189 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004190 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004191 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004192 sub_parser.add_argument('--partition_name',
4193 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004194 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004195 sub_parser.add_argument('--hash_algorithm',
4196 help='Hash algorithm to use (default: sha1)',
4197 default='sha1')
4198 sub_parser.add_argument('--salt',
4199 help='Salt in hex (default: /dev/urandom)')
4200 sub_parser.add_argument('--block_size',
4201 help='Block size (default: 4096)',
4202 type=parse_number,
4203 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004204 # TODO(zeuthen): The --generate_fec option was removed when we
4205 # moved to generating FEC by default. To avoid breaking existing
4206 # users needing to transition we simply just print a warning below
4207 # in add_hashtree_footer(). Remove this option and the warning at
4208 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004209 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004210 help=argparse.SUPPRESS,
4211 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004212 sub_parser.add_argument(
4213 '--do_not_generate_fec',
4214 help='Do not generate forward-error-correction codes',
4215 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004216 sub_parser.add_argument('--fec_num_roots',
4217 help='Number of roots for FEC (default: 2)',
4218 type=parse_number,
4219 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004220 sub_parser.add_argument('--calc_max_image_size',
4221 help=('Don\'t store the hashtree or footer - '
4222 'instead calculate the maximum image size '
4223 'leaving enough room for hashtree '
4224 'and metadata with the given partition '
4225 'size.'),
4226 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004227 sub_parser.add_argument('--output_vbmeta_image',
4228 help='Also write vbmeta struct to file',
4229 type=argparse.FileType('wb'))
4230 sub_parser.add_argument('--do_not_append_vbmeta_image',
4231 help=('Do not append vbmeta struct or footer '
4232 'to the image'),
4233 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004234 # This is different from --setup_rootfs_from_kernel insofar that
4235 # it doesn't take an IMAGE, the generated cmdline will be for the
4236 # hashtree we're adding.
4237 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4238 action='store_true',
4239 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004240 sub_parser.add_argument('--no_hashtree',
4241 action='store_true',
4242 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004243 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004244 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004245 sub_parser.set_defaults(func=self.add_hashtree_footer)
4246
4247 sub_parser = subparsers.add_parser('erase_footer',
4248 help='Erase footer from an image.')
4249 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004250 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004251 type=argparse.FileType('rwb+'),
4252 required=True)
4253 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004254 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004255 action='store_true')
4256 sub_parser.set_defaults(func=self.erase_footer)
4257
David Zeuthen1394f762019-04-30 10:20:11 -04004258 sub_parser = subparsers.add_parser('zero_hashtree',
4259 help='Zero out hashtree and FEC data.')
4260 sub_parser.add_argument('--image',
4261 help='Image with a footer',
4262 type=argparse.FileType('rwb+'),
4263 required=True)
4264 sub_parser.set_defaults(func=self.zero_hashtree)
4265
Jan Monscheeb28b62019-12-05 16:17:09 +01004266 sub_parser = subparsers.add_parser(
4267 'extract_vbmeta_image',
4268 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004269 sub_parser.add_argument('--image',
4270 help='Image with footer',
4271 type=argparse.FileType('rb'),
4272 required=True)
4273 sub_parser.add_argument('--output',
4274 help='Output file name',
4275 type=argparse.FileType('wb'))
4276 sub_parser.add_argument('--padding_size',
4277 metavar='NUMBER',
4278 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004279 'its size is a multiple of NUMBER '
4280 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004281 type=parse_number,
4282 default=0)
4283 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4284
David Zeuthen2bc232b2017-04-19 14:25:19 -04004285 sub_parser = subparsers.add_parser('resize_image',
4286 help='Resize image with a footer.')
4287 sub_parser.add_argument('--image',
4288 help='Image with a footer',
4289 type=argparse.FileType('rwb+'),
4290 required=True)
4291 sub_parser.add_argument('--partition_size',
4292 help='New partition size',
4293 type=parse_number)
4294 sub_parser.set_defaults(func=self.resize_image)
4295
David Zeuthen21e95262016-07-27 17:58:40 -04004296 sub_parser = subparsers.add_parser(
4297 'info_image',
4298 help='Show information about vbmeta or footer.')
4299 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004300 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004301 type=argparse.FileType('rb'),
4302 required=True)
4303 sub_parser.add_argument('--output',
4304 help='Write info to file',
4305 type=argparse.FileType('wt'),
4306 default=sys.stdout)
4307 sub_parser.set_defaults(func=self.info_image)
4308
David Zeuthenb623d8b2017-04-04 16:05:53 -04004309 sub_parser = subparsers.add_parser(
4310 'verify_image',
4311 help='Verify an image.')
4312 sub_parser.add_argument('--image',
4313 help='Image to verify',
4314 type=argparse.FileType('rb'),
4315 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004316 sub_parser.add_argument('--key',
4317 help='Check embedded public key matches KEY',
4318 metavar='KEY',
4319 required=False)
4320 sub_parser.add_argument('--expected_chain_partition',
4321 help='Expected chain partition',
4322 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4323 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004324 sub_parser.add_argument(
4325 '--follow_chain_partitions',
4326 help=('Follows chain partitions even when not '
4327 'specified with the --expected_chain_partition option'),
4328 action='store_true')
4329 sub_parser.add_argument(
4330 '--accept_zeroed_hashtree',
4331 help=('Accept images where the hashtree or FEC data is zeroed out'),
4332 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004333 sub_parser.set_defaults(func=self.verify_image)
4334
David Zeuthenb8643c02018-05-17 17:21:18 -04004335 sub_parser = subparsers.add_parser(
4336 'calculate_vbmeta_digest',
4337 help='Calculate vbmeta digest.')
4338 sub_parser.add_argument('--image',
4339 help='Image to calculate digest for',
4340 type=argparse.FileType('rb'),
4341 required=True)
4342 sub_parser.add_argument('--hash_algorithm',
4343 help='Hash algorithm to use (default: sha256)',
4344 default='sha256')
4345 sub_parser.add_argument('--output',
4346 help='Write hex digest to file (default: stdout)',
4347 type=argparse.FileType('wt'),
4348 default=sys.stdout)
4349 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4350
David Zeuthenf7d2e752018-09-20 13:30:41 -04004351 sub_parser = subparsers.add_parser(
4352 'calculate_kernel_cmdline',
4353 help='Calculate kernel cmdline.')
4354 sub_parser.add_argument('--image',
4355 help='Image to calculate kernel cmdline for',
4356 type=argparse.FileType('rb'),
4357 required=True)
4358 sub_parser.add_argument('--hashtree_disabled',
4359 help='Return the cmdline for hashtree disabled',
4360 action='store_true')
4361 sub_parser.add_argument('--output',
4362 help='Write cmdline to file (default: stdout)',
4363 type=argparse.FileType('wt'),
4364 default=sys.stdout)
4365 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4366
David Zeuthen8b6973b2016-09-20 12:39:49 -04004367 sub_parser = subparsers.add_parser('set_ab_metadata',
4368 help='Set A/B metadata.')
4369 sub_parser.add_argument('--misc_image',
4370 help=('The misc image to modify. If the image does '
4371 'not exist, it will be created.'),
4372 type=argparse.FileType('r+b'),
4373 required=True)
4374 sub_parser.add_argument('--slot_data',
4375 help=('Slot data of the form "priority", '
4376 '"tries_remaining", "sucessful_boot" for '
4377 'slot A followed by the same for slot B, '
4378 'separated by colons. The default value '
4379 'is 15:7:0:14:7:0.'),
4380 default='15:7:0:14:7:0')
4381 sub_parser.set_defaults(func=self.set_ab_metadata)
4382
Darren Krahn147b08d2016-12-20 16:38:29 -08004383 sub_parser = subparsers.add_parser(
4384 'make_atx_certificate',
4385 help='Create an Android Things eXtension (ATX) certificate.')
4386 sub_parser.add_argument('--output',
4387 help='Write certificate to file',
4388 type=argparse.FileType('wb'),
4389 default=sys.stdout)
4390 sub_parser.add_argument('--subject',
4391 help=('Path to subject file'),
4392 type=argparse.FileType('rb'),
4393 required=True)
4394 sub_parser.add_argument('--subject_key',
4395 help=('Path to subject RSA public key file'),
4396 type=argparse.FileType('rb'),
4397 required=True)
4398 sub_parser.add_argument('--subject_key_version',
4399 help=('Version of the subject key'),
4400 type=parse_number,
4401 required=False)
4402 sub_parser.add_argument('--subject_is_intermediate_authority',
4403 help=('Generate an intermediate authority '
4404 'certificate'),
4405 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004406 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004407 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004408 'string'),
4409 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004410 sub_parser.add_argument('--authority_key',
4411 help='Path to authority RSA private key file',
4412 required=False)
4413 sub_parser.add_argument('--signing_helper',
4414 help='Path to helper used for signing',
4415 metavar='APP',
4416 default=None,
4417 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004418 sub_parser.add_argument('--signing_helper_with_files',
4419 help='Path to helper used for signing using files',
4420 metavar='APP',
4421 default=None,
4422 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004423 sub_parser.set_defaults(func=self.make_atx_certificate)
4424
4425 sub_parser = subparsers.add_parser(
4426 'make_atx_permanent_attributes',
4427 help='Create Android Things eXtension (ATX) permanent attributes.')
4428 sub_parser.add_argument('--output',
4429 help='Write attributes to file',
4430 type=argparse.FileType('wb'),
4431 default=sys.stdout)
4432 sub_parser.add_argument('--root_authority_key',
4433 help='Path to authority RSA public key file',
4434 type=argparse.FileType('rb'),
4435 required=True)
4436 sub_parser.add_argument('--product_id',
4437 help=('Path to Product ID file'),
4438 type=argparse.FileType('rb'),
4439 required=True)
4440 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4441
4442 sub_parser = subparsers.add_parser(
4443 'make_atx_metadata',
4444 help='Create Android Things eXtension (ATX) metadata.')
4445 sub_parser.add_argument('--output',
4446 help='Write metadata to file',
4447 type=argparse.FileType('wb'),
4448 default=sys.stdout)
4449 sub_parser.add_argument('--intermediate_key_certificate',
4450 help='Path to intermediate key certificate file',
4451 type=argparse.FileType('rb'),
4452 required=True)
4453 sub_parser.add_argument('--product_key_certificate',
4454 help='Path to product key certificate file',
4455 type=argparse.FileType('rb'),
4456 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004457 sub_parser.set_defaults(func=self.make_atx_metadata)
4458
Darren Krahnfccd64e2018-01-16 17:39:35 -08004459 sub_parser = subparsers.add_parser(
4460 'make_atx_unlock_credential',
4461 help='Create an Android Things eXtension (ATX) unlock credential.')
4462 sub_parser.add_argument('--output',
4463 help='Write credential to file',
4464 type=argparse.FileType('wb'),
4465 default=sys.stdout)
4466 sub_parser.add_argument('--intermediate_key_certificate',
4467 help='Path to intermediate key certificate file',
4468 type=argparse.FileType('rb'),
4469 required=True)
4470 sub_parser.add_argument('--unlock_key_certificate',
4471 help='Path to unlock key certificate file',
4472 type=argparse.FileType('rb'),
4473 required=True)
4474 sub_parser.add_argument('--challenge',
4475 help='Path to the challenge to sign (optional). If '
4476 'this is not provided the challenge signature '
4477 'field is omitted and can be concatenated '
4478 'later.',
4479 required=False)
4480 sub_parser.add_argument('--unlock_key',
4481 help='Path to unlock key (optional). Must be '
4482 'provided if using --challenge.',
4483 required=False)
4484 sub_parser.add_argument('--signing_helper',
4485 help='Path to helper used for signing',
4486 metavar='APP',
4487 default=None,
4488 required=False)
4489 sub_parser.add_argument('--signing_helper_with_files',
4490 help='Path to helper used for signing using files',
4491 metavar='APP',
4492 default=None,
4493 required=False)
4494 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4495
David Zeuthen21e95262016-07-27 17:58:40 -04004496 args = parser.parse_args(argv[1:])
4497 try:
4498 args.func(args)
4499 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004500 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004501 sys.exit(1)
4502
4503 def version(self, _):
4504 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004505 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004506
Jan Monsch2c7be992020-04-03 14:37:13 +02004507 def generate_test_image(self, args):
4508 """Implements the 'generate_test_image' sub-command."""
4509 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4510
David Zeuthen21e95262016-07-27 17:58:40 -04004511 def extract_public_key(self, args):
4512 """Implements the 'extract_public_key' sub-command."""
4513 self.avb.extract_public_key(args.key, args.output)
4514
4515 def make_vbmeta_image(self, args):
4516 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004517 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004518 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004519 args.algorithm, args.key,
4520 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004521 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004522 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004523 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004524 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004525 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004526 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004527 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004528 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004529 args.print_required_libavb_version,
4530 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004531
David Zeuthenb1b994d2017-03-06 18:01:31 -05004532 def append_vbmeta_image(self, args):
4533 """Implements the 'append_vbmeta_image' sub-command."""
4534 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4535 args.partition_size)
4536
David Zeuthen21e95262016-07-27 17:58:40 -04004537 def add_hash_footer(self, args):
4538 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004539 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004540 self.avb.add_hash_footer(args.image.name if args.image else None,
4541 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004542 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004543 args.salt, args.chain_partition, args.algorithm,
4544 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004545 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004546 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004547 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004548 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004549 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004550 args.calc_max_image_size,
4551 args.signing_helper,
4552 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004553 args.internal_release_string,
4554 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004555 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004556 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004557 args.print_required_libavb_version,
4558 args.use_persistent_digest,
4559 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004560
4561 def add_hashtree_footer(self, args):
4562 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004563 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004564 # TODO(zeuthen): Remove when removing support for the
4565 # '--generate_fec' option above.
4566 if args.generate_fec:
4567 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4568 'is now generated by default. Use the option '
4569 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004570 self.avb.add_hashtree_footer(
4571 args.image.name if args.image else None,
4572 args.partition_size,
4573 args.partition_name,
4574 not args.do_not_generate_fec, args.fec_num_roots,
4575 args.hash_algorithm, args.block_size,
4576 args.salt, args.chain_partition, args.algorithm,
4577 args.key, args.public_key_metadata,
4578 args.rollback_index, args.flags, args.prop,
4579 args.prop_from_file,
4580 args.kernel_cmdline,
4581 args.setup_rootfs_from_kernel,
4582 args.setup_as_rootfs_from_kernel,
4583 args.include_descriptors_from_image,
4584 args.calc_max_image_size,
4585 args.signing_helper,
4586 args.signing_helper_with_files,
4587 args.internal_release_string,
4588 args.append_to_release_string,
4589 args.output_vbmeta_image,
4590 args.do_not_append_vbmeta_image,
4591 args.print_required_libavb_version,
4592 args.use_persistent_digest,
4593 args.do_not_use_ab,
4594 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004595
David Zeuthen21e95262016-07-27 17:58:40 -04004596 def erase_footer(self, args):
4597 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004598 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004599
David Zeuthen1394f762019-04-30 10:20:11 -04004600 def zero_hashtree(self, args):
4601 """Implements the 'zero_hashtree' sub-command."""
4602 self.avb.zero_hashtree(args.image.name)
4603
David Zeuthen49936b42018-08-07 17:38:58 -04004604 def extract_vbmeta_image(self, args):
4605 """Implements the 'extract_vbmeta_image' sub-command."""
4606 self.avb.extract_vbmeta_image(args.output, args.image.name,
4607 args.padding_size)
4608
David Zeuthen2bc232b2017-04-19 14:25:19 -04004609 def resize_image(self, args):
4610 """Implements the 'resize_image' sub-command."""
4611 self.avb.resize_image(args.image.name, args.partition_size)
4612
David Zeuthen8b6973b2016-09-20 12:39:49 -04004613 def set_ab_metadata(self, args):
4614 """Implements the 'set_ab_metadata' sub-command."""
4615 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4616
David Zeuthen21e95262016-07-27 17:58:40 -04004617 def info_image(self, args):
4618 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004619 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004620
David Zeuthenb623d8b2017-04-04 16:05:53 -04004621 def verify_image(self, args):
4622 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004623 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004624 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004625 args.follow_chain_partitions,
4626 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004627
David Zeuthenb8643c02018-05-17 17:21:18 -04004628 def calculate_vbmeta_digest(self, args):
4629 """Implements the 'calculate_vbmeta_digest' sub-command."""
4630 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4631 args.output)
4632
David Zeuthenf7d2e752018-09-20 13:30:41 -04004633 def calculate_kernel_cmdline(self, args):
4634 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004635 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4636 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004637
Darren Krahn147b08d2016-12-20 16:38:29 -08004638 def make_atx_certificate(self, args):
4639 """Implements the 'make_atx_certificate' sub-command."""
4640 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004641 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004642 args.subject_key_version,
4643 args.subject.read(),
4644 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004645 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004646 args.signing_helper,
4647 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004648
4649 def make_atx_permanent_attributes(self, args):
4650 """Implements the 'make_atx_permanent_attributes' sub-command."""
4651 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004652 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004653 args.product_id.read())
4654
4655 def make_atx_metadata(self, args):
4656 """Implements the 'make_atx_metadata' sub-command."""
4657 self.avb.make_atx_metadata(args.output,
4658 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004659 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004660
Darren Krahnfccd64e2018-01-16 17:39:35 -08004661 def make_atx_unlock_credential(self, args):
4662 """Implements the 'make_atx_unlock_credential' sub-command."""
4663 self.avb.make_atx_unlock_credential(
4664 args.output,
4665 args.intermediate_key_certificate.read(),
4666 args.unlock_key_certificate.read(),
4667 args.challenge,
4668 args.unlock_key,
4669 args.signing_helper,
4670 args.signing_helper_with_files)
4671
David Zeuthen21e95262016-07-27 17:58:40 -04004672
4673if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004674 if AVB_INVOCATION_LOGFILE:
4675 f = open(AVB_INVOCATION_LOGFILE, 'a')
4676 f.write(' '.join(sys.argv))
4677 f.write('\n')
4678 f.close()
4679
David Zeuthen21e95262016-07-27 17:58:40 -04004680 tool = AvbTool()
4681 tool.run(sys.argv)