blob: 5d7adb195d363493958c0e3a05d5aefc8d040bf9 [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
David Zeuthene3cadca2017-02-22 21:25:46 -050052
David Zeuthen21e95262016-07-27 17:58:40 -040053class AvbError(Exception):
54 """Application-specific errors.
55
56 These errors represent issues for which a stack-trace should not be
57 presented.
58
59 Attributes:
60 message: Error message.
61 """
62
63 def __init__(self, message):
64 Exception.__init__(self, message)
65
66
67class Algorithm(object):
68 """Contains details about an algorithm.
69
Tao Bao80418a52018-07-20 11:41:22 -070070 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040071
72 The constant |ALGORITHMS| is a dictionary from human-readable
73 names (e.g 'SHA256_RSA2048') to instances of this class.
74
75 Attributes:
76 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040077 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040078 hash_num_bytes: Number of bytes used to store the hash.
79 signature_num_bytes: Number of bytes used to store the signature.
80 public_key_num_bytes: Number of bytes used to store the public key.
81 padding: Padding used for signature, if any.
82 """
83
David Zeuthenb623d8b2017-04-04 16:05:53 -040084 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
85 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040088 self.hash_num_bytes = hash_num_bytes
89 self.signature_num_bytes = signature_num_bytes
90 self.public_key_num_bytes = public_key_num_bytes
91 self.padding = padding
92
David Zeuthenb623d8b2017-04-04 16:05:53 -040093
David Zeuthen21e95262016-07-27 17:58:40 -040094# This must be kept in sync with the avb_crypto.h file.
95#
96# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
97# obtained from section 5.2.2 of RFC 4880.
98ALGORITHMS = {
99 'NONE': Algorithm(
100 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400101 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400102 hash_num_bytes=0,
103 signature_num_bytes=0,
104 public_key_num_bytes=0,
105 padding=[]),
106 'SHA256_RSA2048': Algorithm(
107 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400108 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400109 hash_num_bytes=32,
110 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100111 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400112 padding=[
113 # PKCS1-v1_5 padding
114 0x00, 0x01] + [0xff]*202 + [0x00] + [
115 # ASN.1 header
116 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
117 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
118 0x00, 0x04, 0x20,
119 ]),
120 'SHA256_RSA4096': Algorithm(
121 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400122 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400123 hash_num_bytes=32,
124 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100125 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400126 padding=[
127 # PKCS1-v1_5 padding
128 0x00, 0x01] + [0xff]*458 + [0x00] + [
129 # ASN.1 header
130 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
131 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
132 0x00, 0x04, 0x20,
133 ]),
134 'SHA256_RSA8192': Algorithm(
135 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400136 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400137 hash_num_bytes=32,
138 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100139 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400140 padding=[
141 # PKCS1-v1_5 padding
142 0x00, 0x01] + [0xff]*970 + [0x00] + [
143 # ASN.1 header
144 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
145 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
146 0x00, 0x04, 0x20,
147 ]),
148 'SHA512_RSA2048': Algorithm(
149 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400150 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400151 hash_num_bytes=64,
152 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100153 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400154 padding=[
155 # PKCS1-v1_5 padding
156 0x00, 0x01] + [0xff]*170 + [0x00] + [
157 # ASN.1 header
158 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
159 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
160 0x00, 0x04, 0x40
161 ]),
162 'SHA512_RSA4096': Algorithm(
163 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400164 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400165 hash_num_bytes=64,
166 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100167 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400168 padding=[
169 # PKCS1-v1_5 padding
170 0x00, 0x01] + [0xff]*426 + [0x00] + [
171 # ASN.1 header
172 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
173 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
174 0x00, 0x04, 0x40
175 ]),
176 'SHA512_RSA8192': Algorithm(
177 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400178 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400179 hash_num_bytes=64,
180 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100181 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400182 padding=[
183 # PKCS1-v1_5 padding
184 0x00, 0x01] + [0xff]*938 + [0x00] + [
185 # ASN.1 header
186 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
187 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
188 0x00, 0x04, 0x40
189 ]),
190}
191
192
David Zeuthene3cadca2017-02-22 21:25:46 -0500193def get_release_string():
194 """Calculates the release string to use in the VBMeta struct."""
195 # Keep in sync with libavb/avb_version.c:avb_version_string().
196 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
197 AVB_VERSION_MINOR,
198 AVB_VERSION_SUB)
199
200
David Zeuthen21e95262016-07-27 17:58:40 -0400201def round_to_multiple(number, size):
202 """Rounds a number up to nearest multiple of another number.
203
Jan Monschfe00c0a2019-12-11 11:19:40 +0100204 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400205 number: The number to round up.
206 size: The multiple to round up to.
207
208 Returns:
209 If |number| is a multiple of |size|, returns |number|, otherwise
210 returns |number| + |size|.
211 """
212 remainder = number % size
213 if remainder == 0:
214 return number
215 return number + size - remainder
216
217
218def round_to_pow2(number):
219 """Rounds a number up to the next power of 2.
220
Jan Monschfe00c0a2019-12-11 11:19:40 +0100221 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400222 number: The number to round up.
223
224 Returns:
225 If |number| is already a power of 2 then |number| is
226 returned. Otherwise the smallest power of 2 greater than |number|
227 is returned.
228 """
229 return 2**((number - 1).bit_length())
230
231
David Zeuthen21e95262016-07-27 17:58:40 -0400232def encode_long(num_bits, value):
233 """Encodes a long to a bytearray() using a given amount of bits.
234
235 This number is written big-endian, e.g. with the most significant
236 bit first.
237
David Zeuthenb623d8b2017-04-04 16:05:53 -0400238 This is the reverse of decode_long().
239
David Zeuthen21e95262016-07-27 17:58:40 -0400240 Arguments:
241 num_bits: The number of bits to write, e.g. 2048.
242 value: The value to write.
243
244 Returns:
245 A bytearray() with the encoded long.
246 """
247 ret = bytearray()
248 for bit_pos in range(num_bits, 0, -8):
249 octet = (value >> (bit_pos - 8)) & 0xff
250 ret.extend(struct.pack('!B', octet))
251 return ret
252
253
David Zeuthenb623d8b2017-04-04 16:05:53 -0400254def decode_long(blob):
255 """Decodes a long from a bytearray() using a given amount of bits.
256
257 This number is expected to be in big-endian, e.g. with the most
258 significant bit first.
259
260 This is the reverse of encode_long().
261
262 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100263 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400264
265 Returns:
266 The decoded value.
267 """
268 ret = 0
269 for b in bytearray(blob):
270 ret *= 256
271 ret += b
272 return ret
273
274
David Zeuthen21e95262016-07-27 17:58:40 -0400275def egcd(a, b):
276 """Calculate greatest common divisor of two numbers.
277
278 This implementation uses a recursive version of the extended
279 Euclidian algorithm.
280
281 Arguments:
282 a: First number.
283 b: Second number.
284
285 Returns:
286 A tuple (gcd, x, y) that where |gcd| is the greatest common
287 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
288 """
289 if a == 0:
290 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100291 g, y, x = egcd(b % a, a)
292 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400293
294
295def modinv(a, m):
296 """Calculate modular multiplicative inverse of |a| modulo |m|.
297
298 This calculates the number |x| such that |a| * |x| == 1 (modulo
299 |m|). This number only exists if |a| and |m| are co-prime - |None|
300 is returned if this isn't true.
301
302 Arguments:
303 a: The number to calculate a modular inverse of.
304 m: The modulo to use.
305
306 Returns:
307 The modular multiplicative inverse of |a| and |m| or |None| if
308 these numbers are not co-prime.
309 """
310 gcd, x, _ = egcd(a, m)
311 if gcd != 1:
312 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100313 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400314
315
316def parse_number(string):
317 """Parse a string as a number.
318
319 This is just a short-hand for int(string, 0) suitable for use in the
320 |type| parameter of |ArgumentParser|'s add_argument() function. An
321 improvement to just using type=int is that this function supports
322 numbers in other bases, e.g. "0x1234".
323
324 Arguments:
325 string: The string to parse.
326
327 Returns:
328 The parsed integer.
329
330 Raises:
331 ValueError: If the number could not be parsed.
332 """
333 return int(string, 0)
334
335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336class RSAPublicKey(object):
337 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339 Attributes:
340 exponent: The key exponent.
341 modulus: The key modulus.
342 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400343 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400344
345 MODULUS_PREFIX = 'modulus='
346
347 def __init__(self, key_path):
348 """Loads and parses an RSA key from either a private or public key file.
349
350 Arguments:
351 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100352
353 Raises:
354 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400355 """
356 # We used to have something as simple as this:
357 #
358 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
359 # self.exponent = key.e
360 # self.modulus = key.n
361 # self.num_bits = key.size() + 1
362 #
363 # but unfortunately PyCrypto is not available in the builder. So
364 # instead just parse openssl(1) output to get this
365 # information. It's ugly but...
366 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
367 p = subprocess.Popen(args,
368 stdin=subprocess.PIPE,
369 stdout=subprocess.PIPE,
370 stderr=subprocess.PIPE)
371 (pout, perr) = p.communicate()
372 if p.wait() != 0:
373 # Could be just a public key is passed, try that.
374 args.append('-pubin')
375 p = subprocess.Popen(args,
376 stdin=subprocess.PIPE,
377 stdout=subprocess.PIPE,
378 stderr=subprocess.PIPE)
379 (pout, perr) = p.communicate()
380 if p.wait() != 0:
381 raise AvbError('Error getting public key: {}'.format(perr))
382
383 if not pout.lower().startswith(self.MODULUS_PREFIX):
384 raise AvbError('Unexpected modulus output')
385
386 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
387
388 # The exponent is assumed to always be 65537 and the number of
389 # bits can be derived from the modulus by rounding up to the
390 # nearest power of 2.
391 self.modulus = int(modulus_hexstr, 16)
392 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
393 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400394
395
David Zeuthenc68f0822017-03-31 17:22:35 -0400396def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400397 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
398
399 This creates a |AvbRSAPublicKeyHeader| as well as the two large
400 numbers (|key_num_bits| bits long) following it.
401
402 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400403 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400404
405 Returns:
406 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100407
408 Raises:
409 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400410 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400411 key = RSAPublicKey(key_path)
412 if key.exponent != 65537:
413 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400414 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400415 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monsch23e0c622019-12-11 11:23:58 +0100416 b = 2L**32 # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400417 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monsch23e0c622019-12-11 11:23:58 +0100419 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 rrmodn = r * r % key.modulus
421 ret.extend(struct.pack('!II', key.num_bits, n0inv))
422 ret.extend(encode_long(key.num_bits, key.modulus))
423 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400424 return ret
425
426
427def lookup_algorithm_by_type(alg_type):
428 """Looks up algorithm by type.
429
430 Arguments:
431 alg_type: The integer representing the type.
432
433 Returns:
434 A tuple with the algorithm name and an |Algorithm| instance.
435
436 Raises:
437 Exception: If the algorithm cannot be found
438 """
439 for alg_name in ALGORITHMS:
440 alg_data = ALGORITHMS[alg_name]
441 if alg_data.algorithm_type == alg_type:
442 return (alg_name, alg_data)
443 raise AvbError('Unknown algorithm type {}'.format(alg_type))
444
Jan Monsch77cd2022019-12-10 17:18:04 +0100445
Dan Austina7bc4962019-12-02 13:26:08 -0800446def lookup_hash_size_by_type(alg_type):
447 """Looks up hash size by type.
448
449 Arguments:
450 alg_type: The integer representing the type.
451
452 Returns:
453 The corresponding hash size.
454
455 Raises:
456 AvbError: If the algorithm cannot be found.
457 """
458 for alg_name in ALGORITHMS:
459 alg_data = ALGORITHMS[alg_name]
460 if alg_data.algorithm_type == alg_type:
461 return alg_data.hash_num_bytes
462 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400463
Jan Monsch77cd2022019-12-10 17:18:04 +0100464
David Zeuthena156d3d2017-06-01 12:08:09 -0400465def raw_sign(signing_helper, signing_helper_with_files,
466 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900467 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800468 """Computes a raw RSA signature using |signing_helper| or openssl.
469
470 Arguments:
471 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400472 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800473 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900474 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800475 key_path: Path to the private key file. Must be PEM format.
476 raw_data_to_sign: Data to sign (bytearray or str expected).
477
478 Returns:
479 A bytearray containing the signature.
480
481 Raises:
482 Exception: If an error occurs.
483 """
484 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400485 if signing_helper_with_files is not None:
486 signing_file = tempfile.NamedTemporaryFile()
487 signing_file.write(str(raw_data_to_sign))
488 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100489 p = subprocess.Popen([
490 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400491 retcode = p.wait()
492 if retcode != 0:
493 raise AvbError('Error signing')
494 signing_file.seek(0)
495 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800496 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400497 if signing_helper is not None:
498 p = subprocess.Popen(
499 [signing_helper, algorithm_name, key_path],
500 stdin=subprocess.PIPE,
501 stdout=subprocess.PIPE,
502 stderr=subprocess.PIPE)
503 else:
504 p = subprocess.Popen(
505 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
506 stdin=subprocess.PIPE,
507 stdout=subprocess.PIPE,
508 stderr=subprocess.PIPE)
509 (pout, perr) = p.communicate(str(raw_data_to_sign))
510 retcode = p.wait()
511 if retcode != 0:
512 raise AvbError('Error signing: {}'.format(perr))
513 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900514 if len(signature) != signature_num_bytes:
515 raise AvbError('Error signing: Invalid length of signature')
516 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800517
518
David Zeuthenb623d8b2017-04-04 16:05:53 -0400519def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100520 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400521
522 Arguments:
523 vbmeta_header: A AvbVBMetaHeader.
524 vbmeta_blob: The whole vbmeta blob, including the header.
525
526 Returns:
527 True if the signature is valid and corresponds to the embedded
528 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100529
530 Raises:
531 AvbError: If there errors calling out to openssl command during
532 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400533 """
534 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100535 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536 return True
537 header_blob = vbmeta_blob[0:256]
538 auth_offset = 256
539 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
540 aux_size = vbmeta_header.auxiliary_data_block_size
541 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
542 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
543 pubkey_size = vbmeta_header.public_key_size
544 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
545
546 digest_offset = auth_offset + vbmeta_header.hash_offset
547 digest_size = vbmeta_header.hash_size
548 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
549
550 sig_offset = auth_offset + vbmeta_header.signature_offset
551 sig_size = vbmeta_header.signature_size
552 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
553
554 # Now that we've got the stored digest, public key, and signature
555 # all we need to do is to verify. This is the exactly the same
556 # steps as performed in the avb_vbmeta_image_verify() function in
557 # libavb/avb_vbmeta_image.c.
558
559 ha = hashlib.new(alg.hash_name)
560 ha.update(header_blob)
561 ha.update(aux_blob)
562 computed_digest = ha.digest()
563
564 if computed_digest != digest_blob:
565 return False
566
567 padding_and_digest = bytearray(alg.padding)
568 padding_and_digest.extend(computed_digest)
569
570 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100571 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400572 modulus = decode_long(modulus_blob)
573 exponent = 65537
574
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500575 # We used to have this:
576 #
577 # import Crypto.PublicKey.RSA
578 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
579 # if not key.verify(decode_long(padding_and_digest),
580 # (decode_long(sig_blob), None)):
581 # return False
582 # return True
583 #
584 # but since 'avbtool verify_image' is used on the builders we don't want
585 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
586 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
587 '\n'
588 '[pubkeyinfo]\n'
589 'algorithm=SEQUENCE:rsa_alg\n'
590 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
591 '\n'
592 '[rsa_alg]\n'
593 'algorithm=OID:rsaEncryption\n'
594 'parameter=NULL\n'
595 '\n'
596 '[rsapubkey]\n'
597 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100598 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
599 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500600 asn1_tmpfile = tempfile.NamedTemporaryFile()
601 asn1_tmpfile.write(asn1_str)
602 asn1_tmpfile.flush()
603 der_tmpfile = tempfile.NamedTemporaryFile()
604 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100605 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
606 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500607 retcode = p.wait()
608 if retcode != 0:
609 raise AvbError('Error generating DER file')
610
611 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100612 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
613 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500614 stdin=subprocess.PIPE,
615 stdout=subprocess.PIPE,
616 stderr=subprocess.PIPE)
617 (pout, perr) = p.communicate(str(sig_blob))
618 retcode = p.wait()
619 if retcode != 0:
620 raise AvbError('Error verifying data: {}'.format(perr))
621 recovered_data = bytearray(pout)
622 if recovered_data != padding_and_digest:
623 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400624 return False
625 return True
626
627
David Zeuthena4fee8b2016-08-22 15:20:43 -0400628class ImageChunk(object):
629 """Data structure used for representing chunks in Android sparse files.
630
631 Attributes:
632 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
633 chunk_offset: Offset in the sparse file where this chunk begins.
634 output_offset: Offset in de-sparsified file where output begins.
635 output_size: Number of bytes in output.
636 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
637 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
638 """
639
640 FORMAT = '<2H2I'
641 TYPE_RAW = 0xcac1
642 TYPE_FILL = 0xcac2
643 TYPE_DONT_CARE = 0xcac3
644 TYPE_CRC32 = 0xcac4
645
646 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
647 input_offset, fill_data):
648 """Initializes an ImageChunk object.
649
650 Arguments:
651 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
652 chunk_offset: Offset in the sparse file where this chunk begins.
653 output_offset: Offset in de-sparsified file.
654 output_size: Number of bytes in output.
655 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
656 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
657
658 Raises:
659 ValueError: If data is not well-formed.
660 """
661 self.chunk_type = chunk_type
662 self.chunk_offset = chunk_offset
663 self.output_offset = output_offset
664 self.output_size = output_size
665 self.input_offset = input_offset
666 self.fill_data = fill_data
667 # Check invariants.
668 if self.chunk_type == self.TYPE_RAW:
669 if self.fill_data is not None:
670 raise ValueError('RAW chunk cannot have fill_data set.')
671 if not self.input_offset:
672 raise ValueError('RAW chunk must have input_offset set.')
673 elif self.chunk_type == self.TYPE_FILL:
674 if self.fill_data is None:
675 raise ValueError('FILL chunk must have fill_data set.')
676 if self.input_offset:
677 raise ValueError('FILL chunk cannot have input_offset set.')
678 elif self.chunk_type == self.TYPE_DONT_CARE:
679 if self.fill_data is not None:
680 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
681 if self.input_offset:
682 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
683 else:
684 raise ValueError('Invalid chunk type')
685
686
687class ImageHandler(object):
688 """Abstraction for image I/O with support for Android sparse images.
689
690 This class provides an interface for working with image files that
691 may be using the Android Sparse Image format. When an instance is
692 constructed, we test whether it's an Android sparse file. If so,
693 operations will be on the sparse file by interpreting the sparse
694 format, otherwise they will be directly on the file. Either way the
695 operations do the same.
696
697 For reading, this interface mimics a file object - it has seek(),
698 tell(), and read() methods. For writing, only truncation
699 (truncate()) and appending is supported (append_raw() and
700 append_dont_care()). Additionally, data can only be written in units
701 of the block size.
702
703 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400704 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 is_sparse: Whether the file being operated on is sparse.
706 block_size: The block size, typically 4096.
707 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400708 """
709 # See system/core/libsparse/sparse_format.h for details.
710 MAGIC = 0xed26ff3a
711 HEADER_FORMAT = '<I4H4I'
712
713 # These are formats and offset of just the |total_chunks| and
714 # |total_blocks| fields.
715 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
716 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
717
718 def __init__(self, image_filename):
719 """Initializes an image handler.
720
721 Arguments:
722 image_filename: The name of the file to operate on.
723
724 Raises:
725 ValueError: If data in the file is invalid.
726 """
David Zeuthen49936b42018-08-07 17:38:58 -0400727 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100728 self._num_total_blocks = 0
729 self._num_total_chunks = 0
730 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400731 self._read_header()
732
733 def _read_header(self):
734 """Initializes internal data structures used for reading file.
735
736 This may be called multiple times and is typically called after
737 modifying the file (e.g. appending, truncation).
738
739 Raises:
740 ValueError: If data in the file is invalid.
741 """
742 self.is_sparse = False
743 self.block_size = 4096
744 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400745 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400746 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400747 self.image_size = self._image.tell()
748
749 self._image.seek(0, os.SEEK_SET)
750 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
751 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
752 block_size, self._num_total_blocks, self._num_total_chunks,
753 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
754 if magic != self.MAGIC:
755 # Not a sparse image, our job here is done.
756 return
757 if not (major_version == 1 and minor_version == 0):
758 raise ValueError('Encountered sparse image format version {}.{} but '
759 'only 1.0 is supported'.format(major_version,
760 minor_version))
761 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
762 raise ValueError('Unexpected file_hdr_sz value {}.'.
763 format(file_hdr_sz))
764 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
765 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
766 format(chunk_hdr_sz))
767
768 self.block_size = block_size
769
770 # Build an list of chunks by parsing the file.
771 self._chunks = []
772
773 # Find the smallest offset where only "Don't care" chunks
774 # follow. This will be the size of the content in the sparse
775 # image.
776 offset = 0
777 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100778 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400779 chunk_offset = self._image.tell()
780
781 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
782 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
783 header_bin)
784 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
785
David Zeuthena4fee8b2016-08-22 15:20:43 -0400786 if chunk_type == ImageChunk.TYPE_RAW:
787 if data_sz != (chunk_sz * self.block_size):
788 raise ValueError('Raw chunk input size ({}) does not match output '
789 'size ({})'.
790 format(data_sz, chunk_sz*self.block_size))
791 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
792 chunk_offset,
793 output_offset,
794 chunk_sz*self.block_size,
795 self._image.tell(),
796 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700797 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400798
799 elif chunk_type == ImageChunk.TYPE_FILL:
800 if data_sz != 4:
801 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
802 'has {}'.format(data_sz))
803 fill_data = self._image.read(4)
804 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
805 chunk_offset,
806 output_offset,
807 chunk_sz*self.block_size,
808 None,
809 fill_data))
810 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
811 if data_sz != 0:
812 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
813 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400814 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
815 chunk_offset,
816 output_offset,
817 chunk_sz*self.block_size,
818 None,
819 None))
820 elif chunk_type == ImageChunk.TYPE_CRC32:
821 if data_sz != 4:
822 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
823 'this has {}'.format(data_sz))
824 self._image.read(4)
825 else:
826 raise ValueError('Unknown chunk type {}'.format(chunk_type))
827
828 offset += chunk_sz
829 output_offset += chunk_sz*self.block_size
830
831 # Record where sparse data end.
832 self._sparse_end = self._image.tell()
833
834 # Now that we've traversed all chunks, sanity check.
835 if self._num_total_blocks != offset:
836 raise ValueError('The header said we should have {} output blocks, '
837 'but we saw {}'.format(self._num_total_blocks, offset))
838 junk_len = len(self._image.read())
839 if junk_len > 0:
840 raise ValueError('There were {} bytes of extra data at the end of the '
841 'file.'.format(junk_len))
842
David Zeuthen09692692016-09-30 16:16:40 -0400843 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400844 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400845
846 # This is used when bisecting in read() to find the initial slice.
847 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
848
849 self.is_sparse = True
850
851 def _update_chunks_and_blocks(self):
852 """Helper function to update the image header.
853
854 The the |total_chunks| and |total_blocks| fields in the header
855 will be set to value of the |_num_total_blocks| and
856 |_num_total_chunks| attributes.
857
858 """
859 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
860 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
861 self._num_total_blocks,
862 self._num_total_chunks))
863
864 def append_dont_care(self, num_bytes):
865 """Appends a DONT_CARE chunk to the sparse file.
866
867 The given number of bytes must be a multiple of the block size.
868
869 Arguments:
870 num_bytes: Size in number of bytes of the DONT_CARE chunk.
871 """
872 assert num_bytes % self.block_size == 0
873
874 if not self.is_sparse:
875 self._image.seek(0, os.SEEK_END)
876 # This is more efficient that writing NUL bytes since it'll add
877 # a hole on file systems that support sparse files (native
878 # sparse, not Android sparse).
879 self._image.truncate(self._image.tell() + num_bytes)
880 self._read_header()
881 return
882
883 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100884 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400885 self._update_chunks_and_blocks()
886
887 self._image.seek(self._sparse_end, os.SEEK_SET)
888 self._image.write(struct.pack(ImageChunk.FORMAT,
889 ImageChunk.TYPE_DONT_CARE,
890 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100891 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400892 struct.calcsize(ImageChunk.FORMAT)))
893 self._read_header()
894
895 def append_raw(self, data):
896 """Appends a RAW chunk to the sparse file.
897
898 The length of the given data must be a multiple of the block size.
899
900 Arguments:
901 data: Data to append.
902 """
903 assert len(data) % self.block_size == 0
904
905 if not self.is_sparse:
906 self._image.seek(0, os.SEEK_END)
907 self._image.write(data)
908 self._read_header()
909 return
910
911 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100912 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400913 self._update_chunks_and_blocks()
914
915 self._image.seek(self._sparse_end, os.SEEK_SET)
916 self._image.write(struct.pack(ImageChunk.FORMAT,
917 ImageChunk.TYPE_RAW,
918 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100919 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400920 len(data) +
921 struct.calcsize(ImageChunk.FORMAT)))
922 self._image.write(data)
923 self._read_header()
924
925 def append_fill(self, fill_data, size):
926 """Appends a fill chunk to the sparse file.
927
928 The total length of the fill data must be a multiple of the block size.
929
930 Arguments:
931 fill_data: Fill data to append - must be four bytes.
932 size: Number of chunk - must be a multiple of four and the block size.
933 """
934 assert len(fill_data) == 4
935 assert size % 4 == 0
936 assert size % self.block_size == 0
937
938 if not self.is_sparse:
939 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100940 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400941 self._read_header()
942 return
943
944 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 self._update_chunks_and_blocks()
947
948 self._image.seek(self._sparse_end, os.SEEK_SET)
949 self._image.write(struct.pack(ImageChunk.FORMAT,
950 ImageChunk.TYPE_FILL,
951 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100952 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400953 4 + struct.calcsize(ImageChunk.FORMAT)))
954 self._image.write(fill_data)
955 self._read_header()
956
957 def seek(self, offset):
958 """Sets the cursor position for reading from unsparsified file.
959
960 Arguments:
961 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100962
963 Raises:
964 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400965 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700966 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100967 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400968 self._file_pos = offset
969
970 def read(self, size):
971 """Reads data from the unsparsified file.
972
973 This method may return fewer than |size| bytes of data if the end
974 of the file was encountered.
975
976 The file cursor for reading is advanced by the number of bytes
977 read.
978
979 Arguments:
980 size: Number of bytes to read.
981
982 Returns:
983 The data.
984
985 """
986 if not self.is_sparse:
987 self._image.seek(self._file_pos)
988 data = self._image.read(size)
989 self._file_pos += len(data)
990 return data
991
992 # Iterate over all chunks.
993 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
994 self._file_pos) - 1
995 data = bytearray()
996 to_go = size
997 while to_go > 0:
998 chunk = self._chunks[chunk_idx]
999 chunk_pos_offset = self._file_pos - chunk.output_offset
1000 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1001
1002 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1003 self._image.seek(chunk.input_offset + chunk_pos_offset)
1004 data.extend(self._image.read(chunk_pos_to_go))
1005 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001006 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001007 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1008 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1009 else:
1010 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1011 data.extend('\0' * chunk_pos_to_go)
1012
1013 to_go -= chunk_pos_to_go
1014 self._file_pos += chunk_pos_to_go
1015 chunk_idx += 1
1016 # Generate partial read in case of EOF.
1017 if chunk_idx >= len(self._chunks):
1018 break
1019
1020 return data
1021
1022 def tell(self):
1023 """Returns the file cursor position for reading from unsparsified file.
1024
1025 Returns:
1026 The file cursor position for reading.
1027 """
1028 return self._file_pos
1029
1030 def truncate(self, size):
1031 """Truncates the unsparsified file.
1032
1033 Arguments:
1034 size: Desired size of unsparsified file.
1035
1036 Raises:
1037 ValueError: If desired size isn't a multiple of the block size.
1038 """
1039 if not self.is_sparse:
1040 self._image.truncate(size)
1041 self._read_header()
1042 return
1043
1044 if size % self.block_size != 0:
1045 raise ValueError('Cannot truncate to a size which is not a multiple '
1046 'of the block size')
1047
1048 if size == self.image_size:
1049 # Trivial where there's nothing to do.
1050 return
1051 elif size < self.image_size:
1052 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1053 chunk = self._chunks[chunk_idx]
1054 if chunk.output_offset != size:
1055 # Truncation in the middle of a trunk - need to keep the chunk
1056 # and modify it.
1057 chunk_idx_for_update = chunk_idx + 1
1058 num_to_keep = size - chunk.output_offset
1059 assert num_to_keep % self.block_size == 0
1060 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1061 truncate_at = (chunk.chunk_offset +
1062 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1063 data_sz = num_to_keep
1064 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1065 truncate_at = (chunk.chunk_offset +
1066 struct.calcsize(ImageChunk.FORMAT) + 4)
1067 data_sz = 4
1068 else:
1069 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1070 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1071 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001072 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001073 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1074 self._image.seek(chunk.chunk_offset)
1075 self._image.write(struct.pack(ImageChunk.FORMAT,
1076 chunk.chunk_type,
1077 0, # Reserved
1078 chunk_sz,
1079 total_sz))
1080 chunk.output_size = num_to_keep
1081 else:
1082 # Truncation at trunk boundary.
1083 truncate_at = chunk.chunk_offset
1084 chunk_idx_for_update = chunk_idx
1085
1086 self._num_total_chunks = chunk_idx_for_update
1087 self._num_total_blocks = 0
1088 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001089 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001090 self._update_chunks_and_blocks()
1091 self._image.truncate(truncate_at)
1092
1093 # We've modified the file so re-read all data.
1094 self._read_header()
1095 else:
1096 # Truncating to grow - just add a DONT_CARE section.
1097 self.append_dont_care(size - self.image_size)
1098
1099
David Zeuthen21e95262016-07-27 17:58:40 -04001100class AvbDescriptor(object):
1101 """Class for AVB descriptor.
1102
1103 See the |AvbDescriptor| C struct for more information.
1104
1105 Attributes:
1106 tag: The tag identifying what kind of descriptor this is.
1107 data: The data in the descriptor.
1108 """
1109
1110 SIZE = 16
1111 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1112
1113 def __init__(self, data):
1114 """Initializes a new property descriptor.
1115
1116 Arguments:
1117 data: If not None, must be a bytearray().
1118
1119 Raises:
1120 LookupError: If the given descriptor is malformed.
1121 """
1122 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1123
1124 if data:
1125 (self.tag, num_bytes_following) = (
1126 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1127 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1128 else:
1129 self.tag = None
1130 self.data = None
1131
1132 def print_desc(self, o):
1133 """Print the descriptor.
1134
1135 Arguments:
1136 o: The object to write the output to.
1137 """
1138 o.write(' Unknown descriptor:\n')
1139 o.write(' Tag: {}\n'.format(self.tag))
1140 if len(self.data) < 256:
1141 o.write(' Data: {} ({} bytes)\n'.format(
1142 repr(str(self.data)), len(self.data)))
1143 else:
1144 o.write(' Data: {} bytes\n'.format(len(self.data)))
1145
1146 def encode(self):
1147 """Serializes the descriptor.
1148
1149 Returns:
1150 A bytearray() with the descriptor data.
1151 """
1152 num_bytes_following = len(self.data)
1153 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1154 padding_size = nbf_with_padding - num_bytes_following
1155 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1156 padding = struct.pack(str(padding_size) + 'x')
1157 ret = desc + self.data + padding
1158 return bytearray(ret)
1159
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001160 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001161 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001162 """Verifies contents of the descriptor - used in verify_image sub-command.
1163
1164 Arguments:
1165 image_dir: The directory of the file being verified.
1166 image_ext: The extension of the file being verified (e.g. '.img').
1167 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001168 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001169 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001170 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1171 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001172
1173 Returns:
1174 True if the descriptor verifies, False otherwise.
1175 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001176 # Deletes unused parameters to prevent pylint warning unused-argument.
1177 del image_dir, image_ext, expected_chain_partitions_map
1178 del image_containing_descriptor, accept_zeroed_hashtree
1179
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001180 # Nothing to do.
1181 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001182
Jan Monscheeb28b62019-12-05 16:17:09 +01001183
David Zeuthen21e95262016-07-27 17:58:40 -04001184class AvbPropertyDescriptor(AvbDescriptor):
1185 """A class for property descriptors.
1186
1187 See the |AvbPropertyDescriptor| C struct for more information.
1188
1189 Attributes:
1190 key: The key.
1191 value: The key.
1192 """
1193
1194 TAG = 0
1195 SIZE = 32
1196 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1197 'Q' # key size (bytes)
1198 'Q') # value size (bytes)
1199
1200 def __init__(self, data=None):
1201 """Initializes a new property descriptor.
1202
1203 Arguments:
1204 data: If not None, must be a bytearray of size |SIZE|.
1205
1206 Raises:
1207 LookupError: If the given descriptor is malformed.
1208 """
1209 AvbDescriptor.__init__(self, None)
1210 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1211
1212 if data:
1213 (tag, num_bytes_following, key_size,
1214 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1215 expected_size = round_to_multiple(
1216 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1217 if tag != self.TAG or num_bytes_following != expected_size:
1218 raise LookupError('Given data does not look like a property '
1219 'descriptor.')
1220 self.key = data[self.SIZE:(self.SIZE + key_size)]
1221 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1222 value_size)]
1223 else:
1224 self.key = ''
1225 self.value = ''
1226
1227 def print_desc(self, o):
1228 """Print the descriptor.
1229
1230 Arguments:
1231 o: The object to write the output to.
1232 """
1233 if len(self.value) < 256:
1234 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1235 else:
1236 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1237
1238 def encode(self):
1239 """Serializes the descriptor.
1240
1241 Returns:
1242 A bytearray() with the descriptor data.
1243 """
1244 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1245 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1246 padding_size = nbf_with_padding - num_bytes_following
1247 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1248 len(self.key), len(self.value))
1249 padding = struct.pack(str(padding_size) + 'x')
1250 ret = desc + self.key + '\0' + self.value + '\0' + padding
1251 return bytearray(ret)
1252
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001253 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001254 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001255 """Verifies contents of the descriptor - used in verify_image sub-command.
1256
1257 Arguments:
1258 image_dir: The directory of the file being verified.
1259 image_ext: The extension of the file being verified (e.g. '.img').
1260 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001261 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001262 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001263 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1264 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001265
1266 Returns:
1267 True if the descriptor verifies, False otherwise.
1268 """
1269 # Nothing to do.
1270 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001271
Jan Monscheeb28b62019-12-05 16:17:09 +01001272
David Zeuthen21e95262016-07-27 17:58:40 -04001273class AvbHashtreeDescriptor(AvbDescriptor):
1274 """A class for hashtree descriptors.
1275
1276 See the |AvbHashtreeDescriptor| C struct for more information.
1277
1278 Attributes:
1279 dm_verity_version: dm-verity version used.
1280 image_size: Size of the image, after rounding up to |block_size|.
1281 tree_offset: Offset of the hash tree in the file.
1282 tree_size: Size of the tree.
1283 data_block_size: Data block size
1284 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001285 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1286 fec_offset: Offset of FEC data (0 if FEC is not used).
1287 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001288 hash_algorithm: Hash algorithm used.
1289 partition_name: Partition name.
1290 salt: Salt used.
1291 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001292 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001293 """
1294
1295 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001296 RESERVED = 60
1297 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001298 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1299 'L' # dm-verity version used
1300 'Q' # image size (bytes)
1301 'Q' # tree offset (bytes)
1302 'Q' # tree size (bytes)
1303 'L' # data block size (bytes)
1304 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001305 'L' # FEC number of roots
1306 'Q' # FEC offset (bytes)
1307 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001308 '32s' # hash algorithm used
1309 'L' # partition name (bytes)
1310 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001311 'L' # root digest length (bytes)
1312 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001313 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001314
1315 def __init__(self, data=None):
1316 """Initializes a new hashtree descriptor.
1317
1318 Arguments:
1319 data: If not None, must be a bytearray of size |SIZE|.
1320
1321 Raises:
1322 LookupError: If the given descriptor is malformed.
1323 """
1324 AvbDescriptor.__init__(self, None)
1325 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1326
1327 if data:
1328 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1329 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001330 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1331 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001332 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1333 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001334 expected_size = round_to_multiple(
1335 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1336 if tag != self.TAG or num_bytes_following != expected_size:
1337 raise LookupError('Given data does not look like a hashtree '
1338 'descriptor.')
1339 # Nuke NUL-bytes at the end.
1340 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1341 o = 0
1342 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1343 partition_name_len)])
1344 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1345 self.partition_name.decode('utf-8')
1346 o += partition_name_len
1347 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1348 o += salt_len
1349 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1350 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001351 if root_digest_len != 0:
1352 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001353
1354 else:
1355 self.dm_verity_version = 0
1356 self.image_size = 0
1357 self.tree_offset = 0
1358 self.tree_size = 0
1359 self.data_block_size = 0
1360 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001361 self.fec_num_roots = 0
1362 self.fec_offset = 0
1363 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001364 self.hash_algorithm = ''
1365 self.partition_name = ''
1366 self.salt = bytearray()
1367 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001368 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001369
1370 def print_desc(self, o):
1371 """Print the descriptor.
1372
1373 Arguments:
1374 o: The object to write the output to.
1375 """
1376 o.write(' Hashtree descriptor:\n')
1377 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1378 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1379 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1380 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1381 o.write(' Data Block Size: {} bytes\n'.format(
1382 self.data_block_size))
1383 o.write(' Hash Block Size: {} bytes\n'.format(
1384 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001385 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1386 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1387 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001388 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1389 o.write(' Partition Name: {}\n'.format(self.partition_name))
1390 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1391 'hex')))
1392 o.write(' Root Digest: {}\n'.format(str(
1393 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001394 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001395
1396 def encode(self):
1397 """Serializes the descriptor.
1398
1399 Returns:
1400 A bytearray() with the descriptor data.
1401 """
1402 encoded_name = self.partition_name.encode('utf-8')
1403 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1404 len(self.root_digest) - 16)
1405 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1406 padding_size = nbf_with_padding - num_bytes_following
1407 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1408 self.dm_verity_version, self.image_size,
1409 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001410 self.hash_block_size, self.fec_num_roots,
1411 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001412 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001413 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001414 padding = struct.pack(str(padding_size) + 'x')
1415 ret = desc + encoded_name + self.salt + self.root_digest + padding
1416 return bytearray(ret)
1417
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001418 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001419 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001420 """Verifies contents of the descriptor - used in verify_image sub-command.
1421
1422 Arguments:
1423 image_dir: The directory of the file being verified.
1424 image_ext: The extension of the file being verified (e.g. '.img').
1425 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001426 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001427 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001428 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1429 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001430
1431 Returns:
1432 True if the descriptor verifies, False otherwise.
1433 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001434 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001435 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001436 image = image_containing_descriptor
1437 else:
1438 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1439 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001440 # Generate the hashtree and checks that it matches what's in the file.
1441 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1442 digest_padding = round_to_pow2(digest_size) - digest_size
1443 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001444 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001445 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1446 self.data_block_size,
1447 self.hash_algorithm, self.salt,
1448 digest_padding,
1449 hash_level_offsets,
1450 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001451 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001452 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001453 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1454 format(image_filename))
1455 return False
1456 # ... also check that the on-disk hashtree matches
1457 image.seek(self.tree_offset)
1458 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001459 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001460 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001461 print('{}: skipping verification since hashtree is zeroed and '
1462 '--accept_zeroed_hashtree was given'
1463 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001464 else:
1465 if hash_tree != hash_tree_ondisk:
1466 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001467 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001468 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001469 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1470 .format(self.partition_name, self.hash_algorithm, image.filename,
1471 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001472 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1473 # correct but this a) currently requires the 'fec' binary; and b) takes a
1474 # long time; and c) is not strictly needed for verification purposes as
1475 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001476 return True
1477
David Zeuthen21e95262016-07-27 17:58:40 -04001478
1479class AvbHashDescriptor(AvbDescriptor):
1480 """A class for hash descriptors.
1481
1482 See the |AvbHashDescriptor| C struct for more information.
1483
1484 Attributes:
1485 image_size: Image size, in bytes.
1486 hash_algorithm: Hash algorithm used.
1487 partition_name: Partition name.
1488 salt: Salt used.
1489 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001490 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001491 """
1492
1493 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001494 RESERVED = 60
1495 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001496 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1497 'Q' # image size (bytes)
1498 '32s' # hash algorithm used
1499 'L' # partition name (bytes)
1500 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001501 'L' # digest length (bytes)
1502 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001503 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001504
1505 def __init__(self, data=None):
1506 """Initializes a new hash descriptor.
1507
1508 Arguments:
1509 data: If not None, must be a bytearray of size |SIZE|.
1510
1511 Raises:
1512 LookupError: If the given descriptor is malformed.
1513 """
1514 AvbDescriptor.__init__(self, None)
1515 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1516
1517 if data:
1518 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1519 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001520 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1521 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001522 expected_size = round_to_multiple(
1523 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1524 if tag != self.TAG or num_bytes_following != expected_size:
1525 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1526 # Nuke NUL-bytes at the end.
1527 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1528 o = 0
1529 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1530 partition_name_len)])
1531 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1532 self.partition_name.decode('utf-8')
1533 o += partition_name_len
1534 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1535 o += salt_len
1536 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1537 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001538 if digest_len != 0:
1539 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001540
1541 else:
1542 self.image_size = 0
1543 self.hash_algorithm = ''
1544 self.partition_name = ''
1545 self.salt = bytearray()
1546 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001547 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001548
1549 def print_desc(self, o):
1550 """Print the descriptor.
1551
1552 Arguments:
1553 o: The object to write the output to.
1554 """
1555 o.write(' Hash descriptor:\n')
1556 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1557 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1558 o.write(' Partition Name: {}\n'.format(self.partition_name))
1559 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1560 'hex')))
1561 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1562 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001563 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001564
1565 def encode(self):
1566 """Serializes the descriptor.
1567
1568 Returns:
1569 A bytearray() with the descriptor data.
1570 """
1571 encoded_name = self.partition_name.encode('utf-8')
1572 num_bytes_following = (
1573 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1574 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1575 padding_size = nbf_with_padding - num_bytes_following
1576 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1577 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001578 len(self.salt), len(self.digest), self.flags,
1579 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001580 padding = struct.pack(str(padding_size) + 'x')
1581 ret = desc + encoded_name + self.salt + self.digest + padding
1582 return bytearray(ret)
1583
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001584 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001585 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001586 """Verifies contents of the descriptor - used in verify_image sub-command.
1587
1588 Arguments:
1589 image_dir: The directory of the file being verified.
1590 image_ext: The extension of the file being verified (e.g. '.img').
1591 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001592 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001593 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001594 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1595 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001596
1597 Returns:
1598 True if the descriptor verifies, False otherwise.
1599 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001600 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001601 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001602 image = image_containing_descriptor
1603 else:
1604 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1605 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001606 data = image.read(self.image_size)
1607 ha = hashlib.new(self.hash_algorithm)
1608 ha.update(self.salt)
1609 ha.update(data)
1610 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001611 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001612 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001613 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1614 format(self.hash_algorithm, image_filename))
1615 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001616 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1617 .format(self.partition_name, self.hash_algorithm, image.filename,
1618 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001619 return True
1620
David Zeuthen21e95262016-07-27 17:58:40 -04001621
1622class AvbKernelCmdlineDescriptor(AvbDescriptor):
1623 """A class for kernel command-line descriptors.
1624
1625 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1626
1627 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001628 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001629 kernel_cmdline: The kernel command-line.
1630 """
1631
1632 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001633 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001634 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001635 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001636 'L') # cmdline length (bytes)
1637
David Zeuthenfd41eb92016-11-17 12:24:47 -05001638 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1639 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1640
David Zeuthen21e95262016-07-27 17:58:40 -04001641 def __init__(self, data=None):
1642 """Initializes a new kernel cmdline descriptor.
1643
1644 Arguments:
1645 data: If not None, must be a bytearray of size |SIZE|.
1646
1647 Raises:
1648 LookupError: If the given descriptor is malformed.
1649 """
1650 AvbDescriptor.__init__(self, None)
1651 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1652
1653 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001654 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001655 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1656 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1657 8)
1658 if tag != self.TAG or num_bytes_following != expected_size:
1659 raise LookupError('Given data does not look like a kernel cmdline '
1660 'descriptor.')
1661 # Nuke NUL-bytes at the end.
1662 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1663 kernel_cmdline_length)])
1664 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1665 self.kernel_cmdline.decode('utf-8')
1666 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001667 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001668 self.kernel_cmdline = ''
1669
1670 def print_desc(self, o):
1671 """Print the descriptor.
1672
1673 Arguments:
1674 o: The object to write the output to.
1675 """
1676 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001677 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001678 o.write(' Kernel Cmdline: {}\n'.format(repr(
1679 self.kernel_cmdline)))
1680
1681 def encode(self):
1682 """Serializes the descriptor.
1683
1684 Returns:
1685 A bytearray() with the descriptor data.
1686 """
1687 encoded_str = self.kernel_cmdline.encode('utf-8')
1688 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1689 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1690 padding_size = nbf_with_padding - num_bytes_following
1691 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001692 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001693 padding = struct.pack(str(padding_size) + 'x')
1694 ret = desc + encoded_str + padding
1695 return bytearray(ret)
1696
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001697 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001698 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001699 """Verifies contents of the descriptor - used in verify_image sub-command.
1700
1701 Arguments:
1702 image_dir: The directory of the file being verified.
1703 image_ext: The extension of the file being verified (e.g. '.img').
1704 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001705 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001706 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001707 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1708 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001709
1710 Returns:
1711 True if the descriptor verifies, False otherwise.
1712 """
1713 # Nothing to verify.
1714 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001715
Jan Monscheeb28b62019-12-05 16:17:09 +01001716
David Zeuthen21e95262016-07-27 17:58:40 -04001717class AvbChainPartitionDescriptor(AvbDescriptor):
1718 """A class for chained partition descriptors.
1719
1720 See the |AvbChainPartitionDescriptor| C struct for more information.
1721
1722 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001723 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001724 partition_name: Partition name.
1725 public_key: Bytes for the public key.
1726 """
1727
1728 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001729 RESERVED = 64
1730 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001731 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001732 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001733 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001734 'L' + # public_key_size (bytes)
1735 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001736
1737 def __init__(self, data=None):
1738 """Initializes a new chain partition descriptor.
1739
1740 Arguments:
1741 data: If not None, must be a bytearray of size |SIZE|.
1742
1743 Raises:
1744 LookupError: If the given descriptor is malformed.
1745 """
1746 AvbDescriptor.__init__(self, None)
1747 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1748
1749 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001750 (tag, num_bytes_following, self.rollback_index_location,
1751 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001752 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001753 expected_size = round_to_multiple(
1754 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1755 if tag != self.TAG or num_bytes_following != expected_size:
1756 raise LookupError('Given data does not look like a chain partition '
1757 'descriptor.')
1758 o = 0
1759 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1760 partition_name_len)])
1761 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1762 self.partition_name.decode('utf-8')
1763 o += partition_name_len
1764 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1765
1766 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001767 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001768 self.partition_name = ''
1769 self.public_key = bytearray()
1770
1771 def print_desc(self, o):
1772 """Print the descriptor.
1773
1774 Arguments:
1775 o: The object to write the output to.
1776 """
1777 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001778 o.write(' Partition Name: {}\n'.format(self.partition_name))
1779 o.write(' Rollback Index Location: {}\n'.format(
1780 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001781 # Just show the SHA1 of the key, for size reasons.
1782 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001783 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001784
1785 def encode(self):
1786 """Serializes the descriptor.
1787
1788 Returns:
1789 A bytearray() with the descriptor data.
1790 """
1791 encoded_name = self.partition_name.encode('utf-8')
1792 num_bytes_following = (
1793 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1794 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1795 padding_size = nbf_with_padding - num_bytes_following
1796 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001797 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001798 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001799 padding = struct.pack(str(padding_size) + 'x')
1800 ret = desc + encoded_name + self.public_key + padding
1801 return bytearray(ret)
1802
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001803 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001804 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001805 """Verifies contents of the descriptor - used in verify_image sub-command.
1806
1807 Arguments:
1808 image_dir: The directory of the file being verified.
1809 image_ext: The extension of the file being verified (e.g. '.img').
1810 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001811 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001812 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001813 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1814 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001815
1816 Returns:
1817 True if the descriptor verifies, False otherwise.
1818 """
1819 value = expected_chain_partitions_map.get(self.partition_name)
1820 if not value:
1821 sys.stderr.write('No expected chain partition for partition {}. Use '
1822 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001823 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001824 format(self.partition_name))
1825 return False
1826 rollback_index_location, pk_blob = value
1827
1828 if self.rollback_index_location != rollback_index_location:
1829 sys.stderr.write('Expected rollback_index_location {} does not '
1830 'match {} in descriptor for partition {}\n'.
1831 format(rollback_index_location,
1832 self.rollback_index_location,
1833 self.partition_name))
1834 return False
1835
1836 if self.public_key != pk_blob:
1837 sys.stderr.write('Expected public key blob does not match public '
1838 'key blob in descriptor for partition {}\n'.
1839 format(self.partition_name))
1840 return False
1841
Jan Monsch23e0c622019-12-11 11:23:58 +01001842 print('{}: Successfully verified chain partition descriptor matches '
1843 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001844
1845 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001846
1847DESCRIPTOR_CLASSES = [
1848 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1849 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1850]
1851
1852
1853def parse_descriptors(data):
1854 """Parses a blob of data into descriptors.
1855
1856 Arguments:
1857 data: A bytearray() with encoded descriptors.
1858
1859 Returns:
1860 A list of instances of objects derived from AvbDescriptor. For
1861 unknown descriptors, the class AvbDescriptor is used.
1862 """
1863 o = 0
1864 ret = []
1865 while o < len(data):
1866 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1867 if tag < len(DESCRIPTOR_CLASSES):
1868 c = DESCRIPTOR_CLASSES[tag]
1869 else:
1870 c = AvbDescriptor
1871 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1872 o += 16 + nb_following
1873 return ret
1874
1875
1876class AvbFooter(object):
1877 """A class for parsing and writing footers.
1878
1879 Footers are stored at the end of partitions and point to where the
1880 AvbVBMeta blob is located. They also contain the original size of
1881 the image before AVB information was added.
1882
1883 Attributes:
1884 magic: Magic for identifying the footer, see |MAGIC|.
1885 version_major: The major version of avbtool that wrote the footer.
1886 version_minor: The minor version of avbtool that wrote the footer.
1887 original_image_size: Original image size.
1888 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1889 vbmeta_size: Size of the AvbVBMeta blob.
1890 """
1891
1892 MAGIC = 'AVBf'
1893 SIZE = 64
1894 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001895 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1896 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001897 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1898 'Q' # Original image size.
1899 'Q' # Offset of VBMeta blob.
1900 'Q' + # Size of VBMeta blob.
1901 str(RESERVED) + 'x') # padding for reserved bytes
1902
1903 def __init__(self, data=None):
1904 """Initializes a new footer object.
1905
1906 Arguments:
1907 data: If not None, must be a bytearray of size 4096.
1908
1909 Raises:
1910 LookupError: If the given footer is malformed.
1911 struct.error: If the given data has no footer.
1912 """
1913 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1914
1915 if data:
1916 (self.magic, self.version_major, self.version_minor,
1917 self.original_image_size, self.vbmeta_offset,
1918 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1919 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001920 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001921 else:
1922 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001923 self.version_major = self.FOOTER_VERSION_MAJOR
1924 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001925 self.original_image_size = 0
1926 self.vbmeta_offset = 0
1927 self.vbmeta_size = 0
1928
David Zeuthena4fee8b2016-08-22 15:20:43 -04001929 def encode(self):
1930 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001931
David Zeuthena4fee8b2016-08-22 15:20:43 -04001932 Returns:
1933 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001934 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001935 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1936 self.version_minor, self.original_image_size,
1937 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001938
1939
Dan Austina7bc4962019-12-02 13:26:08 -08001940# Android Firmware Transparency Log Data Structures
1941
Jan Monsch77cd2022019-12-10 17:18:04 +01001942
Dan Austina7bc4962019-12-02 13:26:08 -08001943class AvbIcpHeader(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01001944 """A class for the transparency log inclusion proof header.
Dan Austina7bc4962019-12-02 13:26:08 -08001945
1946 Attributes:
1947 magic: Magic for identifying the ICP header.
1948 required_icp_version_major: The major version of AVB that wrote the entry.
1949 required_icp_version_minor: The minor version of AVB that wrote the entry.
1950 algorithm: Hash algorithm used. ID is defined in ALGORITHMS.
1951 icp_count: Number of inclusion proofs represented in this structure.
1952 """
1953
1954 SIZE = 18 # The size of the structure, in bytes
1955 MAGIC = 'AFTL'
Jan Monsch77cd2022019-12-10 17:18:04 +01001956 FORMAT_STRING = ('!4s2L' # magic, major & minor version
1957 'L' # algorithm type for transparency log
1958 'H') # number of inclusion proof entries
Dan Austina7bc4962019-12-02 13:26:08 -08001959
1960 def __init__(self, data=None):
1961 """Initializes a new transparency header object.
1962
1963 Arguments:
1964 data: If not None, must be a bytearray of size == 18.
1965 """
1966 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1967
1968 if data:
Jan Monsch77cd2022019-12-10 17:18:04 +01001969 (self.magic, self.required_icp_version_major,
1970 self.required_icp_version_minor, self.algorithm,
1971 self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08001972 else:
1973 self.magic = self.MAGIC
1974 self.required_icp_version_major = AVB_VERSION_MAJOR
1975 self.required_icp_version_minor = AVB_VERSION_MINOR
1976 self.algorithm = 0
1977 self.icp_count = 0
1978
1979 def save(self, output):
1980 """Serializes the transparency header (18) to disk.
1981
1982 Arguments:
1983 output: The object to write the header to.
1984 """
1985 output.write(self.encode())
1986
1987 def encode(self):
1988 """Serializes the header (18) to a bytearray().
1989
1990 Returns:
1991 A bytearray() with the encoded header.
1992 """
1993 return struct.pack(self.FORMAT_STRING, self.magic,
1994 self.required_icp_version_major,
1995 self.required_icp_version_minor,
1996 self.algorithm, self.icp_count)
Jan Monsch77cd2022019-12-10 17:18:04 +01001997
Dan Austina7bc4962019-12-02 13:26:08 -08001998 def is_valid(self):
1999 """Ensures that values in an AvbIcpHeader structure are sane.
2000
2001 Returns:
2002 True if the values in the AvbIcpHeader are sane, False otherwise.
2003 """
2004 if self.magic != AvbIcpHeader.MAGIC:
Jan Monsch77cd2022019-12-10 17:18:04 +01002005 sys.stderr.write(
2006 'ICP Header: magic value mismatch: {}\n'.format(self.magic))
Dan Austina7bc4962019-12-02 13:26:08 -08002007 return False
2008 if self.required_icp_version_major > AVB_VERSION_MAJOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002009 sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
2010 self.required_icp_version_major))
Dan Austina7bc4962019-12-02 13:26:08 -08002011 return False
2012 if self.required_icp_version_minor > AVB_VERSION_MINOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002013 sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
2014 self.required_icp_version_minor))
Dan Austina7bc4962019-12-02 13:26:08 -08002015 return False
2016 if self.algorithm < 0 or self.algorithm >= len(ALGORITHMS):
Jan Monsch77cd2022019-12-10 17:18:04 +01002017 sys.stderr.write(
2018 'ICP header: algorithm identifier out of range: {}\n'.format(
2019 self.algorithm))
Dan Austina7bc4962019-12-02 13:26:08 -08002020 return False
2021 if self.icp_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002022 sys.stderr.write(
2023 'ICP header: ICP entry count out of range: {}\n'.format(
2024 self.icp_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002025 return False
2026 return True
2027
Dan Austina7bc4962019-12-02 13:26:08 -08002028
2029class AvbIcpEntry(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01002030 """A class for the transparency log inclusion proof entries.
Dan Austina7bc4962019-12-02 13:26:08 -08002031
Jan Monsch77cd2022019-12-10 17:18:04 +01002032 The data that represents each of the components of the ICP entry are stored
2033 immediately following the ICP entry header. The format is log_url,
2034 SignedLogRoot, and inclusion proof hashes.
Dan Austina7bc4962019-12-02 13:26:08 -08002035
2036 Attributes:
2037 log_url_size: Length of the string representing the transparency log URL.
2038 leaf_index: Leaf index in the transparency log representing this entry.
Jan Monsch77cd2022019-12-10 17:18:04 +01002039 signed_root_blob_size: Size of the SignedLogRoot for the transparency log;
2040 treat as an opaque blob for now.
Dan Austina7bc4962019-12-02 13:26:08 -08002041 proof_hash_count: Number of hashes comprising the inclusion proof.
2042 proof_size: The total size of the inclusion proof, in bytes.
2043 next_entry: 1 if there is a next entry, 0 otherwise.
2044 """
2045 SIZE = 22 # The size of the structure, in bytes
2046 FORMAT_STRING = ('!L' # transparency log server url size
2047 'Q' # leaf index
2048 'L' # signed tree root blob size
2049 'B' # number of hashes in the inclusion proof
2050 'L' # size of the inclusion proof in bytes
2051 'B') # next entry marker
2052
2053 def __init__(self, data=None):
2054 """Initializes a new ICP entry object.
2055
2056 Arguments:
2057 data: If not None, must be a bytearray of size == 22.
2058 """
2059 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2060
2061 if data:
2062 (self.log_url_size, self.leaf_index, self.signed_root_blob_size,
Jan Monsch77cd2022019-12-10 17:18:04 +01002063 self.proof_hash_count, self.proof_size,
2064 self.next_entry) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08002065 else:
2066 self.log_url_size = 0
2067 self.leaf_index = 0
2068 self.signed_root_blob_size = 0
2069 self.proof_hash_count = 0
2070 self.proof_size = 0
2071 self.next_entry = 0
2072
2073 def save(self, output):
2074 """Serializes the transparency header (22) to disk.
2075
2076 Arguments:
2077 output: The object to write the header to.
2078 """
2079 output.write(self.encode())
2080
2081 def encode(self):
2082 """Serializes the header (22) to a bytearray().
2083
2084 Returns:
2085 A bytearray() with the encoded header.
2086 """
2087 return struct.pack(self.FORMAT_STRING, self.log_url_size,
2088 self.leaf_index, self.signed_root_blob_size,
2089 self.proof_hash_count, self.proof_size,
2090 self.next_entry)
2091
2092 def is_valid(self):
2093 """Ensures that values in an AvbIcpEntry structure are sane.
2094
2095 Returns:
2096 True if the values in the AvbIcpEntry are sane, False otherwise.
2097 """
2098 if self.log_url_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002099 sys.stderr.write(
2100 'ICP entry: invalid transparency log URL size: {}\n'.format(
2101 self.log_url_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002102 return False
2103 if self.leaf_index < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002104 sys.stderr.write('ICP entry: leaf index out of range: {}\n'.format(
2105 self.leaf_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002106 return False
2107 if self.signed_root_blob_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002108 sys.stderr.write('ICP entry: invalid signed root blob size: {}\n'.format(
2109 self.signed_root_blob_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002110 return False
2111 if self.proof_hash_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002112 sys.stderr.write('ICP entry: invalid proof count: {}\n'.format(
2113 self.proof_hash_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002114 return False
2115 if self.proof_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002116 sys.stderr.write(
2117 'ICP entry: invalid transparency log proof size: {}\n'.format(
2118 self.proof_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002119 return False
2120 if self.next_entry != 0 and self.next_entry != 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002121 sys.stderr.write('ICP entry: invalid next entry value: {}\n'.format(
2122 self.next_entry))
Dan Austina7bc4962019-12-02 13:26:08 -08002123 return False
2124 return True
2125
Jan Monsch77cd2022019-12-10 17:18:04 +01002126
Jan Monsch23e0c622019-12-11 11:23:58 +01002127# TODO(jpm): Clean up linter warnings in this function after the AFTL changes
2128# are in.
2129# pylint: disable=unused-variable
Dan Austina7bc4962019-12-02 13:26:08 -08002130def validate_icp_blob(icp_blob):
2131 """Ensures that values in an ICP section are structurally sane.
2132
2133 Arguments:
Jan Monsch77cd2022019-12-10 17:18:04 +01002134 icp_blob: A bytearray() that contains an ICP, with headers, entries and
2135 data.
Dan Austina7bc4962019-12-02 13:26:08 -08002136
2137 Returns:
2138 True if the values in the ICP section are sane, False otherwise.
2139 """
2140 icp_blob_size = len(icp_blob)
2141 icp_header_bytes = icp_blob[0:AvbIcpHeader.SIZE]
2142 icp_header = AvbIcpHeader(icp_header_bytes)
2143 if not icp_header.is_valid():
2144 sys.stderr.write('Validation of ICP header failed.\n')
2145 return False
2146
2147 icp_count = icp_header.icp_count
2148 algorithm_id = icp_header.algorithm
2149 proof_hash_size = lookup_hash_size_by_type(algorithm_id)
2150
2151 # jump past the header for entry processing
2152 icp_index = AvbIcpHeader.SIZE
2153 # Validate each entry
2154 for i in range(icp_count):
2155 # get the entry header from the ICP blob
2156 cur_icp_entry_blob = icp_blob[icp_index:(icp_index+AvbIcpEntry.SIZE)]
Jan Monsch77cd2022019-12-10 17:18:04 +01002157 cur_icp_entry = AvbIcpEntry(cur_icp_entry_blob)
Dan Austina7bc4962019-12-02 13:26:08 -08002158
2159 # now validate the entry structure
2160 if not cur_icp_entry.is_valid():
2161 sys.stderr.write('Validation of ICP entry {} failed.\n'.format(i))
2162 return False
2163 # jump past the entry header to the data blob
2164 icp_index += AvbIcpEntry.SIZE
2165 # extract each value from the data blob:
Jan Monsch77cd2022019-12-10 17:18:04 +01002166 transparency_log_url = icp_blob[icp_index:(icp_index
2167 + cur_icp_entry.log_url_size)]
Dan Austina7bc4962019-12-02 13:26:08 -08002168 icp_index += cur_icp_entry.log_url_size
2169 sth = icp_blob[icp_index:(icp_index+cur_icp_entry.signed_root_blob_size)]
2170 icp_index += cur_icp_entry.signed_root_blob_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002171 if (cur_icp_entry.proof_size // cur_icp_entry.proof_hash_count
2172 != proof_hash_size):
Jan Monsch77cd2022019-12-10 17:18:04 +01002173 sys.stderr.write('Bad proof size: {} in ICP entry {}'.format(
2174 cur_icp_entry.proof_size, i))
Dan Austina7bc4962019-12-02 13:26:08 -08002175 return False
2176 proof_size = icp_blob[icp_index:(icp_index+cur_icp_entry.proof_size)]
2177 icp_index += cur_icp_entry.proof_size
2178 # check if there is a next entry
2179 if cur_icp_entry.next_entry == 0:
2180 if i != icp_count - 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002181 sys.stderr.write('ICP entry count mismatch\n')
Dan Austina7bc4962019-12-02 13:26:08 -08002182 return False
2183 break
2184 if icp_index != len(icp_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +01002185 sys.stderr.write('ICP blob size mismatch, expected {}, got {}\n'.format(
2186 len(icp_blob), icp_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002187 return False
2188 return True
2189
2190# AFTL structure test functions
2191
Jan Monsch77cd2022019-12-10 17:18:04 +01002192
Dan Austina7bc4962019-12-02 13:26:08 -08002193def test_default_icp_header():
2194 """Performs validation on a default ICP header structure.
2195
2196 Returns:
2197 True if the tests pass, False otherwise.
2198 """
2199
2200 icp_header = AvbIcpHeader()
2201 if icp_header.is_valid():
2202 sys.stdout.write('ICP header: validation passed\n')
2203 return True
Jan Monsch23e0c622019-12-11 11:23:58 +01002204 sys.stdout.write('ICP header: validation failed\n')
2205 return False
Dan Austina7bc4962019-12-02 13:26:08 -08002206
Jan Monsch77cd2022019-12-10 17:18:04 +01002207
Dan Austina7bc4962019-12-02 13:26:08 -08002208def test_icp_header(algorithm, icp_count):
2209 """Create an ICP header structure and attempt to validate it.
2210
Jan Monsch77cd2022019-12-10 17:18:04 +01002211 Arguments:
2212 algorithm: The algorithm to be used.
2213 icp_count: Number of ICPs that follow the ICP header.
2214
Dan Austina7bc4962019-12-02 13:26:08 -08002215 Returns:
2216 True if the tests pass, False otherwise.
2217 """
2218
2219 icp_header = AvbIcpHeader()
2220 icp_header.algorithm = algorithm
2221 icp_header.icp_count = icp_count
2222 if icp_header.is_valid():
2223 sys.stdout.write('ICP header: validation passed\n')
2224 return True
Jan Monsch23e0c622019-12-11 11:23:58 +01002225 sys.stdout.write('ICP header: validation failed\n')
2226 return False
Dan Austina7bc4962019-12-02 13:26:08 -08002227
Jan Monsch77cd2022019-12-10 17:18:04 +01002228
Dan Austina7bc4962019-12-02 13:26:08 -08002229def test_default_icp_entry():
2230 """Performs validation on an empty ICP entry structure.
2231
2232 Returns:
2233 True if the tests pass, False otherwise.
2234 """
2235
2236 icp_entry = AvbIcpEntry()
2237 if icp_entry.is_valid():
2238 sys.stdout.write('ICP entry validation passed\n')
2239 return True
Jan Monsch23e0c622019-12-11 11:23:58 +01002240 sys.stdout.write('ICP entry validation failed\n')
2241 return False
Dan Austina7bc4962019-12-02 13:26:08 -08002242
Jan Monsch77cd2022019-12-10 17:18:04 +01002243
2244def test_icp_entry(log_url_size, leaf_index, signed_root_blob_size,
2245 proof_hash_count, proof_size, next_entry):
Dan Austina7bc4962019-12-02 13:26:08 -08002246 """Create an ICP entry structure and attempt to validate it.
2247
2248 Returns:
2249 True if the tests pass, False otherwise.
2250 """
2251
2252 icp_entry = AvbIcpEntry()
2253 icp_entry.log_url_size = log_url_size
2254 icp_entry.leaf_index = leaf_index
2255 icp_entry.signed_root_blob_size = signed_root_blob_size
2256 icp_entry.proof_hash_count = proof_hash_count
2257 icp_entry.proof_size = proof_size
2258 icp_entry.next_entry = next_entry
2259 if icp_entry.is_valid():
2260 sys.stdout.write('ICP entry validation passed\n')
2261 return True
Jan Monsch23e0c622019-12-11 11:23:58 +01002262 sys.stdout.write('ICP entry validation failed\n')
2263 return False
Dan Austina7bc4962019-12-02 13:26:08 -08002264
Jan Monsch77cd2022019-12-10 17:18:04 +01002265
Dan Austina7bc4962019-12-02 13:26:08 -08002266def test_generate_icp_images():
2267 """Test cases for full AFTL ICP structure generation.
2268
2269 Returns:
2270 True if all tests return expected values, False otherwise.
2271 """
2272
2273 if not test_default_icp_header():
2274 return False
2275
2276 if not test_icp_header(1, 1):
2277 return False
2278
2279 icp_header = AvbIcpHeader()
2280 icp_header.algorithm = 1
2281 icp_header.icp_count = 1
2282
2283 tl_url = 'aftl-test-server.google.com'
2284 sth = bytearray()
2285 # fill each structure with an easily observable pattern for easy validation.
2286 sth.extend('a' * 160)
2287 proof_hashes = bytearray()
2288 proof_hashes.extend('b' * 32)
2289 proof_hashes.extend('c' * 32)
2290 proof_hashes.extend('d' * 32)
2291 proof_hashes.extend('e' * 32)
2292 if not test_icp_entry(len(tl_url), 1, len(sth), 4, len(proof_hashes), 0):
2293 return False
2294 icp_entry = AvbIcpEntry()
2295 icp_entry.log_url_size = len(tl_url)
2296 icp_entry.leaf_index = 1
2297 icp_entry.signed_root_blob_size = len(sth)
2298 icp_entry.proof_hash_count = 4
2299 icp_entry.proof_size = len(proof_hashes)
2300 icp_entry.next_entry = 0
2301 icp_data = bytearray()
2302 icp_data.extend(tl_url)
2303 icp_data.extend(sth)
2304 icp_data.extend(proof_hashes)
2305 icp_blob = bytearray()
2306 icp_blob.extend(icp_header.encode())
2307 icp_blob.extend(icp_entry.encode())
2308 icp_blob.extend(icp_data)
2309 if not validate_icp_blob(icp_blob):
2310 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002311 # Now add a 2nd entry (this should fail).
Dan Austina7bc4962019-12-02 13:26:08 -08002312 tl_url2 = 'aftl-test-server.google.ch'
2313 sth2 = bytearray()
2314 sth2.extend('f' * 192)
2315 proof_hashes2 = bytearray()
2316 proof_hashes2.extend('g' * 32)
2317 proof_hashes2.extend('h' * 32)
2318 if not test_icp_entry(len(tl_url2), 2, len(sth2), 2, len(proof_hashes2), 0):
2319 return False
2320 icp_entry2 = AvbIcpEntry()
2321 icp_entry2.log_url_size = len(tl_url2)
2322 icp_entry2.leaf_index = 2
2323 icp_entry2.signed_root_blob_size = len(sth2)
2324 icp_entry2.proof_hash_count = 2
2325 icp_entry2.proof_size = len(proof_hashes2)
2326 icp_entry2.next_entry = 0
2327 icp_data2 = bytearray()
2328 icp_data2.extend(tl_url2)
2329 icp_data2.extend(sth2)
2330 icp_data2.extend(proof_hashes2)
2331 icp_blob.extend(icp_entry2.encode())
2332 icp_blob.extend(icp_data2)
2333 if validate_icp_blob(icp_blob):
2334 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002335 # Now fix the entries so this passes.
Dan Austina7bc4962019-12-02 13:26:08 -08002336 icp_header.icp_count = 2
2337 icp_entry.next_entry = 1
2338 icp_blob2 = bytearray()
2339 icp_blob2.extend(icp_header.encode())
2340 icp_blob2.extend(icp_entry.encode())
2341 icp_blob2.extend(icp_data)
2342 icp_blob2.extend(icp_entry2.encode())
2343 icp_blob2.extend(icp_data2)
2344 if not validate_icp_blob(icp_blob2):
2345 return False
2346 return True
2347
Jan Monsch77cd2022019-12-10 17:18:04 +01002348
Dan Austina7bc4962019-12-02 13:26:08 -08002349def test_icp_image():
2350 """The main function that handles testing all AFTL related components.
Jan Monsch77cd2022019-12-10 17:18:04 +01002351
2352 Currently performs tests for validation of AFTL related structures.
Dan Austina7bc4962019-12-02 13:26:08 -08002353
2354 Returns:
2355 True if all tests pass, False otherwise.
2356 """
2357
2358 # Header related tests.
2359 default_icp = test_default_icp_header()
2360 # 1 is SHA256/RSA4096
2361 if not test_icp_header(1, 4):
2362 return False
2363 # Header related failure tests.
2364 if test_icp_header(-12, 4):
2365 return False
2366 if test_icp_header(4, -34):
2367 return False
2368 if test_icp_header(10, 10):
2369 return False
2370 # ICP entry related tests.
2371 if not test_default_icp_entry():
2372 return False
2373 if not test_icp_entry(28, 2, 92, 2, 64, 0):
2374 return False
2375 if not test_icp_entry(28, 4, 160, 4, 128, 1):
2376 return False
2377 # ICP entry related failure tests.
2378 if test_icp_entry(-2, 2, 92, 2, 64, 0):
2379 return False
2380 if test_icp_entry(28, -1, 92, 2, 64, 0):
2381 return False
2382 if test_icp_entry(28, 2, -2, 2, 64, 0):
2383 return False
2384 if test_icp_entry(28, 2, 92, 4, 128, 32234):
2385 return False
2386 # Tests for the full AFTL ICP structure.
2387 if not test_generate_icp_images():
2388 return False
2389 sys.stdout.write('All AFTL ICP tests passed.\n')
2390 return True
2391
Jan Monsch77cd2022019-12-10 17:18:04 +01002392
David Zeuthen21e95262016-07-27 17:58:40 -04002393class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002394 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002395
Jan Monschfe00c0a2019-12-11 11:19:40 +01002396 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2397 avb_vbmeta_image.h.
2398
David Zeuthen21e95262016-07-27 17:58:40 -04002399 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002400 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2401 required_libavb_version_major: The major version of libavb required for this
2402 header.
2403 required_libavb_version_minor: The minor version of libavb required for this
2404 header.
2405 authentication_data_block_size: The size of the signature block.
2406 auxiliary_data_block_size: The size of the auxiliary data block.
2407 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2408 enum.
2409 hash_offset: Offset into the "Authentication data" block of hash data.
2410 hash_size: Length of the hash data.
2411 signature_offset: Offset into the "Authentication data" block of signature
2412 data.
2413 signature_size: Length of the signature data.
2414 public_key_offset: Offset into the "Auxiliary data" block of public key
2415 data.
2416 public_key_size: Length of the public key data.
2417 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2418 key metadata.
2419 public_key_metadata_size: Length of the public key metadata. Must be set to
2420 zero if there is no public key metadata.
2421 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2422 data.
2423 descriptors_size: Length of descriptor data.
2424 rollback_index: The rollback index which can be used to prevent rollback to
2425 older versions.
2426 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2427 zero if the vbmeta image is not a top-level image.
2428 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2429 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2430 terminated. Applications must not make assumptions about how this
2431 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002432 """
2433
2434 SIZE = 256
2435
David Zeuthene3cadca2017-02-22 21:25:46 -05002436 # Keep in sync with |reserved0| and |reserved| field of
2437 # |AvbVBMetaImageHeader|.
2438 RESERVED0 = 4
2439 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002440
2441 # Keep in sync with |AvbVBMetaImageHeader|.
2442 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2443 '2Q' # 2 x block size
2444 'L' # algorithm type
2445 '2Q' # offset, size (hash)
2446 '2Q' # offset, size (signature)
2447 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002448 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002449 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002450 'Q' # rollback_index
2451 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002452 str(RESERVED0) + 'x' + # padding for reserved bytes
2453 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002454 str(RESERVED) + 'x') # padding for reserved bytes
2455
2456 def __init__(self, data=None):
2457 """Initializes a new header object.
2458
2459 Arguments:
2460 data: If not None, must be a bytearray of size 8192.
2461
2462 Raises:
2463 Exception: If the given data is malformed.
2464 """
2465 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2466
2467 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002468 (self.magic, self.required_libavb_version_major,
2469 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002470 self.authentication_data_block_size, self.auxiliary_data_block_size,
2471 self.algorithm_type, self.hash_offset, self.hash_size,
2472 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002473 self.public_key_size, self.public_key_metadata_offset,
2474 self.public_key_metadata_size, self.descriptors_offset,
2475 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002476 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002477 self.flags,
2478 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002479 # Nuke NUL-bytes at the end of the string.
2480 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002481 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002482 else:
2483 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002484 # Start by just requiring version 1.0. Code that adds features
2485 # in a future version can use bump_required_libavb_version_minor() to
2486 # bump the minor.
2487 self.required_libavb_version_major = AVB_VERSION_MAJOR
2488 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002489 self.authentication_data_block_size = 0
2490 self.auxiliary_data_block_size = 0
2491 self.algorithm_type = 0
2492 self.hash_offset = 0
2493 self.hash_size = 0
2494 self.signature_offset = 0
2495 self.signature_size = 0
2496 self.public_key_offset = 0
2497 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002498 self.public_key_metadata_offset = 0
2499 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002500 self.descriptors_offset = 0
2501 self.descriptors_size = 0
2502 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002503 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002504 self.release_string = get_release_string()
2505
2506 def bump_required_libavb_version_minor(self, minor):
2507 """Function to bump required_libavb_version_minor.
2508
2509 Call this when writing data that requires a specific libavb
2510 version to parse it.
2511
2512 Arguments:
2513 minor: The minor version of libavb that has support for the feature.
2514 """
2515 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002516 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002517
2518 def save(self, output):
2519 """Serializes the header (256 bytes) to disk.
2520
2521 Arguments:
2522 output: The object to write the output to.
2523 """
2524 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002525 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2526 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002527 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2528 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002529 self.public_key_offset, self.public_key_size,
2530 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002531 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002532 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002533
2534 def encode(self):
2535 """Serializes the header (256) to a bytearray().
2536
2537 Returns:
2538 A bytearray() with the encoded header.
2539 """
2540 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002541 self.required_libavb_version_major,
2542 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002543 self.authentication_data_block_size,
2544 self.auxiliary_data_block_size, self.algorithm_type,
2545 self.hash_offset, self.hash_size, self.signature_offset,
2546 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002547 self.public_key_size, self.public_key_metadata_offset,
2548 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002549 self.descriptors_size, self.rollback_index, self.flags,
2550 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002551
2552
2553class Avb(object):
2554 """Business logic for avbtool command-line tool."""
2555
David Zeuthen8b6973b2016-09-20 12:39:49 -04002556 # Keep in sync with avb_ab_flow.h.
2557 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2558 AB_MAGIC = '\0AB0'
2559 AB_MAJOR_VERSION = 1
2560 AB_MINOR_VERSION = 0
2561 AB_MISC_METADATA_OFFSET = 2048
2562
David Zeuthen09692692016-09-30 16:16:40 -04002563 # Constants for maximum metadata size. These are used to give
2564 # meaningful errors if the value passed in via --partition_size is
2565 # too small and when --calc_max_image_size is used. We use
2566 # conservative figures.
2567 MAX_VBMETA_SIZE = 64 * 1024
2568 MAX_FOOTER_SIZE = 4096
2569
David Zeuthen49936b42018-08-07 17:38:58 -04002570 def extract_vbmeta_image(self, output, image_filename, padding_size):
2571 """Implements the 'extract_vbmeta_image' command.
2572
2573 Arguments:
2574 output: Write vbmeta struct to this file.
2575 image_filename: File to extract vbmeta data from (with a footer).
2576 padding_size: If not 0, pads output so size is a multiple of the number.
2577
2578 Raises:
2579 AvbError: If there's no footer in the image.
2580 """
2581 image = ImageHandler(image_filename)
2582
2583 (footer, _, _, _) = self._parse_image(image)
2584
2585 if not footer:
2586 raise AvbError('Given image does not have a footer.')
2587
2588 image.seek(footer.vbmeta_offset)
2589 vbmeta_blob = image.read(footer.vbmeta_size)
2590 output.write(vbmeta_blob)
2591
2592 if padding_size > 0:
2593 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2594 padding_needed = padded_size - len(vbmeta_blob)
2595 output.write('\0' * padding_needed)
2596
David Zeuthena4fee8b2016-08-22 15:20:43 -04002597 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002598 """Implements the 'erase_footer' command.
2599
2600 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002601 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002602 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002603
2604 Raises:
2605 AvbError: If there's no footer in the image.
2606 """
2607
David Zeuthena4fee8b2016-08-22 15:20:43 -04002608 image = ImageHandler(image_filename)
2609
David Zeuthen21e95262016-07-27 17:58:40 -04002610 (footer, _, descriptors, _) = self._parse_image(image)
2611
2612 if not footer:
2613 raise AvbError('Given image does not have a footer.')
2614
2615 new_image_size = None
2616 if not keep_hashtree:
2617 new_image_size = footer.original_image_size
2618 else:
2619 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002620 # descriptor to figure out the location and size of the hashtree
2621 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002622 for desc in descriptors:
2623 if isinstance(desc, AvbHashtreeDescriptor):
2624 # The hashtree is always just following the main data so the
2625 # new size is easily derived.
2626 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002627 # If the image has FEC codes, also keep those.
2628 if desc.fec_offset > 0:
2629 fec_end = desc.fec_offset + desc.fec_size
2630 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002631 break
2632 if not new_image_size:
2633 raise AvbError('Requested to keep hashtree but no hashtree '
2634 'descriptor was found.')
2635
2636 # And cut...
2637 image.truncate(new_image_size)
2638
David Zeuthen1394f762019-04-30 10:20:11 -04002639 def zero_hashtree(self, image_filename):
2640 """Implements the 'zero_hashtree' command.
2641
2642 Arguments:
2643 image_filename: File to zero hashtree and FEC data from.
2644
2645 Raises:
2646 AvbError: If there's no footer in the image.
2647 """
2648
2649 image = ImageHandler(image_filename)
2650
2651 (footer, _, descriptors, _) = self._parse_image(image)
2652
2653 if not footer:
2654 raise AvbError('Given image does not have a footer.')
2655
2656 # Search for a hashtree descriptor to figure out the location and
2657 # size of the hashtree and FEC.
2658 ht_desc = None
2659 for desc in descriptors:
2660 if isinstance(desc, AvbHashtreeDescriptor):
2661 ht_desc = desc
2662 break
2663
2664 if not ht_desc:
2665 raise AvbError('No hashtree descriptor was found.')
2666
2667 zero_ht_start_offset = ht_desc.tree_offset
2668 zero_ht_num_bytes = ht_desc.tree_size
2669 zero_fec_start_offset = None
2670 zero_fec_num_bytes = 0
2671 if ht_desc.fec_offset > 0:
2672 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2673 raise AvbError('Hash-tree and FEC data must be adjacent.')
2674 zero_fec_start_offset = ht_desc.fec_offset
2675 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002676 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2677 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002678 image.seek(zero_end_offset)
2679 data = image.read(image.image_size - zero_end_offset)
2680
2681 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2682 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2683 # beginning of both hashtree and FEC. (That way, in the future we can add
2684 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2685 #
2686 # Applications can use these markers to detect that the hashtree and/or
2687 # FEC needs to be recomputed.
2688 image.truncate(zero_ht_start_offset)
2689 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2690 image.append_raw(data_zeroed_firstblock)
2691 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2692 if zero_fec_start_offset:
2693 image.append_raw(data_zeroed_firstblock)
2694 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2695 image.append_raw(data)
2696
David Zeuthen2bc232b2017-04-19 14:25:19 -04002697 def resize_image(self, image_filename, partition_size):
2698 """Implements the 'resize_image' command.
2699
2700 Arguments:
2701 image_filename: File with footer to resize.
2702 partition_size: The new size of the image.
2703
2704 Raises:
2705 AvbError: If there's no footer in the image.
2706 """
2707
2708 image = ImageHandler(image_filename)
2709
2710 if partition_size % image.block_size != 0:
2711 raise AvbError('Partition size of {} is not a multiple of the image '
2712 'block size {}.'.format(partition_size,
2713 image.block_size))
2714
Jan Monsch77cd2022019-12-10 17:18:04 +01002715 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002716
2717 if not footer:
2718 raise AvbError('Given image does not have a footer.')
2719
2720 # The vbmeta blob is always at the end of the data so resizing an
2721 # image amounts to just moving the footer around.
2722
2723 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2724 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002725 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2726 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002727
2728 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002729 raise AvbError('Requested size of {} is too small for an image '
2730 'of size {}.'
2731 .format(partition_size,
2732 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002733
2734 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2735 # with enough bytes such that the final Footer block is at the end
2736 # of partition_size.
2737 image.truncate(vbmeta_end_offset)
2738 image.append_dont_care(partition_size - vbmeta_end_offset -
2739 1*image.block_size)
2740
2741 # Just reuse the same footer - only difference is that we're
2742 # writing it in a different place.
2743 footer_blob = footer.encode()
2744 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2745 footer_blob)
2746 image.append_raw(footer_blob_with_padding)
2747
David Zeuthen8b6973b2016-09-20 12:39:49 -04002748 def set_ab_metadata(self, misc_image, slot_data):
2749 """Implements the 'set_ab_metadata' command.
2750
2751 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2752 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2753
2754 Arguments:
2755 misc_image: The misc image to write to.
2756 slot_data: Slot data as a string
2757
2758 Raises:
2759 AvbError: If slot data is malformed.
2760 """
2761 tokens = slot_data.split(':')
2762 if len(tokens) != 6:
2763 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2764 a_priority = int(tokens[0])
2765 a_tries_remaining = int(tokens[1])
2766 a_success = True if int(tokens[2]) != 0 else False
2767 b_priority = int(tokens[3])
2768 b_tries_remaining = int(tokens[4])
2769 b_success = True if int(tokens[5]) != 0 else False
2770
2771 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2772 self.AB_MAGIC,
2773 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2774 a_priority, a_tries_remaining, a_success,
2775 b_priority, b_tries_remaining, b_success)
2776 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2777 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2778 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2779 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2780 misc_image.write(ab_data)
2781
David Zeuthena4fee8b2016-08-22 15:20:43 -04002782 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002783 """Implements the 'info_image' command.
2784
2785 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002786 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002787 output: Output file to write human-readable information to (file object).
2788 """
2789
David Zeuthena4fee8b2016-08-22 15:20:43 -04002790 image = ImageHandler(image_filename)
2791
David Zeuthen21e95262016-07-27 17:58:40 -04002792 o = output
2793
2794 (footer, header, descriptors, image_size) = self._parse_image(image)
2795
2796 if footer:
2797 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2798 footer.version_minor))
2799 o.write('Image size: {} bytes\n'.format(image_size))
2800 o.write('Original image size: {} bytes\n'.format(
2801 footer.original_image_size))
2802 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2803 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2804 o.write('--\n')
2805
2806 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2807
David Zeuthene3cadca2017-02-22 21:25:46 -05002808 o.write('Minimum libavb version: {}.{}{}\n'.format(
2809 header.required_libavb_version_major,
2810 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002811 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002812 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2813 o.write('Authentication Block: {} bytes\n'.format(
2814 header.authentication_data_block_size))
2815 o.write('Auxiliary Block: {} bytes\n'.format(
2816 header.auxiliary_data_block_size))
2817 o.write('Algorithm: {}\n'.format(alg_name))
2818 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002819 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002820 o.write('Release String: \'{}\'\n'.format(
2821 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002822
2823 # Print descriptors.
2824 num_printed = 0
2825 o.write('Descriptors:\n')
2826 for desc in descriptors:
2827 desc.print_desc(o)
2828 num_printed += 1
2829 if num_printed == 0:
2830 o.write(' (none)\n')
2831
Jan Monscheeb28b62019-12-05 16:17:09 +01002832 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2833 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002834 """Implements the 'verify_image' command.
2835
2836 Arguments:
2837 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002838 key_path: None or check that embedded public key matches key at given
2839 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002840 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002841 follow_chain_partitions:
2842 If True, will follows chain partitions even when not specified with
2843 the --expected_chain_partition option
2844 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2845 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002846
2847 Raises:
2848 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002849 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002850 expected_chain_partitions_map = {}
2851 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002852 for cp in expected_chain_partitions:
2853 cp_tokens = cp.split(':')
2854 if len(cp_tokens) != 3:
2855 raise AvbError('Malformed chained partition "{}".'.format(cp))
2856 partition_name = cp_tokens[0]
2857 rollback_index_location = int(cp_tokens[1])
2858 file_path = cp_tokens[2]
2859 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002860 expected_chain_partitions_map[partition_name] = (
2861 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002862
2863 image_dir = os.path.dirname(image_filename)
2864 image_ext = os.path.splitext(image_filename)[1]
2865
2866 key_blob = None
2867 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002868 print('Verifying image {} using key at {}'.format(image_filename,
2869 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002870 key_blob = encode_rsa_key(key_path)
2871 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002872 print('Verifying image {} using embedded public key'.format(
2873 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002874
David Zeuthenb623d8b2017-04-04 16:05:53 -04002875 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002876 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002877 offset = 0
2878 if footer:
2879 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002880
David Zeuthenb623d8b2017-04-04 16:05:53 -04002881 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002882 vbmeta_blob = image.read(header.SIZE
2883 + header.authentication_data_block_size
2884 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002885
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002886 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002887 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002888 raise AvbError('Signature check failed for {} vbmeta struct {}'
2889 .format(alg_name, image_filename))
2890
2891 if key_blob:
2892 # The embedded public key is in the auxiliary block at an offset.
2893 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002894 key_offset += header.authentication_data_block_size
2895 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002896 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2897 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002898 if key_blob != key_blob_in_vbmeta:
2899 raise AvbError('Embedded public key does not match given key.')
2900
2901 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002902 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2903 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002904 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002905 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2906 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002907
2908 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002909 if (isinstance(desc, AvbChainPartitionDescriptor)
2910 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002911 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002912 # In this case we're processing a chain descriptor but don't have a
2913 # --expect_chain_partition ... however --follow_chain_partitions was
2914 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002915 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2916 'and KEY (which has sha1 {}) not specified'
2917 .format(desc.partition_name, desc.rollback_index_location,
2918 hashlib.sha1(desc.public_key).hexdigest()))
2919 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002920 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002921 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002922 # Honor --follow_chain_partitions - add '--' to make the output more
2923 # readable.
2924 if (isinstance(desc, AvbChainPartitionDescriptor)
2925 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002926 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002927 chained_image_filename = os.path.join(image_dir,
2928 desc.partition_name + image_ext)
2929 self.verify_image(chained_image_filename, key_path, None, False,
2930 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002931
David Zeuthenb8643c02018-05-17 17:21:18 -04002932 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2933 """Implements the 'calculate_vbmeta_digest' command.
2934
2935 Arguments:
2936 image_filename: Image file to get information from (file object).
2937 hash_algorithm: Hash algorithm used.
2938 output: Output file to write human-readable information to (file object).
2939 """
2940
2941 image_dir = os.path.dirname(image_filename)
2942 image_ext = os.path.splitext(image_filename)[1]
2943
2944 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002945 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002946 offset = 0
2947 if footer:
2948 offset = footer.vbmeta_offset
2949 size = (header.SIZE + header.authentication_data_block_size +
2950 header.auxiliary_data_block_size)
2951 image.seek(offset)
2952 vbmeta_blob = image.read(size)
2953
2954 hasher = hashlib.new(name=hash_algorithm)
2955 hasher.update(vbmeta_blob)
2956
2957 for desc in descriptors:
2958 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002959 ch_image_filename = os.path.join(image_dir,
2960 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002961 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002962 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002963 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002964 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2965 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002966 if ch_footer:
2967 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002968 ch_image.seek(ch_offset)
2969 ch_vbmeta_blob = ch_image.read(ch_size)
2970 hasher.update(ch_vbmeta_blob)
2971
2972 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002973 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002974
David Zeuthenf7d2e752018-09-20 13:30:41 -04002975 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2976 """Implements the 'calculate_kernel_cmdline' command.
2977
2978 Arguments:
2979 image_filename: Image file to get information from (file object).
2980 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2981 output: Output file to write human-readable information to (file object).
2982 """
2983
2984 image = ImageHandler(image_filename)
2985 _, _, descriptors, _ = self._parse_image(image)
2986
2987 image_dir = os.path.dirname(image_filename)
2988 image_ext = os.path.splitext(image_filename)[1]
2989
2990 cmdline_descriptors = []
2991 for desc in descriptors:
2992 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002993 ch_image_filename = os.path.join(image_dir,
2994 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002995 ch_image = ImageHandler(ch_image_filename)
2996 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2997 for ch_desc in ch_descriptors:
2998 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2999 cmdline_descriptors.append(ch_desc)
3000 elif isinstance(desc, AvbKernelCmdlineDescriptor):
3001 cmdline_descriptors.append(desc)
3002
3003 kernel_cmdline_snippets = []
3004 for desc in cmdline_descriptors:
3005 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01003006 if ((desc.flags &
3007 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3008 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04003009 if hashtree_disabled:
3010 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01003011 if (desc.flags &
3012 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04003013 if not hashtree_disabled:
3014 use_cmdline = False
3015 if use_cmdline:
3016 kernel_cmdline_snippets.append(desc.kernel_cmdline)
3017 output.write(' '.join(kernel_cmdline_snippets))
3018
David Zeuthen21e95262016-07-27 17:58:40 -04003019 def _parse_image(self, image):
3020 """Gets information about an image.
3021
3022 The image can either be a vbmeta or an image with a footer.
3023
3024 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003025 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003026
3027 Returns:
3028 A tuple where the first argument is a AvbFooter (None if there
3029 is no footer on the image), the second argument is a
3030 AvbVBMetaHeader, the third argument is a list of
3031 AvbDescriptor-derived instances, and the fourth argument is the
3032 size of |image|.
3033 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003034 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04003035 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04003036 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04003037 try:
3038 footer = AvbFooter(image.read(AvbFooter.SIZE))
3039 except (LookupError, struct.error):
3040 # Nope, just seek back to the start.
3041 image.seek(0)
3042
3043 vbmeta_offset = 0
3044 if footer:
3045 vbmeta_offset = footer.vbmeta_offset
3046
3047 image.seek(vbmeta_offset)
3048 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3049
3050 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
3051 aux_block_offset = auth_block_offset + h.authentication_data_block_size
3052 desc_start_offset = aux_block_offset + h.descriptors_offset
3053 image.seek(desc_start_offset)
3054 descriptors = parse_descriptors(image.read(h.descriptors_size))
3055
David Zeuthen09692692016-09-30 16:16:40 -04003056 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003057
David Zeuthenb1b994d2017-03-06 18:01:31 -05003058 def _load_vbmeta_blob(self, image):
3059 """Gets the vbmeta struct and associated sections.
3060
3061 The image can either be a vbmeta.img or an image with a footer.
3062
3063 Arguments:
3064 image: An ImageHandler (vbmeta or footer).
3065
3066 Returns:
3067 A blob with the vbmeta struct and other sections.
3068 """
3069 assert isinstance(image, ImageHandler)
3070 footer = None
3071 image.seek(image.image_size - AvbFooter.SIZE)
3072 try:
3073 footer = AvbFooter(image.read(AvbFooter.SIZE))
3074 except (LookupError, struct.error):
3075 # Nope, just seek back to the start.
3076 image.seek(0)
3077
3078 vbmeta_offset = 0
3079 if footer:
3080 vbmeta_offset = footer.vbmeta_offset
3081
3082 image.seek(vbmeta_offset)
3083 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3084
3085 image.seek(vbmeta_offset)
3086 data_size = AvbVBMetaHeader.SIZE
3087 data_size += h.authentication_data_block_size
3088 data_size += h.auxiliary_data_block_size
3089 return image.read(data_size)
3090
David Zeuthen73f2afa2017-05-17 16:54:11 -04003091 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05003092 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04003093
3094 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04003095 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04003096
3097 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05003098 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3099 instructions. There is one for when hashtree is not disabled and one for
3100 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04003101
David Zeuthen21e95262016-07-27 17:58:40 -04003102 """
3103
David Zeuthen21e95262016-07-27 17:58:40 -04003104 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003105 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01003106 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003107 c += ' verity {}'.format(ht.dm_verity_version) # type and version
3108 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
3109 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
3110 c += ' {}'.format(ht.data_block_size) # data_block
3111 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01003112 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
3113 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003114 c += ' {}'.format(ht.hash_algorithm) # hash_alg
3115 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
3116 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
3117 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05003118 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003119 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003120 c += ' ignore_zero_blocks'
3121 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3122 c += ' fec_roots {}'.format(ht.fec_num_roots)
3123 # Note that fec_blocks is the size that FEC covers, *not* the
3124 # size of the FEC data. Since we use FEC for everything up until
3125 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01003126 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
3127 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003128 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05003129 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003130 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003131 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04003132 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04003133
David Zeuthenfd41eb92016-11-17 12:24:47 -05003134 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003135 desc = AvbKernelCmdlineDescriptor()
3136 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05003137 desc.flags = (
3138 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3139
3140 # The descriptor for when hashtree verification is disabled is a lot
3141 # simpler - we just set the root to the partition.
3142 desc_no_ht = AvbKernelCmdlineDescriptor()
3143 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3144 desc_no_ht.flags = (
3145 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
3146
3147 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04003148
David Zeuthen73f2afa2017-05-17 16:54:11 -04003149 def _get_cmdline_descriptors_for_dm_verity(self, image):
3150 """Generate kernel cmdline descriptors for dm-verity.
3151
3152 Arguments:
3153 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
3154
3155 Returns:
3156 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3157 instructions. There is one for when hashtree is not disabled and one for
3158 when it is.
3159
3160 Raises:
3161 AvbError: If |image| doesn't have a hashtree descriptor.
3162
3163 """
3164
3165 (_, _, descriptors, _) = self._parse_image(image)
3166
3167 ht = None
3168 for desc in descriptors:
3169 if isinstance(desc, AvbHashtreeDescriptor):
3170 ht = desc
3171 break
3172
3173 if not ht:
3174 raise AvbError('No hashtree descriptor in given image')
3175
3176 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
3177
Dan Austina7bc4962019-12-02 13:26:08 -08003178 def icp_test_suite(self):
Jan Monsch77cd2022019-12-10 17:18:04 +01003179 """Implements a self-contained test suite for AFTL related code.
Dan Austina7bc4962019-12-02 13:26:08 -08003180
3181 Returns:
3182 True if all tests pass, False otherwise.
3183 """
3184 return test_icp_image()
3185
David Zeuthen21e95262016-07-27 17:58:40 -04003186 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05003187 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003188 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003189 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04003190 include_descriptors_from_image,
3191 signing_helper,
3192 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003193 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003194 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003195 print_required_libavb_version,
3196 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003197 """Implements the 'make_vbmeta_image' command.
3198
3199 Arguments:
3200 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003201 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003202 algorithm_name: Name of algorithm to use.
3203 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003204 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003205 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003206 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003207 props: Properties to insert (list of strings of the form 'key:value').
3208 props_from_file: Properties to insert (list of strings 'key:<path>').
3209 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003210 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04003211 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003212 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003213 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003214 release_string: None or avbtool release string to use instead of default.
3215 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04003216 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04003217 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04003218
3219 Raises:
3220 AvbError: If a chained partition is malformed.
3221 """
3222
David Zeuthen1097a782017-05-31 15:53:17 -04003223 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003224 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003225 if include_descriptors_from_image:
3226 # Use the bump logic in AvbVBMetaHeader to calculate the max required
3227 # version of all included descriptors.
3228 tmp_header = AvbVBMetaHeader()
3229 for image in include_descriptors_from_image:
3230 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
3231 tmp_header.bump_required_libavb_version_minor(
3232 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01003233 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003234 else:
3235 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01003236 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04003237 return
3238
3239 if not output:
3240 raise AvbError('No output file given')
3241
David Zeuthen21e95262016-07-27 17:58:40 -04003242 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04003243 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003244 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003245 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003246 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003247 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003248 include_descriptors_from_image, signing_helper,
3249 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003250 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04003251
3252 # Write entire vbmeta blob (header, authentication, auxiliary).
3253 output.seek(0)
3254 output.write(vbmeta_blob)
3255
David Zeuthen97cb5802017-06-01 16:14:05 -04003256 if padding_size > 0:
3257 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
3258 padding_needed = padded_size - len(vbmeta_blob)
3259 output.write('\0' * padding_needed)
3260
David Zeuthen18666ab2016-11-15 11:18:05 -05003261 def _generate_vbmeta_blob(self, algorithm_name, key_path,
3262 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003263 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003264 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003265 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003266 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003267 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003268 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003269 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003270 release_string, append_to_release_string,
3271 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04003272 """Generates a VBMeta blob.
3273
3274 This blob contains the header (struct AvbVBMetaHeader), the
3275 authentication data block (which contains the hash and signature
3276 for the header and auxiliary block), and the auxiliary block
3277 (which contains descriptors, the public key used, and other data).
3278
3279 The |key| parameter can |None| only if the |algorithm_name| is
3280 'NONE'.
3281
3282 Arguments:
3283 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3284 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003285 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003286 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003287 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003288 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003289 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003290 props: Properties to insert (List of strings of the form 'key:value').
3291 props_from_file: Properties to insert (List of strings 'key:<path>').
3292 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003293 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003294 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003295 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3296 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003297 include_descriptors_from_image: List of file objects for which
3298 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003299 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003300 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003301 release_string: None or avbtool release string.
3302 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003303 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003304
3305 Returns:
3306 A bytearray() with the VBMeta blob.
3307
3308 Raises:
3309 Exception: If the |algorithm_name| is not found, if no key has
3310 been given and the given algorithm requires one, or the key is
3311 of the wrong size.
3312
3313 """
3314 try:
3315 alg = ALGORITHMS[algorithm_name]
3316 except KeyError:
3317 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3318
David Zeuthena5fd3a42017-02-27 16:38:54 -05003319 if not descriptors:
3320 descriptors = []
3321
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003322 h = AvbVBMetaHeader()
3323 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3324
David Zeuthena5fd3a42017-02-27 16:38:54 -05003325 # Insert chained partition descriptors, if any
3326 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04003327 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003328 for cp in chain_partitions:
3329 cp_tokens = cp.split(':')
3330 if len(cp_tokens) != 3:
3331 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003332 partition_name = cp_tokens[0]
3333 rollback_index_location = int(cp_tokens[1])
3334 file_path = cp_tokens[2]
3335 # Check that the same rollback location isn't being used by
3336 # multiple chained partitions.
3337 if used_locations.get(rollback_index_location):
3338 raise AvbError('Rollback Index Location {} is already in use.'.format(
3339 rollback_index_location))
3340 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003341 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003342 desc.partition_name = partition_name
3343 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003344 if desc.rollback_index_location < 1:
3345 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003346 desc.public_key = open(file_path, 'rb').read()
3347 descriptors.append(desc)
3348
David Zeuthen21e95262016-07-27 17:58:40 -04003349 # Descriptors.
3350 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003351 for desc in descriptors:
3352 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003353
3354 # Add properties.
3355 if props:
3356 for prop in props:
3357 idx = prop.find(':')
3358 if idx == -1:
3359 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003360 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003361 desc = AvbPropertyDescriptor()
3362 desc.key = prop[0:idx]
3363 desc.value = prop[(idx + 1):]
3364 encoded_descriptors.extend(desc.encode())
3365 if props_from_file:
3366 for prop in props_from_file:
3367 idx = prop.find(':')
3368 if idx == -1:
3369 raise AvbError('Malformed property "{}".'.format(prop))
3370 desc = AvbPropertyDescriptor()
3371 desc.key = prop[0:idx]
3372 desc.value = prop[(idx + 1):]
3373 file_path = prop[(idx + 1):]
3374 desc.value = open(file_path, 'rb').read()
3375 encoded_descriptors.extend(desc.encode())
3376
David Zeuthen73f2afa2017-05-17 16:54:11 -04003377 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003378 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003379 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003380 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003381 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3382 encoded_descriptors.extend(cmdline_desc[0].encode())
3383 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003384
David Zeuthen73f2afa2017-05-17 16:54:11 -04003385 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3386 if ht_desc_to_setup:
3387 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3388 ht_desc_to_setup)
3389 encoded_descriptors.extend(cmdline_desc[0].encode())
3390 encoded_descriptors.extend(cmdline_desc[1].encode())
3391
David Zeuthen21e95262016-07-27 17:58:40 -04003392 # Add kernel command-lines.
3393 if kernel_cmdlines:
3394 for i in kernel_cmdlines:
3395 desc = AvbKernelCmdlineDescriptor()
3396 desc.kernel_cmdline = i
3397 encoded_descriptors.extend(desc.encode())
3398
3399 # Add descriptors from other images.
3400 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003401 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003402 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003403 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003404 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3405 image_handler)
3406 # Bump the required libavb version to support all included descriptors.
3407 h.bump_required_libavb_version_minor(
3408 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003409 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003410 # The --include_descriptors_from_image option is used in some setups
3411 # with images A and B where both A and B contain a descriptor
3412 # for a partition with the same name. Since it's not meaningful
3413 # to include both descriptors, only include the last seen descriptor.
3414 # See bug 76386656 for details.
3415 if hasattr(desc, 'partition_name'):
3416 key = type(desc).__name__ + '_' + desc.partition_name
3417 descriptors_dict[key] = desc.encode()
3418 else:
3419 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003420 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003421 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003422
David Zeuthen18666ab2016-11-15 11:18:05 -05003423 # Load public key metadata blob, if requested.
3424 pkmd_blob = []
3425 if public_key_metadata_path:
3426 with open(public_key_metadata_path) as f:
3427 pkmd_blob = f.read()
3428
David Zeuthen21e95262016-07-27 17:58:40 -04003429 key = None
3430 encoded_key = bytearray()
3431 if alg.public_key_num_bytes > 0:
3432 if not key_path:
3433 raise AvbError('Key is required for algorithm {}'.format(
3434 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003435 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003436 if len(encoded_key) != alg.public_key_num_bytes:
3437 raise AvbError('Key is wrong size for algorithm {}'.format(
3438 algorithm_name))
3439
David Zeuthene3cadca2017-02-22 21:25:46 -05003440 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003441 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003442 if isinstance(release_string, (str, unicode)):
3443 h.release_string = release_string
3444
3445 # Append to release string, if requested. Also insert a space before.
3446 if isinstance(append_to_release_string, (str, unicode)):
3447 h.release_string += ' ' + append_to_release_string
3448
David Zeuthen18666ab2016-11-15 11:18:05 -05003449 # For the Auxiliary data block, descriptors are stored at offset 0,
3450 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003451 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003452 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003453 h.descriptors_offset = 0
3454 h.descriptors_size = len(encoded_descriptors)
3455 h.public_key_offset = h.descriptors_size
3456 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003457 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3458 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003459
3460 # For the Authentication data block, the hash is first and then
3461 # the signature.
3462 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003463 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003464 h.algorithm_type = alg.algorithm_type
3465 h.hash_offset = 0
3466 h.hash_size = alg.hash_num_bytes
3467 # Signature offset and size - it's stored right after the hash
3468 # (in Authentication data block).
3469 h.signature_offset = alg.hash_num_bytes
3470 h.signature_size = alg.signature_num_bytes
3471
3472 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003473 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003474
3475 # Generate Header data block.
3476 header_data_blob = h.encode()
3477
3478 # Generate Auxiliary data block.
3479 aux_data_blob = bytearray()
3480 aux_data_blob.extend(encoded_descriptors)
3481 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003482 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003483 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3484 aux_data_blob.extend('\0' * padding_bytes)
3485
3486 # Calculate the hash.
3487 binary_hash = bytearray()
3488 binary_signature = bytearray()
3489 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003490 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003491 ha.update(header_data_blob)
3492 ha.update(aux_data_blob)
3493 binary_hash.extend(ha.digest())
3494
3495 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003496 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003497 binary_signature.extend(raw_sign(signing_helper,
3498 signing_helper_with_files,
3499 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003500 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003501 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003502
3503 # Generate Authentication data block.
3504 auth_data_blob = bytearray()
3505 auth_data_blob.extend(binary_hash)
3506 auth_data_blob.extend(binary_signature)
3507 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3508 auth_data_blob.extend('\0' * padding_bytes)
3509
3510 return header_data_blob + auth_data_blob + aux_data_blob
3511
3512 def extract_public_key(self, key_path, output):
3513 """Implements the 'extract_public_key' command.
3514
3515 Arguments:
3516 key_path: The path to a RSA private key file.
3517 output: The file to write to.
3518 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003519 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003520
David Zeuthenb1b994d2017-03-06 18:01:31 -05003521 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3522 partition_size):
3523 """Implementation of the append_vbmeta_image command.
3524
3525 Arguments:
3526 image_filename: File to add the footer to.
3527 vbmeta_image_filename: File to get vbmeta struct from.
3528 partition_size: Size of partition.
3529
3530 Raises:
3531 AvbError: If an argument is incorrect.
3532 """
3533 image = ImageHandler(image_filename)
3534
3535 if partition_size % image.block_size != 0:
3536 raise AvbError('Partition size of {} is not a multiple of the image '
3537 'block size {}.'.format(partition_size,
3538 image.block_size))
3539
3540 # If there's already a footer, truncate the image to its original
3541 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003542 if image.image_size >= AvbFooter.SIZE:
3543 image.seek(image.image_size - AvbFooter.SIZE)
3544 try:
3545 footer = AvbFooter(image.read(AvbFooter.SIZE))
3546 # Existing footer found. Just truncate.
3547 original_image_size = footer.original_image_size
3548 image.truncate(footer.original_image_size)
3549 except (LookupError, struct.error):
3550 original_image_size = image.image_size
3551 else:
3552 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003553 original_image_size = image.image_size
3554
3555 # If anything goes wrong from here-on, restore the image back to
3556 # its original size.
3557 try:
3558 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3559 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3560
3561 # If the image isn't sparse, its size might not be a multiple of
3562 # the block size. This will screw up padding later so just grow it.
3563 if image.image_size % image.block_size != 0:
3564 assert not image.is_sparse
3565 padding_needed = image.block_size - (image.image_size%image.block_size)
3566 image.truncate(image.image_size + padding_needed)
3567
3568 # The append_raw() method requires content with size being a
3569 # multiple of |block_size| so add padding as needed. Also record
3570 # where this is written to since we'll need to put that in the
3571 # footer.
3572 vbmeta_offset = image.image_size
3573 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3574 len(vbmeta_blob))
3575 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3576
3577 # Append vbmeta blob and footer
3578 image.append_raw(vbmeta_blob_with_padding)
3579 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3580
3581 # Now insert a DONT_CARE chunk with enough bytes such that the
3582 # final Footer block is at the end of partition_size..
3583 image.append_dont_care(partition_size - vbmeta_end_offset -
3584 1*image.block_size)
3585
3586 # Generate the Footer that tells where the VBMeta footer
3587 # is. Also put enough padding in the front of the footer since
3588 # we'll write out an entire block.
3589 footer = AvbFooter()
3590 footer.original_image_size = original_image_size
3591 footer.vbmeta_offset = vbmeta_offset
3592 footer.vbmeta_size = len(vbmeta_blob)
3593 footer_blob = footer.encode()
3594 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3595 footer_blob)
3596 image.append_raw(footer_blob_with_padding)
3597
3598 except:
3599 # Truncate back to original size, then re-raise
3600 image.truncate(original_image_size)
3601 raise
3602
David Zeuthena4fee8b2016-08-22 15:20:43 -04003603 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003604 hash_algorithm, salt, chain_partitions, algorithm_name,
3605 key_path,
3606 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003607 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003608 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003609 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003610 signing_helper, signing_helper_with_files,
3611 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003612 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003613 print_required_libavb_version, use_persistent_digest,
3614 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003615 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003616
3617 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003618 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003619 partition_size: Size of partition.
3620 partition_name: Name of partition (without A/B suffix).
3621 hash_algorithm: Hash algorithm to use.
3622 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003623 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003624 algorithm_name: Name of algorithm to use.
3625 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003626 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003627 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003628 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003629 props: Properties to insert (List of strings of the form 'key:value').
3630 props_from_file: Properties to insert (List of strings 'key:<path>').
3631 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003632 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003633 dm-verity kernel cmdline from.
3634 include_descriptors_from_image: List of file objects for which
3635 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003636 calc_max_image_size: Don't store the footer - instead calculate the
3637 maximum image size leaving enough room for metadata with the
3638 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003639 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003640 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003641 release_string: None or avbtool release string.
3642 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003643 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3644 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003645 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003646 use_persistent_digest: Use a persistent digest on device.
3647 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003648
3649 Raises:
3650 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003651 """
David Zeuthen1097a782017-05-31 15:53:17 -04003652
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003653 required_libavb_version_minor = 0
3654 if use_persistent_digest or do_not_use_ab:
3655 required_libavb_version_minor = 1
3656
David Zeuthen1097a782017-05-31 15:53:17 -04003657 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003658 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003659 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003660 return
3661
David Zeuthenbf562452017-05-17 18:04:43 -04003662 # First, calculate the maximum image size such that an image
3663 # this size + metadata (footer + vbmeta struct) fits in
3664 # |partition_size|.
3665 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003666 if partition_size < max_metadata_size:
3667 raise AvbError('Parition size of {} is too small. '
3668 'Needs to be at least {}'.format(
3669 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003670 max_image_size = partition_size - max_metadata_size
3671
3672 # If we're asked to only calculate the maximum image size, we're done.
3673 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003674 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003675 return
3676
David Zeuthena4fee8b2016-08-22 15:20:43 -04003677 image = ImageHandler(image_filename)
3678
3679 if partition_size % image.block_size != 0:
3680 raise AvbError('Partition size of {} is not a multiple of the image '
3681 'block size {}.'.format(partition_size,
3682 image.block_size))
3683
David Zeuthen21e95262016-07-27 17:58:40 -04003684 # If there's already a footer, truncate the image to its original
3685 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3686 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003687 if image.image_size >= AvbFooter.SIZE:
3688 image.seek(image.image_size - AvbFooter.SIZE)
3689 try:
3690 footer = AvbFooter(image.read(AvbFooter.SIZE))
3691 # Existing footer found. Just truncate.
3692 original_image_size = footer.original_image_size
3693 image.truncate(footer.original_image_size)
3694 except (LookupError, struct.error):
3695 original_image_size = image.image_size
3696 else:
3697 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003698 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003699
3700 # If anything goes wrong from here-on, restore the image back to
3701 # its original size.
3702 try:
David Zeuthen09692692016-09-30 16:16:40 -04003703 # If image size exceeds the maximum image size, fail.
3704 if image.image_size > max_image_size:
3705 raise AvbError('Image size of {} exceeds maximum image '
3706 'size of {} in order to fit in a partition '
3707 'size of {}.'.format(image.image_size, max_image_size,
3708 partition_size))
3709
David Zeuthen21e95262016-07-27 17:58:40 -04003710 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3711 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003712 salt = binascii.unhexlify(salt)
3713 elif salt is None and not use_persistent_digest:
3714 # If salt is not explicitly specified, choose a hash that's the same
3715 # size as the hash size. Don't populate a random salt if this
3716 # descriptor is being created to use a persistent digest on device.
3717 hash_size = digest_size
3718 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003719 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003720 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003721
3722 hasher = hashlib.new(name=hash_algorithm, string=salt)
3723 # TODO(zeuthen): might want to read this in chunks to avoid
3724 # memory pressure, then again, this is only supposed to be used
3725 # on kernel/initramfs partitions. Possible optimization.
3726 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003727 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003728 digest = hasher.digest()
3729
3730 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003731 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003732 h_desc.hash_algorithm = hash_algorithm
3733 h_desc.partition_name = partition_name
3734 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003735 h_desc.flags = 0
3736 if do_not_use_ab:
3737 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3738 if not use_persistent_digest:
3739 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003740
3741 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003742 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003743 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003744 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003745 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003746 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003747 include_descriptors_from_image, signing_helper,
3748 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003749 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003750
David Zeuthend247fcb2017-02-16 12:09:27 -05003751 # Write vbmeta blob, if requested.
3752 if output_vbmeta_image:
3753 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003754
David Zeuthend247fcb2017-02-16 12:09:27 -05003755 # Append vbmeta blob and footer, unless requested not to.
3756 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003757 # If the image isn't sparse, its size might not be a multiple of
3758 # the block size. This will screw up padding later so just grow it.
3759 if image.image_size % image.block_size != 0:
3760 assert not image.is_sparse
3761 padding_needed = image.block_size - (
3762 image.image_size % image.block_size)
3763 image.truncate(image.image_size + padding_needed)
3764
3765 # The append_raw() method requires content with size being a
3766 # multiple of |block_size| so add padding as needed. Also record
3767 # where this is written to since we'll need to put that in the
3768 # footer.
3769 vbmeta_offset = image.image_size
3770 padding_needed = (
3771 round_to_multiple(len(vbmeta_blob), image.block_size) -
3772 len(vbmeta_blob))
3773 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3774
David Zeuthend247fcb2017-02-16 12:09:27 -05003775 image.append_raw(vbmeta_blob_with_padding)
3776 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3777
3778 # Now insert a DONT_CARE chunk with enough bytes such that the
3779 # final Footer block is at the end of partition_size..
3780 image.append_dont_care(partition_size - vbmeta_end_offset -
3781 1*image.block_size)
3782
3783 # Generate the Footer that tells where the VBMeta footer
3784 # is. Also put enough padding in the front of the footer since
3785 # we'll write out an entire block.
3786 footer = AvbFooter()
3787 footer.original_image_size = original_image_size
3788 footer.vbmeta_offset = vbmeta_offset
3789 footer.vbmeta_size = len(vbmeta_blob)
3790 footer_blob = footer.encode()
3791 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3792 footer_blob)
3793 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003794
David Zeuthen21e95262016-07-27 17:58:40 -04003795 except:
3796 # Truncate back to original size, then re-raise
3797 image.truncate(original_image_size)
3798 raise
3799
David Zeuthena4fee8b2016-08-22 15:20:43 -04003800 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003801 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003802 block_size, salt, chain_partitions, algorithm_name,
3803 key_path,
3804 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003805 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003806 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003807 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003808 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003809 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003810 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003811 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003812 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003813 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003814 use_persistent_root_digest, do_not_use_ab,
3815 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003816 """Implements the 'add_hashtree_footer' command.
3817
3818 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3819 more information about dm-verity and these hashes.
3820
3821 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003822 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003823 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003824 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003825 generate_fec: If True, generate FEC codes.
3826 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003827 hash_algorithm: Hash algorithm to use.
3828 block_size: Block size to use.
3829 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003830 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003831 algorithm_name: Name of algorithm to use.
3832 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003833 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003834 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003835 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003836 props: Properties to insert (List of strings of the form 'key:value').
3837 props_from_file: Properties to insert (List of strings 'key:<path>').
3838 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003839 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003840 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003841 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3842 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003843 include_descriptors_from_image: List of file objects for which
3844 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003845 calc_max_image_size: Don't store the hashtree or footer - instead
3846 calculate the maximum image size leaving enough room for hashtree
3847 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003848 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003849 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003850 release_string: None or avbtool release string.
3851 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003852 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3853 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003854 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003855 use_persistent_root_digest: Use a persistent root digest on device.
3856 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003857 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003858
3859 Raises:
3860 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003861 """
David Zeuthen1097a782017-05-31 15:53:17 -04003862
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003863 required_libavb_version_minor = 0
3864 if use_persistent_root_digest or do_not_use_ab:
3865 required_libavb_version_minor = 1
3866
David Zeuthen1097a782017-05-31 15:53:17 -04003867 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003868 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003869 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003870 return
3871
David Zeuthen09692692016-09-30 16:16:40 -04003872 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3873 digest_padding = round_to_pow2(digest_size) - digest_size
3874
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003875 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3876 # size such that an image this size + the hashtree + metadata (footer +
3877 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3878 # for metadata.
3879 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003880 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003881 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003882 if not no_hashtree:
3883 (_, max_tree_size) = calc_hash_level_offsets(
3884 partition_size, block_size, digest_size + digest_padding)
3885 if generate_fec:
3886 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003887 max_metadata_size = (max_fec_size + max_tree_size +
3888 self.MAX_VBMETA_SIZE +
3889 self.MAX_FOOTER_SIZE)
3890 max_image_size = partition_size - max_metadata_size
3891 else:
3892 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003893
3894 # If we're asked to only calculate the maximum image size, we're done.
3895 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003896 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003897 return
3898
David Zeuthena4fee8b2016-08-22 15:20:43 -04003899 image = ImageHandler(image_filename)
3900
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003901 if partition_size > 0:
3902 if partition_size % image.block_size != 0:
3903 raise AvbError('Partition size of {} is not a multiple of the image '
3904 'block size {}.'.format(partition_size,
3905 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003906 elif image.image_size % image.block_size != 0:
3907 raise AvbError('File size of {} is not a multiple of the image '
3908 'block size {}.'.format(image.image_size,
3909 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003910
David Zeuthen21e95262016-07-27 17:58:40 -04003911 # If there's already a footer, truncate the image to its original
3912 # size. This way 'avbtool add_hashtree_footer' is idempotent
3913 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003914 if image.image_size >= AvbFooter.SIZE:
3915 image.seek(image.image_size - AvbFooter.SIZE)
3916 try:
3917 footer = AvbFooter(image.read(AvbFooter.SIZE))
3918 # Existing footer found. Just truncate.
3919 original_image_size = footer.original_image_size
3920 image.truncate(footer.original_image_size)
3921 except (LookupError, struct.error):
3922 original_image_size = image.image_size
3923 else:
3924 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003925 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003926
3927 # If anything goes wrong from here-on, restore the image back to
3928 # its original size.
3929 try:
3930 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003931 rounded_image_size = round_to_multiple(image.image_size, block_size)
3932 if rounded_image_size > image.image_size:
3933 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003934
David Zeuthen09692692016-09-30 16:16:40 -04003935 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003936 if partition_size > 0:
3937 if image.image_size > max_image_size:
3938 raise AvbError('Image size of {} exceeds maximum image '
3939 'size of {} in order to fit in a partition '
3940 'size of {}.'.format(image.image_size, max_image_size,
3941 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003942
3943 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003944 salt = binascii.unhexlify(salt)
3945 elif salt is None and not use_persistent_root_digest:
3946 # If salt is not explicitly specified, choose a hash that's the same
3947 # size as the hash size. Don't populate a random salt if this
3948 # descriptor is being created to use a persistent digest on device.
3949 hash_size = digest_size
3950 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003951 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003952 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003953
David Zeuthena4fee8b2016-08-22 15:20:43 -04003954 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003955 # offsets in advance.
3956 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003957 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003958
David Zeuthena4fee8b2016-08-22 15:20:43 -04003959 # If the image isn't sparse, its size might not be a multiple of
3960 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003961 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003962 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003963 padding_needed = image.block_size - (image.image_size%image.block_size)
3964 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003965
David Zeuthena4fee8b2016-08-22 15:20:43 -04003966 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003967 tree_offset = image.image_size
3968 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003969 block_size,
3970 hash_algorithm, salt,
3971 digest_padding,
3972 hash_level_offsets,
3973 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003974
3975 # Generate HashtreeDescriptor with details about the tree we
3976 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003977 if no_hashtree:
3978 tree_size = 0
3979 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003980 ht_desc = AvbHashtreeDescriptor()
3981 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003982 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003983 ht_desc.tree_offset = tree_offset
3984 ht_desc.tree_size = tree_size
3985 ht_desc.data_block_size = block_size
3986 ht_desc.hash_block_size = block_size
3987 ht_desc.hash_algorithm = hash_algorithm
3988 ht_desc.partition_name = partition_name
3989 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003990 if do_not_use_ab:
3991 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3992 if not use_persistent_root_digest:
3993 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003994
David Zeuthen09692692016-09-30 16:16:40 -04003995 # Write the hash tree
3996 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3997 len(hash_tree))
3998 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3999 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004000 len_hashtree_and_fec = len(hash_tree_with_padding)
4001
4002 # Generate FEC codes, if requested.
4003 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09004004 if no_hashtree:
4005 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07004006 else:
4007 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004008 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
4009 len(fec_data))
4010 fec_data_with_padding = fec_data + '\0'*padding_needed
4011 fec_offset = image.image_size
4012 image.append_raw(fec_data_with_padding)
4013 len_hashtree_and_fec += len(fec_data_with_padding)
4014 # Update the hashtree descriptor.
4015 ht_desc.fec_num_roots = fec_num_roots
4016 ht_desc.fec_offset = fec_offset
4017 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04004018
David Zeuthen73f2afa2017-05-17 16:54:11 -04004019 ht_desc_to_setup = None
4020 if setup_as_rootfs_from_kernel:
4021 ht_desc_to_setup = ht_desc
4022
David Zeuthena4fee8b2016-08-22 15:20:43 -04004023 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004024 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04004025 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05004026 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05004027 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004028 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04004029 include_descriptors_from_image, signing_helper,
4030 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004031 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004032 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
4033 len(vbmeta_blob))
4034 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004035
David Zeuthend247fcb2017-02-16 12:09:27 -05004036 # Write vbmeta blob, if requested.
4037 if output_vbmeta_image:
4038 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04004039
David Zeuthend247fcb2017-02-16 12:09:27 -05004040 # Append vbmeta blob and footer, unless requested not to.
4041 if not do_not_append_vbmeta_image:
4042 image.append_raw(vbmeta_blob_with_padding)
4043
4044 # Now insert a DONT_CARE chunk with enough bytes such that the
4045 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004046 if partition_size > 0:
4047 image.append_dont_care(partition_size - image.image_size -
4048 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05004049
4050 # Generate the Footer that tells where the VBMeta footer
4051 # is. Also put enough padding in the front of the footer since
4052 # we'll write out an entire block.
4053 footer = AvbFooter()
4054 footer.original_image_size = original_image_size
4055 footer.vbmeta_offset = vbmeta_offset
4056 footer.vbmeta_size = len(vbmeta_blob)
4057 footer_blob = footer.encode()
4058 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4059 footer_blob)
4060 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004061
David Zeuthen21e95262016-07-27 17:58:40 -04004062 except:
David Zeuthen09692692016-09-30 16:16:40 -04004063 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04004064 image.truncate(original_image_size)
4065 raise
4066
David Zeuthenc68f0822017-03-31 17:22:35 -04004067 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004068 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004069 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004070 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08004071 """Implements the 'make_atx_certificate' command.
4072
4073 Android Things certificates are required for Android Things public key
4074 metadata. They chain the vbmeta signing key for a particular product back to
4075 a fused, permanent root key. These certificates are fixed-length and fixed-
4076 format with the explicit goal of not parsing ASN.1 in bootloader code.
4077
4078 Arguments:
4079 output: Certificate will be written to this file on success.
4080 authority_key_path: A PEM file path with the authority private key.
4081 If None, then a certificate will be created without a
4082 signature. The signature can be created out-of-band
4083 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04004084 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08004085 subject_key_version: A 64-bit version value. If this is None, the number
4086 of seconds since the epoch is used.
4087 subject: A subject identifier. For Product Signing Key certificates this
4088 should be the same Product ID found in the permanent attributes.
4089 is_intermediate_authority: True if the certificate is for an intermediate
4090 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08004091 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08004092 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004093 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08004094 """
4095 signed_data = bytearray()
4096 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004097 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004098 hasher = hashlib.sha256()
4099 hasher.update(subject)
4100 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08004101 if not usage:
4102 usage = 'com.google.android.things.vboot'
4103 if is_intermediate_authority:
4104 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08004105 hasher = hashlib.sha256()
4106 hasher.update(usage)
4107 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07004108 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08004109 subject_key_version = int(time.time())
4110 signed_data.extend(struct.pack('<Q', subject_key_version))
4111 signature = bytearray()
4112 if authority_key_path:
4113 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08004114 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09004115 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01004116 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09004117 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08004118 hasher.update(signed_data)
4119 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04004120 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4121 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004122 alg.signature_num_bytes, authority_key_path,
4123 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08004124 output.write(signed_data)
4125 output.write(signature)
4126
David Zeuthenc68f0822017-03-31 17:22:35 -04004127 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004128 product_id):
4129 """Implements the 'make_atx_permanent_attributes' command.
4130
4131 Android Things permanent attributes are designed to be permanent for a
4132 particular product and a hash of these attributes should be fused into
4133 hardware to enforce this.
4134
4135 Arguments:
4136 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04004137 root_authority_key_path: Path to a PEM or DER public key for
4138 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08004139 product_id: A 16-byte Product ID.
4140
4141 Raises:
4142 AvbError: If an argument is incorrect.
4143 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004144 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004145 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004146 raise AvbError('Invalid Product ID length.')
4147 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004148 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004149 output.write(product_id)
4150
4151 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08004152 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08004153 """Implements the 'make_atx_metadata' command.
4154
4155 Android Things metadata are included in vbmeta images to facilitate
4156 verification. The output of this command can be used as the
4157 public_key_metadata argument to other commands.
4158
4159 Arguments:
4160 output: Metadata will be written to this file on success.
4161 intermediate_key_certificate: A certificate file as output by
4162 make_atx_certificate with
4163 is_intermediate_authority set to true.
4164 product_key_certificate: A certificate file as output by
4165 make_atx_certificate with
4166 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08004167
4168 Raises:
4169 AvbError: If an argument is incorrect.
4170 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004171 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004172 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004173 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08004174 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004175 raise AvbError('Invalid product key certificate length.')
4176 output.write(struct.pack('<I', 1)) # Format Version
4177 output.write(intermediate_key_certificate)
4178 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08004179
Darren Krahnfccd64e2018-01-16 17:39:35 -08004180 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
4181 unlock_key_certificate, challenge_path,
4182 unlock_key_path, signing_helper,
4183 signing_helper_with_files):
4184 """Implements the 'make_atx_unlock_credential' command.
4185
4186 Android Things unlock credentials can be used to authorize the unlock of AVB
4187 on a device. These credentials are presented to an Android Things bootloader
4188 via the fastboot interface in response to a 16-byte challenge. This method
4189 creates all fields of the credential except the challenge signature field
4190 (which is the last field) and can optionally create the challenge signature
4191 field as well if a challenge and the unlock_key_path is provided.
4192
4193 Arguments:
4194 output: The credential will be written to this file on success.
4195 intermediate_key_certificate: A certificate file as output by
4196 make_atx_certificate with
4197 is_intermediate_authority set to true.
4198 unlock_key_certificate: A certificate file as output by
4199 make_atx_certificate with
4200 is_intermediate_authority set to false and the
4201 usage set to
4202 'com.google.android.things.vboot.unlock'.
4203 challenge_path: [optional] A path to the challenge to sign.
4204 unlock_key_path: [optional] A PEM file path with the unlock private key.
4205 signing_helper: Program which signs a hash and returns the signature.
4206 signing_helper_with_files: Same as signing_helper but uses files instead.
4207
4208 Raises:
4209 AvbError: If an argument is incorrect.
4210 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004211 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
4212 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08004213 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4214 raise AvbError('Invalid intermediate key certificate length.')
4215 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4216 raise AvbError('Invalid product key certificate length.')
4217 challenge = bytearray()
4218 if challenge_path:
4219 with open(challenge_path, 'r') as f:
4220 challenge = f.read()
4221 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
4222 raise AvbError('Invalid unlock challenge length.')
4223 output.write(struct.pack('<I', 1)) # Format Version
4224 output.write(intermediate_key_certificate)
4225 output.write(unlock_key_certificate)
4226 if challenge_path and unlock_key_path:
4227 signature = bytearray()
4228 padding_and_hash = bytearray()
4229 algorithm_name = 'SHA512_RSA4096'
4230 alg = ALGORITHMS[algorithm_name]
4231 hasher = hashlib.sha512()
4232 padding_and_hash.extend(alg.padding)
4233 hasher.update(challenge)
4234 padding_and_hash.extend(hasher.digest())
4235 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4236 algorithm_name,
4237 alg.signature_num_bytes, unlock_key_path,
4238 padding_and_hash))
4239 output.write(signature)
4240
David Zeuthen21e95262016-07-27 17:58:40 -04004241
4242def calc_hash_level_offsets(image_size, block_size, digest_size):
4243 """Calculate the offsets of all the hash-levels in a Merkle-tree.
4244
4245 Arguments:
4246 image_size: The size of the image to calculate a Merkle-tree for.
4247 block_size: The block size, e.g. 4096.
4248 digest_size: The size of each hash, e.g. 32 for SHA-256.
4249
4250 Returns:
4251 A tuple where the first argument is an array of offsets and the
4252 second is size of the tree, in bytes.
4253 """
4254 level_offsets = []
4255 level_sizes = []
4256 tree_size = 0
4257
4258 num_levels = 0
4259 size = image_size
4260 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004261 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04004262 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4263
4264 level_sizes.append(level_size)
4265 tree_size += level_size
4266 num_levels += 1
4267
4268 size = level_size
4269
4270 for n in range(0, num_levels):
4271 offset = 0
4272 for m in range(n + 1, num_levels):
4273 offset += level_sizes[m]
4274 level_offsets.append(offset)
4275
David Zeuthena4fee8b2016-08-22 15:20:43 -04004276 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004277
4278
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004279# See system/extras/libfec/include/fec/io.h for these definitions.
4280FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4281FEC_MAGIC = 0xfecfecfe
4282
4283
4284def calc_fec_data_size(image_size, num_roots):
4285 """Calculates how much space FEC data will take.
4286
Jan Monschfe00c0a2019-12-11 11:19:40 +01004287 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004288 image_size: The size of the image.
4289 num_roots: Number of roots.
4290
4291 Returns:
4292 The number of bytes needed for FEC for an image of the given size
4293 and with the requested number of FEC roots.
4294
4295 Raises:
4296 ValueError: If output from the 'fec' tool is invalid.
4297
4298 """
4299 p = subprocess.Popen(
4300 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4301 stdout=subprocess.PIPE,
4302 stderr=subprocess.PIPE)
4303 (pout, perr) = p.communicate()
4304 retcode = p.wait()
4305 if retcode != 0:
4306 raise ValueError('Error invoking fec: {}'.format(perr))
4307 return int(pout)
4308
4309
4310def generate_fec_data(image_filename, num_roots):
4311 """Generate FEC codes for an image.
4312
Jan Monschfe00c0a2019-12-11 11:19:40 +01004313 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004314 image_filename: The filename of the image.
4315 num_roots: Number of roots.
4316
4317 Returns:
4318 The FEC data blob.
4319
4320 Raises:
4321 ValueError: If output from the 'fec' tool is invalid.
4322 """
4323 fec_tmpfile = tempfile.NamedTemporaryFile()
4324 subprocess.check_call(
4325 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4326 fec_tmpfile.name],
4327 stderr=open(os.devnull))
4328 fec_data = fec_tmpfile.read()
4329 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4330 footer_data = fec_data[-footer_size:]
4331 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4332 footer_data)
4333 if magic != FEC_MAGIC:
4334 raise ValueError('Unexpected magic in FEC footer')
4335 return fec_data[0:fec_size]
4336
4337
David Zeuthen21e95262016-07-27 17:58:40 -04004338def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004339 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004340 """Generates a Merkle-tree for a file.
4341
Jan Monschfe00c0a2019-12-11 11:19:40 +01004342 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004343 image: The image, as a file.
4344 image_size: The size of the image.
4345 block_size: The block size, e.g. 4096.
4346 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4347 salt: The salt to use.
4348 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004349 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004350 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004351
4352 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004353 A tuple where the first element is the top-level hash and the
4354 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04004355 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004356 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004357 hash_src_offset = 0
4358 hash_src_size = image_size
4359 level_num = 0
4360 while hash_src_size > block_size:
4361 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004362 remaining = hash_src_size
4363 while remaining > 0:
4364 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004365 # Only read from the file for the first level - for subsequent
4366 # levels, access the array we're building.
4367 if level_num == 0:
4368 image.seek(hash_src_offset + hash_src_size - remaining)
4369 data = image.read(min(remaining, block_size))
4370 else:
4371 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4372 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004373 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004374
4375 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004376 if len(data) < block_size:
4377 hasher.update('\0' * (block_size - len(data)))
4378 level_output += hasher.digest()
4379 if digest_padding > 0:
4380 level_output += '\0' * digest_padding
4381
4382 padding_needed = (round_to_multiple(
4383 len(level_output), block_size) - len(level_output))
4384 level_output += '\0' * padding_needed
4385
David Zeuthena4fee8b2016-08-22 15:20:43 -04004386 # Copy level-output into resulting tree.
4387 offset = hash_level_offsets[level_num]
4388 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004389
David Zeuthena4fee8b2016-08-22 15:20:43 -04004390 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004391 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004392 level_num += 1
4393
4394 hasher = hashlib.new(name=hash_alg_name, string=salt)
4395 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004396 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04004397
4398
4399class AvbTool(object):
4400 """Object for avbtool command-line tool."""
4401
4402 def __init__(self):
4403 """Initializer method."""
4404 self.avb = Avb()
4405
4406 def _add_common_args(self, sub_parser):
4407 """Adds arguments used by several sub-commands.
4408
4409 Arguments:
4410 sub_parser: The parser to add arguments to.
4411 """
4412 sub_parser.add_argument('--algorithm',
4413 help='Algorithm to use (default: NONE)',
4414 metavar='ALGORITHM',
4415 default='NONE')
4416 sub_parser.add_argument('--key',
4417 help='Path to RSA private key file',
4418 metavar='KEY',
4419 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004420 sub_parser.add_argument('--signing_helper',
4421 help='Path to helper used for signing',
4422 metavar='APP',
4423 default=None,
4424 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004425 sub_parser.add_argument('--signing_helper_with_files',
4426 help='Path to helper used for signing using files',
4427 metavar='APP',
4428 default=None,
4429 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004430 sub_parser.add_argument('--public_key_metadata',
4431 help='Path to public key metadata file',
4432 metavar='KEY_METADATA',
4433 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004434 sub_parser.add_argument('--rollback_index',
4435 help='Rollback Index',
4436 type=parse_number,
4437 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004438 # This is used internally for unit tests. Do not include in --help output.
4439 sub_parser.add_argument('--internal_release_string',
4440 help=argparse.SUPPRESS)
4441 sub_parser.add_argument('--append_to_release_string',
4442 help='Text to append to release string',
4443 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004444 sub_parser.add_argument('--prop',
4445 help='Add property',
4446 metavar='KEY:VALUE',
4447 action='append')
4448 sub_parser.add_argument('--prop_from_file',
4449 help='Add property from file',
4450 metavar='KEY:PATH',
4451 action='append')
4452 sub_parser.add_argument('--kernel_cmdline',
4453 help='Add kernel cmdline',
4454 metavar='CMDLINE',
4455 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004456 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4457 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4458 # at some future point.
4459 sub_parser.add_argument('--setup_rootfs_from_kernel',
4460 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004461 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004462 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004463 type=argparse.FileType('rb'))
4464 sub_parser.add_argument('--include_descriptors_from_image',
4465 help='Include descriptors from image',
4466 metavar='IMAGE',
4467 action='append',
4468 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004469 sub_parser.add_argument('--print_required_libavb_version',
4470 help=('Don\'t store the footer - '
4471 'instead calculate the required libavb '
4472 'version for the given options.'),
4473 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004474 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4475 sub_parser.add_argument('--chain_partition',
4476 help='Allow signed integrity-data for partition',
4477 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4478 action='append')
4479 sub_parser.add_argument('--flags',
4480 help='VBMeta flags',
4481 type=parse_number,
4482 default=0)
4483 sub_parser.add_argument('--set_hashtree_disabled_flag',
4484 help='Set the HASHTREE_DISABLED flag',
4485 action='store_true')
4486
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004487 def _add_common_footer_args(self, sub_parser):
4488 """Adds arguments used by add_*_footer sub-commands.
4489
4490 Arguments:
4491 sub_parser: The parser to add arguments to.
4492 """
4493 sub_parser.add_argument('--use_persistent_digest',
4494 help='Use a persistent digest on device instead of '
4495 'storing the digest in the descriptor. This '
4496 'cannot be used with A/B so must be combined '
4497 'with --do_not_use_ab when an A/B suffix is '
4498 'expected at runtime.',
4499 action='store_true')
4500 sub_parser.add_argument('--do_not_use_ab',
4501 help='The partition does not use A/B even when an '
4502 'A/B suffix is present. This must not be used '
4503 'for vbmeta or chained partitions.',
4504 action='store_true')
4505
David Zeuthena5fd3a42017-02-27 16:38:54 -05004506 def _fixup_common_args(self, args):
4507 """Common fixups needed by subcommands.
4508
4509 Arguments:
4510 args: Arguments to modify.
4511
4512 Returns:
4513 The modified arguments.
4514 """
4515 if args.set_hashtree_disabled_flag:
4516 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4517 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004518
4519 def run(self, argv):
4520 """Command-line processor.
4521
4522 Arguments:
4523 argv: Pass sys.argv from main.
4524 """
4525 parser = argparse.ArgumentParser()
4526 subparsers = parser.add_subparsers(title='subcommands')
4527
4528 sub_parser = subparsers.add_parser('version',
4529 help='Prints version of avbtool.')
4530 sub_parser.set_defaults(func=self.version)
4531
4532 sub_parser = subparsers.add_parser('extract_public_key',
4533 help='Extract public key.')
4534 sub_parser.add_argument('--key',
4535 help='Path to RSA private key file',
4536 required=True)
4537 sub_parser.add_argument('--output',
4538 help='Output file name',
4539 type=argparse.FileType('wb'),
4540 required=True)
4541 sub_parser.set_defaults(func=self.extract_public_key)
4542
Jan Monsch77cd2022019-12-10 17:18:04 +01004543 sub_parser = subparsers.add_parser(
4544 'icp_test_suite',
4545 help='Test suite for ICP related functionality.')
Dan Austina7bc4962019-12-02 13:26:08 -08004546 sub_parser.set_defaults(func=self.icp_test_suite)
4547
David Zeuthen21e95262016-07-27 17:58:40 -04004548 sub_parser = subparsers.add_parser('make_vbmeta_image',
4549 help='Makes a vbmeta image.')
4550 sub_parser.add_argument('--output',
4551 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004552 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004553 sub_parser.add_argument('--padding_size',
4554 metavar='NUMBER',
4555 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004556 'its size is a multiple of NUMBER '
4557 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004558 type=parse_number,
4559 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004560 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004561 sub_parser.set_defaults(func=self.make_vbmeta_image)
4562
4563 sub_parser = subparsers.add_parser('add_hash_footer',
4564 help='Add hashes and footer to image.')
4565 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004566 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004567 type=argparse.FileType('rab+'))
4568 sub_parser.add_argument('--partition_size',
4569 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004570 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004571 sub_parser.add_argument('--partition_name',
4572 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004573 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004574 sub_parser.add_argument('--hash_algorithm',
4575 help='Hash algorithm to use (default: sha256)',
4576 default='sha256')
4577 sub_parser.add_argument('--salt',
4578 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004579 sub_parser.add_argument('--calc_max_image_size',
4580 help=('Don\'t store the footer - '
4581 'instead calculate the maximum image size '
4582 'leaving enough room for metadata with '
4583 'the given partition size.'),
4584 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004585 sub_parser.add_argument('--output_vbmeta_image',
4586 help='Also write vbmeta struct to file',
4587 type=argparse.FileType('wb'))
4588 sub_parser.add_argument('--do_not_append_vbmeta_image',
4589 help=('Do not append vbmeta struct or footer '
4590 'to the image'),
4591 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004592 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004593 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004594 sub_parser.set_defaults(func=self.add_hash_footer)
4595
David Zeuthenb1b994d2017-03-06 18:01:31 -05004596 sub_parser = subparsers.add_parser('append_vbmeta_image',
4597 help='Append vbmeta image to image.')
4598 sub_parser.add_argument('--image',
4599 help='Image to append vbmeta blob to',
4600 type=argparse.FileType('rab+'))
4601 sub_parser.add_argument('--partition_size',
4602 help='Partition size',
4603 type=parse_number,
4604 required=True)
4605 sub_parser.add_argument('--vbmeta_image',
4606 help='Image with vbmeta blob to append',
4607 type=argparse.FileType('rb'))
4608 sub_parser.set_defaults(func=self.append_vbmeta_image)
4609
Jan Monscheeb28b62019-12-05 16:17:09 +01004610 sub_parser = subparsers.add_parser(
4611 'add_hashtree_footer',
4612 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004613 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004614 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004615 type=argparse.FileType('rab+'))
4616 sub_parser.add_argument('--partition_size',
4617 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004618 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004619 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004620 sub_parser.add_argument('--partition_name',
4621 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004622 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004623 sub_parser.add_argument('--hash_algorithm',
4624 help='Hash algorithm to use (default: sha1)',
4625 default='sha1')
4626 sub_parser.add_argument('--salt',
4627 help='Salt in hex (default: /dev/urandom)')
4628 sub_parser.add_argument('--block_size',
4629 help='Block size (default: 4096)',
4630 type=parse_number,
4631 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004632 # TODO(zeuthen): The --generate_fec option was removed when we
4633 # moved to generating FEC by default. To avoid breaking existing
4634 # users needing to transition we simply just print a warning below
4635 # in add_hashtree_footer(). Remove this option and the warning at
4636 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004637 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004638 help=argparse.SUPPRESS,
4639 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004640 sub_parser.add_argument(
4641 '--do_not_generate_fec',
4642 help='Do not generate forward-error-correction codes',
4643 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004644 sub_parser.add_argument('--fec_num_roots',
4645 help='Number of roots for FEC (default: 2)',
4646 type=parse_number,
4647 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004648 sub_parser.add_argument('--calc_max_image_size',
4649 help=('Don\'t store the hashtree or footer - '
4650 'instead calculate the maximum image size '
4651 'leaving enough room for hashtree '
4652 'and metadata with the given partition '
4653 'size.'),
4654 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004655 sub_parser.add_argument('--output_vbmeta_image',
4656 help='Also write vbmeta struct to file',
4657 type=argparse.FileType('wb'))
4658 sub_parser.add_argument('--do_not_append_vbmeta_image',
4659 help=('Do not append vbmeta struct or footer '
4660 'to the image'),
4661 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004662 # This is different from --setup_rootfs_from_kernel insofar that
4663 # it doesn't take an IMAGE, the generated cmdline will be for the
4664 # hashtree we're adding.
4665 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4666 action='store_true',
4667 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004668 sub_parser.add_argument('--no_hashtree',
4669 action='store_true',
4670 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004671 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004672 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004673 sub_parser.set_defaults(func=self.add_hashtree_footer)
4674
4675 sub_parser = subparsers.add_parser('erase_footer',
4676 help='Erase footer from an image.')
4677 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004678 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004679 type=argparse.FileType('rwb+'),
4680 required=True)
4681 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004682 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004683 action='store_true')
4684 sub_parser.set_defaults(func=self.erase_footer)
4685
David Zeuthen1394f762019-04-30 10:20:11 -04004686 sub_parser = subparsers.add_parser('zero_hashtree',
4687 help='Zero out hashtree and FEC data.')
4688 sub_parser.add_argument('--image',
4689 help='Image with a footer',
4690 type=argparse.FileType('rwb+'),
4691 required=True)
4692 sub_parser.set_defaults(func=self.zero_hashtree)
4693
Jan Monscheeb28b62019-12-05 16:17:09 +01004694 sub_parser = subparsers.add_parser(
4695 'extract_vbmeta_image',
4696 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004697 sub_parser.add_argument('--image',
4698 help='Image with footer',
4699 type=argparse.FileType('rb'),
4700 required=True)
4701 sub_parser.add_argument('--output',
4702 help='Output file name',
4703 type=argparse.FileType('wb'))
4704 sub_parser.add_argument('--padding_size',
4705 metavar='NUMBER',
4706 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004707 'its size is a multiple of NUMBER '
4708 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004709 type=parse_number,
4710 default=0)
4711 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4712
David Zeuthen2bc232b2017-04-19 14:25:19 -04004713 sub_parser = subparsers.add_parser('resize_image',
4714 help='Resize image with a footer.')
4715 sub_parser.add_argument('--image',
4716 help='Image with a footer',
4717 type=argparse.FileType('rwb+'),
4718 required=True)
4719 sub_parser.add_argument('--partition_size',
4720 help='New partition size',
4721 type=parse_number)
4722 sub_parser.set_defaults(func=self.resize_image)
4723
David Zeuthen21e95262016-07-27 17:58:40 -04004724 sub_parser = subparsers.add_parser(
4725 'info_image',
4726 help='Show information about vbmeta or footer.')
4727 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004728 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004729 type=argparse.FileType('rb'),
4730 required=True)
4731 sub_parser.add_argument('--output',
4732 help='Write info to file',
4733 type=argparse.FileType('wt'),
4734 default=sys.stdout)
4735 sub_parser.set_defaults(func=self.info_image)
4736
David Zeuthenb623d8b2017-04-04 16:05:53 -04004737 sub_parser = subparsers.add_parser(
4738 'verify_image',
4739 help='Verify an image.')
4740 sub_parser.add_argument('--image',
4741 help='Image to verify',
4742 type=argparse.FileType('rb'),
4743 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004744 sub_parser.add_argument('--key',
4745 help='Check embedded public key matches KEY',
4746 metavar='KEY',
4747 required=False)
4748 sub_parser.add_argument('--expected_chain_partition',
4749 help='Expected chain partition',
4750 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4751 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004752 sub_parser.add_argument(
4753 '--follow_chain_partitions',
4754 help=('Follows chain partitions even when not '
4755 'specified with the --expected_chain_partition option'),
4756 action='store_true')
4757 sub_parser.add_argument(
4758 '--accept_zeroed_hashtree',
4759 help=('Accept images where the hashtree or FEC data is zeroed out'),
4760 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004761 sub_parser.set_defaults(func=self.verify_image)
4762
David Zeuthenb8643c02018-05-17 17:21:18 -04004763 sub_parser = subparsers.add_parser(
4764 'calculate_vbmeta_digest',
4765 help='Calculate vbmeta digest.')
4766 sub_parser.add_argument('--image',
4767 help='Image to calculate digest for',
4768 type=argparse.FileType('rb'),
4769 required=True)
4770 sub_parser.add_argument('--hash_algorithm',
4771 help='Hash algorithm to use (default: sha256)',
4772 default='sha256')
4773 sub_parser.add_argument('--output',
4774 help='Write hex digest to file (default: stdout)',
4775 type=argparse.FileType('wt'),
4776 default=sys.stdout)
4777 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4778
David Zeuthenf7d2e752018-09-20 13:30:41 -04004779 sub_parser = subparsers.add_parser(
4780 'calculate_kernel_cmdline',
4781 help='Calculate kernel cmdline.')
4782 sub_parser.add_argument('--image',
4783 help='Image to calculate kernel cmdline for',
4784 type=argparse.FileType('rb'),
4785 required=True)
4786 sub_parser.add_argument('--hashtree_disabled',
4787 help='Return the cmdline for hashtree disabled',
4788 action='store_true')
4789 sub_parser.add_argument('--output',
4790 help='Write cmdline to file (default: stdout)',
4791 type=argparse.FileType('wt'),
4792 default=sys.stdout)
4793 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4794
David Zeuthen8b6973b2016-09-20 12:39:49 -04004795 sub_parser = subparsers.add_parser('set_ab_metadata',
4796 help='Set A/B metadata.')
4797 sub_parser.add_argument('--misc_image',
4798 help=('The misc image to modify. If the image does '
4799 'not exist, it will be created.'),
4800 type=argparse.FileType('r+b'),
4801 required=True)
4802 sub_parser.add_argument('--slot_data',
4803 help=('Slot data of the form "priority", '
4804 '"tries_remaining", "sucessful_boot" for '
4805 'slot A followed by the same for slot B, '
4806 'separated by colons. The default value '
4807 'is 15:7:0:14:7:0.'),
4808 default='15:7:0:14:7:0')
4809 sub_parser.set_defaults(func=self.set_ab_metadata)
4810
Darren Krahn147b08d2016-12-20 16:38:29 -08004811 sub_parser = subparsers.add_parser(
4812 'make_atx_certificate',
4813 help='Create an Android Things eXtension (ATX) certificate.')
4814 sub_parser.add_argument('--output',
4815 help='Write certificate to file',
4816 type=argparse.FileType('wb'),
4817 default=sys.stdout)
4818 sub_parser.add_argument('--subject',
4819 help=('Path to subject file'),
4820 type=argparse.FileType('rb'),
4821 required=True)
4822 sub_parser.add_argument('--subject_key',
4823 help=('Path to subject RSA public key file'),
4824 type=argparse.FileType('rb'),
4825 required=True)
4826 sub_parser.add_argument('--subject_key_version',
4827 help=('Version of the subject key'),
4828 type=parse_number,
4829 required=False)
4830 sub_parser.add_argument('--subject_is_intermediate_authority',
4831 help=('Generate an intermediate authority '
4832 'certificate'),
4833 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004834 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004835 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004836 'string'),
4837 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004838 sub_parser.add_argument('--authority_key',
4839 help='Path to authority RSA private key file',
4840 required=False)
4841 sub_parser.add_argument('--signing_helper',
4842 help='Path to helper used for signing',
4843 metavar='APP',
4844 default=None,
4845 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004846 sub_parser.add_argument('--signing_helper_with_files',
4847 help='Path to helper used for signing using files',
4848 metavar='APP',
4849 default=None,
4850 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004851 sub_parser.set_defaults(func=self.make_atx_certificate)
4852
4853 sub_parser = subparsers.add_parser(
4854 'make_atx_permanent_attributes',
4855 help='Create Android Things eXtension (ATX) permanent attributes.')
4856 sub_parser.add_argument('--output',
4857 help='Write attributes to file',
4858 type=argparse.FileType('wb'),
4859 default=sys.stdout)
4860 sub_parser.add_argument('--root_authority_key',
4861 help='Path to authority RSA public key file',
4862 type=argparse.FileType('rb'),
4863 required=True)
4864 sub_parser.add_argument('--product_id',
4865 help=('Path to Product ID file'),
4866 type=argparse.FileType('rb'),
4867 required=True)
4868 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4869
4870 sub_parser = subparsers.add_parser(
4871 'make_atx_metadata',
4872 help='Create Android Things eXtension (ATX) metadata.')
4873 sub_parser.add_argument('--output',
4874 help='Write metadata to file',
4875 type=argparse.FileType('wb'),
4876 default=sys.stdout)
4877 sub_parser.add_argument('--intermediate_key_certificate',
4878 help='Path to intermediate key certificate file',
4879 type=argparse.FileType('rb'),
4880 required=True)
4881 sub_parser.add_argument('--product_key_certificate',
4882 help='Path to product key certificate file',
4883 type=argparse.FileType('rb'),
4884 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004885 sub_parser.set_defaults(func=self.make_atx_metadata)
4886
Darren Krahnfccd64e2018-01-16 17:39:35 -08004887 sub_parser = subparsers.add_parser(
4888 'make_atx_unlock_credential',
4889 help='Create an Android Things eXtension (ATX) unlock credential.')
4890 sub_parser.add_argument('--output',
4891 help='Write credential to file',
4892 type=argparse.FileType('wb'),
4893 default=sys.stdout)
4894 sub_parser.add_argument('--intermediate_key_certificate',
4895 help='Path to intermediate key certificate file',
4896 type=argparse.FileType('rb'),
4897 required=True)
4898 sub_parser.add_argument('--unlock_key_certificate',
4899 help='Path to unlock key certificate file',
4900 type=argparse.FileType('rb'),
4901 required=True)
4902 sub_parser.add_argument('--challenge',
4903 help='Path to the challenge to sign (optional). If '
4904 'this is not provided the challenge signature '
4905 'field is omitted and can be concatenated '
4906 'later.',
4907 required=False)
4908 sub_parser.add_argument('--unlock_key',
4909 help='Path to unlock key (optional). Must be '
4910 'provided if using --challenge.',
4911 required=False)
4912 sub_parser.add_argument('--signing_helper',
4913 help='Path to helper used for signing',
4914 metavar='APP',
4915 default=None,
4916 required=False)
4917 sub_parser.add_argument('--signing_helper_with_files',
4918 help='Path to helper used for signing using files',
4919 metavar='APP',
4920 default=None,
4921 required=False)
4922 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4923
David Zeuthen21e95262016-07-27 17:58:40 -04004924 args = parser.parse_args(argv[1:])
4925 try:
4926 args.func(args)
4927 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004928 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004929 sys.exit(1)
4930
4931 def version(self, _):
4932 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004933 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004934
4935 def extract_public_key(self, args):
4936 """Implements the 'extract_public_key' sub-command."""
4937 self.avb.extract_public_key(args.key, args.output)
4938
Dan Austina7bc4962019-12-02 13:26:08 -08004939 def icp_test_suite(self, _):
4940 """Implements the 'icp_test_suite' sub-command."""
4941 return self.avb.icp_test_suite()
4942
David Zeuthen21e95262016-07-27 17:58:40 -04004943 def make_vbmeta_image(self, args):
4944 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004945 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004946 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004947 args.algorithm, args.key,
4948 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004949 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004950 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004951 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004952 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004953 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004954 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004955 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004956 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004957 args.print_required_libavb_version,
4958 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004959
David Zeuthenb1b994d2017-03-06 18:01:31 -05004960 def append_vbmeta_image(self, args):
4961 """Implements the 'append_vbmeta_image' sub-command."""
4962 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4963 args.partition_size)
4964
David Zeuthen21e95262016-07-27 17:58:40 -04004965 def add_hash_footer(self, args):
4966 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004967 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004968 self.avb.add_hash_footer(args.image.name if args.image else None,
4969 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004970 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004971 args.salt, args.chain_partition, args.algorithm,
4972 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004973 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004974 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004975 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004976 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004977 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004978 args.calc_max_image_size,
4979 args.signing_helper,
4980 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004981 args.internal_release_string,
4982 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004983 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004984 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004985 args.print_required_libavb_version,
4986 args.use_persistent_digest,
4987 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004988
4989 def add_hashtree_footer(self, args):
4990 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004991 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004992 # TODO(zeuthen): Remove when removing support for the
4993 # '--generate_fec' option above.
4994 if args.generate_fec:
4995 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4996 'is now generated by default. Use the option '
4997 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004998 self.avb.add_hashtree_footer(
4999 args.image.name if args.image else None,
5000 args.partition_size,
5001 args.partition_name,
5002 not args.do_not_generate_fec, args.fec_num_roots,
5003 args.hash_algorithm, args.block_size,
5004 args.salt, args.chain_partition, args.algorithm,
5005 args.key, args.public_key_metadata,
5006 args.rollback_index, args.flags, args.prop,
5007 args.prop_from_file,
5008 args.kernel_cmdline,
5009 args.setup_rootfs_from_kernel,
5010 args.setup_as_rootfs_from_kernel,
5011 args.include_descriptors_from_image,
5012 args.calc_max_image_size,
5013 args.signing_helper,
5014 args.signing_helper_with_files,
5015 args.internal_release_string,
5016 args.append_to_release_string,
5017 args.output_vbmeta_image,
5018 args.do_not_append_vbmeta_image,
5019 args.print_required_libavb_version,
5020 args.use_persistent_digest,
5021 args.do_not_use_ab,
5022 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05005023
David Zeuthen21e95262016-07-27 17:58:40 -04005024 def erase_footer(self, args):
5025 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005026 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04005027
David Zeuthen1394f762019-04-30 10:20:11 -04005028 def zero_hashtree(self, args):
5029 """Implements the 'zero_hashtree' sub-command."""
5030 self.avb.zero_hashtree(args.image.name)
5031
David Zeuthen49936b42018-08-07 17:38:58 -04005032 def extract_vbmeta_image(self, args):
5033 """Implements the 'extract_vbmeta_image' sub-command."""
5034 self.avb.extract_vbmeta_image(args.output, args.image.name,
5035 args.padding_size)
5036
David Zeuthen2bc232b2017-04-19 14:25:19 -04005037 def resize_image(self, args):
5038 """Implements the 'resize_image' sub-command."""
5039 self.avb.resize_image(args.image.name, args.partition_size)
5040
David Zeuthen8b6973b2016-09-20 12:39:49 -04005041 def set_ab_metadata(self, args):
5042 """Implements the 'set_ab_metadata' sub-command."""
5043 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
5044
David Zeuthen21e95262016-07-27 17:58:40 -04005045 def info_image(self, args):
5046 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005047 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04005048
David Zeuthenb623d8b2017-04-04 16:05:53 -04005049 def verify_image(self, args):
5050 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04005051 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05005052 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04005053 args.follow_chain_partitions,
5054 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04005055
David Zeuthenb8643c02018-05-17 17:21:18 -04005056 def calculate_vbmeta_digest(self, args):
5057 """Implements the 'calculate_vbmeta_digest' sub-command."""
5058 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
5059 args.output)
5060
David Zeuthenf7d2e752018-09-20 13:30:41 -04005061 def calculate_kernel_cmdline(self, args):
5062 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01005063 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
5064 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04005065
Darren Krahn147b08d2016-12-20 16:38:29 -08005066 def make_atx_certificate(self, args):
5067 """Implements the 'make_atx_certificate' sub-command."""
5068 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04005069 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005070 args.subject_key_version,
5071 args.subject.read(),
5072 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08005073 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04005074 args.signing_helper,
5075 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08005076
5077 def make_atx_permanent_attributes(self, args):
5078 """Implements the 'make_atx_permanent_attributes' sub-command."""
5079 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04005080 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005081 args.product_id.read())
5082
5083 def make_atx_metadata(self, args):
5084 """Implements the 'make_atx_metadata' sub-command."""
5085 self.avb.make_atx_metadata(args.output,
5086 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08005087 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08005088
Darren Krahnfccd64e2018-01-16 17:39:35 -08005089 def make_atx_unlock_credential(self, args):
5090 """Implements the 'make_atx_unlock_credential' sub-command."""
5091 self.avb.make_atx_unlock_credential(
5092 args.output,
5093 args.intermediate_key_certificate.read(),
5094 args.unlock_key_certificate.read(),
5095 args.challenge,
5096 args.unlock_key,
5097 args.signing_helper,
5098 args.signing_helper_with_files)
5099
David Zeuthen21e95262016-07-27 17:58:40 -04005100
5101if __name__ == '__main__':
5102 tool = AvbTool()
5103 tool.run(sys.argv)