blob: 6b7bb05d70ec56f28ea0f146c542daa85f94a95b [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.
Dan Austin4e6615a2019-12-03 15:19:36 -08001965
1966 Raises:
1967 AvbError if invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08001968 """
1969 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1970
1971 if data:
Jan Monsch77cd2022019-12-10 17:18:04 +01001972 (self.magic, self.required_icp_version_major,
1973 self.required_icp_version_minor, self.algorithm,
1974 self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08001975 else:
1976 self.magic = self.MAGIC
1977 self.required_icp_version_major = AVB_VERSION_MAJOR
1978 self.required_icp_version_minor = AVB_VERSION_MINOR
1979 self.algorithm = 0
1980 self.icp_count = 0
Dan Austin4e6615a2019-12-03 15:19:36 -08001981 if not self.is_valid():
1982 raise AvbError('Invalid structure for AvbIcpHeader')
Dan Austina7bc4962019-12-02 13:26:08 -08001983
1984 def save(self, output):
1985 """Serializes the transparency header (18) to disk.
1986
1987 Arguments:
1988 output: The object to write the header to.
Dan Austin4e6615a2019-12-03 15:19:36 -08001989
1990 Raises:
1991 AvbError if invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08001992 """
1993 output.write(self.encode())
1994
1995 def encode(self):
1996 """Serializes the header (18) to a bytearray().
1997
1998 Returns:
1999 A bytearray() with the encoded header.
Dan Austin4e6615a2019-12-03 15:19:36 -08002000
2001 Raises:
2002 AvbError if invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08002003 """
Dan Austin4e6615a2019-12-03 15:19:36 -08002004 if not self.is_valid():
2005 raise AvbError('Invalid structure for AvbIcpHeader')
Dan Austina7bc4962019-12-02 13:26:08 -08002006 return struct.pack(self.FORMAT_STRING, self.magic,
2007 self.required_icp_version_major,
2008 self.required_icp_version_minor,
2009 self.algorithm, self.icp_count)
Jan Monsch77cd2022019-12-10 17:18:04 +01002010
Dan Austina7bc4962019-12-02 13:26:08 -08002011 def is_valid(self):
2012 """Ensures that values in an AvbIcpHeader structure are sane.
2013
2014 Returns:
2015 True if the values in the AvbIcpHeader are sane, False otherwise.
2016 """
2017 if self.magic != AvbIcpHeader.MAGIC:
Jan Monsch77cd2022019-12-10 17:18:04 +01002018 sys.stderr.write(
2019 'ICP Header: magic value mismatch: {}\n'.format(self.magic))
Dan Austina7bc4962019-12-02 13:26:08 -08002020 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002021
Dan Austina7bc4962019-12-02 13:26:08 -08002022 if self.required_icp_version_major > AVB_VERSION_MAJOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002023 sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
2024 self.required_icp_version_major))
Dan Austina7bc4962019-12-02 13:26:08 -08002025 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002026
Dan Austina7bc4962019-12-02 13:26:08 -08002027 if self.required_icp_version_minor > AVB_VERSION_MINOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002028 sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
2029 self.required_icp_version_minor))
Dan Austina7bc4962019-12-02 13:26:08 -08002030 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002031
Dan Austina7bc4962019-12-02 13:26:08 -08002032 if self.algorithm < 0 or self.algorithm >= len(ALGORITHMS):
Jan Monsch77cd2022019-12-10 17:18:04 +01002033 sys.stderr.write(
2034 'ICP header: algorithm identifier out of range: {}\n'.format(
2035 self.algorithm))
Dan Austina7bc4962019-12-02 13:26:08 -08002036 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002037
Dan Austina7bc4962019-12-02 13:26:08 -08002038 if self.icp_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002039 sys.stderr.write(
2040 'ICP header: ICP entry count out of range: {}\n'.format(
2041 self.icp_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002042 return False
2043 return True
2044
Dan Austina7bc4962019-12-02 13:26:08 -08002045
2046class AvbIcpEntry(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01002047 """A class for the transparency log inclusion proof entries.
Dan Austina7bc4962019-12-02 13:26:08 -08002048
Jan Monsch77cd2022019-12-10 17:18:04 +01002049 The data that represents each of the components of the ICP entry are stored
2050 immediately following the ICP entry header. The format is log_url,
2051 SignedLogRoot, and inclusion proof hashes.
Dan Austina7bc4962019-12-02 13:26:08 -08002052
2053 Attributes:
2054 log_url_size: Length of the string representing the transparency log URL.
2055 leaf_index: Leaf index in the transparency log representing this entry.
Jan Monsch77cd2022019-12-10 17:18:04 +01002056 signed_root_blob_size: Size of the SignedLogRoot for the transparency log;
2057 treat as an opaque blob for now.
Dan Austina7bc4962019-12-02 13:26:08 -08002058 proof_hash_count: Number of hashes comprising the inclusion proof.
2059 proof_size: The total size of the inclusion proof, in bytes.
2060 next_entry: 1 if there is a next entry, 0 otherwise.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002061 log_url: The URL for the transparency log that generated this inclusion
2062 proof.
Dan Austin4e6615a2019-12-03 15:19:36 -08002063 signed_root_blob: The data comprising the signed tree head structure.
2064 proofs: The hashes comprising the inclusion proof.
2065
Dan Austina7bc4962019-12-02 13:26:08 -08002066 """
2067 SIZE = 22 # The size of the structure, in bytes
2068 FORMAT_STRING = ('!L' # transparency log server url size
2069 'Q' # leaf index
2070 'L' # signed tree root blob size
2071 'B' # number of hashes in the inclusion proof
2072 'L' # size of the inclusion proof in bytes
2073 'B') # next entry marker
Dan Austin4e6615a2019-12-03 15:19:36 -08002074 # These are used to capture the log_url, signed_root_blob,
2075 # and the proofs elements for the encode & save function.
Dan Austina7bc4962019-12-02 13:26:08 -08002076
2077 def __init__(self, data=None):
2078 """Initializes a new ICP entry object.
2079
2080 Arguments:
Dan Austin4e6615a2019-12-03 15:19:36 -08002081 data: If not None, must be a bytearray of size >= 22.
2082
2083 Raises:
2084 AvbError: If data does not represent a well-formed AvbIcpEntry.
Dan Austina7bc4962019-12-02 13:26:08 -08002085 """
Dan Austin4e6615a2019-12-03 15:19:36 -08002086 # Assert the header structure is of a sane size.
Dan Austina7bc4962019-12-02 13:26:08 -08002087 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2088
2089 if data:
Dan Austin4e6615a2019-12-03 15:19:36 -08002090 # Deserialize the header from the data blob.
Dan Austina7bc4962019-12-02 13:26:08 -08002091 (self.log_url_size, self.leaf_index, self.signed_root_blob_size,
Jan Monsch5ac429e2019-12-11 19:04:26 +01002092 self.proof_hash_count, self.proof_size, self.next_entry) = struct.unpack(
2093 self.FORMAT_STRING, data[0:self.SIZE])
Dan Austin4e6615a2019-12-03 15:19:36 -08002094 if len(data) >= self.SIZE:
2095 # There's more data. Ensure the data entry size is valid.
2096 if len(data) != self.get_expected_size():
2097 if self.next_entry == 0:
Jan Monsch5ac429e2019-12-11 19:04:26 +01002098 raise AvbError('ICP entry size is not valid {}/{}.'
2099 .format(len(data), self.get_expected_size()))
Dan Austin4e6615a2019-12-03 15:19:36 -08002100 # Deserialize ICP entry components from the data blob.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002101 data_format_string = "{}s{}s{}s".format(
2102 self.log_url_size, self.signed_root_blob_size, self.proof_size)
2103 (self.log_url, self.signed_root_blob, self.proofs) = struct.unpack(
2104 data_format_string, data[self.SIZE:self.get_expected_size()])
Dan Austina7bc4962019-12-02 13:26:08 -08002105 else:
2106 self.log_url_size = 0
2107 self.leaf_index = 0
2108 self.signed_root_blob_size = 0
2109 self.proof_hash_count = 0
2110 self.proof_size = 0
2111 self.next_entry = 0
Dan Austin4e6615a2019-12-03 15:19:36 -08002112 self.log_url = ""
2113 self.signed_root_blob = bytearray()
2114 self.proofs = bytearray()
2115 if not self.is_valid():
2116 raise AvbError('Invalid structure for AvbIcpEntry')
2117
2118 def set_log_url(self, log_url):
2119 """Sets the log_url and log_url_size elements in the AvbIcpEntry.
2120
2121 Arguments:
2122 log_url: The string representing the transparency log URL.
2123 """
2124 self.log_url = log_url
2125 self.log_url_size = len(log_url)
2126
2127 def set_signed_root_blob(self, signed_root_blob):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002128 """Sets signed_root_blob and signed_root_blob_size.
Dan Austin4e6615a2019-12-03 15:19:36 -08002129
2130 Arguments:
Jan Monsch5ac429e2019-12-11 19:04:26 +01002131 signed_root_blob: bytearray containing the SignedLogRoot for the
2132 transparency log.
Dan Austin4e6615a2019-12-03 15:19:36 -08002133 """
2134 self.signed_root_blob = signed_root_blob
2135 self.signed_root_blob_size = len(signed_root_blob)
2136
2137 def set_proofs(self, proof_hash_count, proofs):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002138 """Sets the proof_hash_count, proofs, and proof_size.
Dan Austin4e6615a2019-12-03 15:19:36 -08002139
2140 Arguments:
2141 proof_hash_count: Number of hashes comprising the inclusion proof.
2142 proofs: A bytearray of concatenated hashes comprising the inclusion proof.
2143 """
2144 self.proof_hash_count = proof_hash_count
2145 self.proofs = proofs
2146 self.proof_size = len(proofs)
Dan Austina7bc4962019-12-02 13:26:08 -08002147
2148 def save(self, output):
Dan Austin4e6615a2019-12-03 15:19:36 -08002149 """Serializes the transparency header (22) and data to disk.
Dan Austina7bc4962019-12-02 13:26:08 -08002150
2151 Arguments:
2152 output: The object to write the header to.
Dan Austin4e6615a2019-12-03 15:19:36 -08002153
2154 Raises:
2155 AvbError if invalid entry structure.
Dan Austina7bc4962019-12-02 13:26:08 -08002156 """
2157 output.write(self.encode())
2158
2159 def encode(self):
Dan Austin4e6615a2019-12-03 15:19:36 -08002160 """Serializes the header (22) and data to a bytearray().
Dan Austina7bc4962019-12-02 13:26:08 -08002161
2162 Returns:
2163 A bytearray() with the encoded header.
Dan Austin4e6615a2019-12-03 15:19:36 -08002164
2165 Raises:
2166 AvbError if invalid entry structure.
Dan Austina7bc4962019-12-02 13:26:08 -08002167 """
Dan Austin4e6615a2019-12-03 15:19:36 -08002168 if not self.is_valid():
2169 raise AvbError('Invalid AvbIcpEntry structure')
2170 expected_format_string = "{}{}s{}s{}s".format(self.FORMAT_STRING,
2171 self.log_url_size,
2172 self.signed_root_blob_size,
2173 self.proof_size)
2174 return struct.pack(expected_format_string,
2175 self.log_url_size, self.leaf_index,
2176 self.signed_root_blob_size, self.proof_hash_count,
2177 self.proof_size, self.next_entry, self.log_url,
2178 str(self.signed_root_blob), str(self.proofs))
2179
2180 def get_expected_size(self):
2181 """Gets the expected size of the full entry out of the header.
2182
2183 Returns:
2184 The expected size of the AvbIcpEntry from the header.
2185 """
2186 return (self.SIZE + self.log_url_size +
Jan Monsch5ac429e2019-12-11 19:04:26 +01002187 self.signed_root_blob_size + self.proof_size)
Dan Austina7bc4962019-12-02 13:26:08 -08002188
2189 def is_valid(self):
2190 """Ensures that values in an AvbIcpEntry structure are sane.
2191
2192 Returns:
2193 True if the values in the AvbIcpEntry are sane, False otherwise.
2194 """
Jan Monsch5ac429e2019-12-11 19:04:26 +01002195 if ((self.log_url and self.log_url_size != len(self.log_url))
2196 or (not self.log_url and self.log_url_size != 0)):
2197 sys.stderr.write('ICP entry: invalid URL size: {}\n'
2198 .format(self.log_url_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002199 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002200
Dan Austina7bc4962019-12-02 13:26:08 -08002201 if self.leaf_index < 0:
Jan Monsch5ac429e2019-12-11 19:04:26 +01002202 sys.stderr.write('ICP entry: leaf index out of range: '
2203 '{}\n'.format(self.leaf_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002204 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002205
2206 if ((self.signed_root_blob and self.signed_root_blob_size
2207 != len(self.signed_root_blob))
2208 or (not self.signed_root_blob and self.signed_root_blob_size != 0)):
2209 sys.stderr.write('ICP entry: invalid signed root blob size: '
2210 '{}\n'.format(self.signed_root_blob_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002211 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002212
Dan Austina7bc4962019-12-02 13:26:08 -08002213 if self.proof_hash_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002214 sys.stderr.write('ICP entry: invalid proof count: {}\n'.format(
2215 self.proof_hash_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002216 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002217
2218 if ((self.proofs and self.proof_size != len(self.proofs))
2219 or (not self.proofs and self.proof_size != 0)):
Dan Austin4e6615a2019-12-03 15:19:36 -08002220 sys.stderr.write('ICP entry: invalid transparency log proof size: ')
2221 sys.stderr.write('{}\n'.format(self.proof_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002222 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002223
Dan Austina7bc4962019-12-02 13:26:08 -08002224 if self.next_entry != 0 and self.next_entry != 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002225 sys.stderr.write('ICP entry: invalid next entry value: {}\n'.format(
2226 self.next_entry))
Dan Austina7bc4962019-12-02 13:26:08 -08002227 return False
2228 return True
2229
Jan Monsch5ac429e2019-12-11 19:04:26 +01002230
Dan Austin4e6615a2019-12-03 15:19:36 -08002231class AvbIcpBlob(object):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002232 """A class for the transparency log inclusion proof blob.
2233
2234 This encapsulates an AFTL ICP section with all information required to
2235 validate an inclusion proof.
Jan Monsch77cd2022019-12-10 17:18:04 +01002236
Dan Austin4e6615a2019-12-03 15:19:36 -08002237 Attributes:
2238 icp_header: A header for the section.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002239 icp_entries: A list of AvbIcpEntry objects representing the inclusion
2240 proofs.
Dan Austina7bc4962019-12-02 13:26:08 -08002241 """
Dan Austina7bc4962019-12-02 13:26:08 -08002242
Dan Austin4e6615a2019-12-03 15:19:36 -08002243 def __init__(self, data=None):
2244 """Initializes a new AvbIcpBlob section.
Dan Austina7bc4962019-12-02 13:26:08 -08002245
Dan Austin4e6615a2019-12-03 15:19:36 -08002246 Arguments:
2247 data: If not None, must be a bytearray representing an AvbIcpBlob.
Dan Austina7bc4962019-12-02 13:26:08 -08002248
Dan Austin4e6615a2019-12-03 15:19:36 -08002249 Raises:
2250 AvbError: If the data does not represent a well-formed AvbIcpBlob.
2251 """
2252 if data:
2253 icp_header_bytes = data[0:AvbIcpHeader.SIZE]
2254 self.icp_header = AvbIcpHeader(icp_header_bytes)
2255 if not self.icp_header.is_valid():
2256 raise AvbError('Invalid ICP header.')
2257 icp_count = self.icp_header.icp_count
2258 algorithm_id = self.icp_header.algorithm
Jan Monsch5ac429e2019-12-11 19:04:26 +01002259 # TODO(danielaustin): make use of proof_hash_size.
2260 # pylint: disable=unused-variable
Dan Austin4e6615a2019-12-03 15:19:36 -08002261 proof_hash_size = lookup_hash_size_by_type(algorithm_id)
2262
2263 # Jump past the header for entry deserialization.
2264 icp_index = AvbIcpHeader.SIZE
2265 # Validate each entry.
2266 self.icp_entries = []
Jan Monsch5ac429e2019-12-11 19:04:26 +01002267 # Add_icp_entry updates entries and header, so set header count to
2268 # compensate.
Dan Austin4e6615a2019-12-03 15:19:36 -08002269 self.icp_header.icp_count = 0
2270 for i in range(icp_count):
2271 # Get the entry header from the ICP blob.
2272 cur_icp_entry = AvbIcpEntry(data[icp_index:])
2273 cur_icp_entry_size = cur_icp_entry.get_expected_size()
2274 # Now validate the entry structure.
2275 if not cur_icp_entry.is_valid():
2276 raise AvbError('Validation of ICP entry failed.')
2277 self.add_icp_entry(cur_icp_entry)
2278 # Check if there is a next entry.
2279 if cur_icp_entry.next_entry == 0:
2280 if i != icp_count - 1:
2281 raise AvbError('ICP entry count mismatch')
2282 break
2283 icp_index += cur_icp_entry_size
2284 else:
2285 self.icp_header = AvbIcpHeader()
2286 self.icp_entries = []
2287 if not self.is_valid():
2288 raise AvbError('Malformed ICP blob')
2289
2290 def set_algorithm(self, algorithm_id):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002291 """Sets algorithm to be used by the inclusion proofs in AvbIcpBlob."""
Dan Austin4e6615a2019-12-03 15:19:36 -08002292 self.icp_header.algorithm = algorithm_id
2293
2294 def add_icp_entry(self, avb_icp_entry):
2295 """Adds a new AvbIcpEntry to the AvbIcpBlob, updating fields as necessary.
2296
2297 Arguments:
2298 avb_icp_entry: An AvbIcpEntry structure.
2299 """
2300
2301 # Set the next entry field to denote that a new ICP entry will follow.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002302 if self.icp_entries:
Dan Austin4e6615a2019-12-03 15:19:36 -08002303 self.icp_entries[-1].next_entry = 1
2304 self.icp_entries.append(avb_icp_entry)
2305 self.icp_header.icp_count += 1
2306
2307 def save(self, output):
2308 """Serializes the AvbIcpBlob to disk.
2309
2310 Arguments:
2311 output: The object to write the blob to.
2312
2313 Raises:
2314 AvbError if invalid blob structure.
2315 """
2316 output.write(self.encode())
2317
2318 def encode(self):
2319 """Serialize the AvbIcpBlob to a bytearray()
2320
2321 Returns:
2322 A bytearray() with the encoded header.
2323
2324 Raises:
2325 AvbError if invalid blob structure.
2326 """
2327 # The header and entries are guaranteed to be valid when encode is called.
2328 # Check the entire structure as a whole.
2329 if not self.is_valid():
2330 raise AvbError('Invalid AvbIcpBlob structure.')
2331
2332 icp_blob = bytearray()
2333 icp_blob.extend(self.icp_header.encode())
2334 for icp_entry in self.icp_entries:
2335 icp_blob.extend(icp_entry.encode())
2336 return icp_blob
2337
2338 def is_valid(self):
2339 """Ensures that values in the AvbIcpBlob are sane.
2340
2341 Returns:
2342 True if the values in the AvbIcpBlob are sane, False otherwise.
2343 """
2344 if not self.icp_header.is_valid():
Dan Austina7bc4962019-12-02 13:26:08 -08002345 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002346
2347 if self.icp_header.icp_count != len(self.icp_entries):
Dan Austina7bc4962019-12-02 13:26:08 -08002348 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002349
2350 for icp_entry in self.icp_entries:
2351 if not icp_entry.is_valid():
Dan Austina7bc4962019-12-02 13:26:08 -08002352 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002353 return True
Dan Austina7bc4962019-12-02 13:26:08 -08002354
Jan Monsch77cd2022019-12-10 17:18:04 +01002355
David Zeuthen21e95262016-07-27 17:58:40 -04002356class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002357 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002358
Jan Monschfe00c0a2019-12-11 11:19:40 +01002359 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2360 avb_vbmeta_image.h.
2361
David Zeuthen21e95262016-07-27 17:58:40 -04002362 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002363 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2364 required_libavb_version_major: The major version of libavb required for this
2365 header.
2366 required_libavb_version_minor: The minor version of libavb required for this
2367 header.
2368 authentication_data_block_size: The size of the signature block.
2369 auxiliary_data_block_size: The size of the auxiliary data block.
2370 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2371 enum.
2372 hash_offset: Offset into the "Authentication data" block of hash data.
2373 hash_size: Length of the hash data.
2374 signature_offset: Offset into the "Authentication data" block of signature
2375 data.
2376 signature_size: Length of the signature data.
2377 public_key_offset: Offset into the "Auxiliary data" block of public key
2378 data.
2379 public_key_size: Length of the public key data.
2380 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2381 key metadata.
2382 public_key_metadata_size: Length of the public key metadata. Must be set to
2383 zero if there is no public key metadata.
2384 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2385 data.
2386 descriptors_size: Length of descriptor data.
2387 rollback_index: The rollback index which can be used to prevent rollback to
2388 older versions.
2389 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2390 zero if the vbmeta image is not a top-level image.
2391 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2392 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2393 terminated. Applications must not make assumptions about how this
2394 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002395 """
2396
2397 SIZE = 256
2398
David Zeuthene3cadca2017-02-22 21:25:46 -05002399 # Keep in sync with |reserved0| and |reserved| field of
2400 # |AvbVBMetaImageHeader|.
2401 RESERVED0 = 4
2402 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002403
2404 # Keep in sync with |AvbVBMetaImageHeader|.
2405 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2406 '2Q' # 2 x block size
2407 'L' # algorithm type
2408 '2Q' # offset, size (hash)
2409 '2Q' # offset, size (signature)
2410 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002411 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002412 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002413 'Q' # rollback_index
2414 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002415 str(RESERVED0) + 'x' + # padding for reserved bytes
2416 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002417 str(RESERVED) + 'x') # padding for reserved bytes
2418
2419 def __init__(self, data=None):
2420 """Initializes a new header object.
2421
2422 Arguments:
2423 data: If not None, must be a bytearray of size 8192.
2424
2425 Raises:
2426 Exception: If the given data is malformed.
2427 """
2428 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2429
2430 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002431 (self.magic, self.required_libavb_version_major,
2432 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002433 self.authentication_data_block_size, self.auxiliary_data_block_size,
2434 self.algorithm_type, self.hash_offset, self.hash_size,
2435 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002436 self.public_key_size, self.public_key_metadata_offset,
2437 self.public_key_metadata_size, self.descriptors_offset,
2438 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002439 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002440 self.flags,
2441 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002442 # Nuke NUL-bytes at the end of the string.
2443 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002444 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002445 else:
2446 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002447 # Start by just requiring version 1.0. Code that adds features
2448 # in a future version can use bump_required_libavb_version_minor() to
2449 # bump the minor.
2450 self.required_libavb_version_major = AVB_VERSION_MAJOR
2451 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002452 self.authentication_data_block_size = 0
2453 self.auxiliary_data_block_size = 0
2454 self.algorithm_type = 0
2455 self.hash_offset = 0
2456 self.hash_size = 0
2457 self.signature_offset = 0
2458 self.signature_size = 0
2459 self.public_key_offset = 0
2460 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002461 self.public_key_metadata_offset = 0
2462 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002463 self.descriptors_offset = 0
2464 self.descriptors_size = 0
2465 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002466 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002467 self.release_string = get_release_string()
2468
2469 def bump_required_libavb_version_minor(self, minor):
2470 """Function to bump required_libavb_version_minor.
2471
2472 Call this when writing data that requires a specific libavb
2473 version to parse it.
2474
2475 Arguments:
2476 minor: The minor version of libavb that has support for the feature.
2477 """
2478 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002479 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002480
2481 def save(self, output):
2482 """Serializes the header (256 bytes) to disk.
2483
2484 Arguments:
2485 output: The object to write the output to.
2486 """
2487 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002488 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2489 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002490 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2491 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002492 self.public_key_offset, self.public_key_size,
2493 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002494 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002495 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002496
2497 def encode(self):
2498 """Serializes the header (256) to a bytearray().
2499
2500 Returns:
2501 A bytearray() with the encoded header.
2502 """
2503 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002504 self.required_libavb_version_major,
2505 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002506 self.authentication_data_block_size,
2507 self.auxiliary_data_block_size, self.algorithm_type,
2508 self.hash_offset, self.hash_size, self.signature_offset,
2509 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002510 self.public_key_size, self.public_key_metadata_offset,
2511 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002512 self.descriptors_size, self.rollback_index, self.flags,
2513 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002514
2515
2516class Avb(object):
2517 """Business logic for avbtool command-line tool."""
2518
David Zeuthen8b6973b2016-09-20 12:39:49 -04002519 # Keep in sync with avb_ab_flow.h.
2520 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2521 AB_MAGIC = '\0AB0'
2522 AB_MAJOR_VERSION = 1
2523 AB_MINOR_VERSION = 0
2524 AB_MISC_METADATA_OFFSET = 2048
2525
David Zeuthen09692692016-09-30 16:16:40 -04002526 # Constants for maximum metadata size. These are used to give
2527 # meaningful errors if the value passed in via --partition_size is
2528 # too small and when --calc_max_image_size is used. We use
2529 # conservative figures.
2530 MAX_VBMETA_SIZE = 64 * 1024
2531 MAX_FOOTER_SIZE = 4096
2532
David Zeuthen49936b42018-08-07 17:38:58 -04002533 def extract_vbmeta_image(self, output, image_filename, padding_size):
2534 """Implements the 'extract_vbmeta_image' command.
2535
2536 Arguments:
2537 output: Write vbmeta struct to this file.
2538 image_filename: File to extract vbmeta data from (with a footer).
2539 padding_size: If not 0, pads output so size is a multiple of the number.
2540
2541 Raises:
2542 AvbError: If there's no footer in the image.
2543 """
2544 image = ImageHandler(image_filename)
2545
2546 (footer, _, _, _) = self._parse_image(image)
2547
2548 if not footer:
2549 raise AvbError('Given image does not have a footer.')
2550
2551 image.seek(footer.vbmeta_offset)
2552 vbmeta_blob = image.read(footer.vbmeta_size)
2553 output.write(vbmeta_blob)
2554
2555 if padding_size > 0:
2556 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2557 padding_needed = padded_size - len(vbmeta_blob)
2558 output.write('\0' * padding_needed)
2559
David Zeuthena4fee8b2016-08-22 15:20:43 -04002560 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002561 """Implements the 'erase_footer' command.
2562
2563 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002564 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002565 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002566
2567 Raises:
2568 AvbError: If there's no footer in the image.
2569 """
2570
David Zeuthena4fee8b2016-08-22 15:20:43 -04002571 image = ImageHandler(image_filename)
2572
David Zeuthen21e95262016-07-27 17:58:40 -04002573 (footer, _, descriptors, _) = self._parse_image(image)
2574
2575 if not footer:
2576 raise AvbError('Given image does not have a footer.')
2577
2578 new_image_size = None
2579 if not keep_hashtree:
2580 new_image_size = footer.original_image_size
2581 else:
2582 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002583 # descriptor to figure out the location and size of the hashtree
2584 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002585 for desc in descriptors:
2586 if isinstance(desc, AvbHashtreeDescriptor):
2587 # The hashtree is always just following the main data so the
2588 # new size is easily derived.
2589 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002590 # If the image has FEC codes, also keep those.
2591 if desc.fec_offset > 0:
2592 fec_end = desc.fec_offset + desc.fec_size
2593 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002594 break
2595 if not new_image_size:
2596 raise AvbError('Requested to keep hashtree but no hashtree '
2597 'descriptor was found.')
2598
2599 # And cut...
2600 image.truncate(new_image_size)
2601
David Zeuthen1394f762019-04-30 10:20:11 -04002602 def zero_hashtree(self, image_filename):
2603 """Implements the 'zero_hashtree' command.
2604
2605 Arguments:
2606 image_filename: File to zero hashtree and FEC data from.
2607
2608 Raises:
2609 AvbError: If there's no footer in the image.
2610 """
2611
2612 image = ImageHandler(image_filename)
2613
2614 (footer, _, descriptors, _) = self._parse_image(image)
2615
2616 if not footer:
2617 raise AvbError('Given image does not have a footer.')
2618
2619 # Search for a hashtree descriptor to figure out the location and
2620 # size of the hashtree and FEC.
2621 ht_desc = None
2622 for desc in descriptors:
2623 if isinstance(desc, AvbHashtreeDescriptor):
2624 ht_desc = desc
2625 break
2626
2627 if not ht_desc:
2628 raise AvbError('No hashtree descriptor was found.')
2629
2630 zero_ht_start_offset = ht_desc.tree_offset
2631 zero_ht_num_bytes = ht_desc.tree_size
2632 zero_fec_start_offset = None
2633 zero_fec_num_bytes = 0
2634 if ht_desc.fec_offset > 0:
2635 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2636 raise AvbError('Hash-tree and FEC data must be adjacent.')
2637 zero_fec_start_offset = ht_desc.fec_offset
2638 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002639 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2640 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002641 image.seek(zero_end_offset)
2642 data = image.read(image.image_size - zero_end_offset)
2643
2644 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2645 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2646 # beginning of both hashtree and FEC. (That way, in the future we can add
2647 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2648 #
2649 # Applications can use these markers to detect that the hashtree and/or
2650 # FEC needs to be recomputed.
2651 image.truncate(zero_ht_start_offset)
2652 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2653 image.append_raw(data_zeroed_firstblock)
2654 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2655 if zero_fec_start_offset:
2656 image.append_raw(data_zeroed_firstblock)
2657 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2658 image.append_raw(data)
2659
David Zeuthen2bc232b2017-04-19 14:25:19 -04002660 def resize_image(self, image_filename, partition_size):
2661 """Implements the 'resize_image' command.
2662
2663 Arguments:
2664 image_filename: File with footer to resize.
2665 partition_size: The new size of the image.
2666
2667 Raises:
2668 AvbError: If there's no footer in the image.
2669 """
2670
2671 image = ImageHandler(image_filename)
2672
2673 if partition_size % image.block_size != 0:
2674 raise AvbError('Partition size of {} is not a multiple of the image '
2675 'block size {}.'.format(partition_size,
2676 image.block_size))
2677
Jan Monsch77cd2022019-12-10 17:18:04 +01002678 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002679
2680 if not footer:
2681 raise AvbError('Given image does not have a footer.')
2682
2683 # The vbmeta blob is always at the end of the data so resizing an
2684 # image amounts to just moving the footer around.
2685
2686 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2687 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002688 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2689 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002690
2691 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002692 raise AvbError('Requested size of {} is too small for an image '
2693 'of size {}.'
2694 .format(partition_size,
2695 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002696
2697 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2698 # with enough bytes such that the final Footer block is at the end
2699 # of partition_size.
2700 image.truncate(vbmeta_end_offset)
2701 image.append_dont_care(partition_size - vbmeta_end_offset -
2702 1*image.block_size)
2703
2704 # Just reuse the same footer - only difference is that we're
2705 # writing it in a different place.
2706 footer_blob = footer.encode()
2707 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2708 footer_blob)
2709 image.append_raw(footer_blob_with_padding)
2710
David Zeuthen8b6973b2016-09-20 12:39:49 -04002711 def set_ab_metadata(self, misc_image, slot_data):
2712 """Implements the 'set_ab_metadata' command.
2713
2714 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2715 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2716
2717 Arguments:
2718 misc_image: The misc image to write to.
2719 slot_data: Slot data as a string
2720
2721 Raises:
2722 AvbError: If slot data is malformed.
2723 """
2724 tokens = slot_data.split(':')
2725 if len(tokens) != 6:
2726 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2727 a_priority = int(tokens[0])
2728 a_tries_remaining = int(tokens[1])
2729 a_success = True if int(tokens[2]) != 0 else False
2730 b_priority = int(tokens[3])
2731 b_tries_remaining = int(tokens[4])
2732 b_success = True if int(tokens[5]) != 0 else False
2733
2734 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2735 self.AB_MAGIC,
2736 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2737 a_priority, a_tries_remaining, a_success,
2738 b_priority, b_tries_remaining, b_success)
2739 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2740 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2741 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2742 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2743 misc_image.write(ab_data)
2744
David Zeuthena4fee8b2016-08-22 15:20:43 -04002745 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002746 """Implements the 'info_image' command.
2747
2748 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002749 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002750 output: Output file to write human-readable information to (file object).
2751 """
2752
David Zeuthena4fee8b2016-08-22 15:20:43 -04002753 image = ImageHandler(image_filename)
2754
David Zeuthen21e95262016-07-27 17:58:40 -04002755 o = output
2756
2757 (footer, header, descriptors, image_size) = self._parse_image(image)
2758
2759 if footer:
2760 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2761 footer.version_minor))
2762 o.write('Image size: {} bytes\n'.format(image_size))
2763 o.write('Original image size: {} bytes\n'.format(
2764 footer.original_image_size))
2765 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2766 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2767 o.write('--\n')
2768
2769 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2770
David Zeuthene3cadca2017-02-22 21:25:46 -05002771 o.write('Minimum libavb version: {}.{}{}\n'.format(
2772 header.required_libavb_version_major,
2773 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002774 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002775 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2776 o.write('Authentication Block: {} bytes\n'.format(
2777 header.authentication_data_block_size))
2778 o.write('Auxiliary Block: {} bytes\n'.format(
2779 header.auxiliary_data_block_size))
2780 o.write('Algorithm: {}\n'.format(alg_name))
2781 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002782 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002783 o.write('Release String: \'{}\'\n'.format(
2784 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002785
2786 # Print descriptors.
2787 num_printed = 0
2788 o.write('Descriptors:\n')
2789 for desc in descriptors:
2790 desc.print_desc(o)
2791 num_printed += 1
2792 if num_printed == 0:
2793 o.write(' (none)\n')
2794
Jan Monscheeb28b62019-12-05 16:17:09 +01002795 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2796 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002797 """Implements the 'verify_image' command.
2798
2799 Arguments:
2800 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002801 key_path: None or check that embedded public key matches key at given
2802 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002803 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002804 follow_chain_partitions:
2805 If True, will follows chain partitions even when not specified with
2806 the --expected_chain_partition option
2807 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2808 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002809
2810 Raises:
2811 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002812 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002813 expected_chain_partitions_map = {}
2814 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002815 for cp in expected_chain_partitions:
2816 cp_tokens = cp.split(':')
2817 if len(cp_tokens) != 3:
2818 raise AvbError('Malformed chained partition "{}".'.format(cp))
2819 partition_name = cp_tokens[0]
2820 rollback_index_location = int(cp_tokens[1])
2821 file_path = cp_tokens[2]
2822 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002823 expected_chain_partitions_map[partition_name] = (
2824 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002825
2826 image_dir = os.path.dirname(image_filename)
2827 image_ext = os.path.splitext(image_filename)[1]
2828
2829 key_blob = None
2830 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002831 print('Verifying image {} using key at {}'.format(image_filename,
2832 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002833 key_blob = encode_rsa_key(key_path)
2834 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002835 print('Verifying image {} using embedded public key'.format(
2836 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002837
David Zeuthenb623d8b2017-04-04 16:05:53 -04002838 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002839 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002840 offset = 0
2841 if footer:
2842 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002843
David Zeuthenb623d8b2017-04-04 16:05:53 -04002844 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002845 vbmeta_blob = image.read(header.SIZE
2846 + header.authentication_data_block_size
2847 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002848
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002849 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002850 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002851 raise AvbError('Signature check failed for {} vbmeta struct {}'
2852 .format(alg_name, image_filename))
2853
2854 if key_blob:
2855 # The embedded public key is in the auxiliary block at an offset.
2856 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002857 key_offset += header.authentication_data_block_size
2858 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002859 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2860 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002861 if key_blob != key_blob_in_vbmeta:
2862 raise AvbError('Embedded public key does not match given key.')
2863
2864 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002865 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2866 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002867 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002868 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2869 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002870
2871 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002872 if (isinstance(desc, AvbChainPartitionDescriptor)
2873 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002874 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002875 # In this case we're processing a chain descriptor but don't have a
2876 # --expect_chain_partition ... however --follow_chain_partitions was
2877 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002878 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2879 'and KEY (which has sha1 {}) not specified'
2880 .format(desc.partition_name, desc.rollback_index_location,
2881 hashlib.sha1(desc.public_key).hexdigest()))
2882 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002883 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002884 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002885 # Honor --follow_chain_partitions - add '--' to make the output more
2886 # readable.
2887 if (isinstance(desc, AvbChainPartitionDescriptor)
2888 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002889 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002890 chained_image_filename = os.path.join(image_dir,
2891 desc.partition_name + image_ext)
2892 self.verify_image(chained_image_filename, key_path, None, False,
2893 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002894
David Zeuthenb8643c02018-05-17 17:21:18 -04002895 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2896 """Implements the 'calculate_vbmeta_digest' command.
2897
2898 Arguments:
2899 image_filename: Image file to get information from (file object).
2900 hash_algorithm: Hash algorithm used.
2901 output: Output file to write human-readable information to (file object).
2902 """
2903
2904 image_dir = os.path.dirname(image_filename)
2905 image_ext = os.path.splitext(image_filename)[1]
2906
2907 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002908 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002909 offset = 0
2910 if footer:
2911 offset = footer.vbmeta_offset
2912 size = (header.SIZE + header.authentication_data_block_size +
2913 header.auxiliary_data_block_size)
2914 image.seek(offset)
2915 vbmeta_blob = image.read(size)
2916
2917 hasher = hashlib.new(name=hash_algorithm)
2918 hasher.update(vbmeta_blob)
2919
2920 for desc in descriptors:
2921 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002922 ch_image_filename = os.path.join(image_dir,
2923 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002924 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002925 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002926 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002927 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2928 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002929 if ch_footer:
2930 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002931 ch_image.seek(ch_offset)
2932 ch_vbmeta_blob = ch_image.read(ch_size)
2933 hasher.update(ch_vbmeta_blob)
2934
2935 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002936 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002937
David Zeuthenf7d2e752018-09-20 13:30:41 -04002938 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2939 """Implements the 'calculate_kernel_cmdline' command.
2940
2941 Arguments:
2942 image_filename: Image file to get information from (file object).
2943 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2944 output: Output file to write human-readable information to (file object).
2945 """
2946
2947 image = ImageHandler(image_filename)
2948 _, _, descriptors, _ = self._parse_image(image)
2949
2950 image_dir = os.path.dirname(image_filename)
2951 image_ext = os.path.splitext(image_filename)[1]
2952
2953 cmdline_descriptors = []
2954 for desc in descriptors:
2955 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002956 ch_image_filename = os.path.join(image_dir,
2957 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002958 ch_image = ImageHandler(ch_image_filename)
2959 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2960 for ch_desc in ch_descriptors:
2961 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2962 cmdline_descriptors.append(ch_desc)
2963 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2964 cmdline_descriptors.append(desc)
2965
2966 kernel_cmdline_snippets = []
2967 for desc in cmdline_descriptors:
2968 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002969 if ((desc.flags &
2970 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2971 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002972 if hashtree_disabled:
2973 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002974 if (desc.flags &
2975 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002976 if not hashtree_disabled:
2977 use_cmdline = False
2978 if use_cmdline:
2979 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2980 output.write(' '.join(kernel_cmdline_snippets))
2981
David Zeuthen21e95262016-07-27 17:58:40 -04002982 def _parse_image(self, image):
2983 """Gets information about an image.
2984
2985 The image can either be a vbmeta or an image with a footer.
2986
2987 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002988 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002989
2990 Returns:
2991 A tuple where the first argument is a AvbFooter (None if there
2992 is no footer on the image), the second argument is a
2993 AvbVBMetaHeader, the third argument is a list of
2994 AvbDescriptor-derived instances, and the fourth argument is the
2995 size of |image|.
2996 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002997 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002998 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002999 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04003000 try:
3001 footer = AvbFooter(image.read(AvbFooter.SIZE))
3002 except (LookupError, struct.error):
3003 # Nope, just seek back to the start.
3004 image.seek(0)
3005
3006 vbmeta_offset = 0
3007 if footer:
3008 vbmeta_offset = footer.vbmeta_offset
3009
3010 image.seek(vbmeta_offset)
3011 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3012
3013 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
3014 aux_block_offset = auth_block_offset + h.authentication_data_block_size
3015 desc_start_offset = aux_block_offset + h.descriptors_offset
3016 image.seek(desc_start_offset)
3017 descriptors = parse_descriptors(image.read(h.descriptors_size))
3018
David Zeuthen09692692016-09-30 16:16:40 -04003019 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003020
David Zeuthenb1b994d2017-03-06 18:01:31 -05003021 def _load_vbmeta_blob(self, image):
3022 """Gets the vbmeta struct and associated sections.
3023
3024 The image can either be a vbmeta.img or an image with a footer.
3025
3026 Arguments:
3027 image: An ImageHandler (vbmeta or footer).
3028
3029 Returns:
3030 A blob with the vbmeta struct and other sections.
3031 """
3032 assert isinstance(image, ImageHandler)
3033 footer = None
3034 image.seek(image.image_size - AvbFooter.SIZE)
3035 try:
3036 footer = AvbFooter(image.read(AvbFooter.SIZE))
3037 except (LookupError, struct.error):
3038 # Nope, just seek back to the start.
3039 image.seek(0)
3040
3041 vbmeta_offset = 0
3042 if footer:
3043 vbmeta_offset = footer.vbmeta_offset
3044
3045 image.seek(vbmeta_offset)
3046 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3047
3048 image.seek(vbmeta_offset)
3049 data_size = AvbVBMetaHeader.SIZE
3050 data_size += h.authentication_data_block_size
3051 data_size += h.auxiliary_data_block_size
3052 return image.read(data_size)
3053
David Zeuthen73f2afa2017-05-17 16:54:11 -04003054 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05003055 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04003056
3057 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04003058 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04003059
3060 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05003061 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3062 instructions. There is one for when hashtree is not disabled and one for
3063 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04003064
David Zeuthen21e95262016-07-27 17:58:40 -04003065 """
3066
David Zeuthen21e95262016-07-27 17:58:40 -04003067 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003068 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01003069 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003070 c += ' verity {}'.format(ht.dm_verity_version) # type and version
3071 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
3072 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
3073 c += ' {}'.format(ht.data_block_size) # data_block
3074 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01003075 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
3076 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003077 c += ' {}'.format(ht.hash_algorithm) # hash_alg
3078 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
3079 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
3080 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05003081 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003082 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003083 c += ' ignore_zero_blocks'
3084 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3085 c += ' fec_roots {}'.format(ht.fec_num_roots)
3086 # Note that fec_blocks is the size that FEC covers, *not* the
3087 # size of the FEC data. Since we use FEC for everything up until
3088 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01003089 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
3090 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003091 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05003092 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003093 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003094 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04003095 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04003096
David Zeuthenfd41eb92016-11-17 12:24:47 -05003097 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003098 desc = AvbKernelCmdlineDescriptor()
3099 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05003100 desc.flags = (
3101 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3102
3103 # The descriptor for when hashtree verification is disabled is a lot
3104 # simpler - we just set the root to the partition.
3105 desc_no_ht = AvbKernelCmdlineDescriptor()
3106 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3107 desc_no_ht.flags = (
3108 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
3109
3110 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04003111
David Zeuthen73f2afa2017-05-17 16:54:11 -04003112 def _get_cmdline_descriptors_for_dm_verity(self, image):
3113 """Generate kernel cmdline descriptors for dm-verity.
3114
3115 Arguments:
3116 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
3117
3118 Returns:
3119 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3120 instructions. There is one for when hashtree is not disabled and one for
3121 when it is.
3122
3123 Raises:
3124 AvbError: If |image| doesn't have a hashtree descriptor.
3125
3126 """
3127
3128 (_, _, descriptors, _) = self._parse_image(image)
3129
3130 ht = None
3131 for desc in descriptors:
3132 if isinstance(desc, AvbHashtreeDescriptor):
3133 ht = desc
3134 break
3135
3136 if not ht:
3137 raise AvbError('No hashtree descriptor in given image')
3138
3139 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
3140
David Zeuthen21e95262016-07-27 17:58:40 -04003141 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05003142 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003143 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003144 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04003145 include_descriptors_from_image,
3146 signing_helper,
3147 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003148 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003149 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003150 print_required_libavb_version,
3151 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003152 """Implements the 'make_vbmeta_image' command.
3153
3154 Arguments:
3155 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003156 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003157 algorithm_name: Name of algorithm to use.
3158 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003159 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003160 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003161 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003162 props: Properties to insert (list of strings of the form 'key:value').
3163 props_from_file: Properties to insert (list of strings 'key:<path>').
3164 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003165 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04003166 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003167 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003168 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003169 release_string: None or avbtool release string to use instead of default.
3170 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04003171 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04003172 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04003173
3174 Raises:
3175 AvbError: If a chained partition is malformed.
3176 """
3177
David Zeuthen1097a782017-05-31 15:53:17 -04003178 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003179 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003180 if include_descriptors_from_image:
3181 # Use the bump logic in AvbVBMetaHeader to calculate the max required
3182 # version of all included descriptors.
3183 tmp_header = AvbVBMetaHeader()
3184 for image in include_descriptors_from_image:
3185 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
3186 tmp_header.bump_required_libavb_version_minor(
3187 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01003188 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003189 else:
3190 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01003191 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04003192 return
3193
3194 if not output:
3195 raise AvbError('No output file given')
3196
David Zeuthen21e95262016-07-27 17:58:40 -04003197 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04003198 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003199 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003200 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003201 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003202 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003203 include_descriptors_from_image, signing_helper,
3204 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003205 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04003206
3207 # Write entire vbmeta blob (header, authentication, auxiliary).
3208 output.seek(0)
3209 output.write(vbmeta_blob)
3210
David Zeuthen97cb5802017-06-01 16:14:05 -04003211 if padding_size > 0:
3212 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
3213 padding_needed = padded_size - len(vbmeta_blob)
3214 output.write('\0' * padding_needed)
3215
David Zeuthen18666ab2016-11-15 11:18:05 -05003216 def _generate_vbmeta_blob(self, algorithm_name, key_path,
3217 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003218 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003219 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003220 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003221 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003222 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003223 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003224 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003225 release_string, append_to_release_string,
3226 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04003227 """Generates a VBMeta blob.
3228
3229 This blob contains the header (struct AvbVBMetaHeader), the
3230 authentication data block (which contains the hash and signature
3231 for the header and auxiliary block), and the auxiliary block
3232 (which contains descriptors, the public key used, and other data).
3233
3234 The |key| parameter can |None| only if the |algorithm_name| is
3235 'NONE'.
3236
3237 Arguments:
3238 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3239 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003240 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003241 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003242 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003243 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003244 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003245 props: Properties to insert (List of strings of the form 'key:value').
3246 props_from_file: Properties to insert (List of strings 'key:<path>').
3247 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003248 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003249 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003250 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3251 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003252 include_descriptors_from_image: List of file objects for which
3253 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003254 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003255 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003256 release_string: None or avbtool release string.
3257 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003258 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003259
3260 Returns:
3261 A bytearray() with the VBMeta blob.
3262
3263 Raises:
3264 Exception: If the |algorithm_name| is not found, if no key has
3265 been given and the given algorithm requires one, or the key is
3266 of the wrong size.
3267
3268 """
3269 try:
3270 alg = ALGORITHMS[algorithm_name]
3271 except KeyError:
3272 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3273
David Zeuthena5fd3a42017-02-27 16:38:54 -05003274 if not descriptors:
3275 descriptors = []
3276
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003277 h = AvbVBMetaHeader()
3278 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3279
David Zeuthena5fd3a42017-02-27 16:38:54 -05003280 # Insert chained partition descriptors, if any
3281 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04003282 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003283 for cp in chain_partitions:
3284 cp_tokens = cp.split(':')
3285 if len(cp_tokens) != 3:
3286 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003287 partition_name = cp_tokens[0]
3288 rollback_index_location = int(cp_tokens[1])
3289 file_path = cp_tokens[2]
3290 # Check that the same rollback location isn't being used by
3291 # multiple chained partitions.
3292 if used_locations.get(rollback_index_location):
3293 raise AvbError('Rollback Index Location {} is already in use.'.format(
3294 rollback_index_location))
3295 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003296 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003297 desc.partition_name = partition_name
3298 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003299 if desc.rollback_index_location < 1:
3300 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003301 desc.public_key = open(file_path, 'rb').read()
3302 descriptors.append(desc)
3303
David Zeuthen21e95262016-07-27 17:58:40 -04003304 # Descriptors.
3305 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003306 for desc in descriptors:
3307 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003308
3309 # Add properties.
3310 if props:
3311 for prop in props:
3312 idx = prop.find(':')
3313 if idx == -1:
3314 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003315 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003316 desc = AvbPropertyDescriptor()
3317 desc.key = prop[0:idx]
3318 desc.value = prop[(idx + 1):]
3319 encoded_descriptors.extend(desc.encode())
3320 if props_from_file:
3321 for prop in props_from_file:
3322 idx = prop.find(':')
3323 if idx == -1:
3324 raise AvbError('Malformed property "{}".'.format(prop))
3325 desc = AvbPropertyDescriptor()
3326 desc.key = prop[0:idx]
3327 desc.value = prop[(idx + 1):]
3328 file_path = prop[(idx + 1):]
3329 desc.value = open(file_path, 'rb').read()
3330 encoded_descriptors.extend(desc.encode())
3331
David Zeuthen73f2afa2017-05-17 16:54:11 -04003332 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003333 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003334 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003335 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003336 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3337 encoded_descriptors.extend(cmdline_desc[0].encode())
3338 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003339
David Zeuthen73f2afa2017-05-17 16:54:11 -04003340 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3341 if ht_desc_to_setup:
3342 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3343 ht_desc_to_setup)
3344 encoded_descriptors.extend(cmdline_desc[0].encode())
3345 encoded_descriptors.extend(cmdline_desc[1].encode())
3346
David Zeuthen21e95262016-07-27 17:58:40 -04003347 # Add kernel command-lines.
3348 if kernel_cmdlines:
3349 for i in kernel_cmdlines:
3350 desc = AvbKernelCmdlineDescriptor()
3351 desc.kernel_cmdline = i
3352 encoded_descriptors.extend(desc.encode())
3353
3354 # Add descriptors from other images.
3355 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003356 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003357 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003358 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003359 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3360 image_handler)
3361 # Bump the required libavb version to support all included descriptors.
3362 h.bump_required_libavb_version_minor(
3363 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003364 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003365 # The --include_descriptors_from_image option is used in some setups
3366 # with images A and B where both A and B contain a descriptor
3367 # for a partition with the same name. Since it's not meaningful
3368 # to include both descriptors, only include the last seen descriptor.
3369 # See bug 76386656 for details.
3370 if hasattr(desc, 'partition_name'):
3371 key = type(desc).__name__ + '_' + desc.partition_name
3372 descriptors_dict[key] = desc.encode()
3373 else:
3374 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003375 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003376 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003377
David Zeuthen18666ab2016-11-15 11:18:05 -05003378 # Load public key metadata blob, if requested.
3379 pkmd_blob = []
3380 if public_key_metadata_path:
3381 with open(public_key_metadata_path) as f:
3382 pkmd_blob = f.read()
3383
David Zeuthen21e95262016-07-27 17:58:40 -04003384 key = None
3385 encoded_key = bytearray()
3386 if alg.public_key_num_bytes > 0:
3387 if not key_path:
3388 raise AvbError('Key is required for algorithm {}'.format(
3389 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003390 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003391 if len(encoded_key) != alg.public_key_num_bytes:
3392 raise AvbError('Key is wrong size for algorithm {}'.format(
3393 algorithm_name))
3394
David Zeuthene3cadca2017-02-22 21:25:46 -05003395 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003396 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003397 if isinstance(release_string, (str, unicode)):
3398 h.release_string = release_string
3399
3400 # Append to release string, if requested. Also insert a space before.
3401 if isinstance(append_to_release_string, (str, unicode)):
3402 h.release_string += ' ' + append_to_release_string
3403
David Zeuthen18666ab2016-11-15 11:18:05 -05003404 # For the Auxiliary data block, descriptors are stored at offset 0,
3405 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003406 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003407 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003408 h.descriptors_offset = 0
3409 h.descriptors_size = len(encoded_descriptors)
3410 h.public_key_offset = h.descriptors_size
3411 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003412 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3413 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003414
3415 # For the Authentication data block, the hash is first and then
3416 # the signature.
3417 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003418 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003419 h.algorithm_type = alg.algorithm_type
3420 h.hash_offset = 0
3421 h.hash_size = alg.hash_num_bytes
3422 # Signature offset and size - it's stored right after the hash
3423 # (in Authentication data block).
3424 h.signature_offset = alg.hash_num_bytes
3425 h.signature_size = alg.signature_num_bytes
3426
3427 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003428 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003429
3430 # Generate Header data block.
3431 header_data_blob = h.encode()
3432
3433 # Generate Auxiliary data block.
3434 aux_data_blob = bytearray()
3435 aux_data_blob.extend(encoded_descriptors)
3436 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003437 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003438 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3439 aux_data_blob.extend('\0' * padding_bytes)
3440
3441 # Calculate the hash.
3442 binary_hash = bytearray()
3443 binary_signature = bytearray()
3444 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003445 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003446 ha.update(header_data_blob)
3447 ha.update(aux_data_blob)
3448 binary_hash.extend(ha.digest())
3449
3450 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003451 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003452 binary_signature.extend(raw_sign(signing_helper,
3453 signing_helper_with_files,
3454 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003455 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003456 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003457
3458 # Generate Authentication data block.
3459 auth_data_blob = bytearray()
3460 auth_data_blob.extend(binary_hash)
3461 auth_data_blob.extend(binary_signature)
3462 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3463 auth_data_blob.extend('\0' * padding_bytes)
3464
3465 return header_data_blob + auth_data_blob + aux_data_blob
3466
3467 def extract_public_key(self, key_path, output):
3468 """Implements the 'extract_public_key' command.
3469
3470 Arguments:
3471 key_path: The path to a RSA private key file.
3472 output: The file to write to.
3473 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003474 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003475
David Zeuthenb1b994d2017-03-06 18:01:31 -05003476 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3477 partition_size):
3478 """Implementation of the append_vbmeta_image command.
3479
3480 Arguments:
3481 image_filename: File to add the footer to.
3482 vbmeta_image_filename: File to get vbmeta struct from.
3483 partition_size: Size of partition.
3484
3485 Raises:
3486 AvbError: If an argument is incorrect.
3487 """
3488 image = ImageHandler(image_filename)
3489
3490 if partition_size % image.block_size != 0:
3491 raise AvbError('Partition size of {} is not a multiple of the image '
3492 'block size {}.'.format(partition_size,
3493 image.block_size))
3494
3495 # If there's already a footer, truncate the image to its original
3496 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003497 if image.image_size >= AvbFooter.SIZE:
3498 image.seek(image.image_size - AvbFooter.SIZE)
3499 try:
3500 footer = AvbFooter(image.read(AvbFooter.SIZE))
3501 # Existing footer found. Just truncate.
3502 original_image_size = footer.original_image_size
3503 image.truncate(footer.original_image_size)
3504 except (LookupError, struct.error):
3505 original_image_size = image.image_size
3506 else:
3507 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003508 original_image_size = image.image_size
3509
3510 # If anything goes wrong from here-on, restore the image back to
3511 # its original size.
3512 try:
3513 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3514 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3515
3516 # If the image isn't sparse, its size might not be a multiple of
3517 # the block size. This will screw up padding later so just grow it.
3518 if image.image_size % image.block_size != 0:
3519 assert not image.is_sparse
3520 padding_needed = image.block_size - (image.image_size%image.block_size)
3521 image.truncate(image.image_size + padding_needed)
3522
3523 # The append_raw() method requires content with size being a
3524 # multiple of |block_size| so add padding as needed. Also record
3525 # where this is written to since we'll need to put that in the
3526 # footer.
3527 vbmeta_offset = image.image_size
3528 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3529 len(vbmeta_blob))
3530 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3531
3532 # Append vbmeta blob and footer
3533 image.append_raw(vbmeta_blob_with_padding)
3534 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3535
3536 # Now insert a DONT_CARE chunk with enough bytes such that the
3537 # final Footer block is at the end of partition_size..
3538 image.append_dont_care(partition_size - vbmeta_end_offset -
3539 1*image.block_size)
3540
3541 # Generate the Footer that tells where the VBMeta footer
3542 # is. Also put enough padding in the front of the footer since
3543 # we'll write out an entire block.
3544 footer = AvbFooter()
3545 footer.original_image_size = original_image_size
3546 footer.vbmeta_offset = vbmeta_offset
3547 footer.vbmeta_size = len(vbmeta_blob)
3548 footer_blob = footer.encode()
3549 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3550 footer_blob)
3551 image.append_raw(footer_blob_with_padding)
3552
3553 except:
3554 # Truncate back to original size, then re-raise
3555 image.truncate(original_image_size)
3556 raise
3557
David Zeuthena4fee8b2016-08-22 15:20:43 -04003558 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003559 hash_algorithm, salt, chain_partitions, algorithm_name,
3560 key_path,
3561 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003562 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003563 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003564 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003565 signing_helper, signing_helper_with_files,
3566 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003567 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003568 print_required_libavb_version, use_persistent_digest,
3569 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003570 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003571
3572 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003573 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003574 partition_size: Size of partition.
3575 partition_name: Name of partition (without A/B suffix).
3576 hash_algorithm: Hash algorithm to use.
3577 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003578 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003579 algorithm_name: Name of algorithm to use.
3580 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003581 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003582 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003583 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003584 props: Properties to insert (List of strings of the form 'key:value').
3585 props_from_file: Properties to insert (List of strings 'key:<path>').
3586 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003587 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003588 dm-verity kernel cmdline from.
3589 include_descriptors_from_image: List of file objects for which
3590 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003591 calc_max_image_size: Don't store the footer - instead calculate the
3592 maximum image size leaving enough room for metadata with the
3593 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003594 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003595 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003596 release_string: None or avbtool release string.
3597 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003598 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3599 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003600 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003601 use_persistent_digest: Use a persistent digest on device.
3602 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003603
3604 Raises:
3605 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003606 """
David Zeuthen1097a782017-05-31 15:53:17 -04003607
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003608 required_libavb_version_minor = 0
3609 if use_persistent_digest or do_not_use_ab:
3610 required_libavb_version_minor = 1
3611
David Zeuthen1097a782017-05-31 15:53:17 -04003612 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003613 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003614 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003615 return
3616
David Zeuthenbf562452017-05-17 18:04:43 -04003617 # First, calculate the maximum image size such that an image
3618 # this size + metadata (footer + vbmeta struct) fits in
3619 # |partition_size|.
3620 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003621 if partition_size < max_metadata_size:
3622 raise AvbError('Parition size of {} is too small. '
3623 'Needs to be at least {}'.format(
3624 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003625 max_image_size = partition_size - max_metadata_size
3626
3627 # If we're asked to only calculate the maximum image size, we're done.
3628 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003629 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003630 return
3631
David Zeuthena4fee8b2016-08-22 15:20:43 -04003632 image = ImageHandler(image_filename)
3633
3634 if partition_size % image.block_size != 0:
3635 raise AvbError('Partition size of {} is not a multiple of the image '
3636 'block size {}.'.format(partition_size,
3637 image.block_size))
3638
David Zeuthen21e95262016-07-27 17:58:40 -04003639 # If there's already a footer, truncate the image to its original
3640 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3641 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003642 if image.image_size >= AvbFooter.SIZE:
3643 image.seek(image.image_size - AvbFooter.SIZE)
3644 try:
3645 footer = AvbFooter(image.read(AvbFooter.SIZE))
3646 # Existing footer found. Just truncate.
3647 original_image_size = footer.original_image_size
3648 image.truncate(footer.original_image_size)
3649 except (LookupError, struct.error):
3650 original_image_size = image.image_size
3651 else:
3652 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003653 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003654
3655 # If anything goes wrong from here-on, restore the image back to
3656 # its original size.
3657 try:
David Zeuthen09692692016-09-30 16:16:40 -04003658 # If image size exceeds the maximum image size, fail.
3659 if image.image_size > max_image_size:
3660 raise AvbError('Image size of {} exceeds maximum image '
3661 'size of {} in order to fit in a partition '
3662 'size of {}.'.format(image.image_size, max_image_size,
3663 partition_size))
3664
David Zeuthen21e95262016-07-27 17:58:40 -04003665 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3666 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003667 salt = binascii.unhexlify(salt)
3668 elif salt is None and not use_persistent_digest:
3669 # If salt is not explicitly specified, choose a hash that's the same
3670 # size as the hash size. Don't populate a random salt if this
3671 # descriptor is being created to use a persistent digest on device.
3672 hash_size = digest_size
3673 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003674 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003675 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003676
3677 hasher = hashlib.new(name=hash_algorithm, string=salt)
3678 # TODO(zeuthen): might want to read this in chunks to avoid
3679 # memory pressure, then again, this is only supposed to be used
3680 # on kernel/initramfs partitions. Possible optimization.
3681 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003682 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003683 digest = hasher.digest()
3684
3685 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003686 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003687 h_desc.hash_algorithm = hash_algorithm
3688 h_desc.partition_name = partition_name
3689 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003690 h_desc.flags = 0
3691 if do_not_use_ab:
3692 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3693 if not use_persistent_digest:
3694 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003695
3696 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003697 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003698 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003699 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003700 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003701 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003702 include_descriptors_from_image, signing_helper,
3703 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003704 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003705
David Zeuthend247fcb2017-02-16 12:09:27 -05003706 # Write vbmeta blob, if requested.
3707 if output_vbmeta_image:
3708 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003709
David Zeuthend247fcb2017-02-16 12:09:27 -05003710 # Append vbmeta blob and footer, unless requested not to.
3711 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003712 # If the image isn't sparse, its size might not be a multiple of
3713 # the block size. This will screw up padding later so just grow it.
3714 if image.image_size % image.block_size != 0:
3715 assert not image.is_sparse
3716 padding_needed = image.block_size - (
3717 image.image_size % image.block_size)
3718 image.truncate(image.image_size + padding_needed)
3719
3720 # The append_raw() method requires content with size being a
3721 # multiple of |block_size| so add padding as needed. Also record
3722 # where this is written to since we'll need to put that in the
3723 # footer.
3724 vbmeta_offset = image.image_size
3725 padding_needed = (
3726 round_to_multiple(len(vbmeta_blob), image.block_size) -
3727 len(vbmeta_blob))
3728 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3729
David Zeuthend247fcb2017-02-16 12:09:27 -05003730 image.append_raw(vbmeta_blob_with_padding)
3731 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3732
3733 # Now insert a DONT_CARE chunk with enough bytes such that the
3734 # final Footer block is at the end of partition_size..
3735 image.append_dont_care(partition_size - vbmeta_end_offset -
3736 1*image.block_size)
3737
3738 # Generate the Footer that tells where the VBMeta footer
3739 # is. Also put enough padding in the front of the footer since
3740 # we'll write out an entire block.
3741 footer = AvbFooter()
3742 footer.original_image_size = original_image_size
3743 footer.vbmeta_offset = vbmeta_offset
3744 footer.vbmeta_size = len(vbmeta_blob)
3745 footer_blob = footer.encode()
3746 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3747 footer_blob)
3748 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003749
David Zeuthen21e95262016-07-27 17:58:40 -04003750 except:
3751 # Truncate back to original size, then re-raise
3752 image.truncate(original_image_size)
3753 raise
3754
David Zeuthena4fee8b2016-08-22 15:20:43 -04003755 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003756 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003757 block_size, salt, chain_partitions, algorithm_name,
3758 key_path,
3759 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003760 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003761 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003762 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003763 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003764 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003765 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003766 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003767 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003768 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003769 use_persistent_root_digest, do_not_use_ab,
3770 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003771 """Implements the 'add_hashtree_footer' command.
3772
3773 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3774 more information about dm-verity and these hashes.
3775
3776 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003777 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003778 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003779 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003780 generate_fec: If True, generate FEC codes.
3781 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003782 hash_algorithm: Hash algorithm to use.
3783 block_size: Block size to use.
3784 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003785 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003786 algorithm_name: Name of algorithm to use.
3787 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003788 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003789 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003790 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003791 props: Properties to insert (List of strings of the form 'key:value').
3792 props_from_file: Properties to insert (List of strings 'key:<path>').
3793 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003794 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003795 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003796 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3797 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003798 include_descriptors_from_image: List of file objects for which
3799 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003800 calc_max_image_size: Don't store the hashtree or footer - instead
3801 calculate the maximum image size leaving enough room for hashtree
3802 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003803 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003804 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003805 release_string: None or avbtool release string.
3806 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003807 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3808 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003809 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003810 use_persistent_root_digest: Use a persistent root digest on device.
3811 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003812 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003813
3814 Raises:
3815 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003816 """
David Zeuthen1097a782017-05-31 15:53:17 -04003817
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003818 required_libavb_version_minor = 0
3819 if use_persistent_root_digest or do_not_use_ab:
3820 required_libavb_version_minor = 1
3821
David Zeuthen1097a782017-05-31 15:53:17 -04003822 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003823 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003824 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003825 return
3826
David Zeuthen09692692016-09-30 16:16:40 -04003827 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3828 digest_padding = round_to_pow2(digest_size) - digest_size
3829
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003830 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3831 # size such that an image this size + the hashtree + metadata (footer +
3832 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3833 # for metadata.
3834 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003835 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003836 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003837 if not no_hashtree:
3838 (_, max_tree_size) = calc_hash_level_offsets(
3839 partition_size, block_size, digest_size + digest_padding)
3840 if generate_fec:
3841 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003842 max_metadata_size = (max_fec_size + max_tree_size +
3843 self.MAX_VBMETA_SIZE +
3844 self.MAX_FOOTER_SIZE)
3845 max_image_size = partition_size - max_metadata_size
3846 else:
3847 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003848
3849 # If we're asked to only calculate the maximum image size, we're done.
3850 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003851 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003852 return
3853
David Zeuthena4fee8b2016-08-22 15:20:43 -04003854 image = ImageHandler(image_filename)
3855
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003856 if partition_size > 0:
3857 if partition_size % image.block_size != 0:
3858 raise AvbError('Partition size of {} is not a multiple of the image '
3859 'block size {}.'.format(partition_size,
3860 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003861 elif image.image_size % image.block_size != 0:
3862 raise AvbError('File size of {} is not a multiple of the image '
3863 'block size {}.'.format(image.image_size,
3864 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003865
David Zeuthen21e95262016-07-27 17:58:40 -04003866 # If there's already a footer, truncate the image to its original
3867 # size. This way 'avbtool add_hashtree_footer' is idempotent
3868 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003869 if image.image_size >= AvbFooter.SIZE:
3870 image.seek(image.image_size - AvbFooter.SIZE)
3871 try:
3872 footer = AvbFooter(image.read(AvbFooter.SIZE))
3873 # Existing footer found. Just truncate.
3874 original_image_size = footer.original_image_size
3875 image.truncate(footer.original_image_size)
3876 except (LookupError, struct.error):
3877 original_image_size = image.image_size
3878 else:
3879 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003880 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003881
3882 # If anything goes wrong from here-on, restore the image back to
3883 # its original size.
3884 try:
3885 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003886 rounded_image_size = round_to_multiple(image.image_size, block_size)
3887 if rounded_image_size > image.image_size:
3888 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003889
David Zeuthen09692692016-09-30 16:16:40 -04003890 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003891 if partition_size > 0:
3892 if image.image_size > max_image_size:
3893 raise AvbError('Image size of {} exceeds maximum image '
3894 'size of {} in order to fit in a partition '
3895 'size of {}.'.format(image.image_size, max_image_size,
3896 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003897
3898 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003899 salt = binascii.unhexlify(salt)
3900 elif salt is None and not use_persistent_root_digest:
3901 # If salt is not explicitly specified, choose a hash that's the same
3902 # size as the hash size. Don't populate a random salt if this
3903 # descriptor is being created to use a persistent digest on device.
3904 hash_size = digest_size
3905 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003906 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003907 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003908
David Zeuthena4fee8b2016-08-22 15:20:43 -04003909 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003910 # offsets in advance.
3911 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003912 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003913
David Zeuthena4fee8b2016-08-22 15:20:43 -04003914 # If the image isn't sparse, its size might not be a multiple of
3915 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003916 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003917 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003918 padding_needed = image.block_size - (image.image_size%image.block_size)
3919 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003920
David Zeuthena4fee8b2016-08-22 15:20:43 -04003921 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003922 tree_offset = image.image_size
3923 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003924 block_size,
3925 hash_algorithm, salt,
3926 digest_padding,
3927 hash_level_offsets,
3928 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003929
3930 # Generate HashtreeDescriptor with details about the tree we
3931 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003932 if no_hashtree:
3933 tree_size = 0
3934 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003935 ht_desc = AvbHashtreeDescriptor()
3936 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003937 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003938 ht_desc.tree_offset = tree_offset
3939 ht_desc.tree_size = tree_size
3940 ht_desc.data_block_size = block_size
3941 ht_desc.hash_block_size = block_size
3942 ht_desc.hash_algorithm = hash_algorithm
3943 ht_desc.partition_name = partition_name
3944 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003945 if do_not_use_ab:
3946 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3947 if not use_persistent_root_digest:
3948 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003949
David Zeuthen09692692016-09-30 16:16:40 -04003950 # Write the hash tree
3951 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3952 len(hash_tree))
3953 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3954 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003955 len_hashtree_and_fec = len(hash_tree_with_padding)
3956
3957 # Generate FEC codes, if requested.
3958 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003959 if no_hashtree:
3960 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003961 else:
3962 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003963 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3964 len(fec_data))
3965 fec_data_with_padding = fec_data + '\0'*padding_needed
3966 fec_offset = image.image_size
3967 image.append_raw(fec_data_with_padding)
3968 len_hashtree_and_fec += len(fec_data_with_padding)
3969 # Update the hashtree descriptor.
3970 ht_desc.fec_num_roots = fec_num_roots
3971 ht_desc.fec_offset = fec_offset
3972 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003973
David Zeuthen73f2afa2017-05-17 16:54:11 -04003974 ht_desc_to_setup = None
3975 if setup_as_rootfs_from_kernel:
3976 ht_desc_to_setup = ht_desc
3977
David Zeuthena4fee8b2016-08-22 15:20:43 -04003978 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003979 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003980 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003981 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003982 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003983 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003984 include_descriptors_from_image, signing_helper,
3985 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003986 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003987 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3988 len(vbmeta_blob))
3989 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003990
David Zeuthend247fcb2017-02-16 12:09:27 -05003991 # Write vbmeta blob, if requested.
3992 if output_vbmeta_image:
3993 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003994
David Zeuthend247fcb2017-02-16 12:09:27 -05003995 # Append vbmeta blob and footer, unless requested not to.
3996 if not do_not_append_vbmeta_image:
3997 image.append_raw(vbmeta_blob_with_padding)
3998
3999 # Now insert a DONT_CARE chunk with enough bytes such that the
4000 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004001 if partition_size > 0:
4002 image.append_dont_care(partition_size - image.image_size -
4003 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05004004
4005 # Generate the Footer that tells where the VBMeta footer
4006 # is. Also put enough padding in the front of the footer since
4007 # we'll write out an entire block.
4008 footer = AvbFooter()
4009 footer.original_image_size = original_image_size
4010 footer.vbmeta_offset = vbmeta_offset
4011 footer.vbmeta_size = len(vbmeta_blob)
4012 footer_blob = footer.encode()
4013 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4014 footer_blob)
4015 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004016
David Zeuthen21e95262016-07-27 17:58:40 -04004017 except:
David Zeuthen09692692016-09-30 16:16:40 -04004018 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04004019 image.truncate(original_image_size)
4020 raise
4021
David Zeuthenc68f0822017-03-31 17:22:35 -04004022 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004023 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004024 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004025 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08004026 """Implements the 'make_atx_certificate' command.
4027
4028 Android Things certificates are required for Android Things public key
4029 metadata. They chain the vbmeta signing key for a particular product back to
4030 a fused, permanent root key. These certificates are fixed-length and fixed-
4031 format with the explicit goal of not parsing ASN.1 in bootloader code.
4032
4033 Arguments:
4034 output: Certificate will be written to this file on success.
4035 authority_key_path: A PEM file path with the authority private key.
4036 If None, then a certificate will be created without a
4037 signature. The signature can be created out-of-band
4038 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04004039 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08004040 subject_key_version: A 64-bit version value. If this is None, the number
4041 of seconds since the epoch is used.
4042 subject: A subject identifier. For Product Signing Key certificates this
4043 should be the same Product ID found in the permanent attributes.
4044 is_intermediate_authority: True if the certificate is for an intermediate
4045 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08004046 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08004047 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004048 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08004049 """
4050 signed_data = bytearray()
4051 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004052 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004053 hasher = hashlib.sha256()
4054 hasher.update(subject)
4055 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08004056 if not usage:
4057 usage = 'com.google.android.things.vboot'
4058 if is_intermediate_authority:
4059 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08004060 hasher = hashlib.sha256()
4061 hasher.update(usage)
4062 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07004063 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08004064 subject_key_version = int(time.time())
4065 signed_data.extend(struct.pack('<Q', subject_key_version))
4066 signature = bytearray()
4067 if authority_key_path:
4068 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08004069 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09004070 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01004071 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09004072 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08004073 hasher.update(signed_data)
4074 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04004075 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4076 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004077 alg.signature_num_bytes, authority_key_path,
4078 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08004079 output.write(signed_data)
4080 output.write(signature)
4081
David Zeuthenc68f0822017-03-31 17:22:35 -04004082 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004083 product_id):
4084 """Implements the 'make_atx_permanent_attributes' command.
4085
4086 Android Things permanent attributes are designed to be permanent for a
4087 particular product and a hash of these attributes should be fused into
4088 hardware to enforce this.
4089
4090 Arguments:
4091 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04004092 root_authority_key_path: Path to a PEM or DER public key for
4093 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08004094 product_id: A 16-byte Product ID.
4095
4096 Raises:
4097 AvbError: If an argument is incorrect.
4098 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004099 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004100 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004101 raise AvbError('Invalid Product ID length.')
4102 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004103 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004104 output.write(product_id)
4105
4106 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08004107 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08004108 """Implements the 'make_atx_metadata' command.
4109
4110 Android Things metadata are included in vbmeta images to facilitate
4111 verification. The output of this command can be used as the
4112 public_key_metadata argument to other commands.
4113
4114 Arguments:
4115 output: Metadata will be written to this file on success.
4116 intermediate_key_certificate: A certificate file as output by
4117 make_atx_certificate with
4118 is_intermediate_authority set to true.
4119 product_key_certificate: A certificate file as output by
4120 make_atx_certificate with
4121 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08004122
4123 Raises:
4124 AvbError: If an argument is incorrect.
4125 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004126 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004127 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004128 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08004129 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004130 raise AvbError('Invalid product key certificate length.')
4131 output.write(struct.pack('<I', 1)) # Format Version
4132 output.write(intermediate_key_certificate)
4133 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08004134
Darren Krahnfccd64e2018-01-16 17:39:35 -08004135 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
4136 unlock_key_certificate, challenge_path,
4137 unlock_key_path, signing_helper,
4138 signing_helper_with_files):
4139 """Implements the 'make_atx_unlock_credential' command.
4140
4141 Android Things unlock credentials can be used to authorize the unlock of AVB
4142 on a device. These credentials are presented to an Android Things bootloader
4143 via the fastboot interface in response to a 16-byte challenge. This method
4144 creates all fields of the credential except the challenge signature field
4145 (which is the last field) and can optionally create the challenge signature
4146 field as well if a challenge and the unlock_key_path is provided.
4147
4148 Arguments:
4149 output: The credential will be written to this file on success.
4150 intermediate_key_certificate: A certificate file as output by
4151 make_atx_certificate with
4152 is_intermediate_authority set to true.
4153 unlock_key_certificate: A certificate file as output by
4154 make_atx_certificate with
4155 is_intermediate_authority set to false and the
4156 usage set to
4157 'com.google.android.things.vboot.unlock'.
4158 challenge_path: [optional] A path to the challenge to sign.
4159 unlock_key_path: [optional] A PEM file path with the unlock private key.
4160 signing_helper: Program which signs a hash and returns the signature.
4161 signing_helper_with_files: Same as signing_helper but uses files instead.
4162
4163 Raises:
4164 AvbError: If an argument is incorrect.
4165 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004166 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
4167 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08004168 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4169 raise AvbError('Invalid intermediate key certificate length.')
4170 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4171 raise AvbError('Invalid product key certificate length.')
4172 challenge = bytearray()
4173 if challenge_path:
4174 with open(challenge_path, 'r') as f:
4175 challenge = f.read()
4176 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
4177 raise AvbError('Invalid unlock challenge length.')
4178 output.write(struct.pack('<I', 1)) # Format Version
4179 output.write(intermediate_key_certificate)
4180 output.write(unlock_key_certificate)
4181 if challenge_path and unlock_key_path:
4182 signature = bytearray()
4183 padding_and_hash = bytearray()
4184 algorithm_name = 'SHA512_RSA4096'
4185 alg = ALGORITHMS[algorithm_name]
4186 hasher = hashlib.sha512()
4187 padding_and_hash.extend(alg.padding)
4188 hasher.update(challenge)
4189 padding_and_hash.extend(hasher.digest())
4190 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4191 algorithm_name,
4192 alg.signature_num_bytes, unlock_key_path,
4193 padding_and_hash))
4194 output.write(signature)
4195
David Zeuthen21e95262016-07-27 17:58:40 -04004196
4197def calc_hash_level_offsets(image_size, block_size, digest_size):
4198 """Calculate the offsets of all the hash-levels in a Merkle-tree.
4199
4200 Arguments:
4201 image_size: The size of the image to calculate a Merkle-tree for.
4202 block_size: The block size, e.g. 4096.
4203 digest_size: The size of each hash, e.g. 32 for SHA-256.
4204
4205 Returns:
4206 A tuple where the first argument is an array of offsets and the
4207 second is size of the tree, in bytes.
4208 """
4209 level_offsets = []
4210 level_sizes = []
4211 tree_size = 0
4212
4213 num_levels = 0
4214 size = image_size
4215 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004216 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04004217 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4218
4219 level_sizes.append(level_size)
4220 tree_size += level_size
4221 num_levels += 1
4222
4223 size = level_size
4224
4225 for n in range(0, num_levels):
4226 offset = 0
4227 for m in range(n + 1, num_levels):
4228 offset += level_sizes[m]
4229 level_offsets.append(offset)
4230
David Zeuthena4fee8b2016-08-22 15:20:43 -04004231 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004232
4233
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004234# See system/extras/libfec/include/fec/io.h for these definitions.
4235FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4236FEC_MAGIC = 0xfecfecfe
4237
4238
4239def calc_fec_data_size(image_size, num_roots):
4240 """Calculates how much space FEC data will take.
4241
Jan Monschfe00c0a2019-12-11 11:19:40 +01004242 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004243 image_size: The size of the image.
4244 num_roots: Number of roots.
4245
4246 Returns:
4247 The number of bytes needed for FEC for an image of the given size
4248 and with the requested number of FEC roots.
4249
4250 Raises:
4251 ValueError: If output from the 'fec' tool is invalid.
4252
4253 """
4254 p = subprocess.Popen(
4255 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4256 stdout=subprocess.PIPE,
4257 stderr=subprocess.PIPE)
4258 (pout, perr) = p.communicate()
4259 retcode = p.wait()
4260 if retcode != 0:
4261 raise ValueError('Error invoking fec: {}'.format(perr))
4262 return int(pout)
4263
4264
4265def generate_fec_data(image_filename, num_roots):
4266 """Generate FEC codes for an image.
4267
Jan Monschfe00c0a2019-12-11 11:19:40 +01004268 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004269 image_filename: The filename of the image.
4270 num_roots: Number of roots.
4271
4272 Returns:
4273 The FEC data blob.
4274
4275 Raises:
4276 ValueError: If output from the 'fec' tool is invalid.
4277 """
4278 fec_tmpfile = tempfile.NamedTemporaryFile()
4279 subprocess.check_call(
4280 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4281 fec_tmpfile.name],
4282 stderr=open(os.devnull))
4283 fec_data = fec_tmpfile.read()
4284 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4285 footer_data = fec_data[-footer_size:]
4286 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4287 footer_data)
4288 if magic != FEC_MAGIC:
4289 raise ValueError('Unexpected magic in FEC footer')
4290 return fec_data[0:fec_size]
4291
4292
David Zeuthen21e95262016-07-27 17:58:40 -04004293def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004294 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004295 """Generates a Merkle-tree for a file.
4296
Jan Monschfe00c0a2019-12-11 11:19:40 +01004297 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004298 image: The image, as a file.
4299 image_size: The size of the image.
4300 block_size: The block size, e.g. 4096.
4301 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4302 salt: The salt to use.
4303 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004304 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004305 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004306
4307 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004308 A tuple where the first element is the top-level hash and the
4309 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04004310 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004311 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004312 hash_src_offset = 0
4313 hash_src_size = image_size
4314 level_num = 0
4315 while hash_src_size > block_size:
4316 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004317 remaining = hash_src_size
4318 while remaining > 0:
4319 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004320 # Only read from the file for the first level - for subsequent
4321 # levels, access the array we're building.
4322 if level_num == 0:
4323 image.seek(hash_src_offset + hash_src_size - remaining)
4324 data = image.read(min(remaining, block_size))
4325 else:
4326 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4327 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004328 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004329
4330 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004331 if len(data) < block_size:
4332 hasher.update('\0' * (block_size - len(data)))
4333 level_output += hasher.digest()
4334 if digest_padding > 0:
4335 level_output += '\0' * digest_padding
4336
4337 padding_needed = (round_to_multiple(
4338 len(level_output), block_size) - len(level_output))
4339 level_output += '\0' * padding_needed
4340
David Zeuthena4fee8b2016-08-22 15:20:43 -04004341 # Copy level-output into resulting tree.
4342 offset = hash_level_offsets[level_num]
4343 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004344
David Zeuthena4fee8b2016-08-22 15:20:43 -04004345 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004346 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004347 level_num += 1
4348
4349 hasher = hashlib.new(name=hash_alg_name, string=salt)
4350 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004351 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04004352
4353
4354class AvbTool(object):
4355 """Object for avbtool command-line tool."""
4356
4357 def __init__(self):
4358 """Initializer method."""
4359 self.avb = Avb()
4360
4361 def _add_common_args(self, sub_parser):
4362 """Adds arguments used by several sub-commands.
4363
4364 Arguments:
4365 sub_parser: The parser to add arguments to.
4366 """
4367 sub_parser.add_argument('--algorithm',
4368 help='Algorithm to use (default: NONE)',
4369 metavar='ALGORITHM',
4370 default='NONE')
4371 sub_parser.add_argument('--key',
4372 help='Path to RSA private key file',
4373 metavar='KEY',
4374 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004375 sub_parser.add_argument('--signing_helper',
4376 help='Path to helper used for signing',
4377 metavar='APP',
4378 default=None,
4379 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004380 sub_parser.add_argument('--signing_helper_with_files',
4381 help='Path to helper used for signing using files',
4382 metavar='APP',
4383 default=None,
4384 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004385 sub_parser.add_argument('--public_key_metadata',
4386 help='Path to public key metadata file',
4387 metavar='KEY_METADATA',
4388 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004389 sub_parser.add_argument('--rollback_index',
4390 help='Rollback Index',
4391 type=parse_number,
4392 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004393 # This is used internally for unit tests. Do not include in --help output.
4394 sub_parser.add_argument('--internal_release_string',
4395 help=argparse.SUPPRESS)
4396 sub_parser.add_argument('--append_to_release_string',
4397 help='Text to append to release string',
4398 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004399 sub_parser.add_argument('--prop',
4400 help='Add property',
4401 metavar='KEY:VALUE',
4402 action='append')
4403 sub_parser.add_argument('--prop_from_file',
4404 help='Add property from file',
4405 metavar='KEY:PATH',
4406 action='append')
4407 sub_parser.add_argument('--kernel_cmdline',
4408 help='Add kernel cmdline',
4409 metavar='CMDLINE',
4410 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004411 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4412 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4413 # at some future point.
4414 sub_parser.add_argument('--setup_rootfs_from_kernel',
4415 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004416 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004417 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004418 type=argparse.FileType('rb'))
4419 sub_parser.add_argument('--include_descriptors_from_image',
4420 help='Include descriptors from image',
4421 metavar='IMAGE',
4422 action='append',
4423 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004424 sub_parser.add_argument('--print_required_libavb_version',
4425 help=('Don\'t store the footer - '
4426 'instead calculate the required libavb '
4427 'version for the given options.'),
4428 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004429 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4430 sub_parser.add_argument('--chain_partition',
4431 help='Allow signed integrity-data for partition',
4432 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4433 action='append')
4434 sub_parser.add_argument('--flags',
4435 help='VBMeta flags',
4436 type=parse_number,
4437 default=0)
4438 sub_parser.add_argument('--set_hashtree_disabled_flag',
4439 help='Set the HASHTREE_DISABLED flag',
4440 action='store_true')
4441
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004442 def _add_common_footer_args(self, sub_parser):
4443 """Adds arguments used by add_*_footer sub-commands.
4444
4445 Arguments:
4446 sub_parser: The parser to add arguments to.
4447 """
4448 sub_parser.add_argument('--use_persistent_digest',
4449 help='Use a persistent digest on device instead of '
4450 'storing the digest in the descriptor. This '
4451 'cannot be used with A/B so must be combined '
4452 'with --do_not_use_ab when an A/B suffix is '
4453 'expected at runtime.',
4454 action='store_true')
4455 sub_parser.add_argument('--do_not_use_ab',
4456 help='The partition does not use A/B even when an '
4457 'A/B suffix is present. This must not be used '
4458 'for vbmeta or chained partitions.',
4459 action='store_true')
4460
David Zeuthena5fd3a42017-02-27 16:38:54 -05004461 def _fixup_common_args(self, args):
4462 """Common fixups needed by subcommands.
4463
4464 Arguments:
4465 args: Arguments to modify.
4466
4467 Returns:
4468 The modified arguments.
4469 """
4470 if args.set_hashtree_disabled_flag:
4471 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4472 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004473
4474 def run(self, argv):
4475 """Command-line processor.
4476
4477 Arguments:
4478 argv: Pass sys.argv from main.
4479 """
4480 parser = argparse.ArgumentParser()
4481 subparsers = parser.add_subparsers(title='subcommands')
4482
4483 sub_parser = subparsers.add_parser('version',
4484 help='Prints version of avbtool.')
4485 sub_parser.set_defaults(func=self.version)
4486
4487 sub_parser = subparsers.add_parser('extract_public_key',
4488 help='Extract public key.')
4489 sub_parser.add_argument('--key',
4490 help='Path to RSA private key file',
4491 required=True)
4492 sub_parser.add_argument('--output',
4493 help='Output file name',
4494 type=argparse.FileType('wb'),
4495 required=True)
4496 sub_parser.set_defaults(func=self.extract_public_key)
4497
4498 sub_parser = subparsers.add_parser('make_vbmeta_image',
4499 help='Makes a vbmeta image.')
4500 sub_parser.add_argument('--output',
4501 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004502 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004503 sub_parser.add_argument('--padding_size',
4504 metavar='NUMBER',
4505 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004506 'its size is a multiple of NUMBER '
4507 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004508 type=parse_number,
4509 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004510 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004511 sub_parser.set_defaults(func=self.make_vbmeta_image)
4512
4513 sub_parser = subparsers.add_parser('add_hash_footer',
4514 help='Add hashes and footer to image.')
4515 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004516 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004517 type=argparse.FileType('rab+'))
4518 sub_parser.add_argument('--partition_size',
4519 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004520 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004521 sub_parser.add_argument('--partition_name',
4522 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004523 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004524 sub_parser.add_argument('--hash_algorithm',
4525 help='Hash algorithm to use (default: sha256)',
4526 default='sha256')
4527 sub_parser.add_argument('--salt',
4528 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004529 sub_parser.add_argument('--calc_max_image_size',
4530 help=('Don\'t store the footer - '
4531 'instead calculate the maximum image size '
4532 'leaving enough room for metadata with '
4533 'the given partition size.'),
4534 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004535 sub_parser.add_argument('--output_vbmeta_image',
4536 help='Also write vbmeta struct to file',
4537 type=argparse.FileType('wb'))
4538 sub_parser.add_argument('--do_not_append_vbmeta_image',
4539 help=('Do not append vbmeta struct or footer '
4540 'to the image'),
4541 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004542 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004543 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004544 sub_parser.set_defaults(func=self.add_hash_footer)
4545
David Zeuthenb1b994d2017-03-06 18:01:31 -05004546 sub_parser = subparsers.add_parser('append_vbmeta_image',
4547 help='Append vbmeta image to image.')
4548 sub_parser.add_argument('--image',
4549 help='Image to append vbmeta blob to',
4550 type=argparse.FileType('rab+'))
4551 sub_parser.add_argument('--partition_size',
4552 help='Partition size',
4553 type=parse_number,
4554 required=True)
4555 sub_parser.add_argument('--vbmeta_image',
4556 help='Image with vbmeta blob to append',
4557 type=argparse.FileType('rb'))
4558 sub_parser.set_defaults(func=self.append_vbmeta_image)
4559
Jan Monscheeb28b62019-12-05 16:17:09 +01004560 sub_parser = subparsers.add_parser(
4561 'add_hashtree_footer',
4562 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004563 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004564 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004565 type=argparse.FileType('rab+'))
4566 sub_parser.add_argument('--partition_size',
4567 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004568 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004569 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004570 sub_parser.add_argument('--partition_name',
4571 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004572 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004573 sub_parser.add_argument('--hash_algorithm',
4574 help='Hash algorithm to use (default: sha1)',
4575 default='sha1')
4576 sub_parser.add_argument('--salt',
4577 help='Salt in hex (default: /dev/urandom)')
4578 sub_parser.add_argument('--block_size',
4579 help='Block size (default: 4096)',
4580 type=parse_number,
4581 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004582 # TODO(zeuthen): The --generate_fec option was removed when we
4583 # moved to generating FEC by default. To avoid breaking existing
4584 # users needing to transition we simply just print a warning below
4585 # in add_hashtree_footer(). Remove this option and the warning at
4586 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004587 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004588 help=argparse.SUPPRESS,
4589 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004590 sub_parser.add_argument(
4591 '--do_not_generate_fec',
4592 help='Do not generate forward-error-correction codes',
4593 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004594 sub_parser.add_argument('--fec_num_roots',
4595 help='Number of roots for FEC (default: 2)',
4596 type=parse_number,
4597 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004598 sub_parser.add_argument('--calc_max_image_size',
4599 help=('Don\'t store the hashtree or footer - '
4600 'instead calculate the maximum image size '
4601 'leaving enough room for hashtree '
4602 'and metadata with the given partition '
4603 'size.'),
4604 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004605 sub_parser.add_argument('--output_vbmeta_image',
4606 help='Also write vbmeta struct to file',
4607 type=argparse.FileType('wb'))
4608 sub_parser.add_argument('--do_not_append_vbmeta_image',
4609 help=('Do not append vbmeta struct or footer '
4610 'to the image'),
4611 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004612 # This is different from --setup_rootfs_from_kernel insofar that
4613 # it doesn't take an IMAGE, the generated cmdline will be for the
4614 # hashtree we're adding.
4615 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4616 action='store_true',
4617 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004618 sub_parser.add_argument('--no_hashtree',
4619 action='store_true',
4620 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004621 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004622 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004623 sub_parser.set_defaults(func=self.add_hashtree_footer)
4624
4625 sub_parser = subparsers.add_parser('erase_footer',
4626 help='Erase footer from an image.')
4627 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004628 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004629 type=argparse.FileType('rwb+'),
4630 required=True)
4631 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004632 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004633 action='store_true')
4634 sub_parser.set_defaults(func=self.erase_footer)
4635
David Zeuthen1394f762019-04-30 10:20:11 -04004636 sub_parser = subparsers.add_parser('zero_hashtree',
4637 help='Zero out hashtree and FEC data.')
4638 sub_parser.add_argument('--image',
4639 help='Image with a footer',
4640 type=argparse.FileType('rwb+'),
4641 required=True)
4642 sub_parser.set_defaults(func=self.zero_hashtree)
4643
Jan Monscheeb28b62019-12-05 16:17:09 +01004644 sub_parser = subparsers.add_parser(
4645 'extract_vbmeta_image',
4646 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004647 sub_parser.add_argument('--image',
4648 help='Image with footer',
4649 type=argparse.FileType('rb'),
4650 required=True)
4651 sub_parser.add_argument('--output',
4652 help='Output file name',
4653 type=argparse.FileType('wb'))
4654 sub_parser.add_argument('--padding_size',
4655 metavar='NUMBER',
4656 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004657 'its size is a multiple of NUMBER '
4658 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004659 type=parse_number,
4660 default=0)
4661 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4662
David Zeuthen2bc232b2017-04-19 14:25:19 -04004663 sub_parser = subparsers.add_parser('resize_image',
4664 help='Resize image with a footer.')
4665 sub_parser.add_argument('--image',
4666 help='Image with a footer',
4667 type=argparse.FileType('rwb+'),
4668 required=True)
4669 sub_parser.add_argument('--partition_size',
4670 help='New partition size',
4671 type=parse_number)
4672 sub_parser.set_defaults(func=self.resize_image)
4673
David Zeuthen21e95262016-07-27 17:58:40 -04004674 sub_parser = subparsers.add_parser(
4675 'info_image',
4676 help='Show information about vbmeta or footer.')
4677 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004678 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004679 type=argparse.FileType('rb'),
4680 required=True)
4681 sub_parser.add_argument('--output',
4682 help='Write info to file',
4683 type=argparse.FileType('wt'),
4684 default=sys.stdout)
4685 sub_parser.set_defaults(func=self.info_image)
4686
David Zeuthenb623d8b2017-04-04 16:05:53 -04004687 sub_parser = subparsers.add_parser(
4688 'verify_image',
4689 help='Verify an image.')
4690 sub_parser.add_argument('--image',
4691 help='Image to verify',
4692 type=argparse.FileType('rb'),
4693 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004694 sub_parser.add_argument('--key',
4695 help='Check embedded public key matches KEY',
4696 metavar='KEY',
4697 required=False)
4698 sub_parser.add_argument('--expected_chain_partition',
4699 help='Expected chain partition',
4700 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4701 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004702 sub_parser.add_argument(
4703 '--follow_chain_partitions',
4704 help=('Follows chain partitions even when not '
4705 'specified with the --expected_chain_partition option'),
4706 action='store_true')
4707 sub_parser.add_argument(
4708 '--accept_zeroed_hashtree',
4709 help=('Accept images where the hashtree or FEC data is zeroed out'),
4710 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004711 sub_parser.set_defaults(func=self.verify_image)
4712
David Zeuthenb8643c02018-05-17 17:21:18 -04004713 sub_parser = subparsers.add_parser(
4714 'calculate_vbmeta_digest',
4715 help='Calculate vbmeta digest.')
4716 sub_parser.add_argument('--image',
4717 help='Image to calculate digest for',
4718 type=argparse.FileType('rb'),
4719 required=True)
4720 sub_parser.add_argument('--hash_algorithm',
4721 help='Hash algorithm to use (default: sha256)',
4722 default='sha256')
4723 sub_parser.add_argument('--output',
4724 help='Write hex digest to file (default: stdout)',
4725 type=argparse.FileType('wt'),
4726 default=sys.stdout)
4727 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4728
David Zeuthenf7d2e752018-09-20 13:30:41 -04004729 sub_parser = subparsers.add_parser(
4730 'calculate_kernel_cmdline',
4731 help='Calculate kernel cmdline.')
4732 sub_parser.add_argument('--image',
4733 help='Image to calculate kernel cmdline for',
4734 type=argparse.FileType('rb'),
4735 required=True)
4736 sub_parser.add_argument('--hashtree_disabled',
4737 help='Return the cmdline for hashtree disabled',
4738 action='store_true')
4739 sub_parser.add_argument('--output',
4740 help='Write cmdline to file (default: stdout)',
4741 type=argparse.FileType('wt'),
4742 default=sys.stdout)
4743 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4744
David Zeuthen8b6973b2016-09-20 12:39:49 -04004745 sub_parser = subparsers.add_parser('set_ab_metadata',
4746 help='Set A/B metadata.')
4747 sub_parser.add_argument('--misc_image',
4748 help=('The misc image to modify. If the image does '
4749 'not exist, it will be created.'),
4750 type=argparse.FileType('r+b'),
4751 required=True)
4752 sub_parser.add_argument('--slot_data',
4753 help=('Slot data of the form "priority", '
4754 '"tries_remaining", "sucessful_boot" for '
4755 'slot A followed by the same for slot B, '
4756 'separated by colons. The default value '
4757 'is 15:7:0:14:7:0.'),
4758 default='15:7:0:14:7:0')
4759 sub_parser.set_defaults(func=self.set_ab_metadata)
4760
Darren Krahn147b08d2016-12-20 16:38:29 -08004761 sub_parser = subparsers.add_parser(
4762 'make_atx_certificate',
4763 help='Create an Android Things eXtension (ATX) certificate.')
4764 sub_parser.add_argument('--output',
4765 help='Write certificate to file',
4766 type=argparse.FileType('wb'),
4767 default=sys.stdout)
4768 sub_parser.add_argument('--subject',
4769 help=('Path to subject file'),
4770 type=argparse.FileType('rb'),
4771 required=True)
4772 sub_parser.add_argument('--subject_key',
4773 help=('Path to subject RSA public key file'),
4774 type=argparse.FileType('rb'),
4775 required=True)
4776 sub_parser.add_argument('--subject_key_version',
4777 help=('Version of the subject key'),
4778 type=parse_number,
4779 required=False)
4780 sub_parser.add_argument('--subject_is_intermediate_authority',
4781 help=('Generate an intermediate authority '
4782 'certificate'),
4783 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004784 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004785 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004786 'string'),
4787 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004788 sub_parser.add_argument('--authority_key',
4789 help='Path to authority RSA private key file',
4790 required=False)
4791 sub_parser.add_argument('--signing_helper',
4792 help='Path to helper used for signing',
4793 metavar='APP',
4794 default=None,
4795 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004796 sub_parser.add_argument('--signing_helper_with_files',
4797 help='Path to helper used for signing using files',
4798 metavar='APP',
4799 default=None,
4800 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004801 sub_parser.set_defaults(func=self.make_atx_certificate)
4802
4803 sub_parser = subparsers.add_parser(
4804 'make_atx_permanent_attributes',
4805 help='Create Android Things eXtension (ATX) permanent attributes.')
4806 sub_parser.add_argument('--output',
4807 help='Write attributes to file',
4808 type=argparse.FileType('wb'),
4809 default=sys.stdout)
4810 sub_parser.add_argument('--root_authority_key',
4811 help='Path to authority RSA public key file',
4812 type=argparse.FileType('rb'),
4813 required=True)
4814 sub_parser.add_argument('--product_id',
4815 help=('Path to Product ID file'),
4816 type=argparse.FileType('rb'),
4817 required=True)
4818 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4819
4820 sub_parser = subparsers.add_parser(
4821 'make_atx_metadata',
4822 help='Create Android Things eXtension (ATX) metadata.')
4823 sub_parser.add_argument('--output',
4824 help='Write metadata to file',
4825 type=argparse.FileType('wb'),
4826 default=sys.stdout)
4827 sub_parser.add_argument('--intermediate_key_certificate',
4828 help='Path to intermediate key certificate file',
4829 type=argparse.FileType('rb'),
4830 required=True)
4831 sub_parser.add_argument('--product_key_certificate',
4832 help='Path to product key certificate file',
4833 type=argparse.FileType('rb'),
4834 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004835 sub_parser.set_defaults(func=self.make_atx_metadata)
4836
Darren Krahnfccd64e2018-01-16 17:39:35 -08004837 sub_parser = subparsers.add_parser(
4838 'make_atx_unlock_credential',
4839 help='Create an Android Things eXtension (ATX) unlock credential.')
4840 sub_parser.add_argument('--output',
4841 help='Write credential to file',
4842 type=argparse.FileType('wb'),
4843 default=sys.stdout)
4844 sub_parser.add_argument('--intermediate_key_certificate',
4845 help='Path to intermediate key certificate file',
4846 type=argparse.FileType('rb'),
4847 required=True)
4848 sub_parser.add_argument('--unlock_key_certificate',
4849 help='Path to unlock key certificate file',
4850 type=argparse.FileType('rb'),
4851 required=True)
4852 sub_parser.add_argument('--challenge',
4853 help='Path to the challenge to sign (optional). If '
4854 'this is not provided the challenge signature '
4855 'field is omitted and can be concatenated '
4856 'later.',
4857 required=False)
4858 sub_parser.add_argument('--unlock_key',
4859 help='Path to unlock key (optional). Must be '
4860 'provided if using --challenge.',
4861 required=False)
4862 sub_parser.add_argument('--signing_helper',
4863 help='Path to helper used for signing',
4864 metavar='APP',
4865 default=None,
4866 required=False)
4867 sub_parser.add_argument('--signing_helper_with_files',
4868 help='Path to helper used for signing using files',
4869 metavar='APP',
4870 default=None,
4871 required=False)
4872 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4873
David Zeuthen21e95262016-07-27 17:58:40 -04004874 args = parser.parse_args(argv[1:])
4875 try:
4876 args.func(args)
4877 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004878 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004879 sys.exit(1)
4880
4881 def version(self, _):
4882 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004883 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004884
4885 def extract_public_key(self, args):
4886 """Implements the 'extract_public_key' sub-command."""
4887 self.avb.extract_public_key(args.key, args.output)
4888
4889 def make_vbmeta_image(self, args):
4890 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004891 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004892 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004893 args.algorithm, args.key,
4894 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004895 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004896 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004897 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004898 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004899 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004900 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004901 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004902 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004903 args.print_required_libavb_version,
4904 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004905
David Zeuthenb1b994d2017-03-06 18:01:31 -05004906 def append_vbmeta_image(self, args):
4907 """Implements the 'append_vbmeta_image' sub-command."""
4908 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4909 args.partition_size)
4910
David Zeuthen21e95262016-07-27 17:58:40 -04004911 def add_hash_footer(self, args):
4912 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004913 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004914 self.avb.add_hash_footer(args.image.name if args.image else None,
4915 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004916 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004917 args.salt, args.chain_partition, args.algorithm,
4918 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004919 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004920 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004921 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004922 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004923 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004924 args.calc_max_image_size,
4925 args.signing_helper,
4926 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004927 args.internal_release_string,
4928 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004929 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004930 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004931 args.print_required_libavb_version,
4932 args.use_persistent_digest,
4933 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004934
4935 def add_hashtree_footer(self, args):
4936 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004937 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004938 # TODO(zeuthen): Remove when removing support for the
4939 # '--generate_fec' option above.
4940 if args.generate_fec:
4941 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4942 'is now generated by default. Use the option '
4943 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004944 self.avb.add_hashtree_footer(
4945 args.image.name if args.image else None,
4946 args.partition_size,
4947 args.partition_name,
4948 not args.do_not_generate_fec, args.fec_num_roots,
4949 args.hash_algorithm, args.block_size,
4950 args.salt, args.chain_partition, args.algorithm,
4951 args.key, args.public_key_metadata,
4952 args.rollback_index, args.flags, args.prop,
4953 args.prop_from_file,
4954 args.kernel_cmdline,
4955 args.setup_rootfs_from_kernel,
4956 args.setup_as_rootfs_from_kernel,
4957 args.include_descriptors_from_image,
4958 args.calc_max_image_size,
4959 args.signing_helper,
4960 args.signing_helper_with_files,
4961 args.internal_release_string,
4962 args.append_to_release_string,
4963 args.output_vbmeta_image,
4964 args.do_not_append_vbmeta_image,
4965 args.print_required_libavb_version,
4966 args.use_persistent_digest,
4967 args.do_not_use_ab,
4968 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004969
David Zeuthen21e95262016-07-27 17:58:40 -04004970 def erase_footer(self, args):
4971 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004972 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004973
David Zeuthen1394f762019-04-30 10:20:11 -04004974 def zero_hashtree(self, args):
4975 """Implements the 'zero_hashtree' sub-command."""
4976 self.avb.zero_hashtree(args.image.name)
4977
David Zeuthen49936b42018-08-07 17:38:58 -04004978 def extract_vbmeta_image(self, args):
4979 """Implements the 'extract_vbmeta_image' sub-command."""
4980 self.avb.extract_vbmeta_image(args.output, args.image.name,
4981 args.padding_size)
4982
David Zeuthen2bc232b2017-04-19 14:25:19 -04004983 def resize_image(self, args):
4984 """Implements the 'resize_image' sub-command."""
4985 self.avb.resize_image(args.image.name, args.partition_size)
4986
David Zeuthen8b6973b2016-09-20 12:39:49 -04004987 def set_ab_metadata(self, args):
4988 """Implements the 'set_ab_metadata' sub-command."""
4989 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4990
David Zeuthen21e95262016-07-27 17:58:40 -04004991 def info_image(self, args):
4992 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004993 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004994
David Zeuthenb623d8b2017-04-04 16:05:53 -04004995 def verify_image(self, args):
4996 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004997 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004998 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004999 args.follow_chain_partitions,
5000 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04005001
David Zeuthenb8643c02018-05-17 17:21:18 -04005002 def calculate_vbmeta_digest(self, args):
5003 """Implements the 'calculate_vbmeta_digest' sub-command."""
5004 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
5005 args.output)
5006
David Zeuthenf7d2e752018-09-20 13:30:41 -04005007 def calculate_kernel_cmdline(self, args):
5008 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01005009 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
5010 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04005011
Darren Krahn147b08d2016-12-20 16:38:29 -08005012 def make_atx_certificate(self, args):
5013 """Implements the 'make_atx_certificate' sub-command."""
5014 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04005015 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005016 args.subject_key_version,
5017 args.subject.read(),
5018 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08005019 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04005020 args.signing_helper,
5021 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08005022
5023 def make_atx_permanent_attributes(self, args):
5024 """Implements the 'make_atx_permanent_attributes' sub-command."""
5025 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04005026 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005027 args.product_id.read())
5028
5029 def make_atx_metadata(self, args):
5030 """Implements the 'make_atx_metadata' sub-command."""
5031 self.avb.make_atx_metadata(args.output,
5032 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08005033 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08005034
Darren Krahnfccd64e2018-01-16 17:39:35 -08005035 def make_atx_unlock_credential(self, args):
5036 """Implements the 'make_atx_unlock_credential' sub-command."""
5037 self.avb.make_atx_unlock_credential(
5038 args.output,
5039 args.intermediate_key_certificate.read(),
5040 args.unlock_key_certificate.read(),
5041 args.challenge,
5042 args.unlock_key,
5043 args.signing_helper,
5044 args.signing_helper_with_files)
5045
David Zeuthen21e95262016-07-27 17:58:40 -04005046
5047if __name__ == '__main__':
5048 tool = AvbTool()
5049 tool.run(sys.argv)