blob: f421a3a8a47da038818596cfd949abc60451364a [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040033import math
David Zeuthen21e95262016-07-27 17:58:40 -040034import os
35import struct
36import subprocess
37import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040038import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080039import time
David Zeuthen21e95262016-07-27 17:58:40 -040040
David Zeuthene3cadca2017-02-22 21:25:46 -050041# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040042AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080043AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
Jan Monsch2c7be992020-04-03 14:37:13 +020052# Configuration for enabling logging of calls to avbtool.
53AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
54
David Zeuthene3cadca2017-02-22 21:25:46 -050055
David Zeuthen21e95262016-07-27 17:58:40 -040056class AvbError(Exception):
57 """Application-specific errors.
58
59 These errors represent issues for which a stack-trace should not be
60 presented.
61
62 Attributes:
63 message: Error message.
64 """
65
66 def __init__(self, message):
67 Exception.__init__(self, message)
68
69
70class Algorithm(object):
71 """Contains details about an algorithm.
72
Tao Bao80418a52018-07-20 11:41:22 -070073 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040074
75 The constant |ALGORITHMS| is a dictionary from human-readable
76 names (e.g 'SHA256_RSA2048') to instances of this class.
77
78 Attributes:
79 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040080 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040081 hash_num_bytes: Number of bytes used to store the hash.
82 signature_num_bytes: Number of bytes used to store the signature.
83 public_key_num_bytes: Number of bytes used to store the public key.
84 padding: Padding used for signature, if any.
85 """
86
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
88 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040089 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040090 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040091 self.hash_num_bytes = hash_num_bytes
92 self.signature_num_bytes = signature_num_bytes
93 self.public_key_num_bytes = public_key_num_bytes
94 self.padding = padding
95
David Zeuthenb623d8b2017-04-04 16:05:53 -040096
David Zeuthen21e95262016-07-27 17:58:40 -040097# This must be kept in sync with the avb_crypto.h file.
98#
99# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
100# obtained from section 5.2.2 of RFC 4880.
101ALGORITHMS = {
102 'NONE': Algorithm(
103 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400104 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400105 hash_num_bytes=0,
106 signature_num_bytes=0,
107 public_key_num_bytes=0,
108 padding=[]),
109 'SHA256_RSA2048': Algorithm(
110 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400111 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400112 hash_num_bytes=32,
113 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100114 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*202 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA4096': Algorithm(
124 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400125 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400126 hash_num_bytes=32,
127 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100128 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*458 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA256_RSA8192': Algorithm(
138 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400139 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400140 hash_num_bytes=32,
141 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100142 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400143 padding=[
144 # PKCS1-v1_5 padding
145 0x00, 0x01] + [0xff]*970 + [0x00] + [
146 # ASN.1 header
147 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
148 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
149 0x00, 0x04, 0x20,
150 ]),
151 'SHA512_RSA2048': Algorithm(
152 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400153 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400154 hash_num_bytes=64,
155 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100156 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400157 padding=[
158 # PKCS1-v1_5 padding
159 0x00, 0x01] + [0xff]*170 + [0x00] + [
160 # ASN.1 header
161 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
162 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
163 0x00, 0x04, 0x40
164 ]),
165 'SHA512_RSA4096': Algorithm(
166 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400167 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400168 hash_num_bytes=64,
169 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100170 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400171 padding=[
172 # PKCS1-v1_5 padding
173 0x00, 0x01] + [0xff]*426 + [0x00] + [
174 # ASN.1 header
175 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
176 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
177 0x00, 0x04, 0x40
178 ]),
179 'SHA512_RSA8192': Algorithm(
180 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400181 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400182 hash_num_bytes=64,
183 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100184 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400185 padding=[
186 # PKCS1-v1_5 padding
187 0x00, 0x01] + [0xff]*938 + [0x00] + [
188 # ASN.1 header
189 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
190 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
191 0x00, 0x04, 0x40
192 ]),
193}
194
195
David Zeuthene3cadca2017-02-22 21:25:46 -0500196def get_release_string():
197 """Calculates the release string to use in the VBMeta struct."""
198 # Keep in sync with libavb/avb_version.c:avb_version_string().
199 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
200 AVB_VERSION_MINOR,
201 AVB_VERSION_SUB)
202
203
David Zeuthen21e95262016-07-27 17:58:40 -0400204def round_to_multiple(number, size):
205 """Rounds a number up to nearest multiple of another number.
206
Jan Monschfe00c0a2019-12-11 11:19:40 +0100207 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400208 number: The number to round up.
209 size: The multiple to round up to.
210
211 Returns:
212 If |number| is a multiple of |size|, returns |number|, otherwise
213 returns |number| + |size|.
214 """
215 remainder = number % size
216 if remainder == 0:
217 return number
218 return number + size - remainder
219
220
221def round_to_pow2(number):
222 """Rounds a number up to the next power of 2.
223
Jan Monschfe00c0a2019-12-11 11:19:40 +0100224 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400225 number: The number to round up.
226
227 Returns:
228 If |number| is already a power of 2 then |number| is
229 returned. Otherwise the smallest power of 2 greater than |number|
230 is returned.
231 """
232 return 2**((number - 1).bit_length())
233
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235def encode_long(num_bits, value):
236 """Encodes a long to a bytearray() using a given amount of bits.
237
238 This number is written big-endian, e.g. with the most significant
239 bit first.
240
David Zeuthenb623d8b2017-04-04 16:05:53 -0400241 This is the reverse of decode_long().
242
David Zeuthen21e95262016-07-27 17:58:40 -0400243 Arguments:
244 num_bits: The number of bits to write, e.g. 2048.
245 value: The value to write.
246
247 Returns:
248 A bytearray() with the encoded long.
249 """
250 ret = bytearray()
251 for bit_pos in range(num_bits, 0, -8):
252 octet = (value >> (bit_pos - 8)) & 0xff
253 ret.extend(struct.pack('!B', octet))
254 return ret
255
256
David Zeuthenb623d8b2017-04-04 16:05:53 -0400257def decode_long(blob):
258 """Decodes a long from a bytearray() using a given amount of bits.
259
260 This number is expected to be in big-endian, e.g. with the most
261 significant bit first.
262
263 This is the reverse of encode_long().
264
265 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100266 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400267
268 Returns:
269 The decoded value.
270 """
271 ret = 0
272 for b in bytearray(blob):
273 ret *= 256
274 ret += b
275 return ret
276
277
David Zeuthen21e95262016-07-27 17:58:40 -0400278def egcd(a, b):
279 """Calculate greatest common divisor of two numbers.
280
281 This implementation uses a recursive version of the extended
282 Euclidian algorithm.
283
284 Arguments:
285 a: First number.
286 b: Second number.
287
288 Returns:
289 A tuple (gcd, x, y) that where |gcd| is the greatest common
290 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
291 """
292 if a == 0:
293 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100294 g, y, x = egcd(b % a, a)
295 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400296
297
298def modinv(a, m):
299 """Calculate modular multiplicative inverse of |a| modulo |m|.
300
301 This calculates the number |x| such that |a| * |x| == 1 (modulo
302 |m|). This number only exists if |a| and |m| are co-prime - |None|
303 is returned if this isn't true.
304
305 Arguments:
306 a: The number to calculate a modular inverse of.
307 m: The modulo to use.
308
309 Returns:
310 The modular multiplicative inverse of |a| and |m| or |None| if
311 these numbers are not co-prime.
312 """
313 gcd, x, _ = egcd(a, m)
314 if gcd != 1:
315 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100316 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400317
318
319def parse_number(string):
320 """Parse a string as a number.
321
322 This is just a short-hand for int(string, 0) suitable for use in the
323 |type| parameter of |ArgumentParser|'s add_argument() function. An
324 improvement to just using type=int is that this function supports
325 numbers in other bases, e.g. "0x1234".
326
327 Arguments:
328 string: The string to parse.
329
330 Returns:
331 The parsed integer.
332
333 Raises:
334 ValueError: If the number could not be parsed.
335 """
336 return int(string, 0)
337
338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339class RSAPublicKey(object):
340 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400341
David Zeuthenc68f0822017-03-31 17:22:35 -0400342 Attributes:
343 exponent: The key exponent.
344 modulus: The key modulus.
345 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400346 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400347
348 MODULUS_PREFIX = 'modulus='
349
350 def __init__(self, key_path):
351 """Loads and parses an RSA key from either a private or public key file.
352
353 Arguments:
354 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100355
356 Raises:
357 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400358 """
359 # We used to have something as simple as this:
360 #
361 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
362 # self.exponent = key.e
363 # self.modulus = key.n
364 # self.num_bits = key.size() + 1
365 #
366 # but unfortunately PyCrypto is not available in the builder. So
367 # instead just parse openssl(1) output to get this
368 # information. It's ugly but...
369 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
370 p = subprocess.Popen(args,
371 stdin=subprocess.PIPE,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE)
374 (pout, perr) = p.communicate()
375 if p.wait() != 0:
376 # Could be just a public key is passed, try that.
377 args.append('-pubin')
378 p = subprocess.Popen(args,
379 stdin=subprocess.PIPE,
380 stdout=subprocess.PIPE,
381 stderr=subprocess.PIPE)
382 (pout, perr) = p.communicate()
383 if p.wait() != 0:
384 raise AvbError('Error getting public key: {}'.format(perr))
385
386 if not pout.lower().startswith(self.MODULUS_PREFIX):
387 raise AvbError('Unexpected modulus output')
388
389 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
390
391 # The exponent is assumed to always be 65537 and the number of
392 # bits can be derived from the modulus by rounding up to the
393 # nearest power of 2.
394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
398
David Zeuthenc68f0822017-03-31 17:22:35 -0400399def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400400 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
401
402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
404
405 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
408 Returns:
409 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100410
411 Raises:
412 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400413 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 key = RSAPublicKey(key_path)
415 if key.exponent != 65537:
416 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400417 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monsch23e0c622019-12-11 11:23:58 +0100419 b = 2L**32 # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400421 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monsch23e0c622019-12-11 11:23:58 +0100422 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400423 rrmodn = r * r % key.modulus
424 ret.extend(struct.pack('!II', key.num_bits, n0inv))
425 ret.extend(encode_long(key.num_bits, key.modulus))
426 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400427 return ret
428
429
430def lookup_algorithm_by_type(alg_type):
431 """Looks up algorithm by type.
432
433 Arguments:
434 alg_type: The integer representing the type.
435
436 Returns:
437 A tuple with the algorithm name and an |Algorithm| instance.
438
439 Raises:
440 Exception: If the algorithm cannot be found
441 """
442 for alg_name in ALGORITHMS:
443 alg_data = ALGORITHMS[alg_name]
444 if alg_data.algorithm_type == alg_type:
445 return (alg_name, alg_data)
446 raise AvbError('Unknown algorithm type {}'.format(alg_type))
447
Jan Monsch77cd2022019-12-10 17:18:04 +0100448
Dan Austina7bc4962019-12-02 13:26:08 -0800449def lookup_hash_size_by_type(alg_type):
450 """Looks up hash size by type.
451
452 Arguments:
453 alg_type: The integer representing the type.
454
455 Returns:
456 The corresponding hash size.
457
458 Raises:
459 AvbError: If the algorithm cannot be found.
460 """
461 for alg_name in ALGORITHMS:
462 alg_data = ALGORITHMS[alg_name]
463 if alg_data.algorithm_type == alg_type:
464 return alg_data.hash_num_bytes
465 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400466
Jan Monsch77cd2022019-12-10 17:18:04 +0100467
David Zeuthena156d3d2017-06-01 12:08:09 -0400468def raw_sign(signing_helper, signing_helper_with_files,
469 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900470 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 """Computes a raw RSA signature using |signing_helper| or openssl.
472
473 Arguments:
474 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400475 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800476 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900477 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800478 key_path: Path to the private key file. Must be PEM format.
479 raw_data_to_sign: Data to sign (bytearray or str expected).
480
481 Returns:
482 A bytearray containing the signature.
483
484 Raises:
485 Exception: If an error occurs.
486 """
487 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400488 if signing_helper_with_files is not None:
489 signing_file = tempfile.NamedTemporaryFile()
490 signing_file.write(str(raw_data_to_sign))
491 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100492 p = subprocess.Popen([
493 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400494 retcode = p.wait()
495 if retcode != 0:
496 raise AvbError('Error signing')
497 signing_file.seek(0)
498 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800499 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400500 if signing_helper is not None:
501 p = subprocess.Popen(
502 [signing_helper, algorithm_name, key_path],
503 stdin=subprocess.PIPE,
504 stdout=subprocess.PIPE,
505 stderr=subprocess.PIPE)
506 else:
507 p = subprocess.Popen(
508 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
509 stdin=subprocess.PIPE,
510 stdout=subprocess.PIPE,
511 stderr=subprocess.PIPE)
512 (pout, perr) = p.communicate(str(raw_data_to_sign))
513 retcode = p.wait()
514 if retcode != 0:
515 raise AvbError('Error signing: {}'.format(perr))
516 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900517 if len(signature) != signature_num_bytes:
518 raise AvbError('Error signing: Invalid length of signature')
519 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800520
521
David Zeuthenb623d8b2017-04-04 16:05:53 -0400522def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100523 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400524
525 Arguments:
526 vbmeta_header: A AvbVBMetaHeader.
527 vbmeta_blob: The whole vbmeta blob, including the header.
528
529 Returns:
530 True if the signature is valid and corresponds to the embedded
531 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100532
533 Raises:
534 AvbError: If there errors calling out to openssl command during
535 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536 """
537 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100538 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400539 return True
540 header_blob = vbmeta_blob[0:256]
541 auth_offset = 256
542 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
543 aux_size = vbmeta_header.auxiliary_data_block_size
544 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
545 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
546 pubkey_size = vbmeta_header.public_key_size
547 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
548
549 digest_offset = auth_offset + vbmeta_header.hash_offset
550 digest_size = vbmeta_header.hash_size
551 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
552
553 sig_offset = auth_offset + vbmeta_header.signature_offset
554 sig_size = vbmeta_header.signature_size
555 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
556
557 # Now that we've got the stored digest, public key, and signature
558 # all we need to do is to verify. This is the exactly the same
559 # steps as performed in the avb_vbmeta_image_verify() function in
560 # libavb/avb_vbmeta_image.c.
561
562 ha = hashlib.new(alg.hash_name)
563 ha.update(header_blob)
564 ha.update(aux_blob)
565 computed_digest = ha.digest()
566
567 if computed_digest != digest_blob:
568 return False
569
570 padding_and_digest = bytearray(alg.padding)
571 padding_and_digest.extend(computed_digest)
572
573 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100574 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400575 modulus = decode_long(modulus_blob)
576 exponent = 65537
577
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500578 # We used to have this:
579 #
580 # import Crypto.PublicKey.RSA
581 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
582 # if not key.verify(decode_long(padding_and_digest),
583 # (decode_long(sig_blob), None)):
584 # return False
585 # return True
586 #
587 # but since 'avbtool verify_image' is used on the builders we don't want
588 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
589 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
590 '\n'
591 '[pubkeyinfo]\n'
592 'algorithm=SEQUENCE:rsa_alg\n'
593 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
594 '\n'
595 '[rsa_alg]\n'
596 'algorithm=OID:rsaEncryption\n'
597 'parameter=NULL\n'
598 '\n'
599 '[rsapubkey]\n'
600 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100601 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
602 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500603 asn1_tmpfile = tempfile.NamedTemporaryFile()
604 asn1_tmpfile.write(asn1_str)
605 asn1_tmpfile.flush()
606 der_tmpfile = tempfile.NamedTemporaryFile()
607 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100608 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
609 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500610 retcode = p.wait()
611 if retcode != 0:
612 raise AvbError('Error generating DER file')
613
614 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100615 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
616 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500617 stdin=subprocess.PIPE,
618 stdout=subprocess.PIPE,
619 stderr=subprocess.PIPE)
620 (pout, perr) = p.communicate(str(sig_blob))
621 retcode = p.wait()
622 if retcode != 0:
623 raise AvbError('Error verifying data: {}'.format(perr))
624 recovered_data = bytearray(pout)
625 if recovered_data != padding_and_digest:
626 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400627 return False
628 return True
629
630
David Zeuthena4fee8b2016-08-22 15:20:43 -0400631class ImageChunk(object):
632 """Data structure used for representing chunks in Android sparse files.
633
634 Attributes:
635 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
636 chunk_offset: Offset in the sparse file where this chunk begins.
637 output_offset: Offset in de-sparsified file where output begins.
638 output_size: Number of bytes in output.
639 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
640 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
641 """
642
643 FORMAT = '<2H2I'
644 TYPE_RAW = 0xcac1
645 TYPE_FILL = 0xcac2
646 TYPE_DONT_CARE = 0xcac3
647 TYPE_CRC32 = 0xcac4
648
649 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
650 input_offset, fill_data):
651 """Initializes an ImageChunk object.
652
653 Arguments:
654 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
655 chunk_offset: Offset in the sparse file where this chunk begins.
656 output_offset: Offset in de-sparsified file.
657 output_size: Number of bytes in output.
658 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
659 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
660
661 Raises:
662 ValueError: If data is not well-formed.
663 """
664 self.chunk_type = chunk_type
665 self.chunk_offset = chunk_offset
666 self.output_offset = output_offset
667 self.output_size = output_size
668 self.input_offset = input_offset
669 self.fill_data = fill_data
670 # Check invariants.
671 if self.chunk_type == self.TYPE_RAW:
672 if self.fill_data is not None:
673 raise ValueError('RAW chunk cannot have fill_data set.')
674 if not self.input_offset:
675 raise ValueError('RAW chunk must have input_offset set.')
676 elif self.chunk_type == self.TYPE_FILL:
677 if self.fill_data is None:
678 raise ValueError('FILL chunk must have fill_data set.')
679 if self.input_offset:
680 raise ValueError('FILL chunk cannot have input_offset set.')
681 elif self.chunk_type == self.TYPE_DONT_CARE:
682 if self.fill_data is not None:
683 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
684 if self.input_offset:
685 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
686 else:
687 raise ValueError('Invalid chunk type')
688
689
690class ImageHandler(object):
691 """Abstraction for image I/O with support for Android sparse images.
692
693 This class provides an interface for working with image files that
694 may be using the Android Sparse Image format. When an instance is
695 constructed, we test whether it's an Android sparse file. If so,
696 operations will be on the sparse file by interpreting the sparse
697 format, otherwise they will be directly on the file. Either way the
698 operations do the same.
699
700 For reading, this interface mimics a file object - it has seek(),
701 tell(), and read() methods. For writing, only truncation
702 (truncate()) and appending is supported (append_raw() and
703 append_dont_care()). Additionally, data can only be written in units
704 of the block size.
705
706 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400707 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400708 is_sparse: Whether the file being operated on is sparse.
709 block_size: The block size, typically 4096.
710 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400711 """
712 # See system/core/libsparse/sparse_format.h for details.
713 MAGIC = 0xed26ff3a
714 HEADER_FORMAT = '<I4H4I'
715
716 # These are formats and offset of just the |total_chunks| and
717 # |total_blocks| fields.
718 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
719 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
720
721 def __init__(self, image_filename):
722 """Initializes an image handler.
723
724 Arguments:
725 image_filename: The name of the file to operate on.
726
727 Raises:
728 ValueError: If data in the file is invalid.
729 """
David Zeuthen49936b42018-08-07 17:38:58 -0400730 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100731 self._num_total_blocks = 0
732 self._num_total_chunks = 0
733 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400734 self._read_header()
735
736 def _read_header(self):
737 """Initializes internal data structures used for reading file.
738
739 This may be called multiple times and is typically called after
740 modifying the file (e.g. appending, truncation).
741
742 Raises:
743 ValueError: If data in the file is invalid.
744 """
745 self.is_sparse = False
746 self.block_size = 4096
747 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400748 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400749 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400750 self.image_size = self._image.tell()
751
752 self._image.seek(0, os.SEEK_SET)
753 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
754 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
755 block_size, self._num_total_blocks, self._num_total_chunks,
756 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
757 if magic != self.MAGIC:
758 # Not a sparse image, our job here is done.
759 return
760 if not (major_version == 1 and minor_version == 0):
761 raise ValueError('Encountered sparse image format version {}.{} but '
762 'only 1.0 is supported'.format(major_version,
763 minor_version))
764 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
765 raise ValueError('Unexpected file_hdr_sz value {}.'.
766 format(file_hdr_sz))
767 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
768 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
769 format(chunk_hdr_sz))
770
771 self.block_size = block_size
772
773 # Build an list of chunks by parsing the file.
774 self._chunks = []
775
776 # Find the smallest offset where only "Don't care" chunks
777 # follow. This will be the size of the content in the sparse
778 # image.
779 offset = 0
780 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100781 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400782 chunk_offset = self._image.tell()
783
784 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
785 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
786 header_bin)
787 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
788
David Zeuthena4fee8b2016-08-22 15:20:43 -0400789 if chunk_type == ImageChunk.TYPE_RAW:
790 if data_sz != (chunk_sz * self.block_size):
791 raise ValueError('Raw chunk input size ({}) does not match output '
792 'size ({})'.
793 format(data_sz, chunk_sz*self.block_size))
794 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
795 chunk_offset,
796 output_offset,
797 chunk_sz*self.block_size,
798 self._image.tell(),
799 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700800 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400801
802 elif chunk_type == ImageChunk.TYPE_FILL:
803 if data_sz != 4:
804 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
805 'has {}'.format(data_sz))
806 fill_data = self._image.read(4)
807 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
808 chunk_offset,
809 output_offset,
810 chunk_sz*self.block_size,
811 None,
812 fill_data))
813 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
814 if data_sz != 0:
815 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
816 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400817 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
818 chunk_offset,
819 output_offset,
820 chunk_sz*self.block_size,
821 None,
822 None))
823 elif chunk_type == ImageChunk.TYPE_CRC32:
824 if data_sz != 4:
825 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
826 'this has {}'.format(data_sz))
827 self._image.read(4)
828 else:
829 raise ValueError('Unknown chunk type {}'.format(chunk_type))
830
831 offset += chunk_sz
832 output_offset += chunk_sz*self.block_size
833
834 # Record where sparse data end.
835 self._sparse_end = self._image.tell()
836
837 # Now that we've traversed all chunks, sanity check.
838 if self._num_total_blocks != offset:
839 raise ValueError('The header said we should have {} output blocks, '
840 'but we saw {}'.format(self._num_total_blocks, offset))
841 junk_len = len(self._image.read())
842 if junk_len > 0:
843 raise ValueError('There were {} bytes of extra data at the end of the '
844 'file.'.format(junk_len))
845
David Zeuthen09692692016-09-30 16:16:40 -0400846 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400847 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400848
849 # This is used when bisecting in read() to find the initial slice.
850 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
851
852 self.is_sparse = True
853
854 def _update_chunks_and_blocks(self):
855 """Helper function to update the image header.
856
857 The the |total_chunks| and |total_blocks| fields in the header
858 will be set to value of the |_num_total_blocks| and
859 |_num_total_chunks| attributes.
860
861 """
862 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
863 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
864 self._num_total_blocks,
865 self._num_total_chunks))
866
867 def append_dont_care(self, num_bytes):
868 """Appends a DONT_CARE chunk to the sparse file.
869
870 The given number of bytes must be a multiple of the block size.
871
872 Arguments:
873 num_bytes: Size in number of bytes of the DONT_CARE chunk.
874 """
875 assert num_bytes % self.block_size == 0
876
877 if not self.is_sparse:
878 self._image.seek(0, os.SEEK_END)
879 # This is more efficient that writing NUL bytes since it'll add
880 # a hole on file systems that support sparse files (native
881 # sparse, not Android sparse).
882 self._image.truncate(self._image.tell() + num_bytes)
883 self._read_header()
884 return
885
886 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100887 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400888 self._update_chunks_and_blocks()
889
890 self._image.seek(self._sparse_end, os.SEEK_SET)
891 self._image.write(struct.pack(ImageChunk.FORMAT,
892 ImageChunk.TYPE_DONT_CARE,
893 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100894 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400895 struct.calcsize(ImageChunk.FORMAT)))
896 self._read_header()
897
898 def append_raw(self, data):
899 """Appends a RAW chunk to the sparse file.
900
901 The length of the given data must be a multiple of the block size.
902
903 Arguments:
904 data: Data to append.
905 """
906 assert len(data) % self.block_size == 0
907
908 if not self.is_sparse:
909 self._image.seek(0, os.SEEK_END)
910 self._image.write(data)
911 self._read_header()
912 return
913
914 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100915 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400916 self._update_chunks_and_blocks()
917
918 self._image.seek(self._sparse_end, os.SEEK_SET)
919 self._image.write(struct.pack(ImageChunk.FORMAT,
920 ImageChunk.TYPE_RAW,
921 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100922 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400923 len(data) +
924 struct.calcsize(ImageChunk.FORMAT)))
925 self._image.write(data)
926 self._read_header()
927
928 def append_fill(self, fill_data, size):
929 """Appends a fill chunk to the sparse file.
930
931 The total length of the fill data must be a multiple of the block size.
932
933 Arguments:
934 fill_data: Fill data to append - must be four bytes.
935 size: Number of chunk - must be a multiple of four and the block size.
936 """
937 assert len(fill_data) == 4
938 assert size % 4 == 0
939 assert size % self.block_size == 0
940
941 if not self.is_sparse:
942 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100943 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400944 self._read_header()
945 return
946
947 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100948 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400949 self._update_chunks_and_blocks()
950
951 self._image.seek(self._sparse_end, os.SEEK_SET)
952 self._image.write(struct.pack(ImageChunk.FORMAT,
953 ImageChunk.TYPE_FILL,
954 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100955 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400956 4 + struct.calcsize(ImageChunk.FORMAT)))
957 self._image.write(fill_data)
958 self._read_header()
959
960 def seek(self, offset):
961 """Sets the cursor position for reading from unsparsified file.
962
963 Arguments:
964 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100965
966 Raises:
967 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400968 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700969 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100970 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400971 self._file_pos = offset
972
973 def read(self, size):
974 """Reads data from the unsparsified file.
975
976 This method may return fewer than |size| bytes of data if the end
977 of the file was encountered.
978
979 The file cursor for reading is advanced by the number of bytes
980 read.
981
982 Arguments:
983 size: Number of bytes to read.
984
985 Returns:
986 The data.
987
988 """
989 if not self.is_sparse:
990 self._image.seek(self._file_pos)
991 data = self._image.read(size)
992 self._file_pos += len(data)
993 return data
994
995 # Iterate over all chunks.
996 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
997 self._file_pos) - 1
998 data = bytearray()
999 to_go = size
1000 while to_go > 0:
1001 chunk = self._chunks[chunk_idx]
1002 chunk_pos_offset = self._file_pos - chunk.output_offset
1003 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1004
1005 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1006 self._image.seek(chunk.input_offset + chunk_pos_offset)
1007 data.extend(self._image.read(chunk_pos_to_go))
1008 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001009 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001010 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1011 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1012 else:
1013 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1014 data.extend('\0' * chunk_pos_to_go)
1015
1016 to_go -= chunk_pos_to_go
1017 self._file_pos += chunk_pos_to_go
1018 chunk_idx += 1
1019 # Generate partial read in case of EOF.
1020 if chunk_idx >= len(self._chunks):
1021 break
1022
1023 return data
1024
1025 def tell(self):
1026 """Returns the file cursor position for reading from unsparsified file.
1027
1028 Returns:
1029 The file cursor position for reading.
1030 """
1031 return self._file_pos
1032
1033 def truncate(self, size):
1034 """Truncates the unsparsified file.
1035
1036 Arguments:
1037 size: Desired size of unsparsified file.
1038
1039 Raises:
1040 ValueError: If desired size isn't a multiple of the block size.
1041 """
1042 if not self.is_sparse:
1043 self._image.truncate(size)
1044 self._read_header()
1045 return
1046
1047 if size % self.block_size != 0:
1048 raise ValueError('Cannot truncate to a size which is not a multiple '
1049 'of the block size')
1050
1051 if size == self.image_size:
1052 # Trivial where there's nothing to do.
1053 return
1054 elif size < self.image_size:
1055 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1056 chunk = self._chunks[chunk_idx]
1057 if chunk.output_offset != size:
1058 # Truncation in the middle of a trunk - need to keep the chunk
1059 # and modify it.
1060 chunk_idx_for_update = chunk_idx + 1
1061 num_to_keep = size - chunk.output_offset
1062 assert num_to_keep % self.block_size == 0
1063 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1064 truncate_at = (chunk.chunk_offset +
1065 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1066 data_sz = num_to_keep
1067 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1068 truncate_at = (chunk.chunk_offset +
1069 struct.calcsize(ImageChunk.FORMAT) + 4)
1070 data_sz = 4
1071 else:
1072 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1073 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1074 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001075 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001076 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1077 self._image.seek(chunk.chunk_offset)
1078 self._image.write(struct.pack(ImageChunk.FORMAT,
1079 chunk.chunk_type,
1080 0, # Reserved
1081 chunk_sz,
1082 total_sz))
1083 chunk.output_size = num_to_keep
1084 else:
1085 # Truncation at trunk boundary.
1086 truncate_at = chunk.chunk_offset
1087 chunk_idx_for_update = chunk_idx
1088
1089 self._num_total_chunks = chunk_idx_for_update
1090 self._num_total_blocks = 0
1091 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001092 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001093 self._update_chunks_and_blocks()
1094 self._image.truncate(truncate_at)
1095
1096 # We've modified the file so re-read all data.
1097 self._read_header()
1098 else:
1099 # Truncating to grow - just add a DONT_CARE section.
1100 self.append_dont_care(size - self.image_size)
1101
1102
David Zeuthen21e95262016-07-27 17:58:40 -04001103class AvbDescriptor(object):
1104 """Class for AVB descriptor.
1105
1106 See the |AvbDescriptor| C struct for more information.
1107
1108 Attributes:
1109 tag: The tag identifying what kind of descriptor this is.
1110 data: The data in the descriptor.
1111 """
1112
1113 SIZE = 16
1114 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1115
1116 def __init__(self, data):
1117 """Initializes a new property descriptor.
1118
1119 Arguments:
1120 data: If not None, must be a bytearray().
1121
1122 Raises:
1123 LookupError: If the given descriptor is malformed.
1124 """
1125 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1126
1127 if data:
1128 (self.tag, num_bytes_following) = (
1129 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1130 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1131 else:
1132 self.tag = None
1133 self.data = None
1134
1135 def print_desc(self, o):
1136 """Print the descriptor.
1137
1138 Arguments:
1139 o: The object to write the output to.
1140 """
1141 o.write(' Unknown descriptor:\n')
1142 o.write(' Tag: {}\n'.format(self.tag))
1143 if len(self.data) < 256:
1144 o.write(' Data: {} ({} bytes)\n'.format(
1145 repr(str(self.data)), len(self.data)))
1146 else:
1147 o.write(' Data: {} bytes\n'.format(len(self.data)))
1148
1149 def encode(self):
1150 """Serializes the descriptor.
1151
1152 Returns:
1153 A bytearray() with the descriptor data.
1154 """
1155 num_bytes_following = len(self.data)
1156 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1157 padding_size = nbf_with_padding - num_bytes_following
1158 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1159 padding = struct.pack(str(padding_size) + 'x')
1160 ret = desc + self.data + padding
1161 return bytearray(ret)
1162
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001163 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001164 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001165 """Verifies contents of the descriptor - used in verify_image sub-command.
1166
1167 Arguments:
1168 image_dir: The directory of the file being verified.
1169 image_ext: The extension of the file being verified (e.g. '.img').
1170 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001171 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001172 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001173 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1174 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001175
1176 Returns:
1177 True if the descriptor verifies, False otherwise.
1178 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001179 # Deletes unused parameters to prevent pylint warning unused-argument.
1180 del image_dir, image_ext, expected_chain_partitions_map
1181 del image_containing_descriptor, accept_zeroed_hashtree
1182
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001183 # Nothing to do.
1184 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001185
Jan Monscheeb28b62019-12-05 16:17:09 +01001186
David Zeuthen21e95262016-07-27 17:58:40 -04001187class AvbPropertyDescriptor(AvbDescriptor):
1188 """A class for property descriptors.
1189
1190 See the |AvbPropertyDescriptor| C struct for more information.
1191
1192 Attributes:
1193 key: The key.
1194 value: The key.
1195 """
1196
1197 TAG = 0
1198 SIZE = 32
1199 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1200 'Q' # key size (bytes)
1201 'Q') # value size (bytes)
1202
1203 def __init__(self, data=None):
1204 """Initializes a new property descriptor.
1205
1206 Arguments:
1207 data: If not None, must be a bytearray of size |SIZE|.
1208
1209 Raises:
1210 LookupError: If the given descriptor is malformed.
1211 """
1212 AvbDescriptor.__init__(self, None)
1213 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1214
1215 if data:
1216 (tag, num_bytes_following, key_size,
1217 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1218 expected_size = round_to_multiple(
1219 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1220 if tag != self.TAG or num_bytes_following != expected_size:
1221 raise LookupError('Given data does not look like a property '
1222 'descriptor.')
1223 self.key = data[self.SIZE:(self.SIZE + key_size)]
1224 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1225 value_size)]
1226 else:
1227 self.key = ''
1228 self.value = ''
1229
1230 def print_desc(self, o):
1231 """Print the descriptor.
1232
1233 Arguments:
1234 o: The object to write the output to.
1235 """
1236 if len(self.value) < 256:
1237 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1238 else:
1239 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1240
1241 def encode(self):
1242 """Serializes the descriptor.
1243
1244 Returns:
1245 A bytearray() with the descriptor data.
1246 """
1247 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1248 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1249 padding_size = nbf_with_padding - num_bytes_following
1250 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1251 len(self.key), len(self.value))
1252 padding = struct.pack(str(padding_size) + 'x')
1253 ret = desc + self.key + '\0' + self.value + '\0' + padding
1254 return bytearray(ret)
1255
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001256 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001257 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001258 """Verifies contents of the descriptor - used in verify_image sub-command.
1259
1260 Arguments:
1261 image_dir: The directory of the file being verified.
1262 image_ext: The extension of the file being verified (e.g. '.img').
1263 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001264 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001265 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001266 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1267 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001268
1269 Returns:
1270 True if the descriptor verifies, False otherwise.
1271 """
1272 # Nothing to do.
1273 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001274
Jan Monscheeb28b62019-12-05 16:17:09 +01001275
David Zeuthen21e95262016-07-27 17:58:40 -04001276class AvbHashtreeDescriptor(AvbDescriptor):
1277 """A class for hashtree descriptors.
1278
1279 See the |AvbHashtreeDescriptor| C struct for more information.
1280
1281 Attributes:
1282 dm_verity_version: dm-verity version used.
1283 image_size: Size of the image, after rounding up to |block_size|.
1284 tree_offset: Offset of the hash tree in the file.
1285 tree_size: Size of the tree.
1286 data_block_size: Data block size
1287 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001288 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1289 fec_offset: Offset of FEC data (0 if FEC is not used).
1290 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001291 hash_algorithm: Hash algorithm used.
1292 partition_name: Partition name.
1293 salt: Salt used.
1294 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001295 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001296 """
1297
1298 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001299 RESERVED = 60
1300 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001301 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1302 'L' # dm-verity version used
1303 'Q' # image size (bytes)
1304 'Q' # tree offset (bytes)
1305 'Q' # tree size (bytes)
1306 'L' # data block size (bytes)
1307 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001308 'L' # FEC number of roots
1309 'Q' # FEC offset (bytes)
1310 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001311 '32s' # hash algorithm used
1312 'L' # partition name (bytes)
1313 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001314 'L' # root digest length (bytes)
1315 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001316 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001317
1318 def __init__(self, data=None):
1319 """Initializes a new hashtree descriptor.
1320
1321 Arguments:
1322 data: If not None, must be a bytearray of size |SIZE|.
1323
1324 Raises:
1325 LookupError: If the given descriptor is malformed.
1326 """
1327 AvbDescriptor.__init__(self, None)
1328 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1329
1330 if data:
1331 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1332 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001333 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1334 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001335 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1336 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001337 expected_size = round_to_multiple(
1338 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1339 if tag != self.TAG or num_bytes_following != expected_size:
1340 raise LookupError('Given data does not look like a hashtree '
1341 'descriptor.')
1342 # Nuke NUL-bytes at the end.
1343 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1344 o = 0
1345 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1346 partition_name_len)])
1347 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1348 self.partition_name.decode('utf-8')
1349 o += partition_name_len
1350 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1351 o += salt_len
1352 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1353 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001354 if root_digest_len != 0:
1355 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001356
1357 else:
1358 self.dm_verity_version = 0
1359 self.image_size = 0
1360 self.tree_offset = 0
1361 self.tree_size = 0
1362 self.data_block_size = 0
1363 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001364 self.fec_num_roots = 0
1365 self.fec_offset = 0
1366 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001367 self.hash_algorithm = ''
1368 self.partition_name = ''
1369 self.salt = bytearray()
1370 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001371 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001372
1373 def print_desc(self, o):
1374 """Print the descriptor.
1375
1376 Arguments:
1377 o: The object to write the output to.
1378 """
1379 o.write(' Hashtree descriptor:\n')
1380 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1381 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1382 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1383 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1384 o.write(' Data Block Size: {} bytes\n'.format(
1385 self.data_block_size))
1386 o.write(' Hash Block Size: {} bytes\n'.format(
1387 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001388 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1389 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1390 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001391 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1392 o.write(' Partition Name: {}\n'.format(self.partition_name))
1393 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1394 'hex')))
1395 o.write(' Root Digest: {}\n'.format(str(
1396 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001397 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001398
1399 def encode(self):
1400 """Serializes the descriptor.
1401
1402 Returns:
1403 A bytearray() with the descriptor data.
1404 """
1405 encoded_name = self.partition_name.encode('utf-8')
1406 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1407 len(self.root_digest) - 16)
1408 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1409 padding_size = nbf_with_padding - num_bytes_following
1410 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1411 self.dm_verity_version, self.image_size,
1412 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001413 self.hash_block_size, self.fec_num_roots,
1414 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001415 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001416 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001417 padding = struct.pack(str(padding_size) + 'x')
1418 ret = desc + encoded_name + self.salt + self.root_digest + padding
1419 return bytearray(ret)
1420
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001421 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001422 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001423 """Verifies contents of the descriptor - used in verify_image sub-command.
1424
1425 Arguments:
1426 image_dir: The directory of the file being verified.
1427 image_ext: The extension of the file being verified (e.g. '.img').
1428 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001429 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001430 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001431 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1432 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001433
1434 Returns:
1435 True if the descriptor verifies, False otherwise.
1436 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001437 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001438 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001439 image = image_containing_descriptor
1440 else:
1441 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1442 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001443 # Generate the hashtree and checks that it matches what's in the file.
1444 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1445 digest_padding = round_to_pow2(digest_size) - digest_size
1446 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001447 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001448 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1449 self.data_block_size,
1450 self.hash_algorithm, self.salt,
1451 digest_padding,
1452 hash_level_offsets,
1453 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001454 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001455 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001456 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1457 format(image_filename))
1458 return False
1459 # ... also check that the on-disk hashtree matches
1460 image.seek(self.tree_offset)
1461 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001462 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001463 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001464 print('{}: skipping verification since hashtree is zeroed and '
1465 '--accept_zeroed_hashtree was given'
1466 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001467 else:
1468 if hash_tree != hash_tree_ondisk:
1469 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001470 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001471 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001472 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1473 .format(self.partition_name, self.hash_algorithm, image.filename,
1474 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001475 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1476 # correct but this a) currently requires the 'fec' binary; and b) takes a
1477 # long time; and c) is not strictly needed for verification purposes as
1478 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001479 return True
1480
David Zeuthen21e95262016-07-27 17:58:40 -04001481
1482class AvbHashDescriptor(AvbDescriptor):
1483 """A class for hash descriptors.
1484
1485 See the |AvbHashDescriptor| C struct for more information.
1486
1487 Attributes:
1488 image_size: Image size, in bytes.
1489 hash_algorithm: Hash algorithm used.
1490 partition_name: Partition name.
1491 salt: Salt used.
1492 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001493 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001494 """
1495
1496 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001497 RESERVED = 60
1498 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001499 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1500 'Q' # image size (bytes)
1501 '32s' # hash algorithm used
1502 'L' # partition name (bytes)
1503 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001504 'L' # digest length (bytes)
1505 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001506 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001507
1508 def __init__(self, data=None):
1509 """Initializes a new hash descriptor.
1510
1511 Arguments:
1512 data: If not None, must be a bytearray of size |SIZE|.
1513
1514 Raises:
1515 LookupError: If the given descriptor is malformed.
1516 """
1517 AvbDescriptor.__init__(self, None)
1518 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1519
1520 if data:
1521 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1522 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001523 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1524 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001525 expected_size = round_to_multiple(
1526 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1527 if tag != self.TAG or num_bytes_following != expected_size:
1528 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1529 # Nuke NUL-bytes at the end.
1530 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1531 o = 0
1532 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1533 partition_name_len)])
1534 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1535 self.partition_name.decode('utf-8')
1536 o += partition_name_len
1537 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1538 o += salt_len
1539 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1540 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001541 if digest_len != 0:
1542 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001543
1544 else:
1545 self.image_size = 0
1546 self.hash_algorithm = ''
1547 self.partition_name = ''
1548 self.salt = bytearray()
1549 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001550 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001551
1552 def print_desc(self, o):
1553 """Print the descriptor.
1554
1555 Arguments:
1556 o: The object to write the output to.
1557 """
1558 o.write(' Hash descriptor:\n')
1559 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1560 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1561 o.write(' Partition Name: {}\n'.format(self.partition_name))
1562 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1563 'hex')))
1564 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1565 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001566 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001567
1568 def encode(self):
1569 """Serializes the descriptor.
1570
1571 Returns:
1572 A bytearray() with the descriptor data.
1573 """
1574 encoded_name = self.partition_name.encode('utf-8')
1575 num_bytes_following = (
1576 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1577 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1578 padding_size = nbf_with_padding - num_bytes_following
1579 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1580 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001581 len(self.salt), len(self.digest), self.flags,
1582 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001583 padding = struct.pack(str(padding_size) + 'x')
1584 ret = desc + encoded_name + self.salt + self.digest + padding
1585 return bytearray(ret)
1586
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001587 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001588 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001589 """Verifies contents of the descriptor - used in verify_image sub-command.
1590
1591 Arguments:
1592 image_dir: The directory of the file being verified.
1593 image_ext: The extension of the file being verified (e.g. '.img').
1594 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001595 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001596 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001597 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1598 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001599
1600 Returns:
1601 True if the descriptor verifies, False otherwise.
1602 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001603 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001604 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001605 image = image_containing_descriptor
1606 else:
1607 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1608 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001609 data = image.read(self.image_size)
1610 ha = hashlib.new(self.hash_algorithm)
1611 ha.update(self.salt)
1612 ha.update(data)
1613 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001614 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001615 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001616 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1617 format(self.hash_algorithm, image_filename))
1618 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001619 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1620 .format(self.partition_name, self.hash_algorithm, image.filename,
1621 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001622 return True
1623
David Zeuthen21e95262016-07-27 17:58:40 -04001624
1625class AvbKernelCmdlineDescriptor(AvbDescriptor):
1626 """A class for kernel command-line descriptors.
1627
1628 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1629
1630 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001631 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001632 kernel_cmdline: The kernel command-line.
1633 """
1634
1635 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001636 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001637 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001638 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001639 'L') # cmdline length (bytes)
1640
David Zeuthenfd41eb92016-11-17 12:24:47 -05001641 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1642 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1643
David Zeuthen21e95262016-07-27 17:58:40 -04001644 def __init__(self, data=None):
1645 """Initializes a new kernel cmdline descriptor.
1646
1647 Arguments:
1648 data: If not None, must be a bytearray of size |SIZE|.
1649
1650 Raises:
1651 LookupError: If the given descriptor is malformed.
1652 """
1653 AvbDescriptor.__init__(self, None)
1654 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1655
1656 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001657 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001658 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1659 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1660 8)
1661 if tag != self.TAG or num_bytes_following != expected_size:
1662 raise LookupError('Given data does not look like a kernel cmdline '
1663 'descriptor.')
1664 # Nuke NUL-bytes at the end.
1665 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1666 kernel_cmdline_length)])
1667 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1668 self.kernel_cmdline.decode('utf-8')
1669 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001670 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001671 self.kernel_cmdline = ''
1672
1673 def print_desc(self, o):
1674 """Print the descriptor.
1675
1676 Arguments:
1677 o: The object to write the output to.
1678 """
1679 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001680 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001681 o.write(' Kernel Cmdline: {}\n'.format(repr(
1682 self.kernel_cmdline)))
1683
1684 def encode(self):
1685 """Serializes the descriptor.
1686
1687 Returns:
1688 A bytearray() with the descriptor data.
1689 """
1690 encoded_str = self.kernel_cmdline.encode('utf-8')
1691 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1692 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1693 padding_size = nbf_with_padding - num_bytes_following
1694 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001695 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001696 padding = struct.pack(str(padding_size) + 'x')
1697 ret = desc + encoded_str + padding
1698 return bytearray(ret)
1699
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001700 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001701 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001702 """Verifies contents of the descriptor - used in verify_image sub-command.
1703
1704 Arguments:
1705 image_dir: The directory of the file being verified.
1706 image_ext: The extension of the file being verified (e.g. '.img').
1707 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001708 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001709 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001710 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1711 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001712
1713 Returns:
1714 True if the descriptor verifies, False otherwise.
1715 """
1716 # Nothing to verify.
1717 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001718
Jan Monscheeb28b62019-12-05 16:17:09 +01001719
David Zeuthen21e95262016-07-27 17:58:40 -04001720class AvbChainPartitionDescriptor(AvbDescriptor):
1721 """A class for chained partition descriptors.
1722
1723 See the |AvbChainPartitionDescriptor| C struct for more information.
1724
1725 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001726 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001727 partition_name: Partition name.
1728 public_key: Bytes for the public key.
1729 """
1730
1731 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001732 RESERVED = 64
1733 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001734 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001735 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001736 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001737 'L' + # public_key_size (bytes)
1738 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001739
1740 def __init__(self, data=None):
1741 """Initializes a new chain partition descriptor.
1742
1743 Arguments:
1744 data: If not None, must be a bytearray of size |SIZE|.
1745
1746 Raises:
1747 LookupError: If the given descriptor is malformed.
1748 """
1749 AvbDescriptor.__init__(self, None)
1750 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1751
1752 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001753 (tag, num_bytes_following, self.rollback_index_location,
1754 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001755 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001756 expected_size = round_to_multiple(
1757 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1758 if tag != self.TAG or num_bytes_following != expected_size:
1759 raise LookupError('Given data does not look like a chain partition '
1760 'descriptor.')
1761 o = 0
1762 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1763 partition_name_len)])
1764 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1765 self.partition_name.decode('utf-8')
1766 o += partition_name_len
1767 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1768
1769 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001770 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001771 self.partition_name = ''
1772 self.public_key = bytearray()
1773
1774 def print_desc(self, o):
1775 """Print the descriptor.
1776
1777 Arguments:
1778 o: The object to write the output to.
1779 """
1780 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001781 o.write(' Partition Name: {}\n'.format(self.partition_name))
1782 o.write(' Rollback Index Location: {}\n'.format(
1783 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001784 # Just show the SHA1 of the key, for size reasons.
1785 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001786 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001787
1788 def encode(self):
1789 """Serializes the descriptor.
1790
1791 Returns:
1792 A bytearray() with the descriptor data.
1793 """
1794 encoded_name = self.partition_name.encode('utf-8')
1795 num_bytes_following = (
1796 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1797 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1798 padding_size = nbf_with_padding - num_bytes_following
1799 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001800 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001801 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001802 padding = struct.pack(str(padding_size) + 'x')
1803 ret = desc + encoded_name + self.public_key + padding
1804 return bytearray(ret)
1805
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001806 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001807 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001808 """Verifies contents of the descriptor - used in verify_image sub-command.
1809
1810 Arguments:
1811 image_dir: The directory of the file being verified.
1812 image_ext: The extension of the file being verified (e.g. '.img').
1813 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001814 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001815 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001816 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1817 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001818
1819 Returns:
1820 True if the descriptor verifies, False otherwise.
1821 """
1822 value = expected_chain_partitions_map.get(self.partition_name)
1823 if not value:
1824 sys.stderr.write('No expected chain partition for partition {}. Use '
1825 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001826 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001827 format(self.partition_name))
1828 return False
1829 rollback_index_location, pk_blob = value
1830
1831 if self.rollback_index_location != rollback_index_location:
1832 sys.stderr.write('Expected rollback_index_location {} does not '
1833 'match {} in descriptor for partition {}\n'.
1834 format(rollback_index_location,
1835 self.rollback_index_location,
1836 self.partition_name))
1837 return False
1838
1839 if self.public_key != pk_blob:
1840 sys.stderr.write('Expected public key blob does not match public '
1841 'key blob in descriptor for partition {}\n'.
1842 format(self.partition_name))
1843 return False
1844
Jan Monsch23e0c622019-12-11 11:23:58 +01001845 print('{}: Successfully verified chain partition descriptor matches '
1846 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001847
1848 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001849
1850DESCRIPTOR_CLASSES = [
1851 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1852 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1853]
1854
1855
1856def parse_descriptors(data):
1857 """Parses a blob of data into descriptors.
1858
1859 Arguments:
1860 data: A bytearray() with encoded descriptors.
1861
1862 Returns:
1863 A list of instances of objects derived from AvbDescriptor. For
1864 unknown descriptors, the class AvbDescriptor is used.
1865 """
1866 o = 0
1867 ret = []
1868 while o < len(data):
1869 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1870 if tag < len(DESCRIPTOR_CLASSES):
1871 c = DESCRIPTOR_CLASSES[tag]
1872 else:
1873 c = AvbDescriptor
1874 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1875 o += 16 + nb_following
1876 return ret
1877
1878
1879class AvbFooter(object):
1880 """A class for parsing and writing footers.
1881
1882 Footers are stored at the end of partitions and point to where the
1883 AvbVBMeta blob is located. They also contain the original size of
1884 the image before AVB information was added.
1885
1886 Attributes:
1887 magic: Magic for identifying the footer, see |MAGIC|.
1888 version_major: The major version of avbtool that wrote the footer.
1889 version_minor: The minor version of avbtool that wrote the footer.
1890 original_image_size: Original image size.
1891 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1892 vbmeta_size: Size of the AvbVBMeta blob.
1893 """
1894
1895 MAGIC = 'AVBf'
1896 SIZE = 64
1897 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001898 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1899 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001900 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1901 'Q' # Original image size.
1902 'Q' # Offset of VBMeta blob.
1903 'Q' + # Size of VBMeta blob.
1904 str(RESERVED) + 'x') # padding for reserved bytes
1905
1906 def __init__(self, data=None):
1907 """Initializes a new footer object.
1908
1909 Arguments:
1910 data: If not None, must be a bytearray of size 4096.
1911
1912 Raises:
1913 LookupError: If the given footer is malformed.
1914 struct.error: If the given data has no footer.
1915 """
1916 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1917
1918 if data:
1919 (self.magic, self.version_major, self.version_minor,
1920 self.original_image_size, self.vbmeta_offset,
1921 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1922 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001923 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001924 else:
1925 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001926 self.version_major = self.FOOTER_VERSION_MAJOR
1927 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001928 self.original_image_size = 0
1929 self.vbmeta_offset = 0
1930 self.vbmeta_size = 0
1931
David Zeuthena4fee8b2016-08-22 15:20:43 -04001932 def encode(self):
1933 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001934
David Zeuthena4fee8b2016-08-22 15:20:43 -04001935 Returns:
1936 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001937 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001938 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1939 self.version_minor, self.original_image_size,
1940 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001941
1942
1943class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001944 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001945
Jan Monschfe00c0a2019-12-11 11:19:40 +01001946 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1947 avb_vbmeta_image.h.
1948
David Zeuthen21e95262016-07-27 17:58:40 -04001949 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001950 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1951 required_libavb_version_major: The major version of libavb required for this
1952 header.
1953 required_libavb_version_minor: The minor version of libavb required for this
1954 header.
1955 authentication_data_block_size: The size of the signature block.
1956 auxiliary_data_block_size: The size of the auxiliary data block.
1957 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1958 enum.
1959 hash_offset: Offset into the "Authentication data" block of hash data.
1960 hash_size: Length of the hash data.
1961 signature_offset: Offset into the "Authentication data" block of signature
1962 data.
1963 signature_size: Length of the signature data.
1964 public_key_offset: Offset into the "Auxiliary data" block of public key
1965 data.
1966 public_key_size: Length of the public key data.
1967 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1968 key metadata.
1969 public_key_metadata_size: Length of the public key metadata. Must be set to
1970 zero if there is no public key metadata.
1971 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1972 data.
1973 descriptors_size: Length of descriptor data.
1974 rollback_index: The rollback index which can be used to prevent rollback to
1975 older versions.
1976 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
1977 zero if the vbmeta image is not a top-level image.
1978 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
1979 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
1980 terminated. Applications must not make assumptions about how this
1981 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04001982 """
1983
1984 SIZE = 256
1985
David Zeuthene3cadca2017-02-22 21:25:46 -05001986 # Keep in sync with |reserved0| and |reserved| field of
1987 # |AvbVBMetaImageHeader|.
1988 RESERVED0 = 4
1989 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001990
1991 # Keep in sync with |AvbVBMetaImageHeader|.
1992 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1993 '2Q' # 2 x block size
1994 'L' # algorithm type
1995 '2Q' # offset, size (hash)
1996 '2Q' # offset, size (signature)
1997 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001998 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001999 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002000 'Q' # rollback_index
2001 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002002 str(RESERVED0) + 'x' + # padding for reserved bytes
2003 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002004 str(RESERVED) + 'x') # padding for reserved bytes
2005
2006 def __init__(self, data=None):
2007 """Initializes a new header object.
2008
2009 Arguments:
2010 data: If not None, must be a bytearray of size 8192.
2011
2012 Raises:
2013 Exception: If the given data is malformed.
2014 """
2015 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2016
2017 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002018 (self.magic, self.required_libavb_version_major,
2019 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002020 self.authentication_data_block_size, self.auxiliary_data_block_size,
2021 self.algorithm_type, self.hash_offset, self.hash_size,
2022 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002023 self.public_key_size, self.public_key_metadata_offset,
2024 self.public_key_metadata_size, self.descriptors_offset,
2025 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002026 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002027 self.flags,
2028 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002029 # Nuke NUL-bytes at the end of the string.
2030 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002031 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002032 else:
2033 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002034 # Start by just requiring version 1.0. Code that adds features
2035 # in a future version can use bump_required_libavb_version_minor() to
2036 # bump the minor.
2037 self.required_libavb_version_major = AVB_VERSION_MAJOR
2038 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002039 self.authentication_data_block_size = 0
2040 self.auxiliary_data_block_size = 0
2041 self.algorithm_type = 0
2042 self.hash_offset = 0
2043 self.hash_size = 0
2044 self.signature_offset = 0
2045 self.signature_size = 0
2046 self.public_key_offset = 0
2047 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002048 self.public_key_metadata_offset = 0
2049 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002050 self.descriptors_offset = 0
2051 self.descriptors_size = 0
2052 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002053 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002054 self.release_string = get_release_string()
2055
2056 def bump_required_libavb_version_minor(self, minor):
2057 """Function to bump required_libavb_version_minor.
2058
2059 Call this when writing data that requires a specific libavb
2060 version to parse it.
2061
2062 Arguments:
2063 minor: The minor version of libavb that has support for the feature.
2064 """
2065 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002066 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002067
2068 def save(self, output):
2069 """Serializes the header (256 bytes) to disk.
2070
2071 Arguments:
2072 output: The object to write the output to.
2073 """
2074 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002075 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2076 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002077 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2078 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002079 self.public_key_offset, self.public_key_size,
2080 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002081 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002082 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002083
2084 def encode(self):
2085 """Serializes the header (256) to a bytearray().
2086
2087 Returns:
2088 A bytearray() with the encoded header.
2089 """
2090 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002091 self.required_libavb_version_major,
2092 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002093 self.authentication_data_block_size,
2094 self.auxiliary_data_block_size, self.algorithm_type,
2095 self.hash_offset, self.hash_size, self.signature_offset,
2096 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002097 self.public_key_size, self.public_key_metadata_offset,
2098 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002099 self.descriptors_size, self.rollback_index, self.flags,
2100 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002101
2102
2103class Avb(object):
2104 """Business logic for avbtool command-line tool."""
2105
David Zeuthen8b6973b2016-09-20 12:39:49 -04002106 # Keep in sync with avb_ab_flow.h.
2107 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2108 AB_MAGIC = '\0AB0'
2109 AB_MAJOR_VERSION = 1
2110 AB_MINOR_VERSION = 0
2111 AB_MISC_METADATA_OFFSET = 2048
2112
David Zeuthen09692692016-09-30 16:16:40 -04002113 # Constants for maximum metadata size. These are used to give
2114 # meaningful errors if the value passed in via --partition_size is
2115 # too small and when --calc_max_image_size is used. We use
2116 # conservative figures.
2117 MAX_VBMETA_SIZE = 64 * 1024
2118 MAX_FOOTER_SIZE = 4096
2119
Jan Monsch2c7be992020-04-03 14:37:13 +02002120 def generate_test_image(self, output, image_size, start_byte):
2121 """Generates a test image for testing avbtool with known content.
2122
2123 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2124
2125 Arguments:
2126 output: Write test image to this file.
2127 image_size: The size of the requested file in bytes.
2128 start_byte: The integer value of the start byte to use for pattern
2129 generation.
2130 """
2131 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2132 buf = bytearray()
2133 c = int(math.ceil(image_size / 256.0))
2134 for _ in range(0, c):
2135 buf.extend(pattern)
2136 output.write(buf[0:image_size])
2137
David Zeuthen49936b42018-08-07 17:38:58 -04002138 def extract_vbmeta_image(self, output, image_filename, padding_size):
2139 """Implements the 'extract_vbmeta_image' command.
2140
2141 Arguments:
2142 output: Write vbmeta struct to this file.
2143 image_filename: File to extract vbmeta data from (with a footer).
2144 padding_size: If not 0, pads output so size is a multiple of the number.
2145
2146 Raises:
2147 AvbError: If there's no footer in the image.
2148 """
2149 image = ImageHandler(image_filename)
2150
2151 (footer, _, _, _) = self._parse_image(image)
2152
2153 if not footer:
2154 raise AvbError('Given image does not have a footer.')
2155
2156 image.seek(footer.vbmeta_offset)
2157 vbmeta_blob = image.read(footer.vbmeta_size)
2158 output.write(vbmeta_blob)
2159
2160 if padding_size > 0:
2161 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2162 padding_needed = padded_size - len(vbmeta_blob)
2163 output.write('\0' * padding_needed)
2164
David Zeuthena4fee8b2016-08-22 15:20:43 -04002165 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002166 """Implements the 'erase_footer' command.
2167
2168 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002169 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002170 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002171
2172 Raises:
2173 AvbError: If there's no footer in the image.
2174 """
2175
David Zeuthena4fee8b2016-08-22 15:20:43 -04002176 image = ImageHandler(image_filename)
2177
David Zeuthen21e95262016-07-27 17:58:40 -04002178 (footer, _, descriptors, _) = self._parse_image(image)
2179
2180 if not footer:
2181 raise AvbError('Given image does not have a footer.')
2182
2183 new_image_size = None
2184 if not keep_hashtree:
2185 new_image_size = footer.original_image_size
2186 else:
2187 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002188 # descriptor to figure out the location and size of the hashtree
2189 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002190 for desc in descriptors:
2191 if isinstance(desc, AvbHashtreeDescriptor):
2192 # The hashtree is always just following the main data so the
2193 # new size is easily derived.
2194 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002195 # If the image has FEC codes, also keep those.
2196 if desc.fec_offset > 0:
2197 fec_end = desc.fec_offset + desc.fec_size
2198 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002199 break
2200 if not new_image_size:
2201 raise AvbError('Requested to keep hashtree but no hashtree '
2202 'descriptor was found.')
2203
2204 # And cut...
2205 image.truncate(new_image_size)
2206
David Zeuthen1394f762019-04-30 10:20:11 -04002207 def zero_hashtree(self, image_filename):
2208 """Implements the 'zero_hashtree' command.
2209
2210 Arguments:
2211 image_filename: File to zero hashtree and FEC data from.
2212
2213 Raises:
2214 AvbError: If there's no footer in the image.
2215 """
2216
2217 image = ImageHandler(image_filename)
2218
2219 (footer, _, descriptors, _) = self._parse_image(image)
2220
2221 if not footer:
2222 raise AvbError('Given image does not have a footer.')
2223
2224 # Search for a hashtree descriptor to figure out the location and
2225 # size of the hashtree and FEC.
2226 ht_desc = None
2227 for desc in descriptors:
2228 if isinstance(desc, AvbHashtreeDescriptor):
2229 ht_desc = desc
2230 break
2231
2232 if not ht_desc:
2233 raise AvbError('No hashtree descriptor was found.')
2234
2235 zero_ht_start_offset = ht_desc.tree_offset
2236 zero_ht_num_bytes = ht_desc.tree_size
2237 zero_fec_start_offset = None
2238 zero_fec_num_bytes = 0
2239 if ht_desc.fec_offset > 0:
2240 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2241 raise AvbError('Hash-tree and FEC data must be adjacent.')
2242 zero_fec_start_offset = ht_desc.fec_offset
2243 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002244 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2245 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002246 image.seek(zero_end_offset)
2247 data = image.read(image.image_size - zero_end_offset)
2248
2249 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2250 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2251 # beginning of both hashtree and FEC. (That way, in the future we can add
2252 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2253 #
2254 # Applications can use these markers to detect that the hashtree and/or
2255 # FEC needs to be recomputed.
2256 image.truncate(zero_ht_start_offset)
2257 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2258 image.append_raw(data_zeroed_firstblock)
2259 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2260 if zero_fec_start_offset:
2261 image.append_raw(data_zeroed_firstblock)
2262 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2263 image.append_raw(data)
2264
David Zeuthen2bc232b2017-04-19 14:25:19 -04002265 def resize_image(self, image_filename, partition_size):
2266 """Implements the 'resize_image' command.
2267
2268 Arguments:
2269 image_filename: File with footer to resize.
2270 partition_size: The new size of the image.
2271
2272 Raises:
2273 AvbError: If there's no footer in the image.
2274 """
2275
2276 image = ImageHandler(image_filename)
2277
2278 if partition_size % image.block_size != 0:
2279 raise AvbError('Partition size of {} is not a multiple of the image '
2280 'block size {}.'.format(partition_size,
2281 image.block_size))
2282
Jan Monsch77cd2022019-12-10 17:18:04 +01002283 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002284
2285 if not footer:
2286 raise AvbError('Given image does not have a footer.')
2287
2288 # The vbmeta blob is always at the end of the data so resizing an
2289 # image amounts to just moving the footer around.
2290
2291 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2292 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002293 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2294 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002295
2296 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002297 raise AvbError('Requested size of {} is too small for an image '
2298 'of size {}.'
2299 .format(partition_size,
2300 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002301
2302 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2303 # with enough bytes such that the final Footer block is at the end
2304 # of partition_size.
2305 image.truncate(vbmeta_end_offset)
2306 image.append_dont_care(partition_size - vbmeta_end_offset -
2307 1*image.block_size)
2308
2309 # Just reuse the same footer - only difference is that we're
2310 # writing it in a different place.
2311 footer_blob = footer.encode()
2312 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2313 footer_blob)
2314 image.append_raw(footer_blob_with_padding)
2315
David Zeuthen8b6973b2016-09-20 12:39:49 -04002316 def set_ab_metadata(self, misc_image, slot_data):
2317 """Implements the 'set_ab_metadata' command.
2318
2319 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2320 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2321
2322 Arguments:
2323 misc_image: The misc image to write to.
2324 slot_data: Slot data as a string
2325
2326 Raises:
2327 AvbError: If slot data is malformed.
2328 """
2329 tokens = slot_data.split(':')
2330 if len(tokens) != 6:
2331 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2332 a_priority = int(tokens[0])
2333 a_tries_remaining = int(tokens[1])
2334 a_success = True if int(tokens[2]) != 0 else False
2335 b_priority = int(tokens[3])
2336 b_tries_remaining = int(tokens[4])
2337 b_success = True if int(tokens[5]) != 0 else False
2338
2339 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2340 self.AB_MAGIC,
2341 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2342 a_priority, a_tries_remaining, a_success,
2343 b_priority, b_tries_remaining, b_success)
2344 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2345 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2346 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2347 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2348 misc_image.write(ab_data)
2349
David Zeuthena4fee8b2016-08-22 15:20:43 -04002350 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002351 """Implements the 'info_image' command.
2352
2353 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002354 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002355 output: Output file to write human-readable information to (file object).
2356 """
2357
David Zeuthena4fee8b2016-08-22 15:20:43 -04002358 image = ImageHandler(image_filename)
2359
David Zeuthen21e95262016-07-27 17:58:40 -04002360 o = output
2361
2362 (footer, header, descriptors, image_size) = self._parse_image(image)
2363
Bowgo Tsaid7145942020-03-20 17:03:51 +08002364 # To show the SHA1 of the public key.
2365 vbmeta_blob = self._load_vbmeta_blob(image)
2366 key_offset = (header.SIZE +
2367 header.authentication_data_block_size +
2368 header.public_key_offset)
2369 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2370
David Zeuthen21e95262016-07-27 17:58:40 -04002371 if footer:
2372 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2373 footer.version_minor))
2374 o.write('Image size: {} bytes\n'.format(image_size))
2375 o.write('Original image size: {} bytes\n'.format(
2376 footer.original_image_size))
2377 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2378 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2379 o.write('--\n')
2380
2381 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2382
David Zeuthene3cadca2017-02-22 21:25:46 -05002383 o.write('Minimum libavb version: {}.{}{}\n'.format(
2384 header.required_libavb_version_major,
2385 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002386 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002387 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2388 o.write('Authentication Block: {} bytes\n'.format(
2389 header.authentication_data_block_size))
2390 o.write('Auxiliary Block: {} bytes\n'.format(
2391 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002392 if key_blob:
2393 hexdig = hashlib.sha1(key_blob).hexdigest()
2394 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002395 o.write('Algorithm: {}\n'.format(alg_name))
2396 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002397 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002398 o.write('Release String: \'{}\'\n'.format(
2399 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002400
2401 # Print descriptors.
2402 num_printed = 0
2403 o.write('Descriptors:\n')
2404 for desc in descriptors:
2405 desc.print_desc(o)
2406 num_printed += 1
2407 if num_printed == 0:
2408 o.write(' (none)\n')
2409
Jan Monscheeb28b62019-12-05 16:17:09 +01002410 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2411 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002412 """Implements the 'verify_image' command.
2413
2414 Arguments:
2415 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002416 key_path: None or check that embedded public key matches key at given
2417 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002418 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002419 follow_chain_partitions:
2420 If True, will follows chain partitions even when not specified with
2421 the --expected_chain_partition option
2422 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2423 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002424
2425 Raises:
2426 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002427 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002428 expected_chain_partitions_map = {}
2429 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002430 for cp in expected_chain_partitions:
2431 cp_tokens = cp.split(':')
2432 if len(cp_tokens) != 3:
2433 raise AvbError('Malformed chained partition "{}".'.format(cp))
2434 partition_name = cp_tokens[0]
2435 rollback_index_location = int(cp_tokens[1])
2436 file_path = cp_tokens[2]
2437 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002438 expected_chain_partitions_map[partition_name] = (
2439 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002440
2441 image_dir = os.path.dirname(image_filename)
2442 image_ext = os.path.splitext(image_filename)[1]
2443
2444 key_blob = None
2445 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002446 print('Verifying image {} using key at {}'.format(image_filename,
2447 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002448 key_blob = encode_rsa_key(key_path)
2449 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002450 print('Verifying image {} using embedded public key'.format(
2451 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002452
David Zeuthenb623d8b2017-04-04 16:05:53 -04002453 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002454 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002455 offset = 0
2456 if footer:
2457 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002458
David Zeuthenb623d8b2017-04-04 16:05:53 -04002459 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002460 vbmeta_blob = image.read(header.SIZE
2461 + header.authentication_data_block_size
2462 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002463
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002464 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002465 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002466 raise AvbError('Signature check failed for {} vbmeta struct {}'
2467 .format(alg_name, image_filename))
2468
2469 if key_blob:
2470 # The embedded public key is in the auxiliary block at an offset.
2471 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002472 key_offset += header.authentication_data_block_size
2473 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002474 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2475 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002476 if key_blob != key_blob_in_vbmeta:
2477 raise AvbError('Embedded public key does not match given key.')
2478
2479 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002480 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2481 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002482 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002483 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2484 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002485
2486 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002487 if (isinstance(desc, AvbChainPartitionDescriptor)
2488 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002489 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002490 # In this case we're processing a chain descriptor but don't have a
2491 # --expect_chain_partition ... however --follow_chain_partitions was
2492 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002493 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2494 'and KEY (which has sha1 {}) not specified'
2495 .format(desc.partition_name, desc.rollback_index_location,
2496 hashlib.sha1(desc.public_key).hexdigest()))
2497 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002498 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002499 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002500 # Honor --follow_chain_partitions - add '--' to make the output more
2501 # readable.
2502 if (isinstance(desc, AvbChainPartitionDescriptor)
2503 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002504 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002505 chained_image_filename = os.path.join(image_dir,
2506 desc.partition_name + image_ext)
2507 self.verify_image(chained_image_filename, key_path, None, False,
2508 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002509
David Zeuthenb8643c02018-05-17 17:21:18 -04002510 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2511 """Implements the 'calculate_vbmeta_digest' command.
2512
2513 Arguments:
2514 image_filename: Image file to get information from (file object).
2515 hash_algorithm: Hash algorithm used.
2516 output: Output file to write human-readable information to (file object).
2517 """
2518
2519 image_dir = os.path.dirname(image_filename)
2520 image_ext = os.path.splitext(image_filename)[1]
2521
2522 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002523 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002524 offset = 0
2525 if footer:
2526 offset = footer.vbmeta_offset
2527 size = (header.SIZE + header.authentication_data_block_size +
2528 header.auxiliary_data_block_size)
2529 image.seek(offset)
2530 vbmeta_blob = image.read(size)
2531
2532 hasher = hashlib.new(name=hash_algorithm)
2533 hasher.update(vbmeta_blob)
2534
2535 for desc in descriptors:
2536 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002537 ch_image_filename = os.path.join(image_dir,
2538 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002539 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002540 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002541 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002542 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2543 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002544 if ch_footer:
2545 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002546 ch_image.seek(ch_offset)
2547 ch_vbmeta_blob = ch_image.read(ch_size)
2548 hasher.update(ch_vbmeta_blob)
2549
2550 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002551 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002552
David Zeuthenf7d2e752018-09-20 13:30:41 -04002553 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2554 """Implements the 'calculate_kernel_cmdline' command.
2555
2556 Arguments:
2557 image_filename: Image file to get information from (file object).
2558 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2559 output: Output file to write human-readable information to (file object).
2560 """
2561
2562 image = ImageHandler(image_filename)
2563 _, _, descriptors, _ = self._parse_image(image)
2564
2565 image_dir = os.path.dirname(image_filename)
2566 image_ext = os.path.splitext(image_filename)[1]
2567
2568 cmdline_descriptors = []
2569 for desc in descriptors:
2570 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002571 ch_image_filename = os.path.join(image_dir,
2572 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002573 ch_image = ImageHandler(ch_image_filename)
2574 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2575 for ch_desc in ch_descriptors:
2576 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2577 cmdline_descriptors.append(ch_desc)
2578 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2579 cmdline_descriptors.append(desc)
2580
2581 kernel_cmdline_snippets = []
2582 for desc in cmdline_descriptors:
2583 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002584 if ((desc.flags &
2585 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2586 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002587 if hashtree_disabled:
2588 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002589 if (desc.flags &
2590 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002591 if not hashtree_disabled:
2592 use_cmdline = False
2593 if use_cmdline:
2594 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2595 output.write(' '.join(kernel_cmdline_snippets))
2596
David Zeuthen21e95262016-07-27 17:58:40 -04002597 def _parse_image(self, image):
2598 """Gets information about an image.
2599
2600 The image can either be a vbmeta or an image with a footer.
2601
2602 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002603 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002604
2605 Returns:
2606 A tuple where the first argument is a AvbFooter (None if there
2607 is no footer on the image), the second argument is a
2608 AvbVBMetaHeader, the third argument is a list of
2609 AvbDescriptor-derived instances, and the fourth argument is the
2610 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002611
2612 Raises:
2613 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002614 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002615 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002616 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002617 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002618 try:
2619 footer = AvbFooter(image.read(AvbFooter.SIZE))
2620 except (LookupError, struct.error):
2621 # Nope, just seek back to the start.
2622 image.seek(0)
2623
2624 vbmeta_offset = 0
2625 if footer:
2626 vbmeta_offset = footer.vbmeta_offset
2627
2628 image.seek(vbmeta_offset)
2629 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2630
2631 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2632 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2633 desc_start_offset = aux_block_offset + h.descriptors_offset
2634 image.seek(desc_start_offset)
2635 descriptors = parse_descriptors(image.read(h.descriptors_size))
2636
David Zeuthen09692692016-09-30 16:16:40 -04002637 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002638
David Zeuthenb1b994d2017-03-06 18:01:31 -05002639 def _load_vbmeta_blob(self, image):
2640 """Gets the vbmeta struct and associated sections.
2641
2642 The image can either be a vbmeta.img or an image with a footer.
2643
2644 Arguments:
2645 image: An ImageHandler (vbmeta or footer).
2646
2647 Returns:
2648 A blob with the vbmeta struct and other sections.
2649 """
2650 assert isinstance(image, ImageHandler)
2651 footer = None
2652 image.seek(image.image_size - AvbFooter.SIZE)
2653 try:
2654 footer = AvbFooter(image.read(AvbFooter.SIZE))
2655 except (LookupError, struct.error):
2656 # Nope, just seek back to the start.
2657 image.seek(0)
2658
2659 vbmeta_offset = 0
2660 if footer:
2661 vbmeta_offset = footer.vbmeta_offset
2662
2663 image.seek(vbmeta_offset)
2664 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2665
2666 image.seek(vbmeta_offset)
2667 data_size = AvbVBMetaHeader.SIZE
2668 data_size += h.authentication_data_block_size
2669 data_size += h.auxiliary_data_block_size
2670 return image.read(data_size)
2671
David Zeuthen73f2afa2017-05-17 16:54:11 -04002672 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002673 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002674
2675 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002676 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002677
2678 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002679 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2680 instructions. There is one for when hashtree is not disabled and one for
2681 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002682
David Zeuthen21e95262016-07-27 17:58:40 -04002683 """
2684
David Zeuthen21e95262016-07-27 17:58:40 -04002685 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002686 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002687 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002688 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2689 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2690 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2691 c += ' {}'.format(ht.data_block_size) # data_block
2692 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002693 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2694 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002695 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2696 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2697 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2698 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002699 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002700 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002701 c += ' ignore_zero_blocks'
2702 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2703 c += ' fec_roots {}'.format(ht.fec_num_roots)
2704 # Note that fec_blocks is the size that FEC covers, *not* the
2705 # size of the FEC data. Since we use FEC for everything up until
2706 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002707 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2708 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002709 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002710 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002711 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002712 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002713 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002714
David Zeuthenfd41eb92016-11-17 12:24:47 -05002715 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002716 desc = AvbKernelCmdlineDescriptor()
2717 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002718 desc.flags = (
2719 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2720
2721 # The descriptor for when hashtree verification is disabled is a lot
2722 # simpler - we just set the root to the partition.
2723 desc_no_ht = AvbKernelCmdlineDescriptor()
2724 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2725 desc_no_ht.flags = (
2726 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2727
2728 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002729
David Zeuthen73f2afa2017-05-17 16:54:11 -04002730 def _get_cmdline_descriptors_for_dm_verity(self, image):
2731 """Generate kernel cmdline descriptors for dm-verity.
2732
2733 Arguments:
2734 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2735
2736 Returns:
2737 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2738 instructions. There is one for when hashtree is not disabled and one for
2739 when it is.
2740
2741 Raises:
2742 AvbError: If |image| doesn't have a hashtree descriptor.
2743
2744 """
2745
2746 (_, _, descriptors, _) = self._parse_image(image)
2747
2748 ht = None
2749 for desc in descriptors:
2750 if isinstance(desc, AvbHashtreeDescriptor):
2751 ht = desc
2752 break
2753
2754 if not ht:
2755 raise AvbError('No hashtree descriptor in given image')
2756
2757 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2758
David Zeuthen21e95262016-07-27 17:58:40 -04002759 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002760 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002761 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002762 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002763 include_descriptors_from_image,
2764 signing_helper,
2765 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002766 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002767 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002768 print_required_libavb_version,
2769 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002770 """Implements the 'make_vbmeta_image' command.
2771
2772 Arguments:
2773 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002774 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002775 algorithm_name: Name of algorithm to use.
2776 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002777 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002778 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002779 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002780 props: Properties to insert (list of strings of the form 'key:value').
2781 props_from_file: Properties to insert (list of strings 'key:<path>').
2782 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002783 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002784 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002785 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002786 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002787 release_string: None or avbtool release string to use instead of default.
2788 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002789 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002790 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002791
2792 Raises:
2793 AvbError: If a chained partition is malformed.
2794 """
2795
David Zeuthen1097a782017-05-31 15:53:17 -04002796 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002797 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002798 if include_descriptors_from_image:
2799 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2800 # version of all included descriptors.
2801 tmp_header = AvbVBMetaHeader()
2802 for image in include_descriptors_from_image:
2803 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2804 tmp_header.bump_required_libavb_version_minor(
2805 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002806 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002807 else:
2808 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002809 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002810 return
2811
2812 if not output:
2813 raise AvbError('No output file given')
2814
David Zeuthen21e95262016-07-27 17:58:40 -04002815 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002816 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002817 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002818 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002819 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002820 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002821 include_descriptors_from_image, signing_helper,
2822 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002823 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002824
2825 # Write entire vbmeta blob (header, authentication, auxiliary).
2826 output.seek(0)
2827 output.write(vbmeta_blob)
2828
David Zeuthen97cb5802017-06-01 16:14:05 -04002829 if padding_size > 0:
2830 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2831 padding_needed = padded_size - len(vbmeta_blob)
2832 output.write('\0' * padding_needed)
2833
David Zeuthen18666ab2016-11-15 11:18:05 -05002834 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2835 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002836 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002837 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002838 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002839 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002840 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002841 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002842 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002843 release_string, append_to_release_string,
2844 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002845 """Generates a VBMeta blob.
2846
2847 This blob contains the header (struct AvbVBMetaHeader), the
2848 authentication data block (which contains the hash and signature
2849 for the header and auxiliary block), and the auxiliary block
2850 (which contains descriptors, the public key used, and other data).
2851
2852 The |key| parameter can |None| only if the |algorithm_name| is
2853 'NONE'.
2854
2855 Arguments:
2856 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2857 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002858 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002859 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002860 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002861 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002862 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002863 props: Properties to insert (List of strings of the form 'key:value').
2864 props_from_file: Properties to insert (List of strings 'key:<path>').
2865 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002866 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002867 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002868 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2869 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002870 include_descriptors_from_image: List of file objects for which
2871 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002872 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002873 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002874 release_string: None or avbtool release string.
2875 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002876 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002877
2878 Returns:
2879 A bytearray() with the VBMeta blob.
2880
2881 Raises:
2882 Exception: If the |algorithm_name| is not found, if no key has
2883 been given and the given algorithm requires one, or the key is
2884 of the wrong size.
2885
2886 """
2887 try:
2888 alg = ALGORITHMS[algorithm_name]
2889 except KeyError:
2890 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2891
David Zeuthena5fd3a42017-02-27 16:38:54 -05002892 if not descriptors:
2893 descriptors = []
2894
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002895 h = AvbVBMetaHeader()
2896 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2897
David Zeuthena5fd3a42017-02-27 16:38:54 -05002898 # Insert chained partition descriptors, if any
2899 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002900 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002901 for cp in chain_partitions:
2902 cp_tokens = cp.split(':')
2903 if len(cp_tokens) != 3:
2904 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002905 partition_name = cp_tokens[0]
2906 rollback_index_location = int(cp_tokens[1])
2907 file_path = cp_tokens[2]
2908 # Check that the same rollback location isn't being used by
2909 # multiple chained partitions.
2910 if used_locations.get(rollback_index_location):
2911 raise AvbError('Rollback Index Location {} is already in use.'.format(
2912 rollback_index_location))
2913 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002914 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002915 desc.partition_name = partition_name
2916 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002917 if desc.rollback_index_location < 1:
2918 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002919 desc.public_key = open(file_path, 'rb').read()
2920 descriptors.append(desc)
2921
David Zeuthen21e95262016-07-27 17:58:40 -04002922 # Descriptors.
2923 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002924 for desc in descriptors:
2925 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002926
2927 # Add properties.
2928 if props:
2929 for prop in props:
2930 idx = prop.find(':')
2931 if idx == -1:
2932 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002933 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002934 desc = AvbPropertyDescriptor()
2935 desc.key = prop[0:idx]
2936 desc.value = prop[(idx + 1):]
2937 encoded_descriptors.extend(desc.encode())
2938 if props_from_file:
2939 for prop in props_from_file:
2940 idx = prop.find(':')
2941 if idx == -1:
2942 raise AvbError('Malformed property "{}".'.format(prop))
2943 desc = AvbPropertyDescriptor()
2944 desc.key = prop[0:idx]
2945 desc.value = prop[(idx + 1):]
2946 file_path = prop[(idx + 1):]
2947 desc.value = open(file_path, 'rb').read()
2948 encoded_descriptors.extend(desc.encode())
2949
David Zeuthen73f2afa2017-05-17 16:54:11 -04002950 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002951 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002952 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002953 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002954 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2955 encoded_descriptors.extend(cmdline_desc[0].encode())
2956 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002957
David Zeuthen73f2afa2017-05-17 16:54:11 -04002958 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2959 if ht_desc_to_setup:
2960 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2961 ht_desc_to_setup)
2962 encoded_descriptors.extend(cmdline_desc[0].encode())
2963 encoded_descriptors.extend(cmdline_desc[1].encode())
2964
David Zeuthen21e95262016-07-27 17:58:40 -04002965 # Add kernel command-lines.
2966 if kernel_cmdlines:
2967 for i in kernel_cmdlines:
2968 desc = AvbKernelCmdlineDescriptor()
2969 desc.kernel_cmdline = i
2970 encoded_descriptors.extend(desc.encode())
2971
2972 # Add descriptors from other images.
2973 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002974 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002975 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002976 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002977 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2978 image_handler)
2979 # Bump the required libavb version to support all included descriptors.
2980 h.bump_required_libavb_version_minor(
2981 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002982 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002983 # The --include_descriptors_from_image option is used in some setups
2984 # with images A and B where both A and B contain a descriptor
2985 # for a partition with the same name. Since it's not meaningful
2986 # to include both descriptors, only include the last seen descriptor.
2987 # See bug 76386656 for details.
2988 if hasattr(desc, 'partition_name'):
2989 key = type(desc).__name__ + '_' + desc.partition_name
2990 descriptors_dict[key] = desc.encode()
2991 else:
2992 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002993 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002994 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002995
David Zeuthen18666ab2016-11-15 11:18:05 -05002996 # Load public key metadata blob, if requested.
2997 pkmd_blob = []
2998 if public_key_metadata_path:
2999 with open(public_key_metadata_path) as f:
3000 pkmd_blob = f.read()
3001
David Zeuthen21e95262016-07-27 17:58:40 -04003002 key = None
3003 encoded_key = bytearray()
3004 if alg.public_key_num_bytes > 0:
3005 if not key_path:
3006 raise AvbError('Key is required for algorithm {}'.format(
3007 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003008 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003009 if len(encoded_key) != alg.public_key_num_bytes:
3010 raise AvbError('Key is wrong size for algorithm {}'.format(
3011 algorithm_name))
3012
David Zeuthene3cadca2017-02-22 21:25:46 -05003013 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003014 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003015 if isinstance(release_string, (str, unicode)):
3016 h.release_string = release_string
3017
3018 # Append to release string, if requested. Also insert a space before.
3019 if isinstance(append_to_release_string, (str, unicode)):
3020 h.release_string += ' ' + append_to_release_string
3021
David Zeuthen18666ab2016-11-15 11:18:05 -05003022 # For the Auxiliary data block, descriptors are stored at offset 0,
3023 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003024 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003025 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003026 h.descriptors_offset = 0
3027 h.descriptors_size = len(encoded_descriptors)
3028 h.public_key_offset = h.descriptors_size
3029 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003030 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3031 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003032
3033 # For the Authentication data block, the hash is first and then
3034 # the signature.
3035 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003036 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003037 h.algorithm_type = alg.algorithm_type
3038 h.hash_offset = 0
3039 h.hash_size = alg.hash_num_bytes
3040 # Signature offset and size - it's stored right after the hash
3041 # (in Authentication data block).
3042 h.signature_offset = alg.hash_num_bytes
3043 h.signature_size = alg.signature_num_bytes
3044
3045 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003046 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003047
3048 # Generate Header data block.
3049 header_data_blob = h.encode()
3050
3051 # Generate Auxiliary data block.
3052 aux_data_blob = bytearray()
3053 aux_data_blob.extend(encoded_descriptors)
3054 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003055 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003056 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3057 aux_data_blob.extend('\0' * padding_bytes)
3058
3059 # Calculate the hash.
3060 binary_hash = bytearray()
3061 binary_signature = bytearray()
3062 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003063 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003064 ha.update(header_data_blob)
3065 ha.update(aux_data_blob)
3066 binary_hash.extend(ha.digest())
3067
3068 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003069 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003070 binary_signature.extend(raw_sign(signing_helper,
3071 signing_helper_with_files,
3072 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003073 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003074 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003075
3076 # Generate Authentication data block.
3077 auth_data_blob = bytearray()
3078 auth_data_blob.extend(binary_hash)
3079 auth_data_blob.extend(binary_signature)
3080 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3081 auth_data_blob.extend('\0' * padding_bytes)
3082
3083 return header_data_blob + auth_data_blob + aux_data_blob
3084
3085 def extract_public_key(self, key_path, output):
3086 """Implements the 'extract_public_key' command.
3087
3088 Arguments:
3089 key_path: The path to a RSA private key file.
3090 output: The file to write to.
3091 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003092 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003093
David Zeuthenb1b994d2017-03-06 18:01:31 -05003094 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3095 partition_size):
3096 """Implementation of the append_vbmeta_image command.
3097
3098 Arguments:
3099 image_filename: File to add the footer to.
3100 vbmeta_image_filename: File to get vbmeta struct from.
3101 partition_size: Size of partition.
3102
3103 Raises:
3104 AvbError: If an argument is incorrect.
3105 """
3106 image = ImageHandler(image_filename)
3107
3108 if partition_size % image.block_size != 0:
3109 raise AvbError('Partition size of {} is not a multiple of the image '
3110 'block size {}.'.format(partition_size,
3111 image.block_size))
3112
3113 # If there's already a footer, truncate the image to its original
3114 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003115 if image.image_size >= AvbFooter.SIZE:
3116 image.seek(image.image_size - AvbFooter.SIZE)
3117 try:
3118 footer = AvbFooter(image.read(AvbFooter.SIZE))
3119 # Existing footer found. Just truncate.
3120 original_image_size = footer.original_image_size
3121 image.truncate(footer.original_image_size)
3122 except (LookupError, struct.error):
3123 original_image_size = image.image_size
3124 else:
3125 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003126 original_image_size = image.image_size
3127
3128 # If anything goes wrong from here-on, restore the image back to
3129 # its original size.
3130 try:
3131 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3132 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3133
3134 # If the image isn't sparse, its size might not be a multiple of
3135 # the block size. This will screw up padding later so just grow it.
3136 if image.image_size % image.block_size != 0:
3137 assert not image.is_sparse
3138 padding_needed = image.block_size - (image.image_size%image.block_size)
3139 image.truncate(image.image_size + padding_needed)
3140
3141 # The append_raw() method requires content with size being a
3142 # multiple of |block_size| so add padding as needed. Also record
3143 # where this is written to since we'll need to put that in the
3144 # footer.
3145 vbmeta_offset = image.image_size
3146 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3147 len(vbmeta_blob))
3148 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3149
3150 # Append vbmeta blob and footer
3151 image.append_raw(vbmeta_blob_with_padding)
3152 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3153
3154 # Now insert a DONT_CARE chunk with enough bytes such that the
3155 # final Footer block is at the end of partition_size..
3156 image.append_dont_care(partition_size - vbmeta_end_offset -
3157 1*image.block_size)
3158
3159 # Generate the Footer that tells where the VBMeta footer
3160 # is. Also put enough padding in the front of the footer since
3161 # we'll write out an entire block.
3162 footer = AvbFooter()
3163 footer.original_image_size = original_image_size
3164 footer.vbmeta_offset = vbmeta_offset
3165 footer.vbmeta_size = len(vbmeta_blob)
3166 footer_blob = footer.encode()
3167 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3168 footer_blob)
3169 image.append_raw(footer_blob_with_padding)
3170
3171 except:
3172 # Truncate back to original size, then re-raise
3173 image.truncate(original_image_size)
3174 raise
3175
David Zeuthena4fee8b2016-08-22 15:20:43 -04003176 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003177 hash_algorithm, salt, chain_partitions, algorithm_name,
3178 key_path,
3179 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003180 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003181 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003182 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003183 signing_helper, signing_helper_with_files,
3184 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003185 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003186 print_required_libavb_version, use_persistent_digest,
3187 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003188 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003189
3190 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003191 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003192 partition_size: Size of partition.
3193 partition_name: Name of partition (without A/B suffix).
3194 hash_algorithm: Hash algorithm to use.
3195 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003196 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003197 algorithm_name: Name of algorithm to use.
3198 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003199 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003200 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003201 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003202 props: Properties to insert (List of strings of the form 'key:value').
3203 props_from_file: Properties to insert (List of strings 'key:<path>').
3204 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003205 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003206 dm-verity kernel cmdline from.
3207 include_descriptors_from_image: List of file objects for which
3208 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003209 calc_max_image_size: Don't store the footer - instead calculate the
3210 maximum image size leaving enough room for metadata with the
3211 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003212 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003213 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003214 release_string: None or avbtool release string.
3215 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003216 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3217 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003218 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003219 use_persistent_digest: Use a persistent digest on device.
3220 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003221
3222 Raises:
3223 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003224 """
David Zeuthen1097a782017-05-31 15:53:17 -04003225
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003226 required_libavb_version_minor = 0
3227 if use_persistent_digest or do_not_use_ab:
3228 required_libavb_version_minor = 1
3229
David Zeuthen1097a782017-05-31 15:53:17 -04003230 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003231 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003232 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003233 return
3234
David Zeuthenbf562452017-05-17 18:04:43 -04003235 # First, calculate the maximum image size such that an image
3236 # this size + metadata (footer + vbmeta struct) fits in
3237 # |partition_size|.
3238 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003239 if partition_size < max_metadata_size:
3240 raise AvbError('Parition size of {} is too small. '
3241 'Needs to be at least {}'.format(
3242 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003243 max_image_size = partition_size - max_metadata_size
3244
3245 # If we're asked to only calculate the maximum image size, we're done.
3246 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003247 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003248 return
3249
David Zeuthena4fee8b2016-08-22 15:20:43 -04003250 image = ImageHandler(image_filename)
3251
3252 if partition_size % image.block_size != 0:
3253 raise AvbError('Partition size of {} is not a multiple of the image '
3254 'block size {}.'.format(partition_size,
3255 image.block_size))
3256
David Zeuthen21e95262016-07-27 17:58:40 -04003257 # If there's already a footer, truncate the image to its original
3258 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3259 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003260 if image.image_size >= AvbFooter.SIZE:
3261 image.seek(image.image_size - AvbFooter.SIZE)
3262 try:
3263 footer = AvbFooter(image.read(AvbFooter.SIZE))
3264 # Existing footer found. Just truncate.
3265 original_image_size = footer.original_image_size
3266 image.truncate(footer.original_image_size)
3267 except (LookupError, struct.error):
3268 original_image_size = image.image_size
3269 else:
3270 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003271 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003272
3273 # If anything goes wrong from here-on, restore the image back to
3274 # its original size.
3275 try:
David Zeuthen09692692016-09-30 16:16:40 -04003276 # If image size exceeds the maximum image size, fail.
3277 if image.image_size > max_image_size:
3278 raise AvbError('Image size of {} exceeds maximum image '
3279 'size of {} in order to fit in a partition '
3280 'size of {}.'.format(image.image_size, max_image_size,
3281 partition_size))
3282
David Zeuthen21e95262016-07-27 17:58:40 -04003283 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3284 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003285 salt = binascii.unhexlify(salt)
3286 elif salt is None and not use_persistent_digest:
3287 # If salt is not explicitly specified, choose a hash that's the same
3288 # size as the hash size. Don't populate a random salt if this
3289 # descriptor is being created to use a persistent digest on device.
3290 hash_size = digest_size
3291 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003292 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003293 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003294
3295 hasher = hashlib.new(name=hash_algorithm, string=salt)
3296 # TODO(zeuthen): might want to read this in chunks to avoid
3297 # memory pressure, then again, this is only supposed to be used
3298 # on kernel/initramfs partitions. Possible optimization.
3299 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003300 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003301 digest = hasher.digest()
3302
3303 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003304 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003305 h_desc.hash_algorithm = hash_algorithm
3306 h_desc.partition_name = partition_name
3307 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003308 h_desc.flags = 0
3309 if do_not_use_ab:
3310 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3311 if not use_persistent_digest:
3312 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003313
3314 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003315 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003316 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003317 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003318 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003319 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003320 include_descriptors_from_image, signing_helper,
3321 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003322 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003323
David Zeuthend247fcb2017-02-16 12:09:27 -05003324 # Write vbmeta blob, if requested.
3325 if output_vbmeta_image:
3326 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003327
David Zeuthend247fcb2017-02-16 12:09:27 -05003328 # Append vbmeta blob and footer, unless requested not to.
3329 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003330 # If the image isn't sparse, its size might not be a multiple of
3331 # the block size. This will screw up padding later so just grow it.
3332 if image.image_size % image.block_size != 0:
3333 assert not image.is_sparse
3334 padding_needed = image.block_size - (
3335 image.image_size % image.block_size)
3336 image.truncate(image.image_size + padding_needed)
3337
3338 # The append_raw() method requires content with size being a
3339 # multiple of |block_size| so add padding as needed. Also record
3340 # where this is written to since we'll need to put that in the
3341 # footer.
3342 vbmeta_offset = image.image_size
3343 padding_needed = (
3344 round_to_multiple(len(vbmeta_blob), image.block_size) -
3345 len(vbmeta_blob))
3346 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3347
David Zeuthend247fcb2017-02-16 12:09:27 -05003348 image.append_raw(vbmeta_blob_with_padding)
3349 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3350
3351 # Now insert a DONT_CARE chunk with enough bytes such that the
3352 # final Footer block is at the end of partition_size..
3353 image.append_dont_care(partition_size - vbmeta_end_offset -
3354 1*image.block_size)
3355
3356 # Generate the Footer that tells where the VBMeta footer
3357 # is. Also put enough padding in the front of the footer since
3358 # we'll write out an entire block.
3359 footer = AvbFooter()
3360 footer.original_image_size = original_image_size
3361 footer.vbmeta_offset = vbmeta_offset
3362 footer.vbmeta_size = len(vbmeta_blob)
3363 footer_blob = footer.encode()
3364 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3365 footer_blob)
3366 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003367
David Zeuthen21e95262016-07-27 17:58:40 -04003368 except:
3369 # Truncate back to original size, then re-raise
3370 image.truncate(original_image_size)
3371 raise
3372
David Zeuthena4fee8b2016-08-22 15:20:43 -04003373 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003374 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003375 block_size, salt, chain_partitions, algorithm_name,
3376 key_path,
3377 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003378 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003379 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003380 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003381 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003382 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003383 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003384 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003385 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003386 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003387 use_persistent_root_digest, do_not_use_ab,
3388 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003389 """Implements the 'add_hashtree_footer' command.
3390
3391 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3392 more information about dm-verity and these hashes.
3393
3394 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003395 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003396 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003397 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003398 generate_fec: If True, generate FEC codes.
3399 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003400 hash_algorithm: Hash algorithm to use.
3401 block_size: Block size to use.
3402 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003403 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003404 algorithm_name: Name of algorithm to use.
3405 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003406 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003407 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003408 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003409 props: Properties to insert (List of strings of the form 'key:value').
3410 props_from_file: Properties to insert (List of strings 'key:<path>').
3411 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003412 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003413 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003414 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3415 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003416 include_descriptors_from_image: List of file objects for which
3417 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003418 calc_max_image_size: Don't store the hashtree or footer - instead
3419 calculate the maximum image size leaving enough room for hashtree
3420 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003421 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003422 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003423 release_string: None or avbtool release string.
3424 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003425 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3426 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003427 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003428 use_persistent_root_digest: Use a persistent root digest on device.
3429 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003430 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003431
3432 Raises:
3433 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003434 """
David Zeuthen1097a782017-05-31 15:53:17 -04003435
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003436 required_libavb_version_minor = 0
3437 if use_persistent_root_digest or do_not_use_ab:
3438 required_libavb_version_minor = 1
3439
David Zeuthen1097a782017-05-31 15:53:17 -04003440 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003441 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003442 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003443 return
3444
David Zeuthen09692692016-09-30 16:16:40 -04003445 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3446 digest_padding = round_to_pow2(digest_size) - digest_size
3447
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003448 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3449 # size such that an image this size + the hashtree + metadata (footer +
3450 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3451 # for metadata.
3452 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003453 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003454 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003455 if not no_hashtree:
3456 (_, max_tree_size) = calc_hash_level_offsets(
3457 partition_size, block_size, digest_size + digest_padding)
3458 if generate_fec:
3459 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003460 max_metadata_size = (max_fec_size + max_tree_size +
3461 self.MAX_VBMETA_SIZE +
3462 self.MAX_FOOTER_SIZE)
3463 max_image_size = partition_size - max_metadata_size
3464 else:
3465 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003466
3467 # If we're asked to only calculate the maximum image size, we're done.
3468 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003469 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003470 return
3471
David Zeuthena4fee8b2016-08-22 15:20:43 -04003472 image = ImageHandler(image_filename)
3473
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003474 if partition_size > 0:
3475 if partition_size % image.block_size != 0:
3476 raise AvbError('Partition size of {} is not a multiple of the image '
3477 'block size {}.'.format(partition_size,
3478 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003479 elif image.image_size % image.block_size != 0:
3480 raise AvbError('File size of {} is not a multiple of the image '
3481 'block size {}.'.format(image.image_size,
3482 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003483
David Zeuthen21e95262016-07-27 17:58:40 -04003484 # If there's already a footer, truncate the image to its original
3485 # size. This way 'avbtool add_hashtree_footer' is idempotent
3486 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003487 if image.image_size >= AvbFooter.SIZE:
3488 image.seek(image.image_size - AvbFooter.SIZE)
3489 try:
3490 footer = AvbFooter(image.read(AvbFooter.SIZE))
3491 # Existing footer found. Just truncate.
3492 original_image_size = footer.original_image_size
3493 image.truncate(footer.original_image_size)
3494 except (LookupError, struct.error):
3495 original_image_size = image.image_size
3496 else:
3497 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003498 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003499
3500 # If anything goes wrong from here-on, restore the image back to
3501 # its original size.
3502 try:
3503 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003504 rounded_image_size = round_to_multiple(image.image_size, block_size)
3505 if rounded_image_size > image.image_size:
3506 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003507
David Zeuthen09692692016-09-30 16:16:40 -04003508 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003509 if partition_size > 0:
3510 if image.image_size > max_image_size:
3511 raise AvbError('Image size of {} exceeds maximum image '
3512 'size of {} in order to fit in a partition '
3513 'size of {}.'.format(image.image_size, max_image_size,
3514 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003515
3516 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003517 salt = binascii.unhexlify(salt)
3518 elif salt is None and not use_persistent_root_digest:
3519 # If salt is not explicitly specified, choose a hash that's the same
3520 # size as the hash size. Don't populate a random salt if this
3521 # descriptor is being created to use a persistent digest on device.
3522 hash_size = digest_size
3523 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003524 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003525 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003526
David Zeuthena4fee8b2016-08-22 15:20:43 -04003527 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003528 # offsets in advance.
3529 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003530 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003531
David Zeuthena4fee8b2016-08-22 15:20:43 -04003532 # If the image isn't sparse, its size might not be a multiple of
3533 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003534 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003535 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003536 padding_needed = image.block_size - (image.image_size%image.block_size)
3537 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003538
David Zeuthena4fee8b2016-08-22 15:20:43 -04003539 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003540 tree_offset = image.image_size
3541 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003542 block_size,
3543 hash_algorithm, salt,
3544 digest_padding,
3545 hash_level_offsets,
3546 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003547
3548 # Generate HashtreeDescriptor with details about the tree we
3549 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003550 if no_hashtree:
3551 tree_size = 0
3552 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003553 ht_desc = AvbHashtreeDescriptor()
3554 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003555 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003556 ht_desc.tree_offset = tree_offset
3557 ht_desc.tree_size = tree_size
3558 ht_desc.data_block_size = block_size
3559 ht_desc.hash_block_size = block_size
3560 ht_desc.hash_algorithm = hash_algorithm
3561 ht_desc.partition_name = partition_name
3562 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003563 if do_not_use_ab:
3564 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3565 if not use_persistent_root_digest:
3566 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003567
David Zeuthen09692692016-09-30 16:16:40 -04003568 # Write the hash tree
3569 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3570 len(hash_tree))
3571 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3572 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003573 len_hashtree_and_fec = len(hash_tree_with_padding)
3574
3575 # Generate FEC codes, if requested.
3576 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003577 if no_hashtree:
3578 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003579 else:
3580 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003581 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3582 len(fec_data))
3583 fec_data_with_padding = fec_data + '\0'*padding_needed
3584 fec_offset = image.image_size
3585 image.append_raw(fec_data_with_padding)
3586 len_hashtree_and_fec += len(fec_data_with_padding)
3587 # Update the hashtree descriptor.
3588 ht_desc.fec_num_roots = fec_num_roots
3589 ht_desc.fec_offset = fec_offset
3590 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003591
David Zeuthen73f2afa2017-05-17 16:54:11 -04003592 ht_desc_to_setup = None
3593 if setup_as_rootfs_from_kernel:
3594 ht_desc_to_setup = ht_desc
3595
David Zeuthena4fee8b2016-08-22 15:20:43 -04003596 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003597 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003598 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003599 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003600 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003601 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003602 include_descriptors_from_image, signing_helper,
3603 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003604 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003605 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3606 len(vbmeta_blob))
3607 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003608
David Zeuthend247fcb2017-02-16 12:09:27 -05003609 # Write vbmeta blob, if requested.
3610 if output_vbmeta_image:
3611 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003612
David Zeuthend247fcb2017-02-16 12:09:27 -05003613 # Append vbmeta blob and footer, unless requested not to.
3614 if not do_not_append_vbmeta_image:
3615 image.append_raw(vbmeta_blob_with_padding)
3616
3617 # Now insert a DONT_CARE chunk with enough bytes such that the
3618 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003619 if partition_size > 0:
3620 image.append_dont_care(partition_size - image.image_size -
3621 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003622
3623 # Generate the Footer that tells where the VBMeta footer
3624 # is. Also put enough padding in the front of the footer since
3625 # we'll write out an entire block.
3626 footer = AvbFooter()
3627 footer.original_image_size = original_image_size
3628 footer.vbmeta_offset = vbmeta_offset
3629 footer.vbmeta_size = len(vbmeta_blob)
3630 footer_blob = footer.encode()
3631 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3632 footer_blob)
3633 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003634
David Zeuthen21e95262016-07-27 17:58:40 -04003635 except:
David Zeuthen09692692016-09-30 16:16:40 -04003636 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003637 image.truncate(original_image_size)
3638 raise
3639
David Zeuthenc68f0822017-03-31 17:22:35 -04003640 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003641 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003642 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003643 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003644 """Implements the 'make_atx_certificate' command.
3645
3646 Android Things certificates are required for Android Things public key
3647 metadata. They chain the vbmeta signing key for a particular product back to
3648 a fused, permanent root key. These certificates are fixed-length and fixed-
3649 format with the explicit goal of not parsing ASN.1 in bootloader code.
3650
3651 Arguments:
3652 output: Certificate will be written to this file on success.
3653 authority_key_path: A PEM file path with the authority private key.
3654 If None, then a certificate will be created without a
3655 signature. The signature can be created out-of-band
3656 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003657 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003658 subject_key_version: A 64-bit version value. If this is None, the number
3659 of seconds since the epoch is used.
3660 subject: A subject identifier. For Product Signing Key certificates this
3661 should be the same Product ID found in the permanent attributes.
3662 is_intermediate_authority: True if the certificate is for an intermediate
3663 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003664 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003665 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003666 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003667 """
3668 signed_data = bytearray()
3669 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003670 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003671 hasher = hashlib.sha256()
3672 hasher.update(subject)
3673 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003674 if not usage:
3675 usage = 'com.google.android.things.vboot'
3676 if is_intermediate_authority:
3677 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003678 hasher = hashlib.sha256()
3679 hasher.update(usage)
3680 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003681 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003682 subject_key_version = int(time.time())
3683 signed_data.extend(struct.pack('<Q', subject_key_version))
3684 signature = bytearray()
3685 if authority_key_path:
3686 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003687 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003688 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003689 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003690 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003691 hasher.update(signed_data)
3692 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003693 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3694 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003695 alg.signature_num_bytes, authority_key_path,
3696 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003697 output.write(signed_data)
3698 output.write(signature)
3699
David Zeuthenc68f0822017-03-31 17:22:35 -04003700 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003701 product_id):
3702 """Implements the 'make_atx_permanent_attributes' command.
3703
3704 Android Things permanent attributes are designed to be permanent for a
3705 particular product and a hash of these attributes should be fused into
3706 hardware to enforce this.
3707
3708 Arguments:
3709 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003710 root_authority_key_path: Path to a PEM or DER public key for
3711 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003712 product_id: A 16-byte Product ID.
3713
3714 Raises:
3715 AvbError: If an argument is incorrect.
3716 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003717 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003718 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003719 raise AvbError('Invalid Product ID length.')
3720 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003721 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003722 output.write(product_id)
3723
3724 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003725 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003726 """Implements the 'make_atx_metadata' command.
3727
3728 Android Things metadata are included in vbmeta images to facilitate
3729 verification. The output of this command can be used as the
3730 public_key_metadata argument to other commands.
3731
3732 Arguments:
3733 output: Metadata will be written to this file on success.
3734 intermediate_key_certificate: A certificate file as output by
3735 make_atx_certificate with
3736 is_intermediate_authority set to true.
3737 product_key_certificate: A certificate file as output by
3738 make_atx_certificate with
3739 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003740
3741 Raises:
3742 AvbError: If an argument is incorrect.
3743 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003744 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003745 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003746 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003747 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003748 raise AvbError('Invalid product key certificate length.')
3749 output.write(struct.pack('<I', 1)) # Format Version
3750 output.write(intermediate_key_certificate)
3751 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003752
Darren Krahnfccd64e2018-01-16 17:39:35 -08003753 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3754 unlock_key_certificate, challenge_path,
3755 unlock_key_path, signing_helper,
3756 signing_helper_with_files):
3757 """Implements the 'make_atx_unlock_credential' command.
3758
3759 Android Things unlock credentials can be used to authorize the unlock of AVB
3760 on a device. These credentials are presented to an Android Things bootloader
3761 via the fastboot interface in response to a 16-byte challenge. This method
3762 creates all fields of the credential except the challenge signature field
3763 (which is the last field) and can optionally create the challenge signature
3764 field as well if a challenge and the unlock_key_path is provided.
3765
3766 Arguments:
3767 output: The credential will be written to this file on success.
3768 intermediate_key_certificate: A certificate file as output by
3769 make_atx_certificate with
3770 is_intermediate_authority set to true.
3771 unlock_key_certificate: A certificate file as output by
3772 make_atx_certificate with
3773 is_intermediate_authority set to false and the
3774 usage set to
3775 'com.google.android.things.vboot.unlock'.
3776 challenge_path: [optional] A path to the challenge to sign.
3777 unlock_key_path: [optional] A PEM file path with the unlock private key.
3778 signing_helper: Program which signs a hash and returns the signature.
3779 signing_helper_with_files: Same as signing_helper but uses files instead.
3780
3781 Raises:
3782 AvbError: If an argument is incorrect.
3783 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003784 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3785 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003786 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3787 raise AvbError('Invalid intermediate key certificate length.')
3788 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3789 raise AvbError('Invalid product key certificate length.')
3790 challenge = bytearray()
3791 if challenge_path:
3792 with open(challenge_path, 'r') as f:
3793 challenge = f.read()
3794 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3795 raise AvbError('Invalid unlock challenge length.')
3796 output.write(struct.pack('<I', 1)) # Format Version
3797 output.write(intermediate_key_certificate)
3798 output.write(unlock_key_certificate)
3799 if challenge_path and unlock_key_path:
3800 signature = bytearray()
3801 padding_and_hash = bytearray()
3802 algorithm_name = 'SHA512_RSA4096'
3803 alg = ALGORITHMS[algorithm_name]
3804 hasher = hashlib.sha512()
3805 padding_and_hash.extend(alg.padding)
3806 hasher.update(challenge)
3807 padding_and_hash.extend(hasher.digest())
3808 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3809 algorithm_name,
3810 alg.signature_num_bytes, unlock_key_path,
3811 padding_and_hash))
3812 output.write(signature)
3813
David Zeuthen21e95262016-07-27 17:58:40 -04003814
3815def calc_hash_level_offsets(image_size, block_size, digest_size):
3816 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3817
3818 Arguments:
3819 image_size: The size of the image to calculate a Merkle-tree for.
3820 block_size: The block size, e.g. 4096.
3821 digest_size: The size of each hash, e.g. 32 for SHA-256.
3822
3823 Returns:
3824 A tuple where the first argument is an array of offsets and the
3825 second is size of the tree, in bytes.
3826 """
3827 level_offsets = []
3828 level_sizes = []
3829 tree_size = 0
3830
3831 num_levels = 0
3832 size = image_size
3833 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003834 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003835 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3836
3837 level_sizes.append(level_size)
3838 tree_size += level_size
3839 num_levels += 1
3840
3841 size = level_size
3842
3843 for n in range(0, num_levels):
3844 offset = 0
3845 for m in range(n + 1, num_levels):
3846 offset += level_sizes[m]
3847 level_offsets.append(offset)
3848
David Zeuthena4fee8b2016-08-22 15:20:43 -04003849 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003850
3851
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003852# See system/extras/libfec/include/fec/io.h for these definitions.
3853FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3854FEC_MAGIC = 0xfecfecfe
3855
3856
3857def calc_fec_data_size(image_size, num_roots):
3858 """Calculates how much space FEC data will take.
3859
Jan Monschfe00c0a2019-12-11 11:19:40 +01003860 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003861 image_size: The size of the image.
3862 num_roots: Number of roots.
3863
3864 Returns:
3865 The number of bytes needed for FEC for an image of the given size
3866 and with the requested number of FEC roots.
3867
3868 Raises:
3869 ValueError: If output from the 'fec' tool is invalid.
3870
3871 """
3872 p = subprocess.Popen(
3873 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3874 stdout=subprocess.PIPE,
3875 stderr=subprocess.PIPE)
3876 (pout, perr) = p.communicate()
3877 retcode = p.wait()
3878 if retcode != 0:
3879 raise ValueError('Error invoking fec: {}'.format(perr))
3880 return int(pout)
3881
3882
3883def generate_fec_data(image_filename, num_roots):
3884 """Generate FEC codes for an image.
3885
Jan Monschfe00c0a2019-12-11 11:19:40 +01003886 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003887 image_filename: The filename of the image.
3888 num_roots: Number of roots.
3889
3890 Returns:
3891 The FEC data blob.
3892
3893 Raises:
3894 ValueError: If output from the 'fec' tool is invalid.
3895 """
3896 fec_tmpfile = tempfile.NamedTemporaryFile()
3897 subprocess.check_call(
3898 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3899 fec_tmpfile.name],
3900 stderr=open(os.devnull))
3901 fec_data = fec_tmpfile.read()
3902 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3903 footer_data = fec_data[-footer_size:]
3904 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3905 footer_data)
3906 if magic != FEC_MAGIC:
3907 raise ValueError('Unexpected magic in FEC footer')
3908 return fec_data[0:fec_size]
3909
3910
David Zeuthen21e95262016-07-27 17:58:40 -04003911def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003912 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003913 """Generates a Merkle-tree for a file.
3914
Jan Monschfe00c0a2019-12-11 11:19:40 +01003915 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003916 image: The image, as a file.
3917 image_size: The size of the image.
3918 block_size: The block size, e.g. 4096.
3919 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3920 salt: The salt to use.
3921 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003922 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003923 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003924
3925 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003926 A tuple where the first element is the top-level hash and the
3927 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003928 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003929 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003930 hash_src_offset = 0
3931 hash_src_size = image_size
3932 level_num = 0
3933 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003934 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003935 remaining = hash_src_size
3936 while remaining > 0:
3937 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003938 # Only read from the file for the first level - for subsequent
3939 # levels, access the array we're building.
3940 if level_num == 0:
3941 image.seek(hash_src_offset + hash_src_size - remaining)
3942 data = image.read(min(remaining, block_size))
3943 else:
3944 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3945 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003946 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003947
3948 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003949 if len(data) < block_size:
3950 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003951 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003952 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003953 level_output_list.append('\0' * digest_padding)
3954
3955 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003956
3957 padding_needed = (round_to_multiple(
3958 len(level_output), block_size) - len(level_output))
3959 level_output += '\0' * padding_needed
3960
David Zeuthena4fee8b2016-08-22 15:20:43 -04003961 # Copy level-output into resulting tree.
3962 offset = hash_level_offsets[level_num]
3963 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003964
David Zeuthena4fee8b2016-08-22 15:20:43 -04003965 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003966 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003967 level_num += 1
3968
3969 hasher = hashlib.new(name=hash_alg_name, string=salt)
3970 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003971 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003972
3973
3974class AvbTool(object):
3975 """Object for avbtool command-line tool."""
3976
3977 def __init__(self):
3978 """Initializer method."""
3979 self.avb = Avb()
3980
3981 def _add_common_args(self, sub_parser):
3982 """Adds arguments used by several sub-commands.
3983
3984 Arguments:
3985 sub_parser: The parser to add arguments to.
3986 """
3987 sub_parser.add_argument('--algorithm',
3988 help='Algorithm to use (default: NONE)',
3989 metavar='ALGORITHM',
3990 default='NONE')
3991 sub_parser.add_argument('--key',
3992 help='Path to RSA private key file',
3993 metavar='KEY',
3994 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003995 sub_parser.add_argument('--signing_helper',
3996 help='Path to helper used for signing',
3997 metavar='APP',
3998 default=None,
3999 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004000 sub_parser.add_argument('--signing_helper_with_files',
4001 help='Path to helper used for signing using files',
4002 metavar='APP',
4003 default=None,
4004 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004005 sub_parser.add_argument('--public_key_metadata',
4006 help='Path to public key metadata file',
4007 metavar='KEY_METADATA',
4008 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004009 sub_parser.add_argument('--rollback_index',
4010 help='Rollback Index',
4011 type=parse_number,
4012 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004013 # This is used internally for unit tests. Do not include in --help output.
4014 sub_parser.add_argument('--internal_release_string',
4015 help=argparse.SUPPRESS)
4016 sub_parser.add_argument('--append_to_release_string',
4017 help='Text to append to release string',
4018 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004019 sub_parser.add_argument('--prop',
4020 help='Add property',
4021 metavar='KEY:VALUE',
4022 action='append')
4023 sub_parser.add_argument('--prop_from_file',
4024 help='Add property from file',
4025 metavar='KEY:PATH',
4026 action='append')
4027 sub_parser.add_argument('--kernel_cmdline',
4028 help='Add kernel cmdline',
4029 metavar='CMDLINE',
4030 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004031 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4032 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4033 # at some future point.
4034 sub_parser.add_argument('--setup_rootfs_from_kernel',
4035 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004036 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004037 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004038 type=argparse.FileType('rb'))
4039 sub_parser.add_argument('--include_descriptors_from_image',
4040 help='Include descriptors from image',
4041 metavar='IMAGE',
4042 action='append',
4043 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004044 sub_parser.add_argument('--print_required_libavb_version',
4045 help=('Don\'t store the footer - '
4046 'instead calculate the required libavb '
4047 'version for the given options.'),
4048 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004049 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4050 sub_parser.add_argument('--chain_partition',
4051 help='Allow signed integrity-data for partition',
4052 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4053 action='append')
4054 sub_parser.add_argument('--flags',
4055 help='VBMeta flags',
4056 type=parse_number,
4057 default=0)
4058 sub_parser.add_argument('--set_hashtree_disabled_flag',
4059 help='Set the HASHTREE_DISABLED flag',
4060 action='store_true')
4061
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004062 def _add_common_footer_args(self, sub_parser):
4063 """Adds arguments used by add_*_footer sub-commands.
4064
4065 Arguments:
4066 sub_parser: The parser to add arguments to.
4067 """
4068 sub_parser.add_argument('--use_persistent_digest',
4069 help='Use a persistent digest on device instead of '
4070 'storing the digest in the descriptor. This '
4071 'cannot be used with A/B so must be combined '
4072 'with --do_not_use_ab when an A/B suffix is '
4073 'expected at runtime.',
4074 action='store_true')
4075 sub_parser.add_argument('--do_not_use_ab',
4076 help='The partition does not use A/B even when an '
4077 'A/B suffix is present. This must not be used '
4078 'for vbmeta or chained partitions.',
4079 action='store_true')
4080
David Zeuthena5fd3a42017-02-27 16:38:54 -05004081 def _fixup_common_args(self, args):
4082 """Common fixups needed by subcommands.
4083
4084 Arguments:
4085 args: Arguments to modify.
4086
4087 Returns:
4088 The modified arguments.
4089 """
4090 if args.set_hashtree_disabled_flag:
4091 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4092 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004093
4094 def run(self, argv):
4095 """Command-line processor.
4096
4097 Arguments:
4098 argv: Pass sys.argv from main.
4099 """
4100 parser = argparse.ArgumentParser()
4101 subparsers = parser.add_subparsers(title='subcommands')
4102
Jan Monsch2c7be992020-04-03 14:37:13 +02004103 sub_parser = subparsers.add_parser(
4104 'generate_test_image',
4105 help=('Generates a test image with a known pattern for testing: '
4106 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4107 sub_parser.add_argument('--image_size',
4108 help='Size of image to generate.',
4109 type=parse_number,
4110 required=True)
4111 sub_parser.add_argument('--start_byte',
4112 help='Integer for the start byte of the pattern.',
4113 type=parse_number,
4114 default=0)
4115 sub_parser.add_argument('--output',
4116 help='Output file name.',
4117 type=argparse.FileType('wt'),
4118 default=sys.stdout)
4119 sub_parser.set_defaults(func=self.generate_test_image)
4120
David Zeuthen21e95262016-07-27 17:58:40 -04004121 sub_parser = subparsers.add_parser('version',
4122 help='Prints version of avbtool.')
4123 sub_parser.set_defaults(func=self.version)
4124
4125 sub_parser = subparsers.add_parser('extract_public_key',
4126 help='Extract public key.')
4127 sub_parser.add_argument('--key',
4128 help='Path to RSA private key file',
4129 required=True)
4130 sub_parser.add_argument('--output',
4131 help='Output file name',
4132 type=argparse.FileType('wb'),
4133 required=True)
4134 sub_parser.set_defaults(func=self.extract_public_key)
4135
4136 sub_parser = subparsers.add_parser('make_vbmeta_image',
4137 help='Makes a vbmeta image.')
4138 sub_parser.add_argument('--output',
4139 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004140 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004141 sub_parser.add_argument('--padding_size',
4142 metavar='NUMBER',
4143 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004144 'its size is a multiple of NUMBER '
4145 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004146 type=parse_number,
4147 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004148 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004149 sub_parser.set_defaults(func=self.make_vbmeta_image)
4150
4151 sub_parser = subparsers.add_parser('add_hash_footer',
4152 help='Add hashes and footer to image.')
4153 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004154 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004155 type=argparse.FileType('rab+'))
4156 sub_parser.add_argument('--partition_size',
4157 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004158 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004159 sub_parser.add_argument('--partition_name',
4160 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004161 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004162 sub_parser.add_argument('--hash_algorithm',
4163 help='Hash algorithm to use (default: sha256)',
4164 default='sha256')
4165 sub_parser.add_argument('--salt',
4166 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004167 sub_parser.add_argument('--calc_max_image_size',
4168 help=('Don\'t store the footer - '
4169 'instead calculate the maximum image size '
4170 'leaving enough room for metadata with '
4171 'the given partition size.'),
4172 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004173 sub_parser.add_argument('--output_vbmeta_image',
4174 help='Also write vbmeta struct to file',
4175 type=argparse.FileType('wb'))
4176 sub_parser.add_argument('--do_not_append_vbmeta_image',
4177 help=('Do not append vbmeta struct or footer '
4178 'to the image'),
4179 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004180 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004181 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004182 sub_parser.set_defaults(func=self.add_hash_footer)
4183
David Zeuthenb1b994d2017-03-06 18:01:31 -05004184 sub_parser = subparsers.add_parser('append_vbmeta_image',
4185 help='Append vbmeta image to image.')
4186 sub_parser.add_argument('--image',
4187 help='Image to append vbmeta blob to',
4188 type=argparse.FileType('rab+'))
4189 sub_parser.add_argument('--partition_size',
4190 help='Partition size',
4191 type=parse_number,
4192 required=True)
4193 sub_parser.add_argument('--vbmeta_image',
4194 help='Image with vbmeta blob to append',
4195 type=argparse.FileType('rb'))
4196 sub_parser.set_defaults(func=self.append_vbmeta_image)
4197
Jan Monscheeb28b62019-12-05 16:17:09 +01004198 sub_parser = subparsers.add_parser(
4199 'add_hashtree_footer',
4200 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004201 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004202 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004203 type=argparse.FileType('rab+'))
4204 sub_parser.add_argument('--partition_size',
4205 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004206 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004207 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004208 sub_parser.add_argument('--partition_name',
4209 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004210 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004211 sub_parser.add_argument('--hash_algorithm',
4212 help='Hash algorithm to use (default: sha1)',
4213 default='sha1')
4214 sub_parser.add_argument('--salt',
4215 help='Salt in hex (default: /dev/urandom)')
4216 sub_parser.add_argument('--block_size',
4217 help='Block size (default: 4096)',
4218 type=parse_number,
4219 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004220 # TODO(zeuthen): The --generate_fec option was removed when we
4221 # moved to generating FEC by default. To avoid breaking existing
4222 # users needing to transition we simply just print a warning below
4223 # in add_hashtree_footer(). Remove this option and the warning at
4224 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004225 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004226 help=argparse.SUPPRESS,
4227 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004228 sub_parser.add_argument(
4229 '--do_not_generate_fec',
4230 help='Do not generate forward-error-correction codes',
4231 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004232 sub_parser.add_argument('--fec_num_roots',
4233 help='Number of roots for FEC (default: 2)',
4234 type=parse_number,
4235 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004236 sub_parser.add_argument('--calc_max_image_size',
4237 help=('Don\'t store the hashtree or footer - '
4238 'instead calculate the maximum image size '
4239 'leaving enough room for hashtree '
4240 'and metadata with the given partition '
4241 'size.'),
4242 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004243 sub_parser.add_argument('--output_vbmeta_image',
4244 help='Also write vbmeta struct to file',
4245 type=argparse.FileType('wb'))
4246 sub_parser.add_argument('--do_not_append_vbmeta_image',
4247 help=('Do not append vbmeta struct or footer '
4248 'to the image'),
4249 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004250 # This is different from --setup_rootfs_from_kernel insofar that
4251 # it doesn't take an IMAGE, the generated cmdline will be for the
4252 # hashtree we're adding.
4253 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4254 action='store_true',
4255 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004256 sub_parser.add_argument('--no_hashtree',
4257 action='store_true',
4258 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004259 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004260 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004261 sub_parser.set_defaults(func=self.add_hashtree_footer)
4262
4263 sub_parser = subparsers.add_parser('erase_footer',
4264 help='Erase footer from an image.')
4265 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004266 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004267 type=argparse.FileType('rwb+'),
4268 required=True)
4269 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004270 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004271 action='store_true')
4272 sub_parser.set_defaults(func=self.erase_footer)
4273
David Zeuthen1394f762019-04-30 10:20:11 -04004274 sub_parser = subparsers.add_parser('zero_hashtree',
4275 help='Zero out hashtree and FEC data.')
4276 sub_parser.add_argument('--image',
4277 help='Image with a footer',
4278 type=argparse.FileType('rwb+'),
4279 required=True)
4280 sub_parser.set_defaults(func=self.zero_hashtree)
4281
Jan Monscheeb28b62019-12-05 16:17:09 +01004282 sub_parser = subparsers.add_parser(
4283 'extract_vbmeta_image',
4284 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004285 sub_parser.add_argument('--image',
4286 help='Image with footer',
4287 type=argparse.FileType('rb'),
4288 required=True)
4289 sub_parser.add_argument('--output',
4290 help='Output file name',
4291 type=argparse.FileType('wb'))
4292 sub_parser.add_argument('--padding_size',
4293 metavar='NUMBER',
4294 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004295 'its size is a multiple of NUMBER '
4296 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004297 type=parse_number,
4298 default=0)
4299 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4300
David Zeuthen2bc232b2017-04-19 14:25:19 -04004301 sub_parser = subparsers.add_parser('resize_image',
4302 help='Resize image with a footer.')
4303 sub_parser.add_argument('--image',
4304 help='Image with a footer',
4305 type=argparse.FileType('rwb+'),
4306 required=True)
4307 sub_parser.add_argument('--partition_size',
4308 help='New partition size',
4309 type=parse_number)
4310 sub_parser.set_defaults(func=self.resize_image)
4311
David Zeuthen21e95262016-07-27 17:58:40 -04004312 sub_parser = subparsers.add_parser(
4313 'info_image',
4314 help='Show information about vbmeta or footer.')
4315 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004316 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004317 type=argparse.FileType('rb'),
4318 required=True)
4319 sub_parser.add_argument('--output',
4320 help='Write info to file',
4321 type=argparse.FileType('wt'),
4322 default=sys.stdout)
4323 sub_parser.set_defaults(func=self.info_image)
4324
David Zeuthenb623d8b2017-04-04 16:05:53 -04004325 sub_parser = subparsers.add_parser(
4326 'verify_image',
4327 help='Verify an image.')
4328 sub_parser.add_argument('--image',
4329 help='Image to verify',
4330 type=argparse.FileType('rb'),
4331 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004332 sub_parser.add_argument('--key',
4333 help='Check embedded public key matches KEY',
4334 metavar='KEY',
4335 required=False)
4336 sub_parser.add_argument('--expected_chain_partition',
4337 help='Expected chain partition',
4338 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4339 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004340 sub_parser.add_argument(
4341 '--follow_chain_partitions',
4342 help=('Follows chain partitions even when not '
4343 'specified with the --expected_chain_partition option'),
4344 action='store_true')
4345 sub_parser.add_argument(
4346 '--accept_zeroed_hashtree',
4347 help=('Accept images where the hashtree or FEC data is zeroed out'),
4348 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004349 sub_parser.set_defaults(func=self.verify_image)
4350
David Zeuthenb8643c02018-05-17 17:21:18 -04004351 sub_parser = subparsers.add_parser(
4352 'calculate_vbmeta_digest',
4353 help='Calculate vbmeta digest.')
4354 sub_parser.add_argument('--image',
4355 help='Image to calculate digest for',
4356 type=argparse.FileType('rb'),
4357 required=True)
4358 sub_parser.add_argument('--hash_algorithm',
4359 help='Hash algorithm to use (default: sha256)',
4360 default='sha256')
4361 sub_parser.add_argument('--output',
4362 help='Write hex digest to file (default: stdout)',
4363 type=argparse.FileType('wt'),
4364 default=sys.stdout)
4365 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4366
David Zeuthenf7d2e752018-09-20 13:30:41 -04004367 sub_parser = subparsers.add_parser(
4368 'calculate_kernel_cmdline',
4369 help='Calculate kernel cmdline.')
4370 sub_parser.add_argument('--image',
4371 help='Image to calculate kernel cmdline for',
4372 type=argparse.FileType('rb'),
4373 required=True)
4374 sub_parser.add_argument('--hashtree_disabled',
4375 help='Return the cmdline for hashtree disabled',
4376 action='store_true')
4377 sub_parser.add_argument('--output',
4378 help='Write cmdline to file (default: stdout)',
4379 type=argparse.FileType('wt'),
4380 default=sys.stdout)
4381 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4382
David Zeuthen8b6973b2016-09-20 12:39:49 -04004383 sub_parser = subparsers.add_parser('set_ab_metadata',
4384 help='Set A/B metadata.')
4385 sub_parser.add_argument('--misc_image',
4386 help=('The misc image to modify. If the image does '
4387 'not exist, it will be created.'),
4388 type=argparse.FileType('r+b'),
4389 required=True)
4390 sub_parser.add_argument('--slot_data',
4391 help=('Slot data of the form "priority", '
4392 '"tries_remaining", "sucessful_boot" for '
4393 'slot A followed by the same for slot B, '
4394 'separated by colons. The default value '
4395 'is 15:7:0:14:7:0.'),
4396 default='15:7:0:14:7:0')
4397 sub_parser.set_defaults(func=self.set_ab_metadata)
4398
Darren Krahn147b08d2016-12-20 16:38:29 -08004399 sub_parser = subparsers.add_parser(
4400 'make_atx_certificate',
4401 help='Create an Android Things eXtension (ATX) certificate.')
4402 sub_parser.add_argument('--output',
4403 help='Write certificate to file',
4404 type=argparse.FileType('wb'),
4405 default=sys.stdout)
4406 sub_parser.add_argument('--subject',
4407 help=('Path to subject file'),
4408 type=argparse.FileType('rb'),
4409 required=True)
4410 sub_parser.add_argument('--subject_key',
4411 help=('Path to subject RSA public key file'),
4412 type=argparse.FileType('rb'),
4413 required=True)
4414 sub_parser.add_argument('--subject_key_version',
4415 help=('Version of the subject key'),
4416 type=parse_number,
4417 required=False)
4418 sub_parser.add_argument('--subject_is_intermediate_authority',
4419 help=('Generate an intermediate authority '
4420 'certificate'),
4421 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004422 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004423 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004424 'string'),
4425 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004426 sub_parser.add_argument('--authority_key',
4427 help='Path to authority RSA private key file',
4428 required=False)
4429 sub_parser.add_argument('--signing_helper',
4430 help='Path to helper used for signing',
4431 metavar='APP',
4432 default=None,
4433 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004434 sub_parser.add_argument('--signing_helper_with_files',
4435 help='Path to helper used for signing using files',
4436 metavar='APP',
4437 default=None,
4438 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004439 sub_parser.set_defaults(func=self.make_atx_certificate)
4440
4441 sub_parser = subparsers.add_parser(
4442 'make_atx_permanent_attributes',
4443 help='Create Android Things eXtension (ATX) permanent attributes.')
4444 sub_parser.add_argument('--output',
4445 help='Write attributes to file',
4446 type=argparse.FileType('wb'),
4447 default=sys.stdout)
4448 sub_parser.add_argument('--root_authority_key',
4449 help='Path to authority RSA public key file',
4450 type=argparse.FileType('rb'),
4451 required=True)
4452 sub_parser.add_argument('--product_id',
4453 help=('Path to Product ID file'),
4454 type=argparse.FileType('rb'),
4455 required=True)
4456 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4457
4458 sub_parser = subparsers.add_parser(
4459 'make_atx_metadata',
4460 help='Create Android Things eXtension (ATX) metadata.')
4461 sub_parser.add_argument('--output',
4462 help='Write metadata to file',
4463 type=argparse.FileType('wb'),
4464 default=sys.stdout)
4465 sub_parser.add_argument('--intermediate_key_certificate',
4466 help='Path to intermediate key certificate file',
4467 type=argparse.FileType('rb'),
4468 required=True)
4469 sub_parser.add_argument('--product_key_certificate',
4470 help='Path to product key certificate file',
4471 type=argparse.FileType('rb'),
4472 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004473 sub_parser.set_defaults(func=self.make_atx_metadata)
4474
Darren Krahnfccd64e2018-01-16 17:39:35 -08004475 sub_parser = subparsers.add_parser(
4476 'make_atx_unlock_credential',
4477 help='Create an Android Things eXtension (ATX) unlock credential.')
4478 sub_parser.add_argument('--output',
4479 help='Write credential to file',
4480 type=argparse.FileType('wb'),
4481 default=sys.stdout)
4482 sub_parser.add_argument('--intermediate_key_certificate',
4483 help='Path to intermediate key certificate file',
4484 type=argparse.FileType('rb'),
4485 required=True)
4486 sub_parser.add_argument('--unlock_key_certificate',
4487 help='Path to unlock key certificate file',
4488 type=argparse.FileType('rb'),
4489 required=True)
4490 sub_parser.add_argument('--challenge',
4491 help='Path to the challenge to sign (optional). If '
4492 'this is not provided the challenge signature '
4493 'field is omitted and can be concatenated '
4494 'later.',
4495 required=False)
4496 sub_parser.add_argument('--unlock_key',
4497 help='Path to unlock key (optional). Must be '
4498 'provided if using --challenge.',
4499 required=False)
4500 sub_parser.add_argument('--signing_helper',
4501 help='Path to helper used for signing',
4502 metavar='APP',
4503 default=None,
4504 required=False)
4505 sub_parser.add_argument('--signing_helper_with_files',
4506 help='Path to helper used for signing using files',
4507 metavar='APP',
4508 default=None,
4509 required=False)
4510 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4511
David Zeuthen21e95262016-07-27 17:58:40 -04004512 args = parser.parse_args(argv[1:])
4513 try:
4514 args.func(args)
4515 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004516 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004517 sys.exit(1)
4518
4519 def version(self, _):
4520 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004521 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004522
Jan Monsch2c7be992020-04-03 14:37:13 +02004523 def generate_test_image(self, args):
4524 """Implements the 'generate_test_image' sub-command."""
4525 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4526
David Zeuthen21e95262016-07-27 17:58:40 -04004527 def extract_public_key(self, args):
4528 """Implements the 'extract_public_key' sub-command."""
4529 self.avb.extract_public_key(args.key, args.output)
4530
4531 def make_vbmeta_image(self, args):
4532 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004533 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004534 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004535 args.algorithm, args.key,
4536 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004537 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004538 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004539 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004540 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004541 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004542 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004543 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004544 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004545 args.print_required_libavb_version,
4546 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004547
David Zeuthenb1b994d2017-03-06 18:01:31 -05004548 def append_vbmeta_image(self, args):
4549 """Implements the 'append_vbmeta_image' sub-command."""
4550 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4551 args.partition_size)
4552
David Zeuthen21e95262016-07-27 17:58:40 -04004553 def add_hash_footer(self, args):
4554 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004555 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004556 self.avb.add_hash_footer(args.image.name if args.image else None,
4557 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004558 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004559 args.salt, args.chain_partition, args.algorithm,
4560 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004561 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004562 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004563 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004564 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004565 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004566 args.calc_max_image_size,
4567 args.signing_helper,
4568 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004569 args.internal_release_string,
4570 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004571 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004572 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004573 args.print_required_libavb_version,
4574 args.use_persistent_digest,
4575 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004576
4577 def add_hashtree_footer(self, args):
4578 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004579 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004580 # TODO(zeuthen): Remove when removing support for the
4581 # '--generate_fec' option above.
4582 if args.generate_fec:
4583 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4584 'is now generated by default. Use the option '
4585 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004586 self.avb.add_hashtree_footer(
4587 args.image.name if args.image else None,
4588 args.partition_size,
4589 args.partition_name,
4590 not args.do_not_generate_fec, args.fec_num_roots,
4591 args.hash_algorithm, args.block_size,
4592 args.salt, args.chain_partition, args.algorithm,
4593 args.key, args.public_key_metadata,
4594 args.rollback_index, args.flags, args.prop,
4595 args.prop_from_file,
4596 args.kernel_cmdline,
4597 args.setup_rootfs_from_kernel,
4598 args.setup_as_rootfs_from_kernel,
4599 args.include_descriptors_from_image,
4600 args.calc_max_image_size,
4601 args.signing_helper,
4602 args.signing_helper_with_files,
4603 args.internal_release_string,
4604 args.append_to_release_string,
4605 args.output_vbmeta_image,
4606 args.do_not_append_vbmeta_image,
4607 args.print_required_libavb_version,
4608 args.use_persistent_digest,
4609 args.do_not_use_ab,
4610 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004611
David Zeuthen21e95262016-07-27 17:58:40 -04004612 def erase_footer(self, args):
4613 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004614 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004615
David Zeuthen1394f762019-04-30 10:20:11 -04004616 def zero_hashtree(self, args):
4617 """Implements the 'zero_hashtree' sub-command."""
4618 self.avb.zero_hashtree(args.image.name)
4619
David Zeuthen49936b42018-08-07 17:38:58 -04004620 def extract_vbmeta_image(self, args):
4621 """Implements the 'extract_vbmeta_image' sub-command."""
4622 self.avb.extract_vbmeta_image(args.output, args.image.name,
4623 args.padding_size)
4624
David Zeuthen2bc232b2017-04-19 14:25:19 -04004625 def resize_image(self, args):
4626 """Implements the 'resize_image' sub-command."""
4627 self.avb.resize_image(args.image.name, args.partition_size)
4628
David Zeuthen8b6973b2016-09-20 12:39:49 -04004629 def set_ab_metadata(self, args):
4630 """Implements the 'set_ab_metadata' sub-command."""
4631 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4632
David Zeuthen21e95262016-07-27 17:58:40 -04004633 def info_image(self, args):
4634 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004635 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004636
David Zeuthenb623d8b2017-04-04 16:05:53 -04004637 def verify_image(self, args):
4638 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004639 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004640 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004641 args.follow_chain_partitions,
4642 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004643
David Zeuthenb8643c02018-05-17 17:21:18 -04004644 def calculate_vbmeta_digest(self, args):
4645 """Implements the 'calculate_vbmeta_digest' sub-command."""
4646 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4647 args.output)
4648
David Zeuthenf7d2e752018-09-20 13:30:41 -04004649 def calculate_kernel_cmdline(self, args):
4650 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004651 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4652 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004653
Darren Krahn147b08d2016-12-20 16:38:29 -08004654 def make_atx_certificate(self, args):
4655 """Implements the 'make_atx_certificate' sub-command."""
4656 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004657 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004658 args.subject_key_version,
4659 args.subject.read(),
4660 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004661 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004662 args.signing_helper,
4663 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004664
4665 def make_atx_permanent_attributes(self, args):
4666 """Implements the 'make_atx_permanent_attributes' sub-command."""
4667 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004668 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004669 args.product_id.read())
4670
4671 def make_atx_metadata(self, args):
4672 """Implements the 'make_atx_metadata' sub-command."""
4673 self.avb.make_atx_metadata(args.output,
4674 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004675 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004676
Darren Krahnfccd64e2018-01-16 17:39:35 -08004677 def make_atx_unlock_credential(self, args):
4678 """Implements the 'make_atx_unlock_credential' sub-command."""
4679 self.avb.make_atx_unlock_credential(
4680 args.output,
4681 args.intermediate_key_certificate.read(),
4682 args.unlock_key_certificate.read(),
4683 args.challenge,
4684 args.unlock_key,
4685 args.signing_helper,
4686 args.signing_helper_with_files)
4687
David Zeuthen21e95262016-07-27 17:58:40 -04004688
4689if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004690 if AVB_INVOCATION_LOGFILE:
4691 f = open(AVB_INVOCATION_LOGFILE, 'a')
4692 f.write(' '.join(sys.argv))
4693 f.write('\n')
4694 f.close()
4695
David Zeuthen21e95262016-07-27 17:58:40 -04004696 tool = AvbTool()
4697 tool.run(sys.argv)