blob: 0900f53f5359cccd411e1615ac7e930be70c29d9 [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
Jan Monsch38865f22020-04-08 09:32:58 +0200348 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400349
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.
Jan Monsch38865f22020-04-08 09:32:58 +0200479 raw_data_to_sign: Data to sign as bytes or bytearray.
Darren Krahn147b08d2016-12-20 16:38:29 -0800480
481 Returns:
Jan Monsch38865f22020-04-08 09:32:58 +0200482 The signature as bytes.
Darren Krahn147b08d2016-12-20 16:38:29 -0800483
484 Raises:
Jan Monsch38865f22020-04-08 09:32:58 +0200485 AvbError: If an error occurred during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -0800486 """
487 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400488 if signing_helper_with_files is not None:
489 signing_file = tempfile.NamedTemporaryFile()
Jan Monsch38865f22020-04-08 09:32:58 +0200490 signing_file.write(raw_data_to_sign)
David Zeuthena156d3d2017-06-01 12:08:09 -0400491 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)
Jan Monsch38865f22020-04-08 09:32:58 +0200498 signature = 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)
Jan Monsch38865f22020-04-08 09:32:58 +0200512 (pout, perr) = p.communicate(raw_data_to_sign)
David Zeuthena156d3d2017-06-01 12:08:09 -0400513 retcode = p.wait()
514 if retcode != 0:
515 raise AvbError('Error signing: {}'.format(perr))
Jan Monsch38865f22020-04-08 09:32:58 +0200516 signature = 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.
Jan Monsch38865f22020-04-08 09:32:58 +0200527 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
528 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400529
530 Returns:
531 True if the signature is valid and corresponds to the embedded
532 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100533
534 Raises:
535 AvbError: If there errors calling out to openssl command during
536 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537 """
538 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100539 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400540 return True
541 header_blob = vbmeta_blob[0:256]
542 auth_offset = 256
543 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
544 aux_size = vbmeta_header.auxiliary_data_block_size
545 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
546 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
547 pubkey_size = vbmeta_header.public_key_size
548 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
549
550 digest_offset = auth_offset + vbmeta_header.hash_offset
551 digest_size = vbmeta_header.hash_size
552 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
553
554 sig_offset = auth_offset + vbmeta_header.signature_offset
555 sig_size = vbmeta_header.signature_size
556 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
557
558 # Now that we've got the stored digest, public key, and signature
559 # all we need to do is to verify. This is the exactly the same
560 # steps as performed in the avb_vbmeta_image_verify() function in
561 # libavb/avb_vbmeta_image.c.
562
563 ha = hashlib.new(alg.hash_name)
564 ha.update(header_blob)
565 ha.update(aux_blob)
566 computed_digest = ha.digest()
567
568 if computed_digest != digest_blob:
569 return False
570
571 padding_and_digest = bytearray(alg.padding)
572 padding_and_digest.extend(computed_digest)
573
574 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100575 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400576 modulus = decode_long(modulus_blob)
577 exponent = 65537
578
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500579 # We used to have this:
580 #
581 # import Crypto.PublicKey.RSA
582 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
583 # if not key.verify(decode_long(padding_and_digest),
584 # (decode_long(sig_blob), None)):
585 # return False
586 # return True
587 #
588 # but since 'avbtool verify_image' is used on the builders we don't want
589 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
590 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
591 '\n'
592 '[pubkeyinfo]\n'
593 'algorithm=SEQUENCE:rsa_alg\n'
594 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
595 '\n'
596 '[rsa_alg]\n'
597 'algorithm=OID:rsaEncryption\n'
598 'parameter=NULL\n'
599 '\n'
600 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200601 'n=INTEGER:{}\n'
602 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
603 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500604
Jan Monsch38865f22020-04-08 09:32:58 +0200605 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
606 asn1_tmpfile.write(asn1_str.encode('ascii'))
607 asn1_tmpfile.flush()
608
609 with tempfile.NamedTemporaryFile() as der_tmpfile:
610 p = subprocess.Popen(
611 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
612 der_tmpfile.name, '-noout'])
613 retcode = p.wait()
614 if retcode != 0:
615 raise AvbError('Error generating DER file')
616
617 p = subprocess.Popen(
618 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
619 '-keyform', 'DER', '-raw'],
620 stdin=subprocess.PIPE,
621 stdout=subprocess.PIPE,
622 stderr=subprocess.PIPE)
623 (pout, perr) = p.communicate(sig_blob)
624 retcode = p.wait()
625 if retcode != 0:
626 raise AvbError('Error verifying data: {}'.format(perr))
627 if pout != padding_and_digest:
628 sys.stderr.write('Signature not correct\n')
629 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400630 return True
631
632
David Zeuthena4fee8b2016-08-22 15:20:43 -0400633class ImageChunk(object):
634 """Data structure used for representing chunks in Android sparse files.
635
636 Attributes:
637 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
638 chunk_offset: Offset in the sparse file where this chunk begins.
639 output_offset: Offset in de-sparsified file where output begins.
640 output_size: Number of bytes in output.
641 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
642 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
643 """
644
645 FORMAT = '<2H2I'
646 TYPE_RAW = 0xcac1
647 TYPE_FILL = 0xcac2
648 TYPE_DONT_CARE = 0xcac3
649 TYPE_CRC32 = 0xcac4
650
651 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
652 input_offset, fill_data):
653 """Initializes an ImageChunk object.
654
655 Arguments:
656 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
657 chunk_offset: Offset in the sparse file where this chunk begins.
658 output_offset: Offset in de-sparsified file.
659 output_size: Number of bytes in output.
660 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
661 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
662
663 Raises:
664 ValueError: If data is not well-formed.
665 """
666 self.chunk_type = chunk_type
667 self.chunk_offset = chunk_offset
668 self.output_offset = output_offset
669 self.output_size = output_size
670 self.input_offset = input_offset
671 self.fill_data = fill_data
672 # Check invariants.
673 if self.chunk_type == self.TYPE_RAW:
674 if self.fill_data is not None:
675 raise ValueError('RAW chunk cannot have fill_data set.')
676 if not self.input_offset:
677 raise ValueError('RAW chunk must have input_offset set.')
678 elif self.chunk_type == self.TYPE_FILL:
679 if self.fill_data is None:
680 raise ValueError('FILL chunk must have fill_data set.')
681 if self.input_offset:
682 raise ValueError('FILL chunk cannot have input_offset set.')
683 elif self.chunk_type == self.TYPE_DONT_CARE:
684 if self.fill_data is not None:
685 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
686 if self.input_offset:
687 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
688 else:
689 raise ValueError('Invalid chunk type')
690
691
692class ImageHandler(object):
693 """Abstraction for image I/O with support for Android sparse images.
694
695 This class provides an interface for working with image files that
696 may be using the Android Sparse Image format. When an instance is
697 constructed, we test whether it's an Android sparse file. If so,
698 operations will be on the sparse file by interpreting the sparse
699 format, otherwise they will be directly on the file. Either way the
700 operations do the same.
701
702 For reading, this interface mimics a file object - it has seek(),
703 tell(), and read() methods. For writing, only truncation
704 (truncate()) and appending is supported (append_raw() and
705 append_dont_care()). Additionally, data can only be written in units
706 of the block size.
707
708 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400709 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400710 is_sparse: Whether the file being operated on is sparse.
711 block_size: The block size, typically 4096.
712 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 """
714 # See system/core/libsparse/sparse_format.h for details.
715 MAGIC = 0xed26ff3a
716 HEADER_FORMAT = '<I4H4I'
717
718 # These are formats and offset of just the |total_chunks| and
719 # |total_blocks| fields.
720 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
721 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
722
723 def __init__(self, image_filename):
724 """Initializes an image handler.
725
726 Arguments:
727 image_filename: The name of the file to operate on.
728
729 Raises:
730 ValueError: If data in the file is invalid.
731 """
David Zeuthen49936b42018-08-07 17:38:58 -0400732 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100733 self._num_total_blocks = 0
734 self._num_total_chunks = 0
735 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400736 self._read_header()
737
738 def _read_header(self):
739 """Initializes internal data structures used for reading file.
740
741 This may be called multiple times and is typically called after
742 modifying the file (e.g. appending, truncation).
743
744 Raises:
745 ValueError: If data in the file is invalid.
746 """
747 self.is_sparse = False
748 self.block_size = 4096
749 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400750 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400751 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400752 self.image_size = self._image.tell()
753
754 self._image.seek(0, os.SEEK_SET)
755 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
756 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
757 block_size, self._num_total_blocks, self._num_total_chunks,
758 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
759 if magic != self.MAGIC:
760 # Not a sparse image, our job here is done.
761 return
762 if not (major_version == 1 and minor_version == 0):
763 raise ValueError('Encountered sparse image format version {}.{} but '
764 'only 1.0 is supported'.format(major_version,
765 minor_version))
766 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
767 raise ValueError('Unexpected file_hdr_sz value {}.'.
768 format(file_hdr_sz))
769 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
770 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
771 format(chunk_hdr_sz))
772
773 self.block_size = block_size
774
775 # Build an list of chunks by parsing the file.
776 self._chunks = []
777
778 # Find the smallest offset where only "Don't care" chunks
779 # follow. This will be the size of the content in the sparse
780 # image.
781 offset = 0
782 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100783 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400784 chunk_offset = self._image.tell()
785
786 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
787 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
788 header_bin)
789 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
790
David Zeuthena4fee8b2016-08-22 15:20:43 -0400791 if chunk_type == ImageChunk.TYPE_RAW:
792 if data_sz != (chunk_sz * self.block_size):
793 raise ValueError('Raw chunk input size ({}) does not match output '
794 'size ({})'.
795 format(data_sz, chunk_sz*self.block_size))
796 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
797 chunk_offset,
798 output_offset,
799 chunk_sz*self.block_size,
800 self._image.tell(),
801 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700802 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400803
804 elif chunk_type == ImageChunk.TYPE_FILL:
805 if data_sz != 4:
806 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
807 'has {}'.format(data_sz))
808 fill_data = self._image.read(4)
809 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
810 chunk_offset,
811 output_offset,
812 chunk_sz*self.block_size,
813 None,
814 fill_data))
815 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
816 if data_sz != 0:
817 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
818 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400819 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
820 chunk_offset,
821 output_offset,
822 chunk_sz*self.block_size,
823 None,
824 None))
825 elif chunk_type == ImageChunk.TYPE_CRC32:
826 if data_sz != 4:
827 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
828 'this has {}'.format(data_sz))
829 self._image.read(4)
830 else:
831 raise ValueError('Unknown chunk type {}'.format(chunk_type))
832
833 offset += chunk_sz
834 output_offset += chunk_sz*self.block_size
835
836 # Record where sparse data end.
837 self._sparse_end = self._image.tell()
838
839 # Now that we've traversed all chunks, sanity check.
840 if self._num_total_blocks != offset:
841 raise ValueError('The header said we should have {} output blocks, '
842 'but we saw {}'.format(self._num_total_blocks, offset))
843 junk_len = len(self._image.read())
844 if junk_len > 0:
845 raise ValueError('There were {} bytes of extra data at the end of the '
846 'file.'.format(junk_len))
847
David Zeuthen09692692016-09-30 16:16:40 -0400848 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400849 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400850
851 # This is used when bisecting in read() to find the initial slice.
852 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
853
854 self.is_sparse = True
855
856 def _update_chunks_and_blocks(self):
857 """Helper function to update the image header.
858
859 The the |total_chunks| and |total_blocks| fields in the header
860 will be set to value of the |_num_total_blocks| and
861 |_num_total_chunks| attributes.
862
863 """
864 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
865 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
866 self._num_total_blocks,
867 self._num_total_chunks))
868
869 def append_dont_care(self, num_bytes):
870 """Appends a DONT_CARE chunk to the sparse file.
871
872 The given number of bytes must be a multiple of the block size.
873
874 Arguments:
875 num_bytes: Size in number of bytes of the DONT_CARE chunk.
876 """
877 assert num_bytes % self.block_size == 0
878
879 if not self.is_sparse:
880 self._image.seek(0, os.SEEK_END)
881 # This is more efficient that writing NUL bytes since it'll add
882 # a hole on file systems that support sparse files (native
883 # sparse, not Android sparse).
884 self._image.truncate(self._image.tell() + num_bytes)
885 self._read_header()
886 return
887
888 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100889 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400890 self._update_chunks_and_blocks()
891
892 self._image.seek(self._sparse_end, os.SEEK_SET)
893 self._image.write(struct.pack(ImageChunk.FORMAT,
894 ImageChunk.TYPE_DONT_CARE,
895 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100896 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400897 struct.calcsize(ImageChunk.FORMAT)))
898 self._read_header()
899
900 def append_raw(self, data):
901 """Appends a RAW chunk to the sparse file.
902
903 The length of the given data must be a multiple of the block size.
904
905 Arguments:
906 data: Data to append.
907 """
908 assert len(data) % self.block_size == 0
909
910 if not self.is_sparse:
911 self._image.seek(0, os.SEEK_END)
912 self._image.write(data)
913 self._read_header()
914 return
915
916 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100917 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400918 self._update_chunks_and_blocks()
919
920 self._image.seek(self._sparse_end, os.SEEK_SET)
921 self._image.write(struct.pack(ImageChunk.FORMAT,
922 ImageChunk.TYPE_RAW,
923 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100924 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400925 len(data) +
926 struct.calcsize(ImageChunk.FORMAT)))
927 self._image.write(data)
928 self._read_header()
929
930 def append_fill(self, fill_data, size):
931 """Appends a fill chunk to the sparse file.
932
933 The total length of the fill data must be a multiple of the block size.
934
935 Arguments:
936 fill_data: Fill data to append - must be four bytes.
937 size: Number of chunk - must be a multiple of four and the block size.
938 """
939 assert len(fill_data) == 4
940 assert size % 4 == 0
941 assert size % self.block_size == 0
942
943 if not self.is_sparse:
944 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 self._read_header()
947 return
948
949 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100950 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400951 self._update_chunks_and_blocks()
952
953 self._image.seek(self._sparse_end, os.SEEK_SET)
954 self._image.write(struct.pack(ImageChunk.FORMAT,
955 ImageChunk.TYPE_FILL,
956 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100957 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400958 4 + struct.calcsize(ImageChunk.FORMAT)))
959 self._image.write(fill_data)
960 self._read_header()
961
962 def seek(self, offset):
963 """Sets the cursor position for reading from unsparsified file.
964
965 Arguments:
966 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100967
968 Raises:
969 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400970 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700971 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100972 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400973 self._file_pos = offset
974
975 def read(self, size):
976 """Reads data from the unsparsified file.
977
978 This method may return fewer than |size| bytes of data if the end
979 of the file was encountered.
980
981 The file cursor for reading is advanced by the number of bytes
982 read.
983
984 Arguments:
985 size: Number of bytes to read.
986
987 Returns:
988 The data.
989
990 """
991 if not self.is_sparse:
992 self._image.seek(self._file_pos)
993 data = self._image.read(size)
994 self._file_pos += len(data)
995 return data
996
997 # Iterate over all chunks.
998 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
999 self._file_pos) - 1
1000 data = bytearray()
1001 to_go = size
1002 while to_go > 0:
1003 chunk = self._chunks[chunk_idx]
1004 chunk_pos_offset = self._file_pos - chunk.output_offset
1005 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1006
1007 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1008 self._image.seek(chunk.input_offset + chunk_pos_offset)
1009 data.extend(self._image.read(chunk_pos_to_go))
1010 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001011 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001012 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1013 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1014 else:
1015 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1016 data.extend('\0' * chunk_pos_to_go)
1017
1018 to_go -= chunk_pos_to_go
1019 self._file_pos += chunk_pos_to_go
1020 chunk_idx += 1
1021 # Generate partial read in case of EOF.
1022 if chunk_idx >= len(self._chunks):
1023 break
1024
1025 return data
1026
1027 def tell(self):
1028 """Returns the file cursor position for reading from unsparsified file.
1029
1030 Returns:
1031 The file cursor position for reading.
1032 """
1033 return self._file_pos
1034
1035 def truncate(self, size):
1036 """Truncates the unsparsified file.
1037
1038 Arguments:
1039 size: Desired size of unsparsified file.
1040
1041 Raises:
1042 ValueError: If desired size isn't a multiple of the block size.
1043 """
1044 if not self.is_sparse:
1045 self._image.truncate(size)
1046 self._read_header()
1047 return
1048
1049 if size % self.block_size != 0:
1050 raise ValueError('Cannot truncate to a size which is not a multiple '
1051 'of the block size')
1052
1053 if size == self.image_size:
1054 # Trivial where there's nothing to do.
1055 return
1056 elif size < self.image_size:
1057 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1058 chunk = self._chunks[chunk_idx]
1059 if chunk.output_offset != size:
1060 # Truncation in the middle of a trunk - need to keep the chunk
1061 # and modify it.
1062 chunk_idx_for_update = chunk_idx + 1
1063 num_to_keep = size - chunk.output_offset
1064 assert num_to_keep % self.block_size == 0
1065 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1066 truncate_at = (chunk.chunk_offset +
1067 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1068 data_sz = num_to_keep
1069 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1070 truncate_at = (chunk.chunk_offset +
1071 struct.calcsize(ImageChunk.FORMAT) + 4)
1072 data_sz = 4
1073 else:
1074 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1075 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1076 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001077 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001078 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1079 self._image.seek(chunk.chunk_offset)
1080 self._image.write(struct.pack(ImageChunk.FORMAT,
1081 chunk.chunk_type,
1082 0, # Reserved
1083 chunk_sz,
1084 total_sz))
1085 chunk.output_size = num_to_keep
1086 else:
1087 # Truncation at trunk boundary.
1088 truncate_at = chunk.chunk_offset
1089 chunk_idx_for_update = chunk_idx
1090
1091 self._num_total_chunks = chunk_idx_for_update
1092 self._num_total_blocks = 0
1093 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001094 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001095 self._update_chunks_and_blocks()
1096 self._image.truncate(truncate_at)
1097
1098 # We've modified the file so re-read all data.
1099 self._read_header()
1100 else:
1101 # Truncating to grow - just add a DONT_CARE section.
1102 self.append_dont_care(size - self.image_size)
1103
1104
David Zeuthen21e95262016-07-27 17:58:40 -04001105class AvbDescriptor(object):
1106 """Class for AVB descriptor.
1107
1108 See the |AvbDescriptor| C struct for more information.
1109
1110 Attributes:
1111 tag: The tag identifying what kind of descriptor this is.
1112 data: The data in the descriptor.
1113 """
1114
1115 SIZE = 16
1116 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1117
1118 def __init__(self, data):
1119 """Initializes a new property descriptor.
1120
1121 Arguments:
1122 data: If not None, must be a bytearray().
1123
1124 Raises:
1125 LookupError: If the given descriptor is malformed.
1126 """
1127 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1128
1129 if data:
1130 (self.tag, num_bytes_following) = (
1131 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1132 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1133 else:
1134 self.tag = None
1135 self.data = None
1136
1137 def print_desc(self, o):
1138 """Print the descriptor.
1139
1140 Arguments:
1141 o: The object to write the output to.
1142 """
1143 o.write(' Unknown descriptor:\n')
1144 o.write(' Tag: {}\n'.format(self.tag))
1145 if len(self.data) < 256:
1146 o.write(' Data: {} ({} bytes)\n'.format(
1147 repr(str(self.data)), len(self.data)))
1148 else:
1149 o.write(' Data: {} bytes\n'.format(len(self.data)))
1150
1151 def encode(self):
1152 """Serializes the descriptor.
1153
1154 Returns:
1155 A bytearray() with the descriptor data.
1156 """
1157 num_bytes_following = len(self.data)
1158 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1159 padding_size = nbf_with_padding - num_bytes_following
1160 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1161 padding = struct.pack(str(padding_size) + 'x')
1162 ret = desc + self.data + padding
1163 return bytearray(ret)
1164
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001165 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001166 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001167 """Verifies contents of the descriptor - used in verify_image sub-command.
1168
1169 Arguments:
1170 image_dir: The directory of the file being verified.
1171 image_ext: The extension of the file being verified (e.g. '.img').
1172 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001173 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001174 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001175 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1176 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001177
1178 Returns:
1179 True if the descriptor verifies, False otherwise.
1180 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001181 # Deletes unused parameters to prevent pylint warning unused-argument.
1182 del image_dir, image_ext, expected_chain_partitions_map
1183 del image_containing_descriptor, accept_zeroed_hashtree
1184
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001185 # Nothing to do.
1186 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001187
Jan Monscheeb28b62019-12-05 16:17:09 +01001188
David Zeuthen21e95262016-07-27 17:58:40 -04001189class AvbPropertyDescriptor(AvbDescriptor):
1190 """A class for property descriptors.
1191
1192 See the |AvbPropertyDescriptor| C struct for more information.
1193
1194 Attributes:
1195 key: The key.
1196 value: The key.
1197 """
1198
1199 TAG = 0
1200 SIZE = 32
1201 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1202 'Q' # key size (bytes)
1203 'Q') # value size (bytes)
1204
1205 def __init__(self, data=None):
1206 """Initializes a new property descriptor.
1207
1208 Arguments:
1209 data: If not None, must be a bytearray of size |SIZE|.
1210
1211 Raises:
1212 LookupError: If the given descriptor is malformed.
1213 """
1214 AvbDescriptor.__init__(self, None)
1215 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1216
1217 if data:
1218 (tag, num_bytes_following, key_size,
1219 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1220 expected_size = round_to_multiple(
1221 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1222 if tag != self.TAG or num_bytes_following != expected_size:
1223 raise LookupError('Given data does not look like a property '
1224 'descriptor.')
1225 self.key = data[self.SIZE:(self.SIZE + key_size)]
1226 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1227 value_size)]
1228 else:
1229 self.key = ''
1230 self.value = ''
1231
1232 def print_desc(self, o):
1233 """Print the descriptor.
1234
1235 Arguments:
1236 o: The object to write the output to.
1237 """
1238 if len(self.value) < 256:
1239 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1240 else:
1241 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1242
1243 def encode(self):
1244 """Serializes the descriptor.
1245
1246 Returns:
1247 A bytearray() with the descriptor data.
1248 """
1249 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1250 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1251 padding_size = nbf_with_padding - num_bytes_following
1252 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1253 len(self.key), len(self.value))
1254 padding = struct.pack(str(padding_size) + 'x')
1255 ret = desc + self.key + '\0' + self.value + '\0' + padding
1256 return bytearray(ret)
1257
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001258 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001259 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001260 """Verifies contents of the descriptor - used in verify_image sub-command.
1261
1262 Arguments:
1263 image_dir: The directory of the file being verified.
1264 image_ext: The extension of the file being verified (e.g. '.img').
1265 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001266 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001267 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001268 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1269 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001270
1271 Returns:
1272 True if the descriptor verifies, False otherwise.
1273 """
1274 # Nothing to do.
1275 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001276
Jan Monscheeb28b62019-12-05 16:17:09 +01001277
David Zeuthen21e95262016-07-27 17:58:40 -04001278class AvbHashtreeDescriptor(AvbDescriptor):
1279 """A class for hashtree descriptors.
1280
1281 See the |AvbHashtreeDescriptor| C struct for more information.
1282
1283 Attributes:
1284 dm_verity_version: dm-verity version used.
1285 image_size: Size of the image, after rounding up to |block_size|.
1286 tree_offset: Offset of the hash tree in the file.
1287 tree_size: Size of the tree.
1288 data_block_size: Data block size
1289 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001290 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1291 fec_offset: Offset of FEC data (0 if FEC is not used).
1292 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001293 hash_algorithm: Hash algorithm used.
1294 partition_name: Partition name.
1295 salt: Salt used.
1296 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001297 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001298 """
1299
1300 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001301 RESERVED = 60
1302 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001303 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1304 'L' # dm-verity version used
1305 'Q' # image size (bytes)
1306 'Q' # tree offset (bytes)
1307 'Q' # tree size (bytes)
1308 'L' # data block size (bytes)
1309 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001310 'L' # FEC number of roots
1311 'Q' # FEC offset (bytes)
1312 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001313 '32s' # hash algorithm used
1314 'L' # partition name (bytes)
1315 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001316 'L' # root digest length (bytes)
1317 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001318 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001319
1320 def __init__(self, data=None):
1321 """Initializes a new hashtree descriptor.
1322
1323 Arguments:
1324 data: If not None, must be a bytearray of size |SIZE|.
1325
1326 Raises:
1327 LookupError: If the given descriptor is malformed.
1328 """
1329 AvbDescriptor.__init__(self, None)
1330 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1331
1332 if data:
1333 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1334 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001335 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1336 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001337 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1338 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001339 expected_size = round_to_multiple(
1340 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1341 if tag != self.TAG or num_bytes_following != expected_size:
1342 raise LookupError('Given data does not look like a hashtree '
1343 'descriptor.')
1344 # Nuke NUL-bytes at the end.
1345 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1346 o = 0
1347 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1348 partition_name_len)])
1349 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1350 self.partition_name.decode('utf-8')
1351 o += partition_name_len
1352 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1353 o += salt_len
1354 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1355 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001356 if root_digest_len != 0:
1357 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001358
1359 else:
1360 self.dm_verity_version = 0
1361 self.image_size = 0
1362 self.tree_offset = 0
1363 self.tree_size = 0
1364 self.data_block_size = 0
1365 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001366 self.fec_num_roots = 0
1367 self.fec_offset = 0
1368 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001369 self.hash_algorithm = ''
1370 self.partition_name = ''
1371 self.salt = bytearray()
1372 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001373 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001374
1375 def print_desc(self, o):
1376 """Print the descriptor.
1377
1378 Arguments:
1379 o: The object to write the output to.
1380 """
1381 o.write(' Hashtree descriptor:\n')
1382 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1383 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1384 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1385 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1386 o.write(' Data Block Size: {} bytes\n'.format(
1387 self.data_block_size))
1388 o.write(' Hash Block Size: {} bytes\n'.format(
1389 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001390 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1391 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1392 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001393 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1394 o.write(' Partition Name: {}\n'.format(self.partition_name))
1395 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1396 'hex')))
1397 o.write(' Root Digest: {}\n'.format(str(
1398 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001399 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001400
1401 def encode(self):
1402 """Serializes the descriptor.
1403
1404 Returns:
1405 A bytearray() with the descriptor data.
1406 """
1407 encoded_name = self.partition_name.encode('utf-8')
1408 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1409 len(self.root_digest) - 16)
1410 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1411 padding_size = nbf_with_padding - num_bytes_following
1412 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1413 self.dm_verity_version, self.image_size,
1414 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001415 self.hash_block_size, self.fec_num_roots,
1416 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001417 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001418 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001419 padding = struct.pack(str(padding_size) + 'x')
1420 ret = desc + encoded_name + self.salt + self.root_digest + padding
1421 return bytearray(ret)
1422
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001423 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001424 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001425 """Verifies contents of the descriptor - used in verify_image sub-command.
1426
1427 Arguments:
1428 image_dir: The directory of the file being verified.
1429 image_ext: The extension of the file being verified (e.g. '.img').
1430 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001431 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001432 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001433 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1434 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001435
1436 Returns:
1437 True if the descriptor verifies, False otherwise.
1438 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001439 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001440 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001441 image = image_containing_descriptor
1442 else:
1443 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1444 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001445 # Generate the hashtree and checks that it matches what's in the file.
1446 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1447 digest_padding = round_to_pow2(digest_size) - digest_size
1448 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001449 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001450 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1451 self.data_block_size,
1452 self.hash_algorithm, self.salt,
1453 digest_padding,
1454 hash_level_offsets,
1455 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001456 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001457 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001458 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1459 format(image_filename))
1460 return False
1461 # ... also check that the on-disk hashtree matches
1462 image.seek(self.tree_offset)
1463 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001464 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001465 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001466 print('{}: skipping verification since hashtree is zeroed and '
1467 '--accept_zeroed_hashtree was given'
1468 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001469 else:
1470 if hash_tree != hash_tree_ondisk:
1471 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001472 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001473 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001474 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1475 .format(self.partition_name, self.hash_algorithm, image.filename,
1476 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001477 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1478 # correct but this a) currently requires the 'fec' binary; and b) takes a
1479 # long time; and c) is not strictly needed for verification purposes as
1480 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001481 return True
1482
David Zeuthen21e95262016-07-27 17:58:40 -04001483
1484class AvbHashDescriptor(AvbDescriptor):
1485 """A class for hash descriptors.
1486
1487 See the |AvbHashDescriptor| C struct for more information.
1488
1489 Attributes:
1490 image_size: Image size, in bytes.
1491 hash_algorithm: Hash algorithm used.
1492 partition_name: Partition name.
1493 salt: Salt used.
1494 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001495 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001496 """
1497
1498 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001499 RESERVED = 60
1500 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001501 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1502 'Q' # image size (bytes)
1503 '32s' # hash algorithm used
1504 'L' # partition name (bytes)
1505 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001506 'L' # digest length (bytes)
1507 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001508 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001509
1510 def __init__(self, data=None):
1511 """Initializes a new hash descriptor.
1512
1513 Arguments:
1514 data: If not None, must be a bytearray of size |SIZE|.
1515
1516 Raises:
1517 LookupError: If the given descriptor is malformed.
1518 """
1519 AvbDescriptor.__init__(self, None)
1520 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1521
1522 if data:
1523 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1524 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001525 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1526 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001527 expected_size = round_to_multiple(
1528 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1529 if tag != self.TAG or num_bytes_following != expected_size:
1530 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1531 # Nuke NUL-bytes at the end.
1532 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1533 o = 0
1534 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1535 partition_name_len)])
1536 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1537 self.partition_name.decode('utf-8')
1538 o += partition_name_len
1539 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1540 o += salt_len
1541 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1542 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001543 if digest_len != 0:
1544 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001545
1546 else:
1547 self.image_size = 0
1548 self.hash_algorithm = ''
1549 self.partition_name = ''
1550 self.salt = bytearray()
1551 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001552 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001553
1554 def print_desc(self, o):
1555 """Print the descriptor.
1556
1557 Arguments:
1558 o: The object to write the output to.
1559 """
1560 o.write(' Hash descriptor:\n')
1561 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1562 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1563 o.write(' Partition Name: {}\n'.format(self.partition_name))
1564 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1565 'hex')))
1566 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1567 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001568 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001569
1570 def encode(self):
1571 """Serializes the descriptor.
1572
1573 Returns:
1574 A bytearray() with the descriptor data.
1575 """
1576 encoded_name = self.partition_name.encode('utf-8')
1577 num_bytes_following = (
1578 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1579 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1580 padding_size = nbf_with_padding - num_bytes_following
1581 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1582 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001583 len(self.salt), len(self.digest), self.flags,
1584 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001585 padding = struct.pack(str(padding_size) + 'x')
1586 ret = desc + encoded_name + self.salt + self.digest + padding
1587 return bytearray(ret)
1588
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001589 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001590 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001591 """Verifies contents of the descriptor - used in verify_image sub-command.
1592
1593 Arguments:
1594 image_dir: The directory of the file being verified.
1595 image_ext: The extension of the file being verified (e.g. '.img').
1596 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001597 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001598 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001599 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1600 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001601
1602 Returns:
1603 True if the descriptor verifies, False otherwise.
1604 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001605 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001606 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001607 image = image_containing_descriptor
1608 else:
1609 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1610 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001611 data = image.read(self.image_size)
1612 ha = hashlib.new(self.hash_algorithm)
1613 ha.update(self.salt)
1614 ha.update(data)
1615 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001616 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001617 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001618 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1619 format(self.hash_algorithm, image_filename))
1620 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001621 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1622 .format(self.partition_name, self.hash_algorithm, image.filename,
1623 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001624 return True
1625
David Zeuthen21e95262016-07-27 17:58:40 -04001626
1627class AvbKernelCmdlineDescriptor(AvbDescriptor):
1628 """A class for kernel command-line descriptors.
1629
1630 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1631
1632 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001633 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001634 kernel_cmdline: The kernel command-line.
1635 """
1636
1637 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001638 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001639 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001640 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001641 'L') # cmdline length (bytes)
1642
David Zeuthenfd41eb92016-11-17 12:24:47 -05001643 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1644 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1645
David Zeuthen21e95262016-07-27 17:58:40 -04001646 def __init__(self, data=None):
1647 """Initializes a new kernel cmdline descriptor.
1648
1649 Arguments:
1650 data: If not None, must be a bytearray of size |SIZE|.
1651
1652 Raises:
1653 LookupError: If the given descriptor is malformed.
1654 """
1655 AvbDescriptor.__init__(self, None)
1656 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1657
1658 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001659 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001660 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1661 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1662 8)
1663 if tag != self.TAG or num_bytes_following != expected_size:
1664 raise LookupError('Given data does not look like a kernel cmdline '
1665 'descriptor.')
1666 # Nuke NUL-bytes at the end.
1667 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1668 kernel_cmdline_length)])
1669 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1670 self.kernel_cmdline.decode('utf-8')
1671 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001672 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001673 self.kernel_cmdline = ''
1674
1675 def print_desc(self, o):
1676 """Print the descriptor.
1677
1678 Arguments:
1679 o: The object to write the output to.
1680 """
1681 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001682 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001683 o.write(' Kernel Cmdline: {}\n'.format(repr(
1684 self.kernel_cmdline)))
1685
1686 def encode(self):
1687 """Serializes the descriptor.
1688
1689 Returns:
1690 A bytearray() with the descriptor data.
1691 """
1692 encoded_str = self.kernel_cmdline.encode('utf-8')
1693 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1694 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1695 padding_size = nbf_with_padding - num_bytes_following
1696 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001697 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001698 padding = struct.pack(str(padding_size) + 'x')
1699 ret = desc + encoded_str + padding
1700 return bytearray(ret)
1701
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001702 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001703 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001704 """Verifies contents of the descriptor - used in verify_image sub-command.
1705
1706 Arguments:
1707 image_dir: The directory of the file being verified.
1708 image_ext: The extension of the file being verified (e.g. '.img').
1709 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001710 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001711 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001712 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1713 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001714
1715 Returns:
1716 True if the descriptor verifies, False otherwise.
1717 """
1718 # Nothing to verify.
1719 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001720
Jan Monscheeb28b62019-12-05 16:17:09 +01001721
David Zeuthen21e95262016-07-27 17:58:40 -04001722class AvbChainPartitionDescriptor(AvbDescriptor):
1723 """A class for chained partition descriptors.
1724
1725 See the |AvbChainPartitionDescriptor| C struct for more information.
1726
1727 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001728 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001729 partition_name: Partition name.
1730 public_key: Bytes for the public key.
1731 """
1732
1733 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001734 RESERVED = 64
1735 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001736 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001737 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001738 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001739 'L' + # public_key_size (bytes)
1740 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001741
1742 def __init__(self, data=None):
1743 """Initializes a new chain partition descriptor.
1744
1745 Arguments:
1746 data: If not None, must be a bytearray of size |SIZE|.
1747
1748 Raises:
1749 LookupError: If the given descriptor is malformed.
1750 """
1751 AvbDescriptor.__init__(self, None)
1752 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1753
1754 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001755 (tag, num_bytes_following, self.rollback_index_location,
1756 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001757 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001758 expected_size = round_to_multiple(
1759 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1760 if tag != self.TAG or num_bytes_following != expected_size:
1761 raise LookupError('Given data does not look like a chain partition '
1762 'descriptor.')
1763 o = 0
1764 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1765 partition_name_len)])
1766 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1767 self.partition_name.decode('utf-8')
1768 o += partition_name_len
1769 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1770
1771 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001772 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001773 self.partition_name = ''
1774 self.public_key = bytearray()
1775
1776 def print_desc(self, o):
1777 """Print the descriptor.
1778
1779 Arguments:
1780 o: The object to write the output to.
1781 """
1782 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001783 o.write(' Partition Name: {}\n'.format(self.partition_name))
1784 o.write(' Rollback Index Location: {}\n'.format(
1785 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001786 # Just show the SHA1 of the key, for size reasons.
1787 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001788 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001789
1790 def encode(self):
1791 """Serializes the descriptor.
1792
1793 Returns:
1794 A bytearray() with the descriptor data.
1795 """
1796 encoded_name = self.partition_name.encode('utf-8')
1797 num_bytes_following = (
1798 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1799 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1800 padding_size = nbf_with_padding - num_bytes_following
1801 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001802 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001803 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001804 padding = struct.pack(str(padding_size) + 'x')
1805 ret = desc + encoded_name + self.public_key + padding
1806 return bytearray(ret)
1807
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001808 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001809 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001810 """Verifies contents of the descriptor - used in verify_image sub-command.
1811
1812 Arguments:
1813 image_dir: The directory of the file being verified.
1814 image_ext: The extension of the file being verified (e.g. '.img').
1815 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001816 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001817 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001818 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1819 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001820
1821 Returns:
1822 True if the descriptor verifies, False otherwise.
1823 """
1824 value = expected_chain_partitions_map.get(self.partition_name)
1825 if not value:
1826 sys.stderr.write('No expected chain partition for partition {}. Use '
1827 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001828 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001829 format(self.partition_name))
1830 return False
1831 rollback_index_location, pk_blob = value
1832
1833 if self.rollback_index_location != rollback_index_location:
1834 sys.stderr.write('Expected rollback_index_location {} does not '
1835 'match {} in descriptor for partition {}\n'.
1836 format(rollback_index_location,
1837 self.rollback_index_location,
1838 self.partition_name))
1839 return False
1840
1841 if self.public_key != pk_blob:
1842 sys.stderr.write('Expected public key blob does not match public '
1843 'key blob in descriptor for partition {}\n'.
1844 format(self.partition_name))
1845 return False
1846
Jan Monsch23e0c622019-12-11 11:23:58 +01001847 print('{}: Successfully verified chain partition descriptor matches '
1848 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001849
1850 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001851
1852DESCRIPTOR_CLASSES = [
1853 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1854 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1855]
1856
1857
1858def parse_descriptors(data):
1859 """Parses a blob of data into descriptors.
1860
1861 Arguments:
1862 data: A bytearray() with encoded descriptors.
1863
1864 Returns:
1865 A list of instances of objects derived from AvbDescriptor. For
1866 unknown descriptors, the class AvbDescriptor is used.
1867 """
1868 o = 0
1869 ret = []
1870 while o < len(data):
1871 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1872 if tag < len(DESCRIPTOR_CLASSES):
1873 c = DESCRIPTOR_CLASSES[tag]
1874 else:
1875 c = AvbDescriptor
1876 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1877 o += 16 + nb_following
1878 return ret
1879
1880
1881class AvbFooter(object):
1882 """A class for parsing and writing footers.
1883
1884 Footers are stored at the end of partitions and point to where the
1885 AvbVBMeta blob is located. They also contain the original size of
1886 the image before AVB information was added.
1887
1888 Attributes:
1889 magic: Magic for identifying the footer, see |MAGIC|.
1890 version_major: The major version of avbtool that wrote the footer.
1891 version_minor: The minor version of avbtool that wrote the footer.
1892 original_image_size: Original image size.
1893 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1894 vbmeta_size: Size of the AvbVBMeta blob.
1895 """
1896
1897 MAGIC = 'AVBf'
1898 SIZE = 64
1899 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001900 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1901 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001902 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1903 'Q' # Original image size.
1904 'Q' # Offset of VBMeta blob.
1905 'Q' + # Size of VBMeta blob.
1906 str(RESERVED) + 'x') # padding for reserved bytes
1907
1908 def __init__(self, data=None):
1909 """Initializes a new footer object.
1910
1911 Arguments:
1912 data: If not None, must be a bytearray of size 4096.
1913
1914 Raises:
1915 LookupError: If the given footer is malformed.
1916 struct.error: If the given data has no footer.
1917 """
1918 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1919
1920 if data:
1921 (self.magic, self.version_major, self.version_minor,
1922 self.original_image_size, self.vbmeta_offset,
1923 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1924 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001925 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001926 else:
1927 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001928 self.version_major = self.FOOTER_VERSION_MAJOR
1929 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001930 self.original_image_size = 0
1931 self.vbmeta_offset = 0
1932 self.vbmeta_size = 0
1933
David Zeuthena4fee8b2016-08-22 15:20:43 -04001934 def encode(self):
1935 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001936
David Zeuthena4fee8b2016-08-22 15:20:43 -04001937 Returns:
1938 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001939 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001940 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1941 self.version_minor, self.original_image_size,
1942 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001943
1944
1945class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001946 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001947
Jan Monschfe00c0a2019-12-11 11:19:40 +01001948 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1949 avb_vbmeta_image.h.
1950
David Zeuthen21e95262016-07-27 17:58:40 -04001951 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001952 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1953 required_libavb_version_major: The major version of libavb required for this
1954 header.
1955 required_libavb_version_minor: The minor version of libavb required for this
1956 header.
1957 authentication_data_block_size: The size of the signature block.
1958 auxiliary_data_block_size: The size of the auxiliary data block.
1959 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1960 enum.
1961 hash_offset: Offset into the "Authentication data" block of hash data.
1962 hash_size: Length of the hash data.
1963 signature_offset: Offset into the "Authentication data" block of signature
1964 data.
1965 signature_size: Length of the signature data.
1966 public_key_offset: Offset into the "Auxiliary data" block of public key
1967 data.
1968 public_key_size: Length of the public key data.
1969 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1970 key metadata.
1971 public_key_metadata_size: Length of the public key metadata. Must be set to
1972 zero if there is no public key metadata.
1973 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1974 data.
1975 descriptors_size: Length of descriptor data.
1976 rollback_index: The rollback index which can be used to prevent rollback to
1977 older versions.
1978 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
1979 zero if the vbmeta image is not a top-level image.
1980 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
1981 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
1982 terminated. Applications must not make assumptions about how this
1983 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04001984 """
1985
1986 SIZE = 256
1987
David Zeuthene3cadca2017-02-22 21:25:46 -05001988 # Keep in sync with |reserved0| and |reserved| field of
1989 # |AvbVBMetaImageHeader|.
1990 RESERVED0 = 4
1991 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001992
1993 # Keep in sync with |AvbVBMetaImageHeader|.
1994 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1995 '2Q' # 2 x block size
1996 'L' # algorithm type
1997 '2Q' # offset, size (hash)
1998 '2Q' # offset, size (signature)
1999 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002000 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002001 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002002 'Q' # rollback_index
2003 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002004 str(RESERVED0) + 'x' + # padding for reserved bytes
2005 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002006 str(RESERVED) + 'x') # padding for reserved bytes
2007
2008 def __init__(self, data=None):
2009 """Initializes a new header object.
2010
2011 Arguments:
2012 data: If not None, must be a bytearray of size 8192.
2013
2014 Raises:
2015 Exception: If the given data is malformed.
2016 """
2017 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2018
2019 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002020 (self.magic, self.required_libavb_version_major,
2021 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002022 self.authentication_data_block_size, self.auxiliary_data_block_size,
2023 self.algorithm_type, self.hash_offset, self.hash_size,
2024 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002025 self.public_key_size, self.public_key_metadata_offset,
2026 self.public_key_metadata_size, self.descriptors_offset,
2027 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002028 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002029 self.flags,
2030 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002031 # Nuke NUL-bytes at the end of the string.
2032 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002033 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002034 else:
2035 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002036 # Start by just requiring version 1.0. Code that adds features
2037 # in a future version can use bump_required_libavb_version_minor() to
2038 # bump the minor.
2039 self.required_libavb_version_major = AVB_VERSION_MAJOR
2040 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002041 self.authentication_data_block_size = 0
2042 self.auxiliary_data_block_size = 0
2043 self.algorithm_type = 0
2044 self.hash_offset = 0
2045 self.hash_size = 0
2046 self.signature_offset = 0
2047 self.signature_size = 0
2048 self.public_key_offset = 0
2049 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002050 self.public_key_metadata_offset = 0
2051 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002052 self.descriptors_offset = 0
2053 self.descriptors_size = 0
2054 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002055 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002056 self.release_string = get_release_string()
2057
2058 def bump_required_libavb_version_minor(self, minor):
2059 """Function to bump required_libavb_version_minor.
2060
2061 Call this when writing data that requires a specific libavb
2062 version to parse it.
2063
2064 Arguments:
2065 minor: The minor version of libavb that has support for the feature.
2066 """
2067 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002068 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002069
David Zeuthen21e95262016-07-27 17:58:40 -04002070 def encode(self):
2071 """Serializes the header (256) to a bytearray().
2072
2073 Returns:
2074 A bytearray() with the encoded header.
2075 """
2076 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002077 self.required_libavb_version_major,
2078 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002079 self.authentication_data_block_size,
2080 self.auxiliary_data_block_size, self.algorithm_type,
2081 self.hash_offset, self.hash_size, self.signature_offset,
2082 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002083 self.public_key_size, self.public_key_metadata_offset,
2084 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002085 self.descriptors_size, self.rollback_index, self.flags,
2086 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002087
2088
2089class Avb(object):
2090 """Business logic for avbtool command-line tool."""
2091
David Zeuthen8b6973b2016-09-20 12:39:49 -04002092 # Keep in sync with avb_ab_flow.h.
2093 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2094 AB_MAGIC = '\0AB0'
2095 AB_MAJOR_VERSION = 1
2096 AB_MINOR_VERSION = 0
2097 AB_MISC_METADATA_OFFSET = 2048
2098
David Zeuthen09692692016-09-30 16:16:40 -04002099 # Constants for maximum metadata size. These are used to give
2100 # meaningful errors if the value passed in via --partition_size is
2101 # too small and when --calc_max_image_size is used. We use
2102 # conservative figures.
2103 MAX_VBMETA_SIZE = 64 * 1024
2104 MAX_FOOTER_SIZE = 4096
2105
Jan Monsch2c7be992020-04-03 14:37:13 +02002106 def generate_test_image(self, output, image_size, start_byte):
2107 """Generates a test image for testing avbtool with known content.
2108
2109 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2110
2111 Arguments:
2112 output: Write test image to this file.
2113 image_size: The size of the requested file in bytes.
2114 start_byte: The integer value of the start byte to use for pattern
2115 generation.
2116 """
2117 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2118 buf = bytearray()
2119 c = int(math.ceil(image_size / 256.0))
2120 for _ in range(0, c):
2121 buf.extend(pattern)
2122 output.write(buf[0:image_size])
2123
David Zeuthen49936b42018-08-07 17:38:58 -04002124 def extract_vbmeta_image(self, output, image_filename, padding_size):
2125 """Implements the 'extract_vbmeta_image' command.
2126
2127 Arguments:
2128 output: Write vbmeta struct to this file.
2129 image_filename: File to extract vbmeta data from (with a footer).
2130 padding_size: If not 0, pads output so size is a multiple of the number.
2131
2132 Raises:
2133 AvbError: If there's no footer in the image.
2134 """
2135 image = ImageHandler(image_filename)
2136
2137 (footer, _, _, _) = self._parse_image(image)
2138
2139 if not footer:
2140 raise AvbError('Given image does not have a footer.')
2141
2142 image.seek(footer.vbmeta_offset)
2143 vbmeta_blob = image.read(footer.vbmeta_size)
2144 output.write(vbmeta_blob)
2145
2146 if padding_size > 0:
2147 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2148 padding_needed = padded_size - len(vbmeta_blob)
2149 output.write('\0' * padding_needed)
2150
David Zeuthena4fee8b2016-08-22 15:20:43 -04002151 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002152 """Implements the 'erase_footer' command.
2153
2154 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002155 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002156 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002157
2158 Raises:
2159 AvbError: If there's no footer in the image.
2160 """
2161
David Zeuthena4fee8b2016-08-22 15:20:43 -04002162 image = ImageHandler(image_filename)
2163
David Zeuthen21e95262016-07-27 17:58:40 -04002164 (footer, _, descriptors, _) = self._parse_image(image)
2165
2166 if not footer:
2167 raise AvbError('Given image does not have a footer.')
2168
2169 new_image_size = None
2170 if not keep_hashtree:
2171 new_image_size = footer.original_image_size
2172 else:
2173 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002174 # descriptor to figure out the location and size of the hashtree
2175 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002176 for desc in descriptors:
2177 if isinstance(desc, AvbHashtreeDescriptor):
2178 # The hashtree is always just following the main data so the
2179 # new size is easily derived.
2180 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002181 # If the image has FEC codes, also keep those.
2182 if desc.fec_offset > 0:
2183 fec_end = desc.fec_offset + desc.fec_size
2184 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002185 break
2186 if not new_image_size:
2187 raise AvbError('Requested to keep hashtree but no hashtree '
2188 'descriptor was found.')
2189
2190 # And cut...
2191 image.truncate(new_image_size)
2192
David Zeuthen1394f762019-04-30 10:20:11 -04002193 def zero_hashtree(self, image_filename):
2194 """Implements the 'zero_hashtree' command.
2195
2196 Arguments:
2197 image_filename: File to zero hashtree and FEC data from.
2198
2199 Raises:
2200 AvbError: If there's no footer in the image.
2201 """
2202
2203 image = ImageHandler(image_filename)
2204
2205 (footer, _, descriptors, _) = self._parse_image(image)
2206
2207 if not footer:
2208 raise AvbError('Given image does not have a footer.')
2209
2210 # Search for a hashtree descriptor to figure out the location and
2211 # size of the hashtree and FEC.
2212 ht_desc = None
2213 for desc in descriptors:
2214 if isinstance(desc, AvbHashtreeDescriptor):
2215 ht_desc = desc
2216 break
2217
2218 if not ht_desc:
2219 raise AvbError('No hashtree descriptor was found.')
2220
2221 zero_ht_start_offset = ht_desc.tree_offset
2222 zero_ht_num_bytes = ht_desc.tree_size
2223 zero_fec_start_offset = None
2224 zero_fec_num_bytes = 0
2225 if ht_desc.fec_offset > 0:
2226 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2227 raise AvbError('Hash-tree and FEC data must be adjacent.')
2228 zero_fec_start_offset = ht_desc.fec_offset
2229 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002230 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2231 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002232 image.seek(zero_end_offset)
2233 data = image.read(image.image_size - zero_end_offset)
2234
2235 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2236 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2237 # beginning of both hashtree and FEC. (That way, in the future we can add
2238 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2239 #
2240 # Applications can use these markers to detect that the hashtree and/or
2241 # FEC needs to be recomputed.
2242 image.truncate(zero_ht_start_offset)
2243 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2244 image.append_raw(data_zeroed_firstblock)
2245 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2246 if zero_fec_start_offset:
2247 image.append_raw(data_zeroed_firstblock)
2248 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2249 image.append_raw(data)
2250
David Zeuthen2bc232b2017-04-19 14:25:19 -04002251 def resize_image(self, image_filename, partition_size):
2252 """Implements the 'resize_image' command.
2253
2254 Arguments:
2255 image_filename: File with footer to resize.
2256 partition_size: The new size of the image.
2257
2258 Raises:
2259 AvbError: If there's no footer in the image.
2260 """
2261
2262 image = ImageHandler(image_filename)
2263
2264 if partition_size % image.block_size != 0:
2265 raise AvbError('Partition size of {} is not a multiple of the image '
2266 'block size {}.'.format(partition_size,
2267 image.block_size))
2268
Jan Monsch77cd2022019-12-10 17:18:04 +01002269 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002270
2271 if not footer:
2272 raise AvbError('Given image does not have a footer.')
2273
2274 # The vbmeta blob is always at the end of the data so resizing an
2275 # image amounts to just moving the footer around.
2276
2277 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2278 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002279 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2280 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002281
2282 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002283 raise AvbError('Requested size of {} is too small for an image '
2284 'of size {}.'
2285 .format(partition_size,
2286 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002287
2288 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2289 # with enough bytes such that the final Footer block is at the end
2290 # of partition_size.
2291 image.truncate(vbmeta_end_offset)
2292 image.append_dont_care(partition_size - vbmeta_end_offset -
2293 1*image.block_size)
2294
2295 # Just reuse the same footer - only difference is that we're
2296 # writing it in a different place.
2297 footer_blob = footer.encode()
2298 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2299 footer_blob)
2300 image.append_raw(footer_blob_with_padding)
2301
David Zeuthen8b6973b2016-09-20 12:39:49 -04002302 def set_ab_metadata(self, misc_image, slot_data):
2303 """Implements the 'set_ab_metadata' command.
2304
2305 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2306 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2307
2308 Arguments:
2309 misc_image: The misc image to write to.
2310 slot_data: Slot data as a string
2311
2312 Raises:
2313 AvbError: If slot data is malformed.
2314 """
2315 tokens = slot_data.split(':')
2316 if len(tokens) != 6:
2317 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2318 a_priority = int(tokens[0])
2319 a_tries_remaining = int(tokens[1])
2320 a_success = True if int(tokens[2]) != 0 else False
2321 b_priority = int(tokens[3])
2322 b_tries_remaining = int(tokens[4])
2323 b_success = True if int(tokens[5]) != 0 else False
2324
2325 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2326 self.AB_MAGIC,
2327 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2328 a_priority, a_tries_remaining, a_success,
2329 b_priority, b_tries_remaining, b_success)
2330 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2331 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2332 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2333 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2334 misc_image.write(ab_data)
2335
David Zeuthena4fee8b2016-08-22 15:20:43 -04002336 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002337 """Implements the 'info_image' command.
2338
2339 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002340 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002341 output: Output file to write human-readable information to (file object).
2342 """
2343
David Zeuthena4fee8b2016-08-22 15:20:43 -04002344 image = ImageHandler(image_filename)
2345
David Zeuthen21e95262016-07-27 17:58:40 -04002346 o = output
2347
2348 (footer, header, descriptors, image_size) = self._parse_image(image)
2349
Bowgo Tsaid7145942020-03-20 17:03:51 +08002350 # To show the SHA1 of the public key.
2351 vbmeta_blob = self._load_vbmeta_blob(image)
2352 key_offset = (header.SIZE +
2353 header.authentication_data_block_size +
2354 header.public_key_offset)
2355 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2356
David Zeuthen21e95262016-07-27 17:58:40 -04002357 if footer:
2358 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2359 footer.version_minor))
2360 o.write('Image size: {} bytes\n'.format(image_size))
2361 o.write('Original image size: {} bytes\n'.format(
2362 footer.original_image_size))
2363 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2364 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2365 o.write('--\n')
2366
2367 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2368
David Zeuthene3cadca2017-02-22 21:25:46 -05002369 o.write('Minimum libavb version: {}.{}{}\n'.format(
2370 header.required_libavb_version_major,
2371 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002372 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002373 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2374 o.write('Authentication Block: {} bytes\n'.format(
2375 header.authentication_data_block_size))
2376 o.write('Auxiliary Block: {} bytes\n'.format(
2377 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002378 if key_blob:
2379 hexdig = hashlib.sha1(key_blob).hexdigest()
2380 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002381 o.write('Algorithm: {}\n'.format(alg_name))
2382 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002383 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002384 o.write('Release String: \'{}\'\n'.format(
2385 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002386
2387 # Print descriptors.
2388 num_printed = 0
2389 o.write('Descriptors:\n')
2390 for desc in descriptors:
2391 desc.print_desc(o)
2392 num_printed += 1
2393 if num_printed == 0:
2394 o.write(' (none)\n')
2395
Jan Monscheeb28b62019-12-05 16:17:09 +01002396 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2397 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002398 """Implements the 'verify_image' command.
2399
2400 Arguments:
2401 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002402 key_path: None or check that embedded public key matches key at given
2403 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002404 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002405 follow_chain_partitions:
2406 If True, will follows chain partitions even when not specified with
2407 the --expected_chain_partition option
2408 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2409 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002410
2411 Raises:
2412 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002413 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002414 expected_chain_partitions_map = {}
2415 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002416 for cp in expected_chain_partitions:
2417 cp_tokens = cp.split(':')
2418 if len(cp_tokens) != 3:
2419 raise AvbError('Malformed chained partition "{}".'.format(cp))
2420 partition_name = cp_tokens[0]
2421 rollback_index_location = int(cp_tokens[1])
2422 file_path = cp_tokens[2]
2423 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002424 expected_chain_partitions_map[partition_name] = (
2425 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002426
2427 image_dir = os.path.dirname(image_filename)
2428 image_ext = os.path.splitext(image_filename)[1]
2429
2430 key_blob = None
2431 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002432 print('Verifying image {} using key at {}'.format(image_filename,
2433 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002434 key_blob = encode_rsa_key(key_path)
2435 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002436 print('Verifying image {} using embedded public key'.format(
2437 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002438
David Zeuthenb623d8b2017-04-04 16:05:53 -04002439 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002440 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002441 offset = 0
2442 if footer:
2443 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002444
David Zeuthenb623d8b2017-04-04 16:05:53 -04002445 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002446 vbmeta_blob = image.read(header.SIZE
2447 + header.authentication_data_block_size
2448 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002449
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002450 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002451 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002452 raise AvbError('Signature check failed for {} vbmeta struct {}'
2453 .format(alg_name, image_filename))
2454
2455 if key_blob:
2456 # The embedded public key is in the auxiliary block at an offset.
2457 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002458 key_offset += header.authentication_data_block_size
2459 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002460 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2461 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002462 if key_blob != key_blob_in_vbmeta:
2463 raise AvbError('Embedded public key does not match given key.')
2464
2465 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002466 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2467 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002468 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002469 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2470 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002471
2472 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002473 if (isinstance(desc, AvbChainPartitionDescriptor)
2474 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002475 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002476 # In this case we're processing a chain descriptor but don't have a
2477 # --expect_chain_partition ... however --follow_chain_partitions was
2478 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002479 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2480 'and KEY (which has sha1 {}) not specified'
2481 .format(desc.partition_name, desc.rollback_index_location,
2482 hashlib.sha1(desc.public_key).hexdigest()))
2483 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002484 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002485 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002486 # Honor --follow_chain_partitions - add '--' to make the output more
2487 # readable.
2488 if (isinstance(desc, AvbChainPartitionDescriptor)
2489 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002490 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002491 chained_image_filename = os.path.join(image_dir,
2492 desc.partition_name + image_ext)
2493 self.verify_image(chained_image_filename, key_path, None, False,
2494 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002495
David Zeuthenb8643c02018-05-17 17:21:18 -04002496 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2497 """Implements the 'calculate_vbmeta_digest' command.
2498
2499 Arguments:
2500 image_filename: Image file to get information from (file object).
2501 hash_algorithm: Hash algorithm used.
2502 output: Output file to write human-readable information to (file object).
2503 """
2504
2505 image_dir = os.path.dirname(image_filename)
2506 image_ext = os.path.splitext(image_filename)[1]
2507
2508 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002509 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002510 offset = 0
2511 if footer:
2512 offset = footer.vbmeta_offset
2513 size = (header.SIZE + header.authentication_data_block_size +
2514 header.auxiliary_data_block_size)
2515 image.seek(offset)
2516 vbmeta_blob = image.read(size)
2517
2518 hasher = hashlib.new(name=hash_algorithm)
2519 hasher.update(vbmeta_blob)
2520
2521 for desc in descriptors:
2522 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002523 ch_image_filename = os.path.join(image_dir,
2524 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002525 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002526 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002527 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002528 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2529 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002530 if ch_footer:
2531 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002532 ch_image.seek(ch_offset)
2533 ch_vbmeta_blob = ch_image.read(ch_size)
2534 hasher.update(ch_vbmeta_blob)
2535
2536 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002537 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002538
David Zeuthenf7d2e752018-09-20 13:30:41 -04002539 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2540 """Implements the 'calculate_kernel_cmdline' command.
2541
2542 Arguments:
2543 image_filename: Image file to get information from (file object).
2544 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2545 output: Output file to write human-readable information to (file object).
2546 """
2547
2548 image = ImageHandler(image_filename)
2549 _, _, descriptors, _ = self._parse_image(image)
2550
2551 image_dir = os.path.dirname(image_filename)
2552 image_ext = os.path.splitext(image_filename)[1]
2553
2554 cmdline_descriptors = []
2555 for desc in descriptors:
2556 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002557 ch_image_filename = os.path.join(image_dir,
2558 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002559 ch_image = ImageHandler(ch_image_filename)
2560 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2561 for ch_desc in ch_descriptors:
2562 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2563 cmdline_descriptors.append(ch_desc)
2564 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2565 cmdline_descriptors.append(desc)
2566
2567 kernel_cmdline_snippets = []
2568 for desc in cmdline_descriptors:
2569 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002570 if ((desc.flags &
2571 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2572 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002573 if hashtree_disabled:
2574 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002575 if (desc.flags &
2576 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002577 if not hashtree_disabled:
2578 use_cmdline = False
2579 if use_cmdline:
2580 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2581 output.write(' '.join(kernel_cmdline_snippets))
2582
David Zeuthen21e95262016-07-27 17:58:40 -04002583 def _parse_image(self, image):
2584 """Gets information about an image.
2585
2586 The image can either be a vbmeta or an image with a footer.
2587
2588 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002589 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002590
2591 Returns:
2592 A tuple where the first argument is a AvbFooter (None if there
2593 is no footer on the image), the second argument is a
2594 AvbVBMetaHeader, the third argument is a list of
2595 AvbDescriptor-derived instances, and the fourth argument is the
2596 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002597
2598 Raises:
2599 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002600 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002601 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002602 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002603 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002604 try:
2605 footer = AvbFooter(image.read(AvbFooter.SIZE))
2606 except (LookupError, struct.error):
2607 # Nope, just seek back to the start.
2608 image.seek(0)
2609
2610 vbmeta_offset = 0
2611 if footer:
2612 vbmeta_offset = footer.vbmeta_offset
2613
2614 image.seek(vbmeta_offset)
2615 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2616
2617 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2618 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2619 desc_start_offset = aux_block_offset + h.descriptors_offset
2620 image.seek(desc_start_offset)
2621 descriptors = parse_descriptors(image.read(h.descriptors_size))
2622
David Zeuthen09692692016-09-30 16:16:40 -04002623 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002624
David Zeuthenb1b994d2017-03-06 18:01:31 -05002625 def _load_vbmeta_blob(self, image):
2626 """Gets the vbmeta struct and associated sections.
2627
2628 The image can either be a vbmeta.img or an image with a footer.
2629
2630 Arguments:
2631 image: An ImageHandler (vbmeta or footer).
2632
2633 Returns:
2634 A blob with the vbmeta struct and other sections.
2635 """
2636 assert isinstance(image, ImageHandler)
2637 footer = None
2638 image.seek(image.image_size - AvbFooter.SIZE)
2639 try:
2640 footer = AvbFooter(image.read(AvbFooter.SIZE))
2641 except (LookupError, struct.error):
2642 # Nope, just seek back to the start.
2643 image.seek(0)
2644
2645 vbmeta_offset = 0
2646 if footer:
2647 vbmeta_offset = footer.vbmeta_offset
2648
2649 image.seek(vbmeta_offset)
2650 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2651
2652 image.seek(vbmeta_offset)
2653 data_size = AvbVBMetaHeader.SIZE
2654 data_size += h.authentication_data_block_size
2655 data_size += h.auxiliary_data_block_size
2656 return image.read(data_size)
2657
David Zeuthen73f2afa2017-05-17 16:54:11 -04002658 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002659 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002660
2661 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002662 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002663
2664 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002665 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2666 instructions. There is one for when hashtree is not disabled and one for
2667 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002668
David Zeuthen21e95262016-07-27 17:58:40 -04002669 """
2670
David Zeuthen21e95262016-07-27 17:58:40 -04002671 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002672 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002673 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002674 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2675 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2676 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2677 c += ' {}'.format(ht.data_block_size) # data_block
2678 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002679 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2680 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002681 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2682 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2683 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2684 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002685 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002686 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002687 c += ' ignore_zero_blocks'
2688 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2689 c += ' fec_roots {}'.format(ht.fec_num_roots)
2690 # Note that fec_blocks is the size that FEC covers, *not* the
2691 # size of the FEC data. Since we use FEC for everything up until
2692 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002693 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2694 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002695 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002696 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002697 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002698 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002699 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002700
David Zeuthenfd41eb92016-11-17 12:24:47 -05002701 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002702 desc = AvbKernelCmdlineDescriptor()
2703 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002704 desc.flags = (
2705 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2706
2707 # The descriptor for when hashtree verification is disabled is a lot
2708 # simpler - we just set the root to the partition.
2709 desc_no_ht = AvbKernelCmdlineDescriptor()
2710 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2711 desc_no_ht.flags = (
2712 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2713
2714 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002715
David Zeuthen73f2afa2017-05-17 16:54:11 -04002716 def _get_cmdline_descriptors_for_dm_verity(self, image):
2717 """Generate kernel cmdline descriptors for dm-verity.
2718
2719 Arguments:
2720 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2721
2722 Returns:
2723 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2724 instructions. There is one for when hashtree is not disabled and one for
2725 when it is.
2726
2727 Raises:
2728 AvbError: If |image| doesn't have a hashtree descriptor.
2729
2730 """
2731
2732 (_, _, descriptors, _) = self._parse_image(image)
2733
2734 ht = None
2735 for desc in descriptors:
2736 if isinstance(desc, AvbHashtreeDescriptor):
2737 ht = desc
2738 break
2739
2740 if not ht:
2741 raise AvbError('No hashtree descriptor in given image')
2742
2743 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2744
David Zeuthen21e95262016-07-27 17:58:40 -04002745 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002746 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002747 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002748 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002749 include_descriptors_from_image,
2750 signing_helper,
2751 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002752 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002753 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002754 print_required_libavb_version,
2755 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002756 """Implements the 'make_vbmeta_image' command.
2757
2758 Arguments:
2759 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002760 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002761 algorithm_name: Name of algorithm to use.
2762 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002763 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002764 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002765 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002766 props: Properties to insert (list of strings of the form 'key:value').
2767 props_from_file: Properties to insert (list of strings 'key:<path>').
2768 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002769 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002770 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002771 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002772 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002773 release_string: None or avbtool release string to use instead of default.
2774 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002775 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002776 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002777
2778 Raises:
2779 AvbError: If a chained partition is malformed.
2780 """
2781
David Zeuthen1097a782017-05-31 15:53:17 -04002782 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002783 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002784 if include_descriptors_from_image:
2785 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2786 # version of all included descriptors.
2787 tmp_header = AvbVBMetaHeader()
2788 for image in include_descriptors_from_image:
2789 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2790 tmp_header.bump_required_libavb_version_minor(
2791 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002792 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002793 else:
2794 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002795 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002796 return
2797
2798 if not output:
2799 raise AvbError('No output file given')
2800
David Zeuthen21e95262016-07-27 17:58:40 -04002801 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002802 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002803 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002804 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002805 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002806 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002807 include_descriptors_from_image, signing_helper,
2808 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002809 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002810
2811 # Write entire vbmeta blob (header, authentication, auxiliary).
2812 output.seek(0)
2813 output.write(vbmeta_blob)
2814
David Zeuthen97cb5802017-06-01 16:14:05 -04002815 if padding_size > 0:
2816 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2817 padding_needed = padded_size - len(vbmeta_blob)
2818 output.write('\0' * padding_needed)
2819
David Zeuthen18666ab2016-11-15 11:18:05 -05002820 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2821 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002822 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002823 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002824 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002825 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002826 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002827 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002828 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002829 release_string, append_to_release_string,
2830 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002831 """Generates a VBMeta blob.
2832
2833 This blob contains the header (struct AvbVBMetaHeader), the
2834 authentication data block (which contains the hash and signature
2835 for the header and auxiliary block), and the auxiliary block
2836 (which contains descriptors, the public key used, and other data).
2837
2838 The |key| parameter can |None| only if the |algorithm_name| is
2839 'NONE'.
2840
2841 Arguments:
2842 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2843 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002844 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002845 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002846 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002847 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002848 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002849 props: Properties to insert (List of strings of the form 'key:value').
2850 props_from_file: Properties to insert (List of strings 'key:<path>').
2851 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002852 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002853 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002854 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2855 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002856 include_descriptors_from_image: List of file objects for which
2857 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002858 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002859 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002860 release_string: None or avbtool release string.
2861 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002862 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002863
2864 Returns:
2865 A bytearray() with the VBMeta blob.
2866
2867 Raises:
2868 Exception: If the |algorithm_name| is not found, if no key has
2869 been given and the given algorithm requires one, or the key is
2870 of the wrong size.
2871
2872 """
2873 try:
2874 alg = ALGORITHMS[algorithm_name]
2875 except KeyError:
2876 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2877
David Zeuthena5fd3a42017-02-27 16:38:54 -05002878 if not descriptors:
2879 descriptors = []
2880
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002881 h = AvbVBMetaHeader()
2882 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2883
David Zeuthena5fd3a42017-02-27 16:38:54 -05002884 # Insert chained partition descriptors, if any
2885 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002886 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002887 for cp in chain_partitions:
2888 cp_tokens = cp.split(':')
2889 if len(cp_tokens) != 3:
2890 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002891 partition_name = cp_tokens[0]
2892 rollback_index_location = int(cp_tokens[1])
2893 file_path = cp_tokens[2]
2894 # Check that the same rollback location isn't being used by
2895 # multiple chained partitions.
2896 if used_locations.get(rollback_index_location):
2897 raise AvbError('Rollback Index Location {} is already in use.'.format(
2898 rollback_index_location))
2899 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002900 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002901 desc.partition_name = partition_name
2902 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002903 if desc.rollback_index_location < 1:
2904 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002905 desc.public_key = open(file_path, 'rb').read()
2906 descriptors.append(desc)
2907
David Zeuthen21e95262016-07-27 17:58:40 -04002908 # Descriptors.
2909 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002910 for desc in descriptors:
2911 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002912
2913 # Add properties.
2914 if props:
2915 for prop in props:
2916 idx = prop.find(':')
2917 if idx == -1:
2918 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002919 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002920 desc = AvbPropertyDescriptor()
2921 desc.key = prop[0:idx]
2922 desc.value = prop[(idx + 1):]
2923 encoded_descriptors.extend(desc.encode())
2924 if props_from_file:
2925 for prop in props_from_file:
2926 idx = prop.find(':')
2927 if idx == -1:
2928 raise AvbError('Malformed property "{}".'.format(prop))
2929 desc = AvbPropertyDescriptor()
2930 desc.key = prop[0:idx]
2931 desc.value = prop[(idx + 1):]
2932 file_path = prop[(idx + 1):]
2933 desc.value = open(file_path, 'rb').read()
2934 encoded_descriptors.extend(desc.encode())
2935
David Zeuthen73f2afa2017-05-17 16:54:11 -04002936 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002937 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002938 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002939 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002940 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2941 encoded_descriptors.extend(cmdline_desc[0].encode())
2942 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002943
David Zeuthen73f2afa2017-05-17 16:54:11 -04002944 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2945 if ht_desc_to_setup:
2946 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2947 ht_desc_to_setup)
2948 encoded_descriptors.extend(cmdline_desc[0].encode())
2949 encoded_descriptors.extend(cmdline_desc[1].encode())
2950
David Zeuthen21e95262016-07-27 17:58:40 -04002951 # Add kernel command-lines.
2952 if kernel_cmdlines:
2953 for i in kernel_cmdlines:
2954 desc = AvbKernelCmdlineDescriptor()
2955 desc.kernel_cmdline = i
2956 encoded_descriptors.extend(desc.encode())
2957
2958 # Add descriptors from other images.
2959 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002960 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002961 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002962 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002963 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2964 image_handler)
2965 # Bump the required libavb version to support all included descriptors.
2966 h.bump_required_libavb_version_minor(
2967 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002968 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002969 # The --include_descriptors_from_image option is used in some setups
2970 # with images A and B where both A and B contain a descriptor
2971 # for a partition with the same name. Since it's not meaningful
2972 # to include both descriptors, only include the last seen descriptor.
2973 # See bug 76386656 for details.
2974 if hasattr(desc, 'partition_name'):
2975 key = type(desc).__name__ + '_' + desc.partition_name
2976 descriptors_dict[key] = desc.encode()
2977 else:
2978 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002979 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002980 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002981
David Zeuthen18666ab2016-11-15 11:18:05 -05002982 # Load public key metadata blob, if requested.
2983 pkmd_blob = []
2984 if public_key_metadata_path:
2985 with open(public_key_metadata_path) as f:
2986 pkmd_blob = f.read()
2987
David Zeuthen21e95262016-07-27 17:58:40 -04002988 key = None
2989 encoded_key = bytearray()
2990 if alg.public_key_num_bytes > 0:
2991 if not key_path:
2992 raise AvbError('Key is required for algorithm {}'.format(
2993 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002994 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002995 if len(encoded_key) != alg.public_key_num_bytes:
2996 raise AvbError('Key is wrong size for algorithm {}'.format(
2997 algorithm_name))
2998
David Zeuthene3cadca2017-02-22 21:25:46 -05002999 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003000 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003001 if isinstance(release_string, (str, unicode)):
3002 h.release_string = release_string
3003
3004 # Append to release string, if requested. Also insert a space before.
3005 if isinstance(append_to_release_string, (str, unicode)):
3006 h.release_string += ' ' + append_to_release_string
3007
David Zeuthen18666ab2016-11-15 11:18:05 -05003008 # For the Auxiliary data block, descriptors are stored at offset 0,
3009 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003010 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003011 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003012 h.descriptors_offset = 0
3013 h.descriptors_size = len(encoded_descriptors)
3014 h.public_key_offset = h.descriptors_size
3015 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003016 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3017 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003018
3019 # For the Authentication data block, the hash is first and then
3020 # the signature.
3021 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003022 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003023 h.algorithm_type = alg.algorithm_type
3024 h.hash_offset = 0
3025 h.hash_size = alg.hash_num_bytes
3026 # Signature offset and size - it's stored right after the hash
3027 # (in Authentication data block).
3028 h.signature_offset = alg.hash_num_bytes
3029 h.signature_size = alg.signature_num_bytes
3030
3031 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003032 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003033
3034 # Generate Header data block.
3035 header_data_blob = h.encode()
3036
3037 # Generate Auxiliary data block.
3038 aux_data_blob = bytearray()
3039 aux_data_blob.extend(encoded_descriptors)
3040 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003041 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003042 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3043 aux_data_blob.extend('\0' * padding_bytes)
3044
3045 # Calculate the hash.
3046 binary_hash = bytearray()
3047 binary_signature = bytearray()
3048 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003049 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003050 ha.update(header_data_blob)
3051 ha.update(aux_data_blob)
3052 binary_hash.extend(ha.digest())
3053
3054 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003055 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003056 binary_signature.extend(raw_sign(signing_helper,
3057 signing_helper_with_files,
3058 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003059 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003060 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003061
3062 # Generate Authentication data block.
3063 auth_data_blob = bytearray()
3064 auth_data_blob.extend(binary_hash)
3065 auth_data_blob.extend(binary_signature)
3066 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3067 auth_data_blob.extend('\0' * padding_bytes)
3068
3069 return header_data_blob + auth_data_blob + aux_data_blob
3070
3071 def extract_public_key(self, key_path, output):
3072 """Implements the 'extract_public_key' command.
3073
3074 Arguments:
3075 key_path: The path to a RSA private key file.
3076 output: The file to write to.
3077 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003078 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003079
David Zeuthenb1b994d2017-03-06 18:01:31 -05003080 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3081 partition_size):
3082 """Implementation of the append_vbmeta_image command.
3083
3084 Arguments:
3085 image_filename: File to add the footer to.
3086 vbmeta_image_filename: File to get vbmeta struct from.
3087 partition_size: Size of partition.
3088
3089 Raises:
3090 AvbError: If an argument is incorrect.
3091 """
3092 image = ImageHandler(image_filename)
3093
3094 if partition_size % image.block_size != 0:
3095 raise AvbError('Partition size of {} is not a multiple of the image '
3096 'block size {}.'.format(partition_size,
3097 image.block_size))
3098
3099 # If there's already a footer, truncate the image to its original
3100 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003101 if image.image_size >= AvbFooter.SIZE:
3102 image.seek(image.image_size - AvbFooter.SIZE)
3103 try:
3104 footer = AvbFooter(image.read(AvbFooter.SIZE))
3105 # Existing footer found. Just truncate.
3106 original_image_size = footer.original_image_size
3107 image.truncate(footer.original_image_size)
3108 except (LookupError, struct.error):
3109 original_image_size = image.image_size
3110 else:
3111 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003112 original_image_size = image.image_size
3113
3114 # If anything goes wrong from here-on, restore the image back to
3115 # its original size.
3116 try:
3117 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3118 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3119
3120 # If the image isn't sparse, its size might not be a multiple of
3121 # the block size. This will screw up padding later so just grow it.
3122 if image.image_size % image.block_size != 0:
3123 assert not image.is_sparse
3124 padding_needed = image.block_size - (image.image_size%image.block_size)
3125 image.truncate(image.image_size + padding_needed)
3126
3127 # The append_raw() method requires content with size being a
3128 # multiple of |block_size| so add padding as needed. Also record
3129 # where this is written to since we'll need to put that in the
3130 # footer.
3131 vbmeta_offset = image.image_size
3132 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3133 len(vbmeta_blob))
3134 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3135
3136 # Append vbmeta blob and footer
3137 image.append_raw(vbmeta_blob_with_padding)
3138 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3139
3140 # Now insert a DONT_CARE chunk with enough bytes such that the
3141 # final Footer block is at the end of partition_size..
3142 image.append_dont_care(partition_size - vbmeta_end_offset -
3143 1*image.block_size)
3144
3145 # Generate the Footer that tells where the VBMeta footer
3146 # is. Also put enough padding in the front of the footer since
3147 # we'll write out an entire block.
3148 footer = AvbFooter()
3149 footer.original_image_size = original_image_size
3150 footer.vbmeta_offset = vbmeta_offset
3151 footer.vbmeta_size = len(vbmeta_blob)
3152 footer_blob = footer.encode()
3153 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3154 footer_blob)
3155 image.append_raw(footer_blob_with_padding)
3156
3157 except:
3158 # Truncate back to original size, then re-raise
3159 image.truncate(original_image_size)
3160 raise
3161
David Zeuthena4fee8b2016-08-22 15:20:43 -04003162 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003163 hash_algorithm, salt, chain_partitions, algorithm_name,
3164 key_path,
3165 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003166 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003167 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003168 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003169 signing_helper, signing_helper_with_files,
3170 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003171 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003172 print_required_libavb_version, use_persistent_digest,
3173 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003174 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003175
3176 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003177 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003178 partition_size: Size of partition.
3179 partition_name: Name of partition (without A/B suffix).
3180 hash_algorithm: Hash algorithm to use.
3181 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003182 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003183 algorithm_name: Name of algorithm to use.
3184 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003185 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003186 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003187 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003188 props: Properties to insert (List of strings of the form 'key:value').
3189 props_from_file: Properties to insert (List of strings 'key:<path>').
3190 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003191 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003192 dm-verity kernel cmdline from.
3193 include_descriptors_from_image: List of file objects for which
3194 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003195 calc_max_image_size: Don't store the footer - instead calculate the
3196 maximum image size leaving enough room for metadata with the
3197 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003198 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003199 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003200 release_string: None or avbtool release string.
3201 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003202 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3203 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003204 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003205 use_persistent_digest: Use a persistent digest on device.
3206 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003207
3208 Raises:
3209 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003210 """
David Zeuthen1097a782017-05-31 15:53:17 -04003211
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003212 required_libavb_version_minor = 0
3213 if use_persistent_digest or do_not_use_ab:
3214 required_libavb_version_minor = 1
3215
David Zeuthen1097a782017-05-31 15:53:17 -04003216 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003217 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003218 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003219 return
3220
David Zeuthenbf562452017-05-17 18:04:43 -04003221 # First, calculate the maximum image size such that an image
3222 # this size + metadata (footer + vbmeta struct) fits in
3223 # |partition_size|.
3224 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003225 if partition_size < max_metadata_size:
3226 raise AvbError('Parition size of {} is too small. '
3227 'Needs to be at least {}'.format(
3228 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003229 max_image_size = partition_size - max_metadata_size
3230
3231 # If we're asked to only calculate the maximum image size, we're done.
3232 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003233 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003234 return
3235
David Zeuthena4fee8b2016-08-22 15:20:43 -04003236 image = ImageHandler(image_filename)
3237
3238 if partition_size % image.block_size != 0:
3239 raise AvbError('Partition size of {} is not a multiple of the image '
3240 'block size {}.'.format(partition_size,
3241 image.block_size))
3242
David Zeuthen21e95262016-07-27 17:58:40 -04003243 # If there's already a footer, truncate the image to its original
3244 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3245 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003246 if image.image_size >= AvbFooter.SIZE:
3247 image.seek(image.image_size - AvbFooter.SIZE)
3248 try:
3249 footer = AvbFooter(image.read(AvbFooter.SIZE))
3250 # Existing footer found. Just truncate.
3251 original_image_size = footer.original_image_size
3252 image.truncate(footer.original_image_size)
3253 except (LookupError, struct.error):
3254 original_image_size = image.image_size
3255 else:
3256 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003257 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003258
3259 # If anything goes wrong from here-on, restore the image back to
3260 # its original size.
3261 try:
David Zeuthen09692692016-09-30 16:16:40 -04003262 # If image size exceeds the maximum image size, fail.
3263 if image.image_size > max_image_size:
3264 raise AvbError('Image size of {} exceeds maximum image '
3265 'size of {} in order to fit in a partition '
3266 'size of {}.'.format(image.image_size, max_image_size,
3267 partition_size))
3268
David Zeuthen21e95262016-07-27 17:58:40 -04003269 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3270 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003271 salt = binascii.unhexlify(salt)
3272 elif salt is None and not use_persistent_digest:
3273 # If salt is not explicitly specified, choose a hash that's the same
3274 # size as the hash size. Don't populate a random salt if this
3275 # descriptor is being created to use a persistent digest on device.
3276 hash_size = digest_size
3277 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003278 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003279 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003280
3281 hasher = hashlib.new(name=hash_algorithm, string=salt)
3282 # TODO(zeuthen): might want to read this in chunks to avoid
3283 # memory pressure, then again, this is only supposed to be used
3284 # on kernel/initramfs partitions. Possible optimization.
3285 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003286 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003287 digest = hasher.digest()
3288
3289 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003290 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003291 h_desc.hash_algorithm = hash_algorithm
3292 h_desc.partition_name = partition_name
3293 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003294 h_desc.flags = 0
3295 if do_not_use_ab:
3296 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3297 if not use_persistent_digest:
3298 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003299
3300 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003301 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003302 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003303 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003304 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003305 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003306 include_descriptors_from_image, signing_helper,
3307 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003308 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003309
David Zeuthend247fcb2017-02-16 12:09:27 -05003310 # Write vbmeta blob, if requested.
3311 if output_vbmeta_image:
3312 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003313
David Zeuthend247fcb2017-02-16 12:09:27 -05003314 # Append vbmeta blob and footer, unless requested not to.
3315 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003316 # If the image isn't sparse, its size might not be a multiple of
3317 # the block size. This will screw up padding later so just grow it.
3318 if image.image_size % image.block_size != 0:
3319 assert not image.is_sparse
3320 padding_needed = image.block_size - (
3321 image.image_size % image.block_size)
3322 image.truncate(image.image_size + padding_needed)
3323
3324 # The append_raw() method requires content with size being a
3325 # multiple of |block_size| so add padding as needed. Also record
3326 # where this is written to since we'll need to put that in the
3327 # footer.
3328 vbmeta_offset = image.image_size
3329 padding_needed = (
3330 round_to_multiple(len(vbmeta_blob), image.block_size) -
3331 len(vbmeta_blob))
3332 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3333
David Zeuthend247fcb2017-02-16 12:09:27 -05003334 image.append_raw(vbmeta_blob_with_padding)
3335 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3336
3337 # Now insert a DONT_CARE chunk with enough bytes such that the
3338 # final Footer block is at the end of partition_size..
3339 image.append_dont_care(partition_size - vbmeta_end_offset -
3340 1*image.block_size)
3341
3342 # Generate the Footer that tells where the VBMeta footer
3343 # is. Also put enough padding in the front of the footer since
3344 # we'll write out an entire block.
3345 footer = AvbFooter()
3346 footer.original_image_size = original_image_size
3347 footer.vbmeta_offset = vbmeta_offset
3348 footer.vbmeta_size = len(vbmeta_blob)
3349 footer_blob = footer.encode()
3350 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3351 footer_blob)
3352 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003353
David Zeuthen21e95262016-07-27 17:58:40 -04003354 except:
3355 # Truncate back to original size, then re-raise
3356 image.truncate(original_image_size)
3357 raise
3358
David Zeuthena4fee8b2016-08-22 15:20:43 -04003359 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003360 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003361 block_size, salt, chain_partitions, algorithm_name,
3362 key_path,
3363 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003364 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003365 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003366 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003367 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003368 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003369 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003370 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003371 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003372 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003373 use_persistent_root_digest, do_not_use_ab,
3374 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003375 """Implements the 'add_hashtree_footer' command.
3376
3377 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3378 more information about dm-verity and these hashes.
3379
3380 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003381 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003382 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003383 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003384 generate_fec: If True, generate FEC codes.
3385 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003386 hash_algorithm: Hash algorithm to use.
3387 block_size: Block size to use.
3388 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003389 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003390 algorithm_name: Name of algorithm to use.
3391 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003392 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003393 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003394 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003395 props: Properties to insert (List of strings of the form 'key:value').
3396 props_from_file: Properties to insert (List of strings 'key:<path>').
3397 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003398 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003399 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003400 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3401 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003402 include_descriptors_from_image: List of file objects for which
3403 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003404 calc_max_image_size: Don't store the hashtree or footer - instead
3405 calculate the maximum image size leaving enough room for hashtree
3406 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003407 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003408 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003409 release_string: None or avbtool release string.
3410 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003411 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3412 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003413 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003414 use_persistent_root_digest: Use a persistent root digest on device.
3415 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003416 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003417
3418 Raises:
3419 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003420 """
David Zeuthen1097a782017-05-31 15:53:17 -04003421
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003422 required_libavb_version_minor = 0
3423 if use_persistent_root_digest or do_not_use_ab:
3424 required_libavb_version_minor = 1
3425
David Zeuthen1097a782017-05-31 15:53:17 -04003426 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003427 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003428 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003429 return
3430
David Zeuthen09692692016-09-30 16:16:40 -04003431 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3432 digest_padding = round_to_pow2(digest_size) - digest_size
3433
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003434 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3435 # size such that an image this size + the hashtree + metadata (footer +
3436 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3437 # for metadata.
3438 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003439 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003440 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003441 if not no_hashtree:
3442 (_, max_tree_size) = calc_hash_level_offsets(
3443 partition_size, block_size, digest_size + digest_padding)
3444 if generate_fec:
3445 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003446 max_metadata_size = (max_fec_size + max_tree_size +
3447 self.MAX_VBMETA_SIZE +
3448 self.MAX_FOOTER_SIZE)
3449 max_image_size = partition_size - max_metadata_size
3450 else:
3451 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003452
3453 # If we're asked to only calculate the maximum image size, we're done.
3454 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003455 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003456 return
3457
David Zeuthena4fee8b2016-08-22 15:20:43 -04003458 image = ImageHandler(image_filename)
3459
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003460 if partition_size > 0:
3461 if partition_size % image.block_size != 0:
3462 raise AvbError('Partition size of {} is not a multiple of the image '
3463 'block size {}.'.format(partition_size,
3464 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003465 elif image.image_size % image.block_size != 0:
3466 raise AvbError('File size of {} is not a multiple of the image '
3467 'block size {}.'.format(image.image_size,
3468 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003469
David Zeuthen21e95262016-07-27 17:58:40 -04003470 # If there's already a footer, truncate the image to its original
3471 # size. This way 'avbtool add_hashtree_footer' is idempotent
3472 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003473 if image.image_size >= AvbFooter.SIZE:
3474 image.seek(image.image_size - AvbFooter.SIZE)
3475 try:
3476 footer = AvbFooter(image.read(AvbFooter.SIZE))
3477 # Existing footer found. Just truncate.
3478 original_image_size = footer.original_image_size
3479 image.truncate(footer.original_image_size)
3480 except (LookupError, struct.error):
3481 original_image_size = image.image_size
3482 else:
3483 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003484 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003485
3486 # If anything goes wrong from here-on, restore the image back to
3487 # its original size.
3488 try:
3489 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003490 rounded_image_size = round_to_multiple(image.image_size, block_size)
3491 if rounded_image_size > image.image_size:
3492 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003493
David Zeuthen09692692016-09-30 16:16:40 -04003494 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003495 if partition_size > 0:
3496 if image.image_size > max_image_size:
3497 raise AvbError('Image size of {} exceeds maximum image '
3498 'size of {} in order to fit in a partition '
3499 'size of {}.'.format(image.image_size, max_image_size,
3500 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003501
3502 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003503 salt = binascii.unhexlify(salt)
3504 elif salt is None and not use_persistent_root_digest:
3505 # If salt is not explicitly specified, choose a hash that's the same
3506 # size as the hash size. Don't populate a random salt if this
3507 # descriptor is being created to use a persistent digest on device.
3508 hash_size = digest_size
3509 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003510 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003511 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003512
David Zeuthena4fee8b2016-08-22 15:20:43 -04003513 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003514 # offsets in advance.
3515 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003516 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003517
David Zeuthena4fee8b2016-08-22 15:20:43 -04003518 # If the image isn't sparse, its size might not be a multiple of
3519 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003520 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003521 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003522 padding_needed = image.block_size - (image.image_size%image.block_size)
3523 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003524
David Zeuthena4fee8b2016-08-22 15:20:43 -04003525 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003526 tree_offset = image.image_size
3527 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003528 block_size,
3529 hash_algorithm, salt,
3530 digest_padding,
3531 hash_level_offsets,
3532 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003533
3534 # Generate HashtreeDescriptor with details about the tree we
3535 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003536 if no_hashtree:
3537 tree_size = 0
3538 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003539 ht_desc = AvbHashtreeDescriptor()
3540 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003541 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003542 ht_desc.tree_offset = tree_offset
3543 ht_desc.tree_size = tree_size
3544 ht_desc.data_block_size = block_size
3545 ht_desc.hash_block_size = block_size
3546 ht_desc.hash_algorithm = hash_algorithm
3547 ht_desc.partition_name = partition_name
3548 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003549 if do_not_use_ab:
3550 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3551 if not use_persistent_root_digest:
3552 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003553
David Zeuthen09692692016-09-30 16:16:40 -04003554 # Write the hash tree
3555 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3556 len(hash_tree))
3557 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3558 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003559 len_hashtree_and_fec = len(hash_tree_with_padding)
3560
3561 # Generate FEC codes, if requested.
3562 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003563 if no_hashtree:
3564 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003565 else:
3566 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003567 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3568 len(fec_data))
3569 fec_data_with_padding = fec_data + '\0'*padding_needed
3570 fec_offset = image.image_size
3571 image.append_raw(fec_data_with_padding)
3572 len_hashtree_and_fec += len(fec_data_with_padding)
3573 # Update the hashtree descriptor.
3574 ht_desc.fec_num_roots = fec_num_roots
3575 ht_desc.fec_offset = fec_offset
3576 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003577
David Zeuthen73f2afa2017-05-17 16:54:11 -04003578 ht_desc_to_setup = None
3579 if setup_as_rootfs_from_kernel:
3580 ht_desc_to_setup = ht_desc
3581
David Zeuthena4fee8b2016-08-22 15:20:43 -04003582 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003583 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003584 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003585 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003586 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003587 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003588 include_descriptors_from_image, signing_helper,
3589 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003590 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003591 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3592 len(vbmeta_blob))
3593 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003594
David Zeuthend247fcb2017-02-16 12:09:27 -05003595 # Write vbmeta blob, if requested.
3596 if output_vbmeta_image:
3597 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003598
David Zeuthend247fcb2017-02-16 12:09:27 -05003599 # Append vbmeta blob and footer, unless requested not to.
3600 if not do_not_append_vbmeta_image:
3601 image.append_raw(vbmeta_blob_with_padding)
3602
3603 # Now insert a DONT_CARE chunk with enough bytes such that the
3604 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003605 if partition_size > 0:
3606 image.append_dont_care(partition_size - image.image_size -
3607 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003608
3609 # Generate the Footer that tells where the VBMeta footer
3610 # is. Also put enough padding in the front of the footer since
3611 # we'll write out an entire block.
3612 footer = AvbFooter()
3613 footer.original_image_size = original_image_size
3614 footer.vbmeta_offset = vbmeta_offset
3615 footer.vbmeta_size = len(vbmeta_blob)
3616 footer_blob = footer.encode()
3617 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3618 footer_blob)
3619 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003620
David Zeuthen21e95262016-07-27 17:58:40 -04003621 except:
David Zeuthen09692692016-09-30 16:16:40 -04003622 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003623 image.truncate(original_image_size)
3624 raise
3625
David Zeuthenc68f0822017-03-31 17:22:35 -04003626 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003627 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003628 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003629 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003630 """Implements the 'make_atx_certificate' command.
3631
3632 Android Things certificates are required for Android Things public key
3633 metadata. They chain the vbmeta signing key for a particular product back to
3634 a fused, permanent root key. These certificates are fixed-length and fixed-
3635 format with the explicit goal of not parsing ASN.1 in bootloader code.
3636
3637 Arguments:
3638 output: Certificate will be written to this file on success.
3639 authority_key_path: A PEM file path with the authority private key.
3640 If None, then a certificate will be created without a
3641 signature. The signature can be created out-of-band
3642 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003643 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003644 subject_key_version: A 64-bit version value. If this is None, the number
3645 of seconds since the epoch is used.
3646 subject: A subject identifier. For Product Signing Key certificates this
3647 should be the same Product ID found in the permanent attributes.
3648 is_intermediate_authority: True if the certificate is for an intermediate
3649 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003650 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003651 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003652 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003653 """
3654 signed_data = bytearray()
3655 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003656 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003657 hasher = hashlib.sha256()
3658 hasher.update(subject)
3659 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003660 if not usage:
3661 usage = 'com.google.android.things.vboot'
3662 if is_intermediate_authority:
3663 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003664 hasher = hashlib.sha256()
3665 hasher.update(usage)
3666 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003667 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003668 subject_key_version = int(time.time())
3669 signed_data.extend(struct.pack('<Q', subject_key_version))
3670 signature = bytearray()
3671 if authority_key_path:
3672 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003673 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003674 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003675 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003676 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003677 hasher.update(signed_data)
3678 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003679 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3680 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003681 alg.signature_num_bytes, authority_key_path,
3682 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003683 output.write(signed_data)
3684 output.write(signature)
3685
David Zeuthenc68f0822017-03-31 17:22:35 -04003686 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003687 product_id):
3688 """Implements the 'make_atx_permanent_attributes' command.
3689
3690 Android Things permanent attributes are designed to be permanent for a
3691 particular product and a hash of these attributes should be fused into
3692 hardware to enforce this.
3693
3694 Arguments:
3695 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003696 root_authority_key_path: Path to a PEM or DER public key for
3697 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003698 product_id: A 16-byte Product ID.
3699
3700 Raises:
3701 AvbError: If an argument is incorrect.
3702 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003703 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003704 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003705 raise AvbError('Invalid Product ID length.')
3706 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003707 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003708 output.write(product_id)
3709
3710 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003711 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003712 """Implements the 'make_atx_metadata' command.
3713
3714 Android Things metadata are included in vbmeta images to facilitate
3715 verification. The output of this command can be used as the
3716 public_key_metadata argument to other commands.
3717
3718 Arguments:
3719 output: Metadata will be written to this file on success.
3720 intermediate_key_certificate: A certificate file as output by
3721 make_atx_certificate with
3722 is_intermediate_authority set to true.
3723 product_key_certificate: A certificate file as output by
3724 make_atx_certificate with
3725 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003726
3727 Raises:
3728 AvbError: If an argument is incorrect.
3729 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003730 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003731 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003732 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003733 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003734 raise AvbError('Invalid product key certificate length.')
3735 output.write(struct.pack('<I', 1)) # Format Version
3736 output.write(intermediate_key_certificate)
3737 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003738
Darren Krahnfccd64e2018-01-16 17:39:35 -08003739 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3740 unlock_key_certificate, challenge_path,
3741 unlock_key_path, signing_helper,
3742 signing_helper_with_files):
3743 """Implements the 'make_atx_unlock_credential' command.
3744
3745 Android Things unlock credentials can be used to authorize the unlock of AVB
3746 on a device. These credentials are presented to an Android Things bootloader
3747 via the fastboot interface in response to a 16-byte challenge. This method
3748 creates all fields of the credential except the challenge signature field
3749 (which is the last field) and can optionally create the challenge signature
3750 field as well if a challenge and the unlock_key_path is provided.
3751
3752 Arguments:
3753 output: The credential will be written to this file on success.
3754 intermediate_key_certificate: A certificate file as output by
3755 make_atx_certificate with
3756 is_intermediate_authority set to true.
3757 unlock_key_certificate: A certificate file as output by
3758 make_atx_certificate with
3759 is_intermediate_authority set to false and the
3760 usage set to
3761 'com.google.android.things.vboot.unlock'.
3762 challenge_path: [optional] A path to the challenge to sign.
3763 unlock_key_path: [optional] A PEM file path with the unlock private key.
3764 signing_helper: Program which signs a hash and returns the signature.
3765 signing_helper_with_files: Same as signing_helper but uses files instead.
3766
3767 Raises:
3768 AvbError: If an argument is incorrect.
3769 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003770 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3771 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003772 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3773 raise AvbError('Invalid intermediate key certificate length.')
3774 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3775 raise AvbError('Invalid product key certificate length.')
3776 challenge = bytearray()
3777 if challenge_path:
3778 with open(challenge_path, 'r') as f:
3779 challenge = f.read()
3780 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3781 raise AvbError('Invalid unlock challenge length.')
3782 output.write(struct.pack('<I', 1)) # Format Version
3783 output.write(intermediate_key_certificate)
3784 output.write(unlock_key_certificate)
3785 if challenge_path and unlock_key_path:
3786 signature = bytearray()
3787 padding_and_hash = bytearray()
3788 algorithm_name = 'SHA512_RSA4096'
3789 alg = ALGORITHMS[algorithm_name]
3790 hasher = hashlib.sha512()
3791 padding_and_hash.extend(alg.padding)
3792 hasher.update(challenge)
3793 padding_and_hash.extend(hasher.digest())
3794 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3795 algorithm_name,
3796 alg.signature_num_bytes, unlock_key_path,
3797 padding_and_hash))
3798 output.write(signature)
3799
David Zeuthen21e95262016-07-27 17:58:40 -04003800
3801def calc_hash_level_offsets(image_size, block_size, digest_size):
3802 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3803
3804 Arguments:
3805 image_size: The size of the image to calculate a Merkle-tree for.
3806 block_size: The block size, e.g. 4096.
3807 digest_size: The size of each hash, e.g. 32 for SHA-256.
3808
3809 Returns:
3810 A tuple where the first argument is an array of offsets and the
3811 second is size of the tree, in bytes.
3812 """
3813 level_offsets = []
3814 level_sizes = []
3815 tree_size = 0
3816
3817 num_levels = 0
3818 size = image_size
3819 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003820 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003821 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3822
3823 level_sizes.append(level_size)
3824 tree_size += level_size
3825 num_levels += 1
3826
3827 size = level_size
3828
3829 for n in range(0, num_levels):
3830 offset = 0
3831 for m in range(n + 1, num_levels):
3832 offset += level_sizes[m]
3833 level_offsets.append(offset)
3834
David Zeuthena4fee8b2016-08-22 15:20:43 -04003835 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003836
3837
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003838# See system/extras/libfec/include/fec/io.h for these definitions.
3839FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3840FEC_MAGIC = 0xfecfecfe
3841
3842
3843def calc_fec_data_size(image_size, num_roots):
3844 """Calculates how much space FEC data will take.
3845
Jan Monschfe00c0a2019-12-11 11:19:40 +01003846 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003847 image_size: The size of the image.
3848 num_roots: Number of roots.
3849
3850 Returns:
3851 The number of bytes needed for FEC for an image of the given size
3852 and with the requested number of FEC roots.
3853
3854 Raises:
3855 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003856 """
3857 p = subprocess.Popen(
3858 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3859 stdout=subprocess.PIPE,
3860 stderr=subprocess.PIPE)
3861 (pout, perr) = p.communicate()
3862 retcode = p.wait()
3863 if retcode != 0:
3864 raise ValueError('Error invoking fec: {}'.format(perr))
3865 return int(pout)
3866
3867
3868def generate_fec_data(image_filename, num_roots):
3869 """Generate FEC codes for an image.
3870
Jan Monschfe00c0a2019-12-11 11:19:40 +01003871 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003872 image_filename: The filename of the image.
3873 num_roots: Number of roots.
3874
3875 Returns:
3876 The FEC data blob.
3877
3878 Raises:
3879 ValueError: If output from the 'fec' tool is invalid.
3880 """
3881 fec_tmpfile = tempfile.NamedTemporaryFile()
3882 subprocess.check_call(
3883 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3884 fec_tmpfile.name],
3885 stderr=open(os.devnull))
3886 fec_data = fec_tmpfile.read()
3887 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3888 footer_data = fec_data[-footer_size:]
3889 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3890 footer_data)
3891 if magic != FEC_MAGIC:
3892 raise ValueError('Unexpected magic in FEC footer')
3893 return fec_data[0:fec_size]
3894
3895
David Zeuthen21e95262016-07-27 17:58:40 -04003896def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003897 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003898 """Generates a Merkle-tree for a file.
3899
Jan Monschfe00c0a2019-12-11 11:19:40 +01003900 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003901 image: The image, as a file.
3902 image_size: The size of the image.
3903 block_size: The block size, e.g. 4096.
3904 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3905 salt: The salt to use.
3906 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003907 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003908 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003909
3910 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003911 A tuple where the first element is the top-level hash and the
3912 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003913 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003914 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003915 hash_src_offset = 0
3916 hash_src_size = image_size
3917 level_num = 0
3918 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003919 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003920 remaining = hash_src_size
3921 while remaining > 0:
3922 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003923 # Only read from the file for the first level - for subsequent
3924 # levels, access the array we're building.
3925 if level_num == 0:
3926 image.seek(hash_src_offset + hash_src_size - remaining)
3927 data = image.read(min(remaining, block_size))
3928 else:
3929 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3930 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003931 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003932
3933 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003934 if len(data) < block_size:
3935 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003936 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003937 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003938 level_output_list.append('\0' * digest_padding)
3939
3940 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003941
3942 padding_needed = (round_to_multiple(
3943 len(level_output), block_size) - len(level_output))
3944 level_output += '\0' * padding_needed
3945
David Zeuthena4fee8b2016-08-22 15:20:43 -04003946 # Copy level-output into resulting tree.
3947 offset = hash_level_offsets[level_num]
3948 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003949
David Zeuthena4fee8b2016-08-22 15:20:43 -04003950 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003951 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003952 level_num += 1
3953
3954 hasher = hashlib.new(name=hash_alg_name, string=salt)
3955 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003956 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003957
3958
3959class AvbTool(object):
3960 """Object for avbtool command-line tool."""
3961
3962 def __init__(self):
3963 """Initializer method."""
3964 self.avb = Avb()
3965
3966 def _add_common_args(self, sub_parser):
3967 """Adds arguments used by several sub-commands.
3968
3969 Arguments:
3970 sub_parser: The parser to add arguments to.
3971 """
3972 sub_parser.add_argument('--algorithm',
3973 help='Algorithm to use (default: NONE)',
3974 metavar='ALGORITHM',
3975 default='NONE')
3976 sub_parser.add_argument('--key',
3977 help='Path to RSA private key file',
3978 metavar='KEY',
3979 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003980 sub_parser.add_argument('--signing_helper',
3981 help='Path to helper used for signing',
3982 metavar='APP',
3983 default=None,
3984 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003985 sub_parser.add_argument('--signing_helper_with_files',
3986 help='Path to helper used for signing using files',
3987 metavar='APP',
3988 default=None,
3989 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003990 sub_parser.add_argument('--public_key_metadata',
3991 help='Path to public key metadata file',
3992 metavar='KEY_METADATA',
3993 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003994 sub_parser.add_argument('--rollback_index',
3995 help='Rollback Index',
3996 type=parse_number,
3997 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003998 # This is used internally for unit tests. Do not include in --help output.
3999 sub_parser.add_argument('--internal_release_string',
4000 help=argparse.SUPPRESS)
4001 sub_parser.add_argument('--append_to_release_string',
4002 help='Text to append to release string',
4003 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004004 sub_parser.add_argument('--prop',
4005 help='Add property',
4006 metavar='KEY:VALUE',
4007 action='append')
4008 sub_parser.add_argument('--prop_from_file',
4009 help='Add property from file',
4010 metavar='KEY:PATH',
4011 action='append')
4012 sub_parser.add_argument('--kernel_cmdline',
4013 help='Add kernel cmdline',
4014 metavar='CMDLINE',
4015 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004016 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4017 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4018 # at some future point.
4019 sub_parser.add_argument('--setup_rootfs_from_kernel',
4020 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004021 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004022 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004023 type=argparse.FileType('rb'))
4024 sub_parser.add_argument('--include_descriptors_from_image',
4025 help='Include descriptors from image',
4026 metavar='IMAGE',
4027 action='append',
4028 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004029 sub_parser.add_argument('--print_required_libavb_version',
4030 help=('Don\'t store the footer - '
4031 'instead calculate the required libavb '
4032 'version for the given options.'),
4033 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004034 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4035 sub_parser.add_argument('--chain_partition',
4036 help='Allow signed integrity-data for partition',
4037 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4038 action='append')
4039 sub_parser.add_argument('--flags',
4040 help='VBMeta flags',
4041 type=parse_number,
4042 default=0)
4043 sub_parser.add_argument('--set_hashtree_disabled_flag',
4044 help='Set the HASHTREE_DISABLED flag',
4045 action='store_true')
4046
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004047 def _add_common_footer_args(self, sub_parser):
4048 """Adds arguments used by add_*_footer sub-commands.
4049
4050 Arguments:
4051 sub_parser: The parser to add arguments to.
4052 """
4053 sub_parser.add_argument('--use_persistent_digest',
4054 help='Use a persistent digest on device instead of '
4055 'storing the digest in the descriptor. This '
4056 'cannot be used with A/B so must be combined '
4057 'with --do_not_use_ab when an A/B suffix is '
4058 'expected at runtime.',
4059 action='store_true')
4060 sub_parser.add_argument('--do_not_use_ab',
4061 help='The partition does not use A/B even when an '
4062 'A/B suffix is present. This must not be used '
4063 'for vbmeta or chained partitions.',
4064 action='store_true')
4065
David Zeuthena5fd3a42017-02-27 16:38:54 -05004066 def _fixup_common_args(self, args):
4067 """Common fixups needed by subcommands.
4068
4069 Arguments:
4070 args: Arguments to modify.
4071
4072 Returns:
4073 The modified arguments.
4074 """
4075 if args.set_hashtree_disabled_flag:
4076 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4077 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004078
4079 def run(self, argv):
4080 """Command-line processor.
4081
4082 Arguments:
4083 argv: Pass sys.argv from main.
4084 """
4085 parser = argparse.ArgumentParser()
4086 subparsers = parser.add_subparsers(title='subcommands')
4087
Jan Monsch2c7be992020-04-03 14:37:13 +02004088 sub_parser = subparsers.add_parser(
4089 'generate_test_image',
4090 help=('Generates a test image with a known pattern for testing: '
4091 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4092 sub_parser.add_argument('--image_size',
4093 help='Size of image to generate.',
4094 type=parse_number,
4095 required=True)
4096 sub_parser.add_argument('--start_byte',
4097 help='Integer for the start byte of the pattern.',
4098 type=parse_number,
4099 default=0)
4100 sub_parser.add_argument('--output',
4101 help='Output file name.',
4102 type=argparse.FileType('wt'),
4103 default=sys.stdout)
4104 sub_parser.set_defaults(func=self.generate_test_image)
4105
David Zeuthen21e95262016-07-27 17:58:40 -04004106 sub_parser = subparsers.add_parser('version',
4107 help='Prints version of avbtool.')
4108 sub_parser.set_defaults(func=self.version)
4109
4110 sub_parser = subparsers.add_parser('extract_public_key',
4111 help='Extract public key.')
4112 sub_parser.add_argument('--key',
4113 help='Path to RSA private key file',
4114 required=True)
4115 sub_parser.add_argument('--output',
4116 help='Output file name',
4117 type=argparse.FileType('wb'),
4118 required=True)
4119 sub_parser.set_defaults(func=self.extract_public_key)
4120
4121 sub_parser = subparsers.add_parser('make_vbmeta_image',
4122 help='Makes a vbmeta image.')
4123 sub_parser.add_argument('--output',
4124 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004125 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004126 sub_parser.add_argument('--padding_size',
4127 metavar='NUMBER',
4128 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004129 'its size is a multiple of NUMBER '
4130 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004131 type=parse_number,
4132 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004133 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004134 sub_parser.set_defaults(func=self.make_vbmeta_image)
4135
4136 sub_parser = subparsers.add_parser('add_hash_footer',
4137 help='Add hashes and footer to image.')
4138 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004139 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004140 type=argparse.FileType('rab+'))
4141 sub_parser.add_argument('--partition_size',
4142 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004143 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004144 sub_parser.add_argument('--partition_name',
4145 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004146 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004147 sub_parser.add_argument('--hash_algorithm',
4148 help='Hash algorithm to use (default: sha256)',
4149 default='sha256')
4150 sub_parser.add_argument('--salt',
4151 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004152 sub_parser.add_argument('--calc_max_image_size',
4153 help=('Don\'t store the footer - '
4154 'instead calculate the maximum image size '
4155 'leaving enough room for metadata with '
4156 'the given partition size.'),
4157 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004158 sub_parser.add_argument('--output_vbmeta_image',
4159 help='Also write vbmeta struct to file',
4160 type=argparse.FileType('wb'))
4161 sub_parser.add_argument('--do_not_append_vbmeta_image',
4162 help=('Do not append vbmeta struct or footer '
4163 'to the image'),
4164 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004165 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004166 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004167 sub_parser.set_defaults(func=self.add_hash_footer)
4168
David Zeuthenb1b994d2017-03-06 18:01:31 -05004169 sub_parser = subparsers.add_parser('append_vbmeta_image',
4170 help='Append vbmeta image to image.')
4171 sub_parser.add_argument('--image',
4172 help='Image to append vbmeta blob to',
4173 type=argparse.FileType('rab+'))
4174 sub_parser.add_argument('--partition_size',
4175 help='Partition size',
4176 type=parse_number,
4177 required=True)
4178 sub_parser.add_argument('--vbmeta_image',
4179 help='Image with vbmeta blob to append',
4180 type=argparse.FileType('rb'))
4181 sub_parser.set_defaults(func=self.append_vbmeta_image)
4182
Jan Monscheeb28b62019-12-05 16:17:09 +01004183 sub_parser = subparsers.add_parser(
4184 'add_hashtree_footer',
4185 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004186 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004187 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004188 type=argparse.FileType('rab+'))
4189 sub_parser.add_argument('--partition_size',
4190 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004191 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004192 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004193 sub_parser.add_argument('--partition_name',
4194 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004195 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004196 sub_parser.add_argument('--hash_algorithm',
4197 help='Hash algorithm to use (default: sha1)',
4198 default='sha1')
4199 sub_parser.add_argument('--salt',
4200 help='Salt in hex (default: /dev/urandom)')
4201 sub_parser.add_argument('--block_size',
4202 help='Block size (default: 4096)',
4203 type=parse_number,
4204 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004205 # TODO(zeuthen): The --generate_fec option was removed when we
4206 # moved to generating FEC by default. To avoid breaking existing
4207 # users needing to transition we simply just print a warning below
4208 # in add_hashtree_footer(). Remove this option and the warning at
4209 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004210 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004211 help=argparse.SUPPRESS,
4212 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004213 sub_parser.add_argument(
4214 '--do_not_generate_fec',
4215 help='Do not generate forward-error-correction codes',
4216 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004217 sub_parser.add_argument('--fec_num_roots',
4218 help='Number of roots for FEC (default: 2)',
4219 type=parse_number,
4220 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004221 sub_parser.add_argument('--calc_max_image_size',
4222 help=('Don\'t store the hashtree or footer - '
4223 'instead calculate the maximum image size '
4224 'leaving enough room for hashtree '
4225 'and metadata with the given partition '
4226 'size.'),
4227 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004228 sub_parser.add_argument('--output_vbmeta_image',
4229 help='Also write vbmeta struct to file',
4230 type=argparse.FileType('wb'))
4231 sub_parser.add_argument('--do_not_append_vbmeta_image',
4232 help=('Do not append vbmeta struct or footer '
4233 'to the image'),
4234 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004235 # This is different from --setup_rootfs_from_kernel insofar that
4236 # it doesn't take an IMAGE, the generated cmdline will be for the
4237 # hashtree we're adding.
4238 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4239 action='store_true',
4240 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004241 sub_parser.add_argument('--no_hashtree',
4242 action='store_true',
4243 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004244 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004245 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004246 sub_parser.set_defaults(func=self.add_hashtree_footer)
4247
4248 sub_parser = subparsers.add_parser('erase_footer',
4249 help='Erase footer from an image.')
4250 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004251 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004252 type=argparse.FileType('rwb+'),
4253 required=True)
4254 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004255 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004256 action='store_true')
4257 sub_parser.set_defaults(func=self.erase_footer)
4258
David Zeuthen1394f762019-04-30 10:20:11 -04004259 sub_parser = subparsers.add_parser('zero_hashtree',
4260 help='Zero out hashtree and FEC data.')
4261 sub_parser.add_argument('--image',
4262 help='Image with a footer',
4263 type=argparse.FileType('rwb+'),
4264 required=True)
4265 sub_parser.set_defaults(func=self.zero_hashtree)
4266
Jan Monscheeb28b62019-12-05 16:17:09 +01004267 sub_parser = subparsers.add_parser(
4268 'extract_vbmeta_image',
4269 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004270 sub_parser.add_argument('--image',
4271 help='Image with footer',
4272 type=argparse.FileType('rb'),
4273 required=True)
4274 sub_parser.add_argument('--output',
4275 help='Output file name',
4276 type=argparse.FileType('wb'))
4277 sub_parser.add_argument('--padding_size',
4278 metavar='NUMBER',
4279 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004280 'its size is a multiple of NUMBER '
4281 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004282 type=parse_number,
4283 default=0)
4284 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4285
David Zeuthen2bc232b2017-04-19 14:25:19 -04004286 sub_parser = subparsers.add_parser('resize_image',
4287 help='Resize image with a footer.')
4288 sub_parser.add_argument('--image',
4289 help='Image with a footer',
4290 type=argparse.FileType('rwb+'),
4291 required=True)
4292 sub_parser.add_argument('--partition_size',
4293 help='New partition size',
4294 type=parse_number)
4295 sub_parser.set_defaults(func=self.resize_image)
4296
David Zeuthen21e95262016-07-27 17:58:40 -04004297 sub_parser = subparsers.add_parser(
4298 'info_image',
4299 help='Show information about vbmeta or footer.')
4300 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004301 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004302 type=argparse.FileType('rb'),
4303 required=True)
4304 sub_parser.add_argument('--output',
4305 help='Write info to file',
4306 type=argparse.FileType('wt'),
4307 default=sys.stdout)
4308 sub_parser.set_defaults(func=self.info_image)
4309
David Zeuthenb623d8b2017-04-04 16:05:53 -04004310 sub_parser = subparsers.add_parser(
4311 'verify_image',
4312 help='Verify an image.')
4313 sub_parser.add_argument('--image',
4314 help='Image to verify',
4315 type=argparse.FileType('rb'),
4316 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004317 sub_parser.add_argument('--key',
4318 help='Check embedded public key matches KEY',
4319 metavar='KEY',
4320 required=False)
4321 sub_parser.add_argument('--expected_chain_partition',
4322 help='Expected chain partition',
4323 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4324 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004325 sub_parser.add_argument(
4326 '--follow_chain_partitions',
4327 help=('Follows chain partitions even when not '
4328 'specified with the --expected_chain_partition option'),
4329 action='store_true')
4330 sub_parser.add_argument(
4331 '--accept_zeroed_hashtree',
4332 help=('Accept images where the hashtree or FEC data is zeroed out'),
4333 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004334 sub_parser.set_defaults(func=self.verify_image)
4335
David Zeuthenb8643c02018-05-17 17:21:18 -04004336 sub_parser = subparsers.add_parser(
4337 'calculate_vbmeta_digest',
4338 help='Calculate vbmeta digest.')
4339 sub_parser.add_argument('--image',
4340 help='Image to calculate digest for',
4341 type=argparse.FileType('rb'),
4342 required=True)
4343 sub_parser.add_argument('--hash_algorithm',
4344 help='Hash algorithm to use (default: sha256)',
4345 default='sha256')
4346 sub_parser.add_argument('--output',
4347 help='Write hex digest to file (default: stdout)',
4348 type=argparse.FileType('wt'),
4349 default=sys.stdout)
4350 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4351
David Zeuthenf7d2e752018-09-20 13:30:41 -04004352 sub_parser = subparsers.add_parser(
4353 'calculate_kernel_cmdline',
4354 help='Calculate kernel cmdline.')
4355 sub_parser.add_argument('--image',
4356 help='Image to calculate kernel cmdline for',
4357 type=argparse.FileType('rb'),
4358 required=True)
4359 sub_parser.add_argument('--hashtree_disabled',
4360 help='Return the cmdline for hashtree disabled',
4361 action='store_true')
4362 sub_parser.add_argument('--output',
4363 help='Write cmdline to file (default: stdout)',
4364 type=argparse.FileType('wt'),
4365 default=sys.stdout)
4366 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4367
David Zeuthen8b6973b2016-09-20 12:39:49 -04004368 sub_parser = subparsers.add_parser('set_ab_metadata',
4369 help='Set A/B metadata.')
4370 sub_parser.add_argument('--misc_image',
4371 help=('The misc image to modify. If the image does '
4372 'not exist, it will be created.'),
4373 type=argparse.FileType('r+b'),
4374 required=True)
4375 sub_parser.add_argument('--slot_data',
4376 help=('Slot data of the form "priority", '
4377 '"tries_remaining", "sucessful_boot" for '
4378 'slot A followed by the same for slot B, '
4379 'separated by colons. The default value '
4380 'is 15:7:0:14:7:0.'),
4381 default='15:7:0:14:7:0')
4382 sub_parser.set_defaults(func=self.set_ab_metadata)
4383
Darren Krahn147b08d2016-12-20 16:38:29 -08004384 sub_parser = subparsers.add_parser(
4385 'make_atx_certificate',
4386 help='Create an Android Things eXtension (ATX) certificate.')
4387 sub_parser.add_argument('--output',
4388 help='Write certificate to file',
4389 type=argparse.FileType('wb'),
4390 default=sys.stdout)
4391 sub_parser.add_argument('--subject',
4392 help=('Path to subject file'),
4393 type=argparse.FileType('rb'),
4394 required=True)
4395 sub_parser.add_argument('--subject_key',
4396 help=('Path to subject RSA public key file'),
4397 type=argparse.FileType('rb'),
4398 required=True)
4399 sub_parser.add_argument('--subject_key_version',
4400 help=('Version of the subject key'),
4401 type=parse_number,
4402 required=False)
4403 sub_parser.add_argument('--subject_is_intermediate_authority',
4404 help=('Generate an intermediate authority '
4405 'certificate'),
4406 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004407 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004408 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004409 'string'),
4410 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004411 sub_parser.add_argument('--authority_key',
4412 help='Path to authority RSA private key file',
4413 required=False)
4414 sub_parser.add_argument('--signing_helper',
4415 help='Path to helper used for signing',
4416 metavar='APP',
4417 default=None,
4418 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004419 sub_parser.add_argument('--signing_helper_with_files',
4420 help='Path to helper used for signing using files',
4421 metavar='APP',
4422 default=None,
4423 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004424 sub_parser.set_defaults(func=self.make_atx_certificate)
4425
4426 sub_parser = subparsers.add_parser(
4427 'make_atx_permanent_attributes',
4428 help='Create Android Things eXtension (ATX) permanent attributes.')
4429 sub_parser.add_argument('--output',
4430 help='Write attributes to file',
4431 type=argparse.FileType('wb'),
4432 default=sys.stdout)
4433 sub_parser.add_argument('--root_authority_key',
4434 help='Path to authority RSA public key file',
4435 type=argparse.FileType('rb'),
4436 required=True)
4437 sub_parser.add_argument('--product_id',
4438 help=('Path to Product ID file'),
4439 type=argparse.FileType('rb'),
4440 required=True)
4441 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4442
4443 sub_parser = subparsers.add_parser(
4444 'make_atx_metadata',
4445 help='Create Android Things eXtension (ATX) metadata.')
4446 sub_parser.add_argument('--output',
4447 help='Write metadata to file',
4448 type=argparse.FileType('wb'),
4449 default=sys.stdout)
4450 sub_parser.add_argument('--intermediate_key_certificate',
4451 help='Path to intermediate key certificate file',
4452 type=argparse.FileType('rb'),
4453 required=True)
4454 sub_parser.add_argument('--product_key_certificate',
4455 help='Path to product key certificate file',
4456 type=argparse.FileType('rb'),
4457 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004458 sub_parser.set_defaults(func=self.make_atx_metadata)
4459
Darren Krahnfccd64e2018-01-16 17:39:35 -08004460 sub_parser = subparsers.add_parser(
4461 'make_atx_unlock_credential',
4462 help='Create an Android Things eXtension (ATX) unlock credential.')
4463 sub_parser.add_argument('--output',
4464 help='Write credential to file',
4465 type=argparse.FileType('wb'),
4466 default=sys.stdout)
4467 sub_parser.add_argument('--intermediate_key_certificate',
4468 help='Path to intermediate key certificate file',
4469 type=argparse.FileType('rb'),
4470 required=True)
4471 sub_parser.add_argument('--unlock_key_certificate',
4472 help='Path to unlock key certificate file',
4473 type=argparse.FileType('rb'),
4474 required=True)
4475 sub_parser.add_argument('--challenge',
4476 help='Path to the challenge to sign (optional). If '
4477 'this is not provided the challenge signature '
4478 'field is omitted and can be concatenated '
4479 'later.',
4480 required=False)
4481 sub_parser.add_argument('--unlock_key',
4482 help='Path to unlock key (optional). Must be '
4483 'provided if using --challenge.',
4484 required=False)
4485 sub_parser.add_argument('--signing_helper',
4486 help='Path to helper used for signing',
4487 metavar='APP',
4488 default=None,
4489 required=False)
4490 sub_parser.add_argument('--signing_helper_with_files',
4491 help='Path to helper used for signing using files',
4492 metavar='APP',
4493 default=None,
4494 required=False)
4495 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4496
David Zeuthen21e95262016-07-27 17:58:40 -04004497 args = parser.parse_args(argv[1:])
4498 try:
4499 args.func(args)
4500 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004501 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004502 sys.exit(1)
4503
4504 def version(self, _):
4505 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004506 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004507
Jan Monsch2c7be992020-04-03 14:37:13 +02004508 def generate_test_image(self, args):
4509 """Implements the 'generate_test_image' sub-command."""
4510 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4511
David Zeuthen21e95262016-07-27 17:58:40 -04004512 def extract_public_key(self, args):
4513 """Implements the 'extract_public_key' sub-command."""
4514 self.avb.extract_public_key(args.key, args.output)
4515
4516 def make_vbmeta_image(self, args):
4517 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004518 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004519 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004520 args.algorithm, args.key,
4521 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004522 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004523 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004524 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004525 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004526 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004527 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004528 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004529 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004530 args.print_required_libavb_version,
4531 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004532
David Zeuthenb1b994d2017-03-06 18:01:31 -05004533 def append_vbmeta_image(self, args):
4534 """Implements the 'append_vbmeta_image' sub-command."""
4535 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4536 args.partition_size)
4537
David Zeuthen21e95262016-07-27 17:58:40 -04004538 def add_hash_footer(self, args):
4539 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004540 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004541 self.avb.add_hash_footer(args.image.name if args.image else None,
4542 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004543 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004544 args.salt, args.chain_partition, args.algorithm,
4545 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004546 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004547 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004548 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004549 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004550 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004551 args.calc_max_image_size,
4552 args.signing_helper,
4553 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004554 args.internal_release_string,
4555 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004556 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004557 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004558 args.print_required_libavb_version,
4559 args.use_persistent_digest,
4560 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004561
4562 def add_hashtree_footer(self, args):
4563 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004564 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004565 # TODO(zeuthen): Remove when removing support for the
4566 # '--generate_fec' option above.
4567 if args.generate_fec:
4568 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4569 'is now generated by default. Use the option '
4570 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004571 self.avb.add_hashtree_footer(
4572 args.image.name if args.image else None,
4573 args.partition_size,
4574 args.partition_name,
4575 not args.do_not_generate_fec, args.fec_num_roots,
4576 args.hash_algorithm, args.block_size,
4577 args.salt, args.chain_partition, args.algorithm,
4578 args.key, args.public_key_metadata,
4579 args.rollback_index, args.flags, args.prop,
4580 args.prop_from_file,
4581 args.kernel_cmdline,
4582 args.setup_rootfs_from_kernel,
4583 args.setup_as_rootfs_from_kernel,
4584 args.include_descriptors_from_image,
4585 args.calc_max_image_size,
4586 args.signing_helper,
4587 args.signing_helper_with_files,
4588 args.internal_release_string,
4589 args.append_to_release_string,
4590 args.output_vbmeta_image,
4591 args.do_not_append_vbmeta_image,
4592 args.print_required_libavb_version,
4593 args.use_persistent_digest,
4594 args.do_not_use_ab,
4595 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004596
David Zeuthen21e95262016-07-27 17:58:40 -04004597 def erase_footer(self, args):
4598 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004599 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004600
David Zeuthen1394f762019-04-30 10:20:11 -04004601 def zero_hashtree(self, args):
4602 """Implements the 'zero_hashtree' sub-command."""
4603 self.avb.zero_hashtree(args.image.name)
4604
David Zeuthen49936b42018-08-07 17:38:58 -04004605 def extract_vbmeta_image(self, args):
4606 """Implements the 'extract_vbmeta_image' sub-command."""
4607 self.avb.extract_vbmeta_image(args.output, args.image.name,
4608 args.padding_size)
4609
David Zeuthen2bc232b2017-04-19 14:25:19 -04004610 def resize_image(self, args):
4611 """Implements the 'resize_image' sub-command."""
4612 self.avb.resize_image(args.image.name, args.partition_size)
4613
David Zeuthen8b6973b2016-09-20 12:39:49 -04004614 def set_ab_metadata(self, args):
4615 """Implements the 'set_ab_metadata' sub-command."""
4616 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4617
David Zeuthen21e95262016-07-27 17:58:40 -04004618 def info_image(self, args):
4619 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004620 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004621
David Zeuthenb623d8b2017-04-04 16:05:53 -04004622 def verify_image(self, args):
4623 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004624 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004625 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004626 args.follow_chain_partitions,
4627 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004628
David Zeuthenb8643c02018-05-17 17:21:18 -04004629 def calculate_vbmeta_digest(self, args):
4630 """Implements the 'calculate_vbmeta_digest' sub-command."""
4631 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4632 args.output)
4633
David Zeuthenf7d2e752018-09-20 13:30:41 -04004634 def calculate_kernel_cmdline(self, args):
4635 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004636 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4637 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004638
Darren Krahn147b08d2016-12-20 16:38:29 -08004639 def make_atx_certificate(self, args):
4640 """Implements the 'make_atx_certificate' sub-command."""
4641 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004642 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004643 args.subject_key_version,
4644 args.subject.read(),
4645 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004646 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004647 args.signing_helper,
4648 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004649
4650 def make_atx_permanent_attributes(self, args):
4651 """Implements the 'make_atx_permanent_attributes' sub-command."""
4652 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004653 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004654 args.product_id.read())
4655
4656 def make_atx_metadata(self, args):
4657 """Implements the 'make_atx_metadata' sub-command."""
4658 self.avb.make_atx_metadata(args.output,
4659 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004660 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004661
Darren Krahnfccd64e2018-01-16 17:39:35 -08004662 def make_atx_unlock_credential(self, args):
4663 """Implements the 'make_atx_unlock_credential' sub-command."""
4664 self.avb.make_atx_unlock_credential(
4665 args.output,
4666 args.intermediate_key_certificate.read(),
4667 args.unlock_key_certificate.read(),
4668 args.challenge,
4669 args.unlock_key,
4670 args.signing_helper,
4671 args.signing_helper_with_files)
4672
David Zeuthen21e95262016-07-27 17:58:40 -04004673
4674if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004675 if AVB_INVOCATION_LOGFILE:
4676 f = open(AVB_INVOCATION_LOGFILE, 'a')
4677 f.write(' '.join(sys.argv))
4678 f.write('\n')
4679 f.close()
4680
David Zeuthen21e95262016-07-27 17:58:40 -04004681 tool = AvbTool()
4682 tool.run(sys.argv)