blob: dad60d6f3f3bfa34e0fbeba15d23a0a3cbb19257 [file] [log] [blame]
Jan Monsch13efb5f2020-04-22 17:34:24 +02001#!/usr/bin/env python3
David Zeuthen21e95262016-07-27 17:58:40 -04002
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
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthen34b6b492020-04-13 14:45:02 -040031import json
David Zeuthenc68f0822017-03-31 17:22:35 -040032import math
David Zeuthen21e95262016-07-27 17:58:40 -040033import os
34import struct
35import subprocess
36import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040037import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080038import time
David Zeuthen21e95262016-07-27 17:58:40 -040039
David Zeuthene3cadca2017-02-22 21:25:46 -050040# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040041AVB_VERSION_MAJOR = 1
Varun Sharmade538272020-04-10 15:22:31 -070042AVB_VERSION_MINOR = 2
David Zeuthene3cadca2017-02-22 21:25:46 -050043AVB_VERSION_SUB = 0
44
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080045# Keep in sync with libavb/avb_footer.h.
46AVB_FOOTER_VERSION_MAJOR = 1
47AVB_FOOTER_VERSION_MINOR = 0
48
David Zeuthen58305522017-01-11 17:42:47 -050049AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040050
Jan Monsch2c7be992020-04-03 14:37:13 +020051# Configuration for enabling logging of calls to avbtool.
52AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
53
David Zeuthene3cadca2017-02-22 21:25:46 -050054
David Zeuthen21e95262016-07-27 17:58:40 -040055class AvbError(Exception):
56 """Application-specific errors.
57
58 These errors represent issues for which a stack-trace should not be
59 presented.
60
61 Attributes:
62 message: Error message.
63 """
64
65 def __init__(self, message):
66 Exception.__init__(self, message)
67
68
69class Algorithm(object):
70 """Contains details about an algorithm.
71
Tao Bao80418a52018-07-20 11:41:22 -070072 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040073
74 The constant |ALGORITHMS| is a dictionary from human-readable
75 names (e.g 'SHA256_RSA2048') to instances of this class.
76
77 Attributes:
78 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040079 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040080 hash_num_bytes: Number of bytes used to store the hash.
81 signature_num_bytes: Number of bytes used to store the signature.
82 public_key_num_bytes: Number of bytes used to store the public key.
Jan Monsch9c130122020-04-14 13:43:51 +020083 padding: Padding used for signature as bytes, if any.
David Zeuthen21e95262016-07-27 17:58:40 -040084 """
85
David Zeuthenb623d8b2017-04-04 16:05:53 -040086 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
87 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040088 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040089 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040090 self.hash_num_bytes = hash_num_bytes
91 self.signature_num_bytes = signature_num_bytes
92 self.public_key_num_bytes = public_key_num_bytes
93 self.padding = padding
94
David Zeuthenb623d8b2017-04-04 16:05:53 -040095
David Zeuthen21e95262016-07-27 17:58:40 -040096# This must be kept in sync with the avb_crypto.h file.
97#
98# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
99# obtained from section 5.2.2 of RFC 4880.
100ALGORITHMS = {
101 'NONE': Algorithm(
102 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400103 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400104 hash_num_bytes=0,
105 signature_num_bytes=0,
106 public_key_num_bytes=0,
Jan Monsch9c130122020-04-14 13:43:51 +0200107 padding=b''),
David Zeuthen21e95262016-07-27 17:58:40 -0400108 'SHA256_RSA2048': Algorithm(
109 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400110 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400111 hash_num_bytes=32,
112 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100113 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200114 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400115 # PKCS1-v1_5 padding
116 0x00, 0x01] + [0xff]*202 + [0x00] + [
117 # ASN.1 header
118 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
119 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
120 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200121 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400122 'SHA256_RSA4096': Algorithm(
123 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400124 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400125 hash_num_bytes=32,
126 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100127 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200128 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400129 # PKCS1-v1_5 padding
130 0x00, 0x01] + [0xff]*458 + [0x00] + [
131 # ASN.1 header
132 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
133 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
134 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200135 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400136 'SHA256_RSA8192': Algorithm(
137 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400138 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400139 hash_num_bytes=32,
140 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100141 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200142 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400143 # PKCS1-v1_5 padding
144 0x00, 0x01] + [0xff]*970 + [0x00] + [
145 # ASN.1 header
146 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
147 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
148 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200149 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400150 'SHA512_RSA2048': Algorithm(
151 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400152 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400153 hash_num_bytes=64,
154 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100155 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200156 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400157 # PKCS1-v1_5 padding
158 0x00, 0x01] + [0xff]*170 + [0x00] + [
159 # ASN.1 header
160 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
161 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
162 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200163 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400164 'SHA512_RSA4096': Algorithm(
165 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400166 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400167 hash_num_bytes=64,
168 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100169 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200170 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400171 # PKCS1-v1_5 padding
172 0x00, 0x01] + [0xff]*426 + [0x00] + [
173 # ASN.1 header
174 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
175 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
176 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200177 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400178 'SHA512_RSA8192': Algorithm(
179 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400180 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400181 hash_num_bytes=64,
182 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100183 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200184 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400185 # PKCS1-v1_5 padding
186 0x00, 0x01] + [0xff]*938 + [0x00] + [
187 # ASN.1 header
188 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
189 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
190 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200191 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400192}
193
194
David Zeuthene3cadca2017-02-22 21:25:46 -0500195def get_release_string():
196 """Calculates the release string to use in the VBMeta struct."""
197 # Keep in sync with libavb/avb_version.c:avb_version_string().
198 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
199 AVB_VERSION_MINOR,
200 AVB_VERSION_SUB)
201
202
David Zeuthen21e95262016-07-27 17:58:40 -0400203def round_to_multiple(number, size):
204 """Rounds a number up to nearest multiple of another number.
205
Jan Monschfe00c0a2019-12-11 11:19:40 +0100206 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400207 number: The number to round up.
208 size: The multiple to round up to.
209
210 Returns:
211 If |number| is a multiple of |size|, returns |number|, otherwise
212 returns |number| + |size|.
213 """
214 remainder = number % size
215 if remainder == 0:
216 return number
217 return number + size - remainder
218
219
220def round_to_pow2(number):
221 """Rounds a number up to the next power of 2.
222
Jan Monschfe00c0a2019-12-11 11:19:40 +0100223 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400224 number: The number to round up.
225
226 Returns:
227 If |number| is already a power of 2 then |number| is
228 returned. Otherwise the smallest power of 2 greater than |number|
229 is returned.
230 """
231 return 2**((number - 1).bit_length())
232
233
David Zeuthen21e95262016-07-27 17:58:40 -0400234def encode_long(num_bits, value):
235 """Encodes a long to a bytearray() using a given amount of bits.
236
237 This number is written big-endian, e.g. with the most significant
238 bit first.
239
David Zeuthenb623d8b2017-04-04 16:05:53 -0400240 This is the reverse of decode_long().
241
David Zeuthen21e95262016-07-27 17:58:40 -0400242 Arguments:
243 num_bits: The number of bits to write, e.g. 2048.
244 value: The value to write.
245
246 Returns:
247 A bytearray() with the encoded long.
248 """
249 ret = bytearray()
250 for bit_pos in range(num_bits, 0, -8):
251 octet = (value >> (bit_pos - 8)) & 0xff
252 ret.extend(struct.pack('!B', octet))
253 return ret
254
255
David Zeuthenb623d8b2017-04-04 16:05:53 -0400256def decode_long(blob):
257 """Decodes a long from a bytearray() using a given amount of bits.
258
259 This number is expected to be in big-endian, e.g. with the most
260 significant bit first.
261
262 This is the reverse of encode_long().
263
264 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100265 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400266
267 Returns:
268 The decoded value.
269 """
270 ret = 0
271 for b in bytearray(blob):
272 ret *= 256
273 ret += b
274 return ret
275
276
David Zeuthen21e95262016-07-27 17:58:40 -0400277def egcd(a, b):
278 """Calculate greatest common divisor of two numbers.
279
280 This implementation uses a recursive version of the extended
281 Euclidian algorithm.
282
283 Arguments:
284 a: First number.
285 b: Second number.
286
287 Returns:
288 A tuple (gcd, x, y) that where |gcd| is the greatest common
289 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
290 """
291 if a == 0:
292 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100293 g, y, x = egcd(b % a, a)
294 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400295
296
297def modinv(a, m):
298 """Calculate modular multiplicative inverse of |a| modulo |m|.
299
300 This calculates the number |x| such that |a| * |x| == 1 (modulo
301 |m|). This number only exists if |a| and |m| are co-prime - |None|
302 is returned if this isn't true.
303
304 Arguments:
305 a: The number to calculate a modular inverse of.
306 m: The modulo to use.
307
308 Returns:
309 The modular multiplicative inverse of |a| and |m| or |None| if
310 these numbers are not co-prime.
311 """
312 gcd, x, _ = egcd(a, m)
313 if gcd != 1:
314 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100315 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400316
317
318def parse_number(string):
319 """Parse a string as a number.
320
321 This is just a short-hand for int(string, 0) suitable for use in the
322 |type| parameter of |ArgumentParser|'s add_argument() function. An
323 improvement to just using type=int is that this function supports
324 numbers in other bases, e.g. "0x1234".
325
326 Arguments:
327 string: The string to parse.
328
329 Returns:
330 The parsed integer.
331
332 Raises:
333 ValueError: If the number could not be parsed.
334 """
335 return int(string, 0)
336
337
David Zeuthenc68f0822017-03-31 17:22:35 -0400338class RSAPublicKey(object):
339 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400340
David Zeuthenc68f0822017-03-31 17:22:35 -0400341 Attributes:
342 exponent: The key exponent.
343 modulus: The key modulus.
344 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400345 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400346
Jan Monsch38865f22020-04-08 09:32:58 +0200347 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400348
349 def __init__(self, key_path):
350 """Loads and parses an RSA key from either a private or public key file.
351
352 Arguments:
353 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100354
355 Raises:
356 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400357 """
358 # We used to have something as simple as this:
359 #
360 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
361 # self.exponent = key.e
362 # self.modulus = key.n
363 # self.num_bits = key.size() + 1
364 #
365 # but unfortunately PyCrypto is not available in the builder. So
366 # instead just parse openssl(1) output to get this
367 # information. It's ugly but...
368 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
369 p = subprocess.Popen(args,
370 stdin=subprocess.PIPE,
371 stdout=subprocess.PIPE,
372 stderr=subprocess.PIPE)
373 (pout, perr) = p.communicate()
374 if p.wait() != 0:
375 # Could be just a public key is passed, try that.
376 args.append('-pubin')
377 p = subprocess.Popen(args,
378 stdin=subprocess.PIPE,
379 stdout=subprocess.PIPE,
380 stderr=subprocess.PIPE)
381 (pout, perr) = p.communicate()
382 if p.wait() != 0:
383 raise AvbError('Error getting public key: {}'.format(perr))
384
385 if not pout.lower().startswith(self.MODULUS_PREFIX):
386 raise AvbError('Unexpected modulus output')
387
388 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
389
390 # The exponent is assumed to always be 65537 and the number of
391 # bits can be derived from the modulus by rounding up to the
392 # nearest power of 2.
Jan Monsch9c130122020-04-14 13:43:51 +0200393 self.key_path = key_path
David Zeuthenc68f0822017-03-31 17:22:35 -0400394 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
Jan Monsch9c130122020-04-14 13:43:51 +0200398 def encode(self):
399 """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
David Zeuthen21e95262016-07-27 17:58:40 -0400400
Jan Monsch9c130122020-04-14 13:43:51 +0200401 This creates a |AvbRSAPublicKeyHeader| as well as the two large
402 numbers (|key_num_bits| bits long) following it.
David Zeuthen21e95262016-07-27 17:58:40 -0400403
Jan Monsch9c130122020-04-14 13:43:51 +0200404 Returns:
405 The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -0400406
Jan Monsch9c130122020-04-14 13:43:51 +0200407 Raises:
408 AvbError: If given RSA key exponent is not 65537.
409 """
410 if self.exponent != 65537:
411 raise AvbError('Only RSA keys with exponent 65537 are supported.')
412 ret = bytearray()
413 # Calculate n0inv = -1/n[0] (mod 2^32)
414 b = 2 ** 32
415 n0inv = b - modinv(self.modulus, b)
416 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
417 r = 2 ** self.modulus.bit_length()
418 rrmodn = r * r % self.modulus
419 ret.extend(struct.pack('!II', self.num_bits, n0inv))
420 ret.extend(encode_long(self.num_bits, self.modulus))
421 ret.extend(encode_long(self.num_bits, rrmodn))
422 return bytes(ret)
David Zeuthen21e95262016-07-27 17:58:40 -0400423
Jan Monsch9c130122020-04-14 13:43:51 +0200424 def sign(self, algorithm_name, data_to_sign, signing_helper=None,
425 signing_helper_with_files=None):
426 """Sign given data using |signing_helper| or openssl.
Jan Monsch77cd2022019-12-10 17:18:04 +0100427
Jan Monsch9c130122020-04-14 13:43:51 +0200428 openssl is used if neither the parameters signing_helper nor
429 signing_helper_with_files are given.
430
431 Arguments:
432 algorithm_name: The algorithm name as per the ALGORITHMS dict.
433 data_to_sign: Data to sign as bytes or bytearray.
434 signing_helper: Program which signs a hash and returns the signature.
435 signing_helper_with_files: Same as signing_helper but uses files instead.
436
437 Returns:
438 The signature as bytes.
439
440 Raises:
441 AvbError: If an error occurred during signing.
442 """
443 # Checks requested algorithm for validity.
444 algorithm = ALGORITHMS.get(algorithm_name)
445 if not algorithm:
446 raise AvbError('Algorithm with name {} is not supported.'
447 .format(algorithm_name))
448
449 if self.num_bits != (algorithm.signature_num_bytes * 8):
450 raise AvbError('Key size of key ({} bits) does not match key size '
451 '({} bits) of given algorithm {}.'
452 .format(self.num_bits, algorithm.signature_num_bytes * 8,
453 algorithm_name))
454
455 # Hashes the data.
456 hasher = hashlib.new(algorithm.hash_name)
457 hasher.update(data_to_sign)
458 digest = hasher.digest()
459
460 # Calculates the signature.
461 padding_and_hash = algorithm.padding + digest
462 p = None
463 if signing_helper_with_files is not None:
464 with tempfile.NamedTemporaryFile() as signing_file:
465 signing_file.write(padding_and_hash)
466 signing_file.flush()
467 p = subprocess.Popen([signing_helper_with_files, algorithm_name,
468 self.key_path, signing_file.name])
469 retcode = p.wait()
470 if retcode != 0:
471 raise AvbError('Error signing')
472 signing_file.seek(0)
473 signature = signing_file.read()
474 else:
475 if signing_helper is not None:
476 p = subprocess.Popen(
477 [signing_helper, algorithm_name, self.key_path],
478 stdin=subprocess.PIPE,
479 stdout=subprocess.PIPE,
480 stderr=subprocess.PIPE)
481 else:
482 p = subprocess.Popen(
483 ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
484 stdin=subprocess.PIPE,
485 stdout=subprocess.PIPE,
486 stderr=subprocess.PIPE)
487 (pout, perr) = p.communicate(padding_and_hash)
488 retcode = p.wait()
489 if retcode != 0:
490 raise AvbError('Error signing: {}'.format(perr))
491 signature = pout
492 if len(signature) != algorithm.signature_num_bytes:
493 raise AvbError('Error signing: Invalid length of signature')
494 return signature
David Zeuthen21e95262016-07-27 17:58:40 -0400495
496
497def lookup_algorithm_by_type(alg_type):
498 """Looks up algorithm by type.
499
500 Arguments:
501 alg_type: The integer representing the type.
502
503 Returns:
504 A tuple with the algorithm name and an |Algorithm| instance.
505
506 Raises:
507 Exception: If the algorithm cannot be found
508 """
509 for alg_name in ALGORITHMS:
510 alg_data = ALGORITHMS[alg_name]
511 if alg_data.algorithm_type == alg_type:
512 return (alg_name, alg_data)
513 raise AvbError('Unknown algorithm type {}'.format(alg_type))
514
Jan Monsch77cd2022019-12-10 17:18:04 +0100515
Dan Austina7bc4962019-12-02 13:26:08 -0800516def lookup_hash_size_by_type(alg_type):
517 """Looks up hash size by type.
518
519 Arguments:
520 alg_type: The integer representing the type.
521
522 Returns:
523 The corresponding hash size.
524
525 Raises:
526 AvbError: If the algorithm cannot be found.
527 """
528 for alg_name in ALGORITHMS:
529 alg_data = ALGORITHMS[alg_name]
530 if alg_data.algorithm_type == alg_type:
531 return alg_data.hash_num_bytes
532 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400533
Jan Monsch77cd2022019-12-10 17:18:04 +0100534
David Zeuthenb623d8b2017-04-04 16:05:53 -0400535def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100536 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537
538 Arguments:
539 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200540 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
541 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400542
543 Returns:
544 True if the signature is valid and corresponds to the embedded
545 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100546
547 Raises:
548 AvbError: If there errors calling out to openssl command during
549 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400550 """
551 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100552 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400553 return True
554 header_blob = vbmeta_blob[0:256]
555 auth_offset = 256
556 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
557 aux_size = vbmeta_header.auxiliary_data_block_size
558 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
559 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
560 pubkey_size = vbmeta_header.public_key_size
561 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
562
563 digest_offset = auth_offset + vbmeta_header.hash_offset
564 digest_size = vbmeta_header.hash_size
565 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
566
567 sig_offset = auth_offset + vbmeta_header.signature_offset
568 sig_size = vbmeta_header.signature_size
569 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
570
571 # Now that we've got the stored digest, public key, and signature
572 # all we need to do is to verify. This is the exactly the same
573 # steps as performed in the avb_vbmeta_image_verify() function in
574 # libavb/avb_vbmeta_image.c.
575
576 ha = hashlib.new(alg.hash_name)
577 ha.update(header_blob)
578 ha.update(aux_blob)
579 computed_digest = ha.digest()
580
581 if computed_digest != digest_blob:
582 return False
583
Jan Monsch9c130122020-04-14 13:43:51 +0200584 padding_and_digest = alg.padding + computed_digest
David Zeuthenb623d8b2017-04-04 16:05:53 -0400585
586 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100587 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400588 modulus = decode_long(modulus_blob)
589 exponent = 65537
590
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500591 # We used to have this:
592 #
593 # import Crypto.PublicKey.RSA
594 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
595 # if not key.verify(decode_long(padding_and_digest),
596 # (decode_long(sig_blob), None)):
597 # return False
598 # return True
599 #
600 # but since 'avbtool verify_image' is used on the builders we don't want
601 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
602 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
603 '\n'
604 '[pubkeyinfo]\n'
605 'algorithm=SEQUENCE:rsa_alg\n'
606 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
607 '\n'
608 '[rsa_alg]\n'
609 'algorithm=OID:rsaEncryption\n'
610 'parameter=NULL\n'
611 '\n'
612 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200613 'n=INTEGER:{}\n'
614 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
615 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500616
Jan Monsch38865f22020-04-08 09:32:58 +0200617 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
618 asn1_tmpfile.write(asn1_str.encode('ascii'))
619 asn1_tmpfile.flush()
620
621 with tempfile.NamedTemporaryFile() as der_tmpfile:
622 p = subprocess.Popen(
623 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
624 der_tmpfile.name, '-noout'])
625 retcode = p.wait()
626 if retcode != 0:
627 raise AvbError('Error generating DER file')
628
629 p = subprocess.Popen(
630 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
631 '-keyform', 'DER', '-raw'],
632 stdin=subprocess.PIPE,
633 stdout=subprocess.PIPE,
634 stderr=subprocess.PIPE)
635 (pout, perr) = p.communicate(sig_blob)
636 retcode = p.wait()
637 if retcode != 0:
638 raise AvbError('Error verifying data: {}'.format(perr))
639 if pout != padding_and_digest:
640 sys.stderr.write('Signature not correct\n')
641 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400642 return True
643
644
David Zeuthena4fee8b2016-08-22 15:20:43 -0400645class ImageChunk(object):
646 """Data structure used for representing chunks in Android sparse files.
647
648 Attributes:
649 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
650 chunk_offset: Offset in the sparse file where this chunk begins.
651 output_offset: Offset in de-sparsified file where output begins.
652 output_size: Number of bytes in output.
653 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
654 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
655 """
656
657 FORMAT = '<2H2I'
658 TYPE_RAW = 0xcac1
659 TYPE_FILL = 0xcac2
660 TYPE_DONT_CARE = 0xcac3
661 TYPE_CRC32 = 0xcac4
662
663 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
664 input_offset, fill_data):
665 """Initializes an ImageChunk object.
666
667 Arguments:
668 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
669 chunk_offset: Offset in the sparse file where this chunk begins.
670 output_offset: Offset in de-sparsified file.
671 output_size: Number of bytes in output.
672 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200673 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400674
675 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200676 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400677 """
678 self.chunk_type = chunk_type
679 self.chunk_offset = chunk_offset
680 self.output_offset = output_offset
681 self.output_size = output_size
682 self.input_offset = input_offset
683 self.fill_data = fill_data
684 # Check invariants.
685 if self.chunk_type == self.TYPE_RAW:
686 if self.fill_data is not None:
687 raise ValueError('RAW chunk cannot have fill_data set.')
688 if not self.input_offset:
689 raise ValueError('RAW chunk must have input_offset set.')
690 elif self.chunk_type == self.TYPE_FILL:
691 if self.fill_data is None:
692 raise ValueError('FILL chunk must have fill_data set.')
693 if self.input_offset:
694 raise ValueError('FILL chunk cannot have input_offset set.')
695 elif self.chunk_type == self.TYPE_DONT_CARE:
696 if self.fill_data is not None:
697 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
698 if self.input_offset:
699 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
700 else:
701 raise ValueError('Invalid chunk type')
702
703
704class ImageHandler(object):
705 """Abstraction for image I/O with support for Android sparse images.
706
707 This class provides an interface for working with image files that
708 may be using the Android Sparse Image format. When an instance is
709 constructed, we test whether it's an Android sparse file. If so,
710 operations will be on the sparse file by interpreting the sparse
711 format, otherwise they will be directly on the file. Either way the
712 operations do the same.
713
714 For reading, this interface mimics a file object - it has seek(),
715 tell(), and read() methods. For writing, only truncation
716 (truncate()) and appending is supported (append_raw() and
717 append_dont_care()). Additionally, data can only be written in units
718 of the block size.
719
720 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400721 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400722 is_sparse: Whether the file being operated on is sparse.
723 block_size: The block size, typically 4096.
724 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400725 """
726 # See system/core/libsparse/sparse_format.h for details.
727 MAGIC = 0xed26ff3a
728 HEADER_FORMAT = '<I4H4I'
729
730 # These are formats and offset of just the |total_chunks| and
731 # |total_blocks| fields.
732 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
733 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
734
735 def __init__(self, image_filename):
736 """Initializes an image handler.
737
738 Arguments:
739 image_filename: The name of the file to operate on.
740
741 Raises:
742 ValueError: If data in the file is invalid.
743 """
David Zeuthen49936b42018-08-07 17:38:58 -0400744 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100745 self._num_total_blocks = 0
746 self._num_total_chunks = 0
747 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400748 self._read_header()
749
750 def _read_header(self):
751 """Initializes internal data structures used for reading file.
752
753 This may be called multiple times and is typically called after
754 modifying the file (e.g. appending, truncation).
755
756 Raises:
757 ValueError: If data in the file is invalid.
758 """
759 self.is_sparse = False
760 self.block_size = 4096
761 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400762 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400763 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400764 self.image_size = self._image.tell()
765
766 self._image.seek(0, os.SEEK_SET)
767 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
768 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
769 block_size, self._num_total_blocks, self._num_total_chunks,
770 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
771 if magic != self.MAGIC:
772 # Not a sparse image, our job here is done.
773 return
774 if not (major_version == 1 and minor_version == 0):
775 raise ValueError('Encountered sparse image format version {}.{} but '
776 'only 1.0 is supported'.format(major_version,
777 minor_version))
778 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
779 raise ValueError('Unexpected file_hdr_sz value {}.'.
780 format(file_hdr_sz))
781 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
782 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
783 format(chunk_hdr_sz))
784
785 self.block_size = block_size
786
787 # Build an list of chunks by parsing the file.
788 self._chunks = []
789
790 # Find the smallest offset where only "Don't care" chunks
791 # follow. This will be the size of the content in the sparse
792 # image.
793 offset = 0
794 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100795 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400796 chunk_offset = self._image.tell()
797
798 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
799 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
800 header_bin)
801 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
802
David Zeuthena4fee8b2016-08-22 15:20:43 -0400803 if chunk_type == ImageChunk.TYPE_RAW:
804 if data_sz != (chunk_sz * self.block_size):
805 raise ValueError('Raw chunk input size ({}) does not match output '
806 'size ({})'.
807 format(data_sz, chunk_sz*self.block_size))
808 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
809 chunk_offset,
810 output_offset,
811 chunk_sz*self.block_size,
812 self._image.tell(),
813 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700814 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400815
816 elif chunk_type == ImageChunk.TYPE_FILL:
817 if data_sz != 4:
818 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
819 'has {}'.format(data_sz))
820 fill_data = self._image.read(4)
821 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
822 chunk_offset,
823 output_offset,
824 chunk_sz*self.block_size,
825 None,
826 fill_data))
827 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
828 if data_sz != 0:
829 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
830 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400831 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
832 chunk_offset,
833 output_offset,
834 chunk_sz*self.block_size,
835 None,
836 None))
837 elif chunk_type == ImageChunk.TYPE_CRC32:
838 if data_sz != 4:
839 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
840 'this has {}'.format(data_sz))
841 self._image.read(4)
842 else:
843 raise ValueError('Unknown chunk type {}'.format(chunk_type))
844
845 offset += chunk_sz
846 output_offset += chunk_sz*self.block_size
847
848 # Record where sparse data end.
849 self._sparse_end = self._image.tell()
850
851 # Now that we've traversed all chunks, sanity check.
852 if self._num_total_blocks != offset:
853 raise ValueError('The header said we should have {} output blocks, '
854 'but we saw {}'.format(self._num_total_blocks, offset))
855 junk_len = len(self._image.read())
856 if junk_len > 0:
857 raise ValueError('There were {} bytes of extra data at the end of the '
858 'file.'.format(junk_len))
859
David Zeuthen09692692016-09-30 16:16:40 -0400860 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400861 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400862
863 # This is used when bisecting in read() to find the initial slice.
864 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
865
866 self.is_sparse = True
867
868 def _update_chunks_and_blocks(self):
869 """Helper function to update the image header.
870
871 The the |total_chunks| and |total_blocks| fields in the header
872 will be set to value of the |_num_total_blocks| and
873 |_num_total_chunks| attributes.
874
875 """
876 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
877 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
878 self._num_total_blocks,
879 self._num_total_chunks))
880
881 def append_dont_care(self, num_bytes):
882 """Appends a DONT_CARE chunk to the sparse file.
883
884 The given number of bytes must be a multiple of the block size.
885
886 Arguments:
887 num_bytes: Size in number of bytes of the DONT_CARE chunk.
888 """
889 assert num_bytes % self.block_size == 0
890
891 if not self.is_sparse:
892 self._image.seek(0, os.SEEK_END)
893 # This is more efficient that writing NUL bytes since it'll add
894 # a hole on file systems that support sparse files (native
895 # sparse, not Android sparse).
896 self._image.truncate(self._image.tell() + num_bytes)
897 self._read_header()
898 return
899
900 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100901 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400902 self._update_chunks_and_blocks()
903
904 self._image.seek(self._sparse_end, os.SEEK_SET)
905 self._image.write(struct.pack(ImageChunk.FORMAT,
906 ImageChunk.TYPE_DONT_CARE,
907 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100908 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400909 struct.calcsize(ImageChunk.FORMAT)))
910 self._read_header()
911
912 def append_raw(self, data):
913 """Appends a RAW chunk to the sparse file.
914
915 The length of the given data must be a multiple of the block size.
916
917 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200918 data: Data to append as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400919 """
920 assert len(data) % self.block_size == 0
921
922 if not self.is_sparse:
923 self._image.seek(0, os.SEEK_END)
924 self._image.write(data)
925 self._read_header()
926 return
927
928 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100929 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400930 self._update_chunks_and_blocks()
931
932 self._image.seek(self._sparse_end, os.SEEK_SET)
933 self._image.write(struct.pack(ImageChunk.FORMAT,
934 ImageChunk.TYPE_RAW,
935 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100936 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400937 len(data) +
938 struct.calcsize(ImageChunk.FORMAT)))
939 self._image.write(data)
940 self._read_header()
941
942 def append_fill(self, fill_data, size):
943 """Appends a fill chunk to the sparse file.
944
945 The total length of the fill data must be a multiple of the block size.
946
947 Arguments:
948 fill_data: Fill data to append - must be four bytes.
949 size: Number of chunk - must be a multiple of four and the block size.
950 """
951 assert len(fill_data) == 4
952 assert size % 4 == 0
953 assert size % self.block_size == 0
954
955 if not self.is_sparse:
956 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100957 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400958 self._read_header()
959 return
960
961 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100962 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400963 self._update_chunks_and_blocks()
964
965 self._image.seek(self._sparse_end, os.SEEK_SET)
966 self._image.write(struct.pack(ImageChunk.FORMAT,
967 ImageChunk.TYPE_FILL,
968 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100969 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400970 4 + struct.calcsize(ImageChunk.FORMAT)))
971 self._image.write(fill_data)
972 self._read_header()
973
974 def seek(self, offset):
975 """Sets the cursor position for reading from unsparsified file.
976
977 Arguments:
978 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100979
980 Raises:
981 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400982 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700983 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +0200984 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400985 self._file_pos = offset
986
987 def read(self, size):
988 """Reads data from the unsparsified file.
989
990 This method may return fewer than |size| bytes of data if the end
991 of the file was encountered.
992
993 The file cursor for reading is advanced by the number of bytes
994 read.
995
996 Arguments:
997 size: Number of bytes to read.
998
999 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +02001000 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001001 """
1002 if not self.is_sparse:
1003 self._image.seek(self._file_pos)
1004 data = self._image.read(size)
1005 self._file_pos += len(data)
1006 return data
1007
1008 # Iterate over all chunks.
1009 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1010 self._file_pos) - 1
1011 data = bytearray()
1012 to_go = size
1013 while to_go > 0:
1014 chunk = self._chunks[chunk_idx]
1015 chunk_pos_offset = self._file_pos - chunk.output_offset
1016 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1017
1018 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1019 self._image.seek(chunk.input_offset + chunk_pos_offset)
1020 data.extend(self._image.read(chunk_pos_to_go))
1021 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001022 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001023 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1024 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1025 else:
1026 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001027 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001028
1029 to_go -= chunk_pos_to_go
1030 self._file_pos += chunk_pos_to_go
1031 chunk_idx += 1
1032 # Generate partial read in case of EOF.
1033 if chunk_idx >= len(self._chunks):
1034 break
1035
Jan Monsch8347da92020-04-08 12:41:49 +02001036 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001037
1038 def tell(self):
1039 """Returns the file cursor position for reading from unsparsified file.
1040
1041 Returns:
1042 The file cursor position for reading.
1043 """
1044 return self._file_pos
1045
1046 def truncate(self, size):
1047 """Truncates the unsparsified file.
1048
1049 Arguments:
1050 size: Desired size of unsparsified file.
1051
1052 Raises:
1053 ValueError: If desired size isn't a multiple of the block size.
1054 """
1055 if not self.is_sparse:
1056 self._image.truncate(size)
1057 self._read_header()
1058 return
1059
1060 if size % self.block_size != 0:
1061 raise ValueError('Cannot truncate to a size which is not a multiple '
1062 'of the block size')
1063
1064 if size == self.image_size:
1065 # Trivial where there's nothing to do.
1066 return
Jan Monsch9c130122020-04-14 13:43:51 +02001067
1068 if size < self.image_size:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001069 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1070 chunk = self._chunks[chunk_idx]
1071 if chunk.output_offset != size:
1072 # Truncation in the middle of a trunk - need to keep the chunk
1073 # and modify it.
1074 chunk_idx_for_update = chunk_idx + 1
1075 num_to_keep = size - chunk.output_offset
1076 assert num_to_keep % self.block_size == 0
1077 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1078 truncate_at = (chunk.chunk_offset +
1079 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1080 data_sz = num_to_keep
1081 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1082 truncate_at = (chunk.chunk_offset +
1083 struct.calcsize(ImageChunk.FORMAT) + 4)
1084 data_sz = 4
1085 else:
1086 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1087 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1088 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001089 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001090 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1091 self._image.seek(chunk.chunk_offset)
1092 self._image.write(struct.pack(ImageChunk.FORMAT,
1093 chunk.chunk_type,
1094 0, # Reserved
1095 chunk_sz,
1096 total_sz))
1097 chunk.output_size = num_to_keep
1098 else:
1099 # Truncation at trunk boundary.
1100 truncate_at = chunk.chunk_offset
1101 chunk_idx_for_update = chunk_idx
1102
1103 self._num_total_chunks = chunk_idx_for_update
1104 self._num_total_blocks = 0
1105 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001106 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001107 self._update_chunks_and_blocks()
1108 self._image.truncate(truncate_at)
1109
1110 # We've modified the file so re-read all data.
1111 self._read_header()
1112 else:
1113 # Truncating to grow - just add a DONT_CARE section.
1114 self.append_dont_care(size - self.image_size)
1115
1116
David Zeuthen21e95262016-07-27 17:58:40 -04001117class AvbDescriptor(object):
1118 """Class for AVB descriptor.
1119
1120 See the |AvbDescriptor| C struct for more information.
1121
1122 Attributes:
1123 tag: The tag identifying what kind of descriptor this is.
1124 data: The data in the descriptor.
1125 """
1126
1127 SIZE = 16
1128 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1129
1130 def __init__(self, data):
1131 """Initializes a new property descriptor.
1132
1133 Arguments:
1134 data: If not None, must be a bytearray().
1135
1136 Raises:
1137 LookupError: If the given descriptor is malformed.
1138 """
1139 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1140
1141 if data:
1142 (self.tag, num_bytes_following) = (
1143 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1144 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1145 else:
1146 self.tag = None
1147 self.data = None
1148
1149 def print_desc(self, o):
1150 """Print the descriptor.
1151
1152 Arguments:
1153 o: The object to write the output to.
1154 """
1155 o.write(' Unknown descriptor:\n')
1156 o.write(' Tag: {}\n'.format(self.tag))
1157 if len(self.data) < 256:
1158 o.write(' Data: {} ({} bytes)\n'.format(
1159 repr(str(self.data)), len(self.data)))
1160 else:
1161 o.write(' Data: {} bytes\n'.format(len(self.data)))
1162
1163 def encode(self):
1164 """Serializes the descriptor.
1165
1166 Returns:
1167 A bytearray() with the descriptor data.
1168 """
1169 num_bytes_following = len(self.data)
1170 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1171 padding_size = nbf_with_padding - num_bytes_following
1172 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1173 padding = struct.pack(str(padding_size) + 'x')
1174 ret = desc + self.data + padding
1175 return bytearray(ret)
1176
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001177 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001178 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001179 """Verifies contents of the descriptor - used in verify_image sub-command.
1180
1181 Arguments:
1182 image_dir: The directory of the file being verified.
1183 image_ext: The extension of the file being verified (e.g. '.img').
1184 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001185 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001186 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001187 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1188 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001189
1190 Returns:
1191 True if the descriptor verifies, False otherwise.
1192 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001193 # Deletes unused parameters to prevent pylint warning unused-argument.
1194 del image_dir, image_ext, expected_chain_partitions_map
1195 del image_containing_descriptor, accept_zeroed_hashtree
1196
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001197 # Nothing to do.
1198 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001199
Jan Monscheeb28b62019-12-05 16:17:09 +01001200
David Zeuthen21e95262016-07-27 17:58:40 -04001201class AvbPropertyDescriptor(AvbDescriptor):
1202 """A class for property descriptors.
1203
1204 See the |AvbPropertyDescriptor| C struct for more information.
1205
1206 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001207 key: The key as string.
1208 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001209 """
1210
1211 TAG = 0
1212 SIZE = 32
1213 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001214 'Q' # key size (bytes)
1215 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001216
1217 def __init__(self, data=None):
1218 """Initializes a new property descriptor.
1219
1220 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001221 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001222
1223 Raises:
1224 LookupError: If the given descriptor is malformed.
1225 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001226 super(AvbPropertyDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001227 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1228
1229 if data:
1230 (tag, num_bytes_following, key_size,
1231 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1232 expected_size = round_to_multiple(
1233 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1234 if tag != self.TAG or num_bytes_following != expected_size:
1235 raise LookupError('Given data does not look like a property '
1236 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001237 try:
1238 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1239 except UnicodeDecodeError as e:
1240 raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001241 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1242 value_size)]
1243 else:
1244 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001245 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001246
1247 def print_desc(self, o):
1248 """Print the descriptor.
1249
1250 Arguments:
1251 o: The object to write the output to.
1252 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001253 # Go forward with python 3, bytes are represented with the 'b' prefix,
1254 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1255 # the same between python 2 and python 3.
1256 printable_value = repr(self.value)
1257 if printable_value.startswith('b\''):
1258 printable_value = printable_value[1:]
1259
David Zeuthen21e95262016-07-27 17:58:40 -04001260 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001261 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001262 else:
1263 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1264
1265 def encode(self):
1266 """Serializes the descriptor.
1267
1268 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001269 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001270 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001271 key_encoded = self.key.encode('utf-8')
1272 num_bytes_following = (
1273 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001274 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1275 padding_size = nbf_with_padding - num_bytes_following
1276 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001277 len(key_encoded), len(self.value))
1278 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1279 padding_size * b'\0')
1280 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001281
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001282 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001283 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001284 """Verifies contents of the descriptor - used in verify_image sub-command.
1285
1286 Arguments:
1287 image_dir: The directory of the file being verified.
1288 image_ext: The extension of the file being verified (e.g. '.img').
1289 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001290 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001291 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001292 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1293 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001294
1295 Returns:
1296 True if the descriptor verifies, False otherwise.
1297 """
1298 # Nothing to do.
1299 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001300
Jan Monscheeb28b62019-12-05 16:17:09 +01001301
David Zeuthen21e95262016-07-27 17:58:40 -04001302class AvbHashtreeDescriptor(AvbDescriptor):
1303 """A class for hashtree descriptors.
1304
1305 See the |AvbHashtreeDescriptor| C struct for more information.
1306
1307 Attributes:
1308 dm_verity_version: dm-verity version used.
1309 image_size: Size of the image, after rounding up to |block_size|.
1310 tree_offset: Offset of the hash tree in the file.
1311 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001312 data_block_size: Data block size.
1313 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001314 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1315 fec_offset: Offset of FEC data (0 if FEC is not used).
1316 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001317 hash_algorithm: Hash algorithm used as string.
1318 partition_name: Partition name as string.
1319 salt: Salt used as bytes.
1320 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001321 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001322 """
1323
1324 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001325 RESERVED = 60
1326 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001327 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001328 'L' # dm-verity version used
1329 'Q' # image size (bytes)
1330 'Q' # tree offset (bytes)
1331 'Q' # tree size (bytes)
1332 'L' # data block size (bytes)
1333 'L' # hash block size (bytes)
1334 'L' # FEC number of roots
1335 'Q' # FEC offset (bytes)
1336 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001337 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001338 'L' # partition name (bytes)
1339 'L' # salt length (bytes)
1340 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001341 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001342 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001343
1344 def __init__(self, data=None):
1345 """Initializes a new hashtree descriptor.
1346
1347 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001348 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001349
1350 Raises:
1351 LookupError: If the given descriptor is malformed.
1352 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001353 super(AvbHashtreeDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001354 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1355
1356 if data:
1357 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1358 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001359 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1360 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001361 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1362 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001363 expected_size = round_to_multiple(
1364 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1365 if tag != self.TAG or num_bytes_following != expected_size:
1366 raise LookupError('Given data does not look like a hashtree '
1367 'descriptor.')
1368 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001369 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001370 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001371 try:
1372 self.partition_name = data[
1373 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1374 ].decode('utf-8')
1375 except UnicodeDecodeError as e:
1376 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1377 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001378 o += partition_name_len
1379 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1380 o += salt_len
1381 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001382 if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001383 if root_digest_len != 0:
1384 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001385
1386 else:
1387 self.dm_verity_version = 0
1388 self.image_size = 0
1389 self.tree_offset = 0
1390 self.tree_size = 0
1391 self.data_block_size = 0
1392 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001393 self.fec_num_roots = 0
1394 self.fec_offset = 0
1395 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001396 self.hash_algorithm = ''
1397 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001398 self.salt = b''
1399 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001400 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001401
1402 def print_desc(self, o):
1403 """Print the descriptor.
1404
1405 Arguments:
1406 o: The object to write the output to.
1407 """
1408 o.write(' Hashtree descriptor:\n')
1409 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1410 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1411 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1412 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1413 o.write(' Data Block Size: {} bytes\n'.format(
1414 self.data_block_size))
1415 o.write(' Hash Block Size: {} bytes\n'.format(
1416 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001417 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1418 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1419 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001420 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1421 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001422 o.write(' Salt: {}\n'.format(self.salt.hex()))
1423 o.write(' Root Digest: {}\n'.format(self.root_digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001424 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001425
1426 def encode(self):
1427 """Serializes the descriptor.
1428
1429 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001430 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001431 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001432 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1433 partition_name_encoded = self.partition_name.encode('utf-8')
1434 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1435 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001436 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1437 padding_size = nbf_with_padding - num_bytes_following
1438 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1439 self.dm_verity_version, self.image_size,
1440 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001441 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001442 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1443 len(partition_name_encoded), len(self.salt),
1444 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1445 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1446 padding_size * b'\0')
1447 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001448
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001449 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001450 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001451 """Verifies contents of the descriptor - used in verify_image sub-command.
1452
1453 Arguments:
1454 image_dir: The directory of the file being verified.
1455 image_ext: The extension of the file being verified (e.g. '.img').
1456 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001457 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001458 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001459 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1460 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001461
1462 Returns:
1463 True if the descriptor verifies, False otherwise.
1464 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001465 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001466 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001467 image = image_containing_descriptor
1468 else:
1469 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1470 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001471 # Generate the hashtree and checks that it matches what's in the file.
Jan Monsch6f27bb12020-04-07 07:33:26 +02001472 digest_size = len(hashlib.new(self.hash_algorithm).digest())
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001473 digest_padding = round_to_pow2(digest_size) - digest_size
1474 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001475 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001476 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1477 self.data_block_size,
1478 self.hash_algorithm, self.salt,
1479 digest_padding,
1480 hash_level_offsets,
1481 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001482 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001483 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001484 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1485 format(image_filename))
1486 return False
1487 # ... also check that the on-disk hashtree matches
1488 image.seek(self.tree_offset)
1489 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001490 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001491 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001492 print('{}: skipping verification since hashtree is zeroed and '
1493 '--accept_zeroed_hashtree was given'
1494 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001495 else:
1496 if hash_tree != hash_tree_ondisk:
1497 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001498 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001499 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001500 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1501 .format(self.partition_name, self.hash_algorithm, image.filename,
1502 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001503 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1504 # correct but this a) currently requires the 'fec' binary; and b) takes a
1505 # long time; and c) is not strictly needed for verification purposes as
1506 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001507 return True
1508
David Zeuthen21e95262016-07-27 17:58:40 -04001509
1510class AvbHashDescriptor(AvbDescriptor):
1511 """A class for hash descriptors.
1512
1513 See the |AvbHashDescriptor| C struct for more information.
1514
1515 Attributes:
1516 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001517 hash_algorithm: Hash algorithm used as string.
1518 partition_name: Partition name as string.
1519 salt: Salt used as bytes.
1520 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001521 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001522 """
1523
1524 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001525 RESERVED = 60
1526 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001527 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001528 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001529 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001530 'L' # partition name (bytes)
1531 'L' # salt length (bytes)
1532 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001533 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001534 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001535
1536 def __init__(self, data=None):
1537 """Initializes a new hash descriptor.
1538
1539 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001540 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001541
1542 Raises:
1543 LookupError: If the given descriptor is malformed.
1544 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001545 super(AvbHashDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001546 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1547
1548 if data:
1549 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1550 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001551 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1552 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001553 expected_size = round_to_multiple(
1554 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1555 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001556 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001557 # Nuke NUL-bytes at the end.
Jan Monschee6fccd2020-04-09 19:36:13 +02001558 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001559 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001560 try:
1561 self.partition_name = data[
1562 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1563 ].decode('utf-8')
1564 except UnicodeDecodeError as e:
1565 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1566 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001567 o += partition_name_len
1568 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1569 o += salt_len
1570 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001571 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001572 if digest_len != 0:
1573 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001574
1575 else:
1576 self.image_size = 0
1577 self.hash_algorithm = ''
1578 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001579 self.salt = b''
1580 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001581 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001582
1583 def print_desc(self, o):
1584 """Print the descriptor.
1585
1586 Arguments:
1587 o: The object to write the output to.
1588 """
1589 o.write(' Hash descriptor:\n')
1590 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1591 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1592 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monsch25040d92020-04-22 22:48:20 +02001593 o.write(' Salt: {}\n'.format(self.salt.hex()))
1594 o.write(' Digest: {}\n'.format(self.digest.hex()))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001595 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001596
1597 def encode(self):
1598 """Serializes the descriptor.
1599
1600 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001601 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001602 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001603 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1604 partition_name_encoded = self.partition_name.encode('utf-8')
1605 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1606 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001607 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1608 padding_size = nbf_with_padding - num_bytes_following
1609 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001610 self.image_size, hash_algorithm_encoded,
1611 len(partition_name_encoded), len(self.salt),
1612 len(self.digest), self.flags, self.RESERVED * b'\0')
1613 ret = (desc + partition_name_encoded + self.salt + self.digest +
1614 padding_size * b'\0')
1615 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001616
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001617 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001618 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001619 """Verifies contents of the descriptor - used in verify_image sub-command.
1620
1621 Arguments:
1622 image_dir: The directory of the file being verified.
1623 image_ext: The extension of the file being verified (e.g. '.img').
1624 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001625 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001626 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001627 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1628 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001629
1630 Returns:
1631 True if the descriptor verifies, False otherwise.
1632 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001633 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001634 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001635 image = image_containing_descriptor
1636 else:
1637 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1638 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001639 data = image.read(self.image_size)
1640 ha = hashlib.new(self.hash_algorithm)
1641 ha.update(self.salt)
1642 ha.update(data)
1643 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001644 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001645 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001646 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1647 format(self.hash_algorithm, image_filename))
1648 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001649 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1650 .format(self.partition_name, self.hash_algorithm, image.filename,
1651 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001652 return True
1653
David Zeuthen21e95262016-07-27 17:58:40 -04001654
1655class AvbKernelCmdlineDescriptor(AvbDescriptor):
1656 """A class for kernel command-line descriptors.
1657
1658 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1659
1660 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001661 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001662 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001663 """
1664
1665 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001666 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001667 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001668 'L' # flags
1669 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001670
David Zeuthenfd41eb92016-11-17 12:24:47 -05001671 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1672 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1673
David Zeuthen21e95262016-07-27 17:58:40 -04001674 def __init__(self, data=None):
1675 """Initializes a new kernel cmdline descriptor.
1676
1677 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001678 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001679
1680 Raises:
1681 LookupError: If the given descriptor is malformed.
1682 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001683 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001684 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1685
1686 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001687 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001688 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1689 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1690 8)
1691 if tag != self.TAG or num_bytes_following != expected_size:
1692 raise LookupError('Given data does not look like a kernel cmdline '
1693 'descriptor.')
1694 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001695 try:
1696 self.kernel_cmdline = data[
1697 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1698 except UnicodeDecodeError as e:
1699 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1700 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001701 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001702 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001703 self.kernel_cmdline = ''
1704
1705 def print_desc(self, o):
1706 """Print the descriptor.
1707
1708 Arguments:
1709 o: The object to write the output to.
1710 """
1711 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001712 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001713 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001714
1715 def encode(self):
1716 """Serializes the descriptor.
1717
1718 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001719 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001720 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001721 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1722 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001723 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1724 padding_size = nbf_with_padding - num_bytes_following
1725 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001726 self.flags, len(kernel_cmd_encoded))
1727 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1728 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001729
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001730 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001731 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001732 """Verifies contents of the descriptor - used in verify_image sub-command.
1733
1734 Arguments:
1735 image_dir: The directory of the file being verified.
1736 image_ext: The extension of the file being verified (e.g. '.img').
1737 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001738 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001739 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001740 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1741 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001742
1743 Returns:
1744 True if the descriptor verifies, False otherwise.
1745 """
1746 # Nothing to verify.
1747 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001748
Jan Monscheeb28b62019-12-05 16:17:09 +01001749
David Zeuthen21e95262016-07-27 17:58:40 -04001750class AvbChainPartitionDescriptor(AvbDescriptor):
1751 """A class for chained partition descriptors.
1752
1753 See the |AvbChainPartitionDescriptor| C struct for more information.
1754
1755 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001756 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001757 partition_name: Partition name as string.
1758 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001759 """
1760
1761 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001762 RESERVED = 64
1763 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001764 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001765 'L' # rollback_index_location
1766 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001767 'L' + # public_key_size (bytes)
1768 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001769
1770 def __init__(self, data=None):
1771 """Initializes a new chain partition descriptor.
1772
1773 Arguments:
1774 data: If not None, must be a bytearray of size |SIZE|.
1775
1776 Raises:
1777 LookupError: If the given descriptor is malformed.
1778 """
1779 AvbDescriptor.__init__(self, None)
1780 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1781
1782 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001783 (tag, num_bytes_following, self.rollback_index_location,
1784 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001785 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001786 expected_size = round_to_multiple(
1787 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1788 if tag != self.TAG or num_bytes_following != expected_size:
1789 raise LookupError('Given data does not look like a chain partition '
1790 'descriptor.')
1791 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001792 try:
1793 self.partition_name = data[
1794 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1795 ].decode('utf-8')
1796 except UnicodeDecodeError as e:
1797 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1798 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001799 o += partition_name_len
1800 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1801
1802 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001803 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001804 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001805 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001806
1807 def print_desc(self, o):
1808 """Print the descriptor.
1809
1810 Arguments:
1811 o: The object to write the output to.
1812 """
1813 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001814 o.write(' Partition Name: {}\n'.format(self.partition_name))
1815 o.write(' Rollback Index Location: {}\n'.format(
1816 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001817 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001818 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1819 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001820
1821 def encode(self):
1822 """Serializes the descriptor.
1823
1824 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001825 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001826 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001827 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001828 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001829 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001830 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1831 padding_size = nbf_with_padding - num_bytes_following
1832 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001833 self.rollback_index_location,
1834 len(partition_name_encoded), len(self.public_key),
1835 self.RESERVED * b'\0')
1836 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1837 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001838
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001839 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001840 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001841 """Verifies contents of the descriptor - used in verify_image sub-command.
1842
1843 Arguments:
1844 image_dir: The directory of the file being verified.
1845 image_ext: The extension of the file being verified (e.g. '.img').
1846 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001847 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001848 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001849 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1850 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001851
1852 Returns:
1853 True if the descriptor verifies, False otherwise.
1854 """
1855 value = expected_chain_partitions_map.get(self.partition_name)
1856 if not value:
1857 sys.stderr.write('No expected chain partition for partition {}. Use '
1858 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001859 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001860 format(self.partition_name))
1861 return False
1862 rollback_index_location, pk_blob = value
1863
1864 if self.rollback_index_location != rollback_index_location:
1865 sys.stderr.write('Expected rollback_index_location {} does not '
1866 'match {} in descriptor for partition {}\n'.
1867 format(rollback_index_location,
1868 self.rollback_index_location,
1869 self.partition_name))
1870 return False
1871
1872 if self.public_key != pk_blob:
1873 sys.stderr.write('Expected public key blob does not match public '
1874 'key blob in descriptor for partition {}\n'.
1875 format(self.partition_name))
1876 return False
1877
Jan Monsch23e0c622019-12-11 11:23:58 +01001878 print('{}: Successfully verified chain partition descriptor matches '
1879 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001880
1881 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001882
1883DESCRIPTOR_CLASSES = [
1884 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1885 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1886]
1887
1888
1889def parse_descriptors(data):
1890 """Parses a blob of data into descriptors.
1891
1892 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001893 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001894
1895 Returns:
1896 A list of instances of objects derived from AvbDescriptor. For
1897 unknown descriptors, the class AvbDescriptor is used.
1898 """
1899 o = 0
1900 ret = []
1901 while o < len(data):
1902 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1903 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001904 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001905 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001906 clazz = AvbDescriptor
1907 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001908 o += 16 + nb_following
1909 return ret
1910
1911
1912class AvbFooter(object):
1913 """A class for parsing and writing footers.
1914
1915 Footers are stored at the end of partitions and point to where the
1916 AvbVBMeta blob is located. They also contain the original size of
1917 the image before AVB information was added.
1918
1919 Attributes:
1920 magic: Magic for identifying the footer, see |MAGIC|.
1921 version_major: The major version of avbtool that wrote the footer.
1922 version_minor: The minor version of avbtool that wrote the footer.
1923 original_image_size: Original image size.
1924 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1925 vbmeta_size: Size of the AvbVBMeta blob.
1926 """
1927
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001928 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001929 SIZE = 64
1930 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001931 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1932 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001933 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001934 'Q' # Original image size.
1935 'Q' # Offset of VBMeta blob.
1936 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001937 str(RESERVED) + 'x') # padding for reserved bytes
1938
1939 def __init__(self, data=None):
1940 """Initializes a new footer object.
1941
1942 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001943 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001944
1945 Raises:
1946 LookupError: If the given footer is malformed.
1947 struct.error: If the given data has no footer.
1948 """
1949 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1950
1951 if data:
1952 (self.magic, self.version_major, self.version_minor,
1953 self.original_image_size, self.vbmeta_offset,
1954 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1955 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001956 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001957 else:
1958 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001959 self.version_major = self.FOOTER_VERSION_MAJOR
1960 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001961 self.original_image_size = 0
1962 self.vbmeta_offset = 0
1963 self.vbmeta_size = 0
1964
David Zeuthena4fee8b2016-08-22 15:20:43 -04001965 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001966 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001967
David Zeuthena4fee8b2016-08-22 15:20:43 -04001968 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001969 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001970 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001971 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1972 self.version_minor, self.original_image_size,
1973 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001974
1975
1976class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001977 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001978
Jan Monschfe00c0a2019-12-11 11:19:40 +01001979 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1980 avb_vbmeta_image.h.
1981
David Zeuthen21e95262016-07-27 17:58:40 -04001982 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001983 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1984 required_libavb_version_major: The major version of libavb required for this
1985 header.
1986 required_libavb_version_minor: The minor version of libavb required for this
1987 header.
1988 authentication_data_block_size: The size of the signature block.
1989 auxiliary_data_block_size: The size of the auxiliary data block.
1990 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1991 enum.
1992 hash_offset: Offset into the "Authentication data" block of hash data.
1993 hash_size: Length of the hash data.
1994 signature_offset: Offset into the "Authentication data" block of signature
1995 data.
1996 signature_size: Length of the signature data.
1997 public_key_offset: Offset into the "Auxiliary data" block of public key
1998 data.
1999 public_key_size: Length of the public key data.
2000 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2001 key metadata.
2002 public_key_metadata_size: Length of the public key metadata. Must be set to
2003 zero if there is no public key metadata.
2004 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2005 data.
2006 descriptors_size: Length of descriptor data.
2007 rollback_index: The rollback index which can be used to prevent rollback to
2008 older versions.
2009 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2010 zero if the vbmeta image is not a top-level image.
Varun Sharmade538272020-04-10 15:22:31 -07002011 rollback_index_location: The location of the rollback index defined in this
2012 header. Only valid for the main vbmeta. For chained partitions, the
2013 rollback index location must be specified in the
2014 AvbChainPartitionDescriptor and this value must be set to 0.
Jan Monschfe00c0a2019-12-11 11:19:40 +01002015 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2016 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2017 terminated. Applications must not make assumptions about how this
2018 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002019 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002020 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002021 SIZE = 256
2022
Varun Sharmade538272020-04-10 15:22:31 -07002023 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthene3cadca2017-02-22 21:25:46 -05002024 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002025
2026 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002027 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2028 '2Q' # 2 x block size
2029 'L' # algorithm type
2030 '2Q' # offset, size (hash)
2031 '2Q' # offset, size (signature)
2032 '2Q' # offset, size (public key)
2033 '2Q' # offset, size (public key metadata)
2034 '2Q' # offset, size (descriptors)
2035 'Q' # rollback_index
Varun Sharmade538272020-04-10 15:22:31 -07002036 'L' # flags
2037 'L' # rollback_index_location
David Zeuthene3cadca2017-02-22 21:25:46 -05002038 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002039 str(RESERVED) + 'x') # padding for reserved bytes
2040
2041 def __init__(self, data=None):
2042 """Initializes a new header object.
2043
2044 Arguments:
2045 data: If not None, must be a bytearray of size 8192.
2046
2047 Raises:
2048 Exception: If the given data is malformed.
2049 """
2050 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2051
2052 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002053 (self.magic, self.required_libavb_version_major,
2054 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002055 self.authentication_data_block_size, self.auxiliary_data_block_size,
2056 self.algorithm_type, self.hash_offset, self.hash_size,
2057 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002058 self.public_key_size, self.public_key_metadata_offset,
2059 self.public_key_metadata_size, self.descriptors_offset,
2060 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002061 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002062 self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002063 self.rollback_index_location,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002064 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002065 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002066 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002067 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002068 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002069 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002070 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002071 # Start by just requiring version 1.0. Code that adds features
2072 # in a future version can use bump_required_libavb_version_minor() to
2073 # bump the minor.
2074 self.required_libavb_version_major = AVB_VERSION_MAJOR
2075 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002076 self.authentication_data_block_size = 0
2077 self.auxiliary_data_block_size = 0
2078 self.algorithm_type = 0
2079 self.hash_offset = 0
2080 self.hash_size = 0
2081 self.signature_offset = 0
2082 self.signature_size = 0
2083 self.public_key_offset = 0
2084 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002085 self.public_key_metadata_offset = 0
2086 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002087 self.descriptors_offset = 0
2088 self.descriptors_size = 0
2089 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002090 self.flags = 0
Varun Sharmade538272020-04-10 15:22:31 -07002091 self.rollback_index_location = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002092 self.release_string = get_release_string()
2093
2094 def bump_required_libavb_version_minor(self, minor):
2095 """Function to bump required_libavb_version_minor.
2096
2097 Call this when writing data that requires a specific libavb
2098 version to parse it.
2099
2100 Arguments:
2101 minor: The minor version of libavb that has support for the feature.
2102 """
2103 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002104 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002105
David Zeuthen21e95262016-07-27 17:58:40 -04002106 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002107 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002108
2109 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002110 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002111 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002112 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002113 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002114 self.required_libavb_version_major,
2115 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002116 self.authentication_data_block_size,
2117 self.auxiliary_data_block_size, self.algorithm_type,
2118 self.hash_offset, self.hash_size, self.signature_offset,
2119 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002120 self.public_key_size, self.public_key_metadata_offset,
2121 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002122 self.descriptors_size, self.rollback_index, self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002123 self.rollback_index_location, release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002124
2125
2126class Avb(object):
2127 """Business logic for avbtool command-line tool."""
2128
David Zeuthen8b6973b2016-09-20 12:39:49 -04002129 # Keep in sync with avb_ab_flow.h.
2130 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002131 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002132 AB_MAJOR_VERSION = 1
2133 AB_MINOR_VERSION = 0
2134 AB_MISC_METADATA_OFFSET = 2048
2135
David Zeuthen09692692016-09-30 16:16:40 -04002136 # Constants for maximum metadata size. These are used to give
2137 # meaningful errors if the value passed in via --partition_size is
2138 # too small and when --calc_max_image_size is used. We use
2139 # conservative figures.
2140 MAX_VBMETA_SIZE = 64 * 1024
2141 MAX_FOOTER_SIZE = 4096
2142
Jan Monsch2c7be992020-04-03 14:37:13 +02002143 def generate_test_image(self, output, image_size, start_byte):
2144 """Generates a test image for testing avbtool with known content.
2145
2146 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2147
2148 Arguments:
2149 output: Write test image to this file.
2150 image_size: The size of the requested file in bytes.
2151 start_byte: The integer value of the start byte to use for pattern
2152 generation.
2153 """
2154 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2155 buf = bytearray()
2156 c = int(math.ceil(image_size / 256.0))
2157 for _ in range(0, c):
2158 buf.extend(pattern)
2159 output.write(buf[0:image_size])
2160
David Zeuthen49936b42018-08-07 17:38:58 -04002161 def extract_vbmeta_image(self, output, image_filename, padding_size):
2162 """Implements the 'extract_vbmeta_image' command.
2163
2164 Arguments:
2165 output: Write vbmeta struct to this file.
2166 image_filename: File to extract vbmeta data from (with a footer).
2167 padding_size: If not 0, pads output so size is a multiple of the number.
2168
2169 Raises:
2170 AvbError: If there's no footer in the image.
2171 """
2172 image = ImageHandler(image_filename)
David Zeuthen49936b42018-08-07 17:38:58 -04002173 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002174 if not footer:
2175 raise AvbError('Given image does not have a footer.')
2176
2177 image.seek(footer.vbmeta_offset)
2178 vbmeta_blob = image.read(footer.vbmeta_size)
2179 output.write(vbmeta_blob)
2180
2181 if padding_size > 0:
2182 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2183 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002184 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002185
David Zeuthena4fee8b2016-08-22 15:20:43 -04002186 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002187 """Implements the 'erase_footer' command.
2188
2189 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002190 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002191 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002192
2193 Raises:
2194 AvbError: If there's no footer in the image.
2195 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002196 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002197 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002198 if not footer:
2199 raise AvbError('Given image does not have a footer.')
2200
2201 new_image_size = None
2202 if not keep_hashtree:
2203 new_image_size = footer.original_image_size
2204 else:
2205 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002206 # descriptor to figure out the location and size of the hashtree
2207 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002208 for desc in descriptors:
2209 if isinstance(desc, AvbHashtreeDescriptor):
2210 # The hashtree is always just following the main data so the
2211 # new size is easily derived.
2212 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002213 # If the image has FEC codes, also keep those.
2214 if desc.fec_offset > 0:
2215 fec_end = desc.fec_offset + desc.fec_size
2216 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002217 break
2218 if not new_image_size:
2219 raise AvbError('Requested to keep hashtree but no hashtree '
2220 'descriptor was found.')
2221
2222 # And cut...
2223 image.truncate(new_image_size)
2224
David Zeuthen1394f762019-04-30 10:20:11 -04002225 def zero_hashtree(self, image_filename):
2226 """Implements the 'zero_hashtree' command.
2227
2228 Arguments:
2229 image_filename: File to zero hashtree and FEC data from.
2230
2231 Raises:
2232 AvbError: If there's no footer in the image.
2233 """
David Zeuthen1394f762019-04-30 10:20:11 -04002234 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002235 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002236 if not footer:
2237 raise AvbError('Given image does not have a footer.')
2238
2239 # Search for a hashtree descriptor to figure out the location and
2240 # size of the hashtree and FEC.
2241 ht_desc = None
2242 for desc in descriptors:
2243 if isinstance(desc, AvbHashtreeDescriptor):
2244 ht_desc = desc
2245 break
2246
2247 if not ht_desc:
2248 raise AvbError('No hashtree descriptor was found.')
2249
2250 zero_ht_start_offset = ht_desc.tree_offset
2251 zero_ht_num_bytes = ht_desc.tree_size
2252 zero_fec_start_offset = None
2253 zero_fec_num_bytes = 0
2254 if ht_desc.fec_offset > 0:
2255 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2256 raise AvbError('Hash-tree and FEC data must be adjacent.')
2257 zero_fec_start_offset = ht_desc.fec_offset
2258 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002259 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2260 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002261 image.seek(zero_end_offset)
2262 data = image.read(image.image_size - zero_end_offset)
2263
2264 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2265 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2266 # beginning of both hashtree and FEC. (That way, in the future we can add
2267 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2268 #
2269 # Applications can use these markers to detect that the hashtree and/or
2270 # FEC needs to be recomputed.
2271 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002272 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002273 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002274 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002275 if zero_fec_start_offset:
2276 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002277 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002278 image.append_raw(data)
2279
David Zeuthen2bc232b2017-04-19 14:25:19 -04002280 def resize_image(self, image_filename, partition_size):
2281 """Implements the 'resize_image' command.
2282
2283 Arguments:
2284 image_filename: File with footer to resize.
2285 partition_size: The new size of the image.
2286
2287 Raises:
2288 AvbError: If there's no footer in the image.
2289 """
2290
2291 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002292 if partition_size % image.block_size != 0:
2293 raise AvbError('Partition size of {} is not a multiple of the image '
2294 'block size {}.'.format(partition_size,
2295 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002296 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002297 if not footer:
2298 raise AvbError('Given image does not have a footer.')
2299
2300 # The vbmeta blob is always at the end of the data so resizing an
2301 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002302 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2303 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002304 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2305 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002306
Jan Monschb1d920f2020-04-09 12:59:28 +02002307 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002308 raise AvbError('Requested size of {} is too small for an image '
2309 'of size {}.'
2310 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002311 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002312
2313 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2314 # with enough bytes such that the final Footer block is at the end
2315 # of partition_size.
2316 image.truncate(vbmeta_end_offset)
2317 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002318 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002319
2320 # Just reuse the same footer - only difference is that we're
2321 # writing it in a different place.
2322 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002323 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002324 footer_blob)
2325 image.append_raw(footer_blob_with_padding)
2326
David Zeuthen8b6973b2016-09-20 12:39:49 -04002327 def set_ab_metadata(self, misc_image, slot_data):
2328 """Implements the 'set_ab_metadata' command.
2329
2330 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2331 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2332
2333 Arguments:
2334 misc_image: The misc image to write to.
2335 slot_data: Slot data as a string
2336
2337 Raises:
2338 AvbError: If slot data is malformed.
2339 """
2340 tokens = slot_data.split(':')
2341 if len(tokens) != 6:
2342 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2343 a_priority = int(tokens[0])
2344 a_tries_remaining = int(tokens[1])
Jan Monsch9c130122020-04-14 13:43:51 +02002345 a_success = int(tokens[2]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002346 b_priority = int(tokens[3])
2347 b_tries_remaining = int(tokens[4])
Jan Monsch9c130122020-04-14 13:43:51 +02002348 b_success = int(tokens[5]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002349
2350 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2351 self.AB_MAGIC,
2352 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2353 a_priority, a_tries_remaining, a_success,
2354 b_priority, b_tries_remaining, b_success)
2355 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2356 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2357 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2358 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2359 misc_image.write(ab_data)
2360
David Zeuthena4fee8b2016-08-22 15:20:43 -04002361 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002362 """Implements the 'info_image' command.
2363
2364 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002365 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002366 output: Output file to write human-readable information to (file object).
2367 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002368 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002369 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002370 (footer, header, descriptors, image_size) = self._parse_image(image)
2371
Bowgo Tsaid7145942020-03-20 17:03:51 +08002372 # To show the SHA1 of the public key.
2373 vbmeta_blob = self._load_vbmeta_blob(image)
2374 key_offset = (header.SIZE +
2375 header.authentication_data_block_size +
2376 header.public_key_offset)
2377 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2378
David Zeuthen21e95262016-07-27 17:58:40 -04002379 if footer:
2380 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2381 footer.version_minor))
2382 o.write('Image size: {} bytes\n'.format(image_size))
2383 o.write('Original image size: {} bytes\n'.format(
2384 footer.original_image_size))
2385 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2386 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2387 o.write('--\n')
2388
2389 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2390
David Zeuthene3cadca2017-02-22 21:25:46 -05002391 o.write('Minimum libavb version: {}.{}{}\n'.format(
2392 header.required_libavb_version_major,
2393 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002394 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002395 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2396 o.write('Authentication Block: {} bytes\n'.format(
2397 header.authentication_data_block_size))
2398 o.write('Auxiliary Block: {} bytes\n'.format(
2399 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002400 if key_blob:
2401 hexdig = hashlib.sha1(key_blob).hexdigest()
2402 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002403 o.write('Algorithm: {}\n'.format(alg_name))
2404 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002405 o.write('Flags: {}\n'.format(header.flags))
Jan Monscha18b2ec2020-04-22 11:43:35 +02002406 o.write('Rollback Index Location: {}\n'.format(
2407 header.rollback_index_location))
Jan Monschb1d920f2020-04-09 12:59:28 +02002408 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002409
2410 # Print descriptors.
2411 num_printed = 0
2412 o.write('Descriptors:\n')
2413 for desc in descriptors:
2414 desc.print_desc(o)
2415 num_printed += 1
2416 if num_printed == 0:
2417 o.write(' (none)\n')
2418
Jan Monscheeb28b62019-12-05 16:17:09 +01002419 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2420 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002421 """Implements the 'verify_image' command.
2422
2423 Arguments:
2424 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002425 key_path: None or check that embedded public key matches key at given
2426 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002427 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002428 follow_chain_partitions:
2429 If True, will follows chain partitions even when not specified with
2430 the --expected_chain_partition option
2431 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2432 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002433
2434 Raises:
2435 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002436 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002437 expected_chain_partitions_map = {}
2438 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002439 for cp in expected_chain_partitions:
2440 cp_tokens = cp.split(':')
2441 if len(cp_tokens) != 3:
2442 raise AvbError('Malformed chained partition "{}".'.format(cp))
2443 partition_name = cp_tokens[0]
2444 rollback_index_location = int(cp_tokens[1])
2445 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002446 with open(file_path, 'rb') as f:
2447 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002448 expected_chain_partitions_map[partition_name] = (
2449 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002450
2451 image_dir = os.path.dirname(image_filename)
2452 image_ext = os.path.splitext(image_filename)[1]
2453
2454 key_blob = None
2455 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002456 print('Verifying image {} using key at {}'.format(image_filename,
2457 key_path))
Jan Monsch9c130122020-04-14 13:43:51 +02002458 key_blob = RSAPublicKey(key_path).encode()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002459 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002460 print('Verifying image {} using embedded public key'.format(
2461 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002462
David Zeuthenb623d8b2017-04-04 16:05:53 -04002463 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002464 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002465 offset = 0
2466 if footer:
2467 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002468
David Zeuthenb623d8b2017-04-04 16:05:53 -04002469 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002470 vbmeta_blob = image.read(header.SIZE
2471 + header.authentication_data_block_size
2472 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002473
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002474 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002475 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002476 raise AvbError('Signature check failed for {} vbmeta struct {}'
2477 .format(alg_name, image_filename))
2478
2479 if key_blob:
2480 # The embedded public key is in the auxiliary block at an offset.
2481 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002482 key_offset += header.authentication_data_block_size
2483 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002484 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2485 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002486 if key_blob != key_blob_in_vbmeta:
2487 raise AvbError('Embedded public key does not match given key.')
2488
2489 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002490 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2491 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002492 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002493 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2494 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002495
2496 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002497 if (isinstance(desc, AvbChainPartitionDescriptor)
2498 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002499 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002500 # In this case we're processing a chain descriptor but don't have a
2501 # --expect_chain_partition ... however --follow_chain_partitions was
2502 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002503 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2504 'and KEY (which has sha1 {}) not specified'
2505 .format(desc.partition_name, desc.rollback_index_location,
2506 hashlib.sha1(desc.public_key).hexdigest()))
2507 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002508 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002509 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002510 # Honor --follow_chain_partitions - add '--' to make the output more
2511 # readable.
2512 if (isinstance(desc, AvbChainPartitionDescriptor)
2513 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002514 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002515 chained_image_filename = os.path.join(image_dir,
2516 desc.partition_name + image_ext)
2517 self.verify_image(chained_image_filename, key_path, None, False,
2518 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002519
David Zeuthen34b6b492020-04-13 14:45:02 -04002520 def print_partition_digests(self, image_filename, output, as_json):
2521 """Implements the 'print_partition_digests' command.
2522
2523 Arguments:
2524 image_filename: Image file to get information from (file object).
2525 output: Output file to write human-readable information to (file object).
2526 as_json: If True, print information as JSON
2527
2528 Raises:
2529 AvbError: If getting the partition digests from the image fails.
2530 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002531 image_dir = os.path.dirname(image_filename)
2532 image_ext = os.path.splitext(image_filename)[1]
2533 json_partitions = None
2534 if as_json:
2535 json_partitions = []
Jan Monschcc9939a2020-04-16 09:15:20 +02002536 self._print_partition_digests(
2537 image_filename, output, json_partitions, image_dir, image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002538 if as_json:
2539 output.write(json.dumps({'partitions': json_partitions}, indent=2))
2540
Jan Monschcc9939a2020-04-16 09:15:20 +02002541 def _print_partition_digests(self, image_filename, output, json_partitions,
2542 image_dir, image_ext):
David Zeuthen34b6b492020-04-13 14:45:02 -04002543 """Helper for printing partitions.
2544
2545 Arguments:
2546 image_filename: Image file to get information from (file object).
2547 output: Output file to write human-readable information to (file object).
Jan Monschcc9939a2020-04-16 09:15:20 +02002548 json_partitions: If not None, don't print to output, instead add partition
2549 information to this list.
David Zeuthen34b6b492020-04-13 14:45:02 -04002550 image_dir: The directory to use when looking for chained partition files.
2551 image_ext: The extension to use for chained partition files.
2552
2553 Raises:
2554 AvbError: If getting the partition digests from the image fails.
2555 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002556 image = ImageHandler(image_filename)
2557 (_, _, descriptors, _) = self._parse_image(image)
2558
2559 for desc in descriptors:
2560 if isinstance(desc, AvbHashDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002561 digest = desc.digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002562 if json_partitions is not None:
2563 json_partitions.append({'name': desc.partition_name,
2564 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002565 else:
2566 output.write('{}: {}\n'.format(desc.partition_name, digest))
2567 elif isinstance(desc, AvbHashtreeDescriptor):
Jan Monsch25040d92020-04-22 22:48:20 +02002568 digest = desc.root_digest.hex()
Jan Monschcc9939a2020-04-16 09:15:20 +02002569 if json_partitions is not None:
2570 json_partitions.append({'name': desc.partition_name,
2571 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002572 else:
2573 output.write('{}: {}\n'.format(desc.partition_name, digest))
2574 elif isinstance(desc, AvbChainPartitionDescriptor):
2575 chained_image_filename = os.path.join(image_dir,
2576 desc.partition_name + image_ext)
Jan Monschcc9939a2020-04-16 09:15:20 +02002577 self._print_partition_digests(
2578 chained_image_filename, output, json_partitions, image_dir,
2579 image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002580
David Zeuthenb8643c02018-05-17 17:21:18 -04002581 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2582 """Implements the 'calculate_vbmeta_digest' command.
2583
2584 Arguments:
2585 image_filename: Image file to get information from (file object).
2586 hash_algorithm: Hash algorithm used.
2587 output: Output file to write human-readable information to (file object).
2588 """
2589
2590 image_dir = os.path.dirname(image_filename)
2591 image_ext = os.path.splitext(image_filename)[1]
2592
2593 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002594 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002595 offset = 0
2596 if footer:
2597 offset = footer.vbmeta_offset
2598 size = (header.SIZE + header.authentication_data_block_size +
2599 header.auxiliary_data_block_size)
2600 image.seek(offset)
2601 vbmeta_blob = image.read(size)
2602
Jan Monsch6f27bb12020-04-07 07:33:26 +02002603 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002604 hasher.update(vbmeta_blob)
2605
2606 for desc in descriptors:
2607 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002608 ch_image_filename = os.path.join(image_dir,
2609 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002610 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002611 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002612 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002613 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2614 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002615 if ch_footer:
2616 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002617 ch_image.seek(ch_offset)
2618 ch_vbmeta_blob = ch_image.read(ch_size)
2619 hasher.update(ch_vbmeta_blob)
2620
2621 digest = hasher.digest()
Jan Monsch25040d92020-04-22 22:48:20 +02002622 output.write('{}\n'.format(digest.hex()))
David Zeuthenb8643c02018-05-17 17:21:18 -04002623
David Zeuthenf7d2e752018-09-20 13:30:41 -04002624 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2625 """Implements the 'calculate_kernel_cmdline' command.
2626
2627 Arguments:
2628 image_filename: Image file to get information from (file object).
2629 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2630 output: Output file to write human-readable information to (file object).
2631 """
2632
2633 image = ImageHandler(image_filename)
2634 _, _, descriptors, _ = self._parse_image(image)
2635
2636 image_dir = os.path.dirname(image_filename)
2637 image_ext = os.path.splitext(image_filename)[1]
2638
2639 cmdline_descriptors = []
2640 for desc in descriptors:
2641 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002642 ch_image_filename = os.path.join(image_dir,
2643 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002644 ch_image = ImageHandler(ch_image_filename)
2645 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2646 for ch_desc in ch_descriptors:
2647 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2648 cmdline_descriptors.append(ch_desc)
2649 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2650 cmdline_descriptors.append(desc)
2651
2652 kernel_cmdline_snippets = []
2653 for desc in cmdline_descriptors:
2654 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002655 if ((desc.flags &
2656 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2657 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002658 if hashtree_disabled:
2659 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002660 if (desc.flags &
2661 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002662 if not hashtree_disabled:
2663 use_cmdline = False
2664 if use_cmdline:
2665 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2666 output.write(' '.join(kernel_cmdline_snippets))
2667
David Zeuthen21e95262016-07-27 17:58:40 -04002668 def _parse_image(self, image):
2669 """Gets information about an image.
2670
2671 The image can either be a vbmeta or an image with a footer.
2672
2673 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002674 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002675
2676 Returns:
2677 A tuple where the first argument is a AvbFooter (None if there
2678 is no footer on the image), the second argument is a
2679 AvbVBMetaHeader, the third argument is a list of
2680 AvbDescriptor-derived instances, and the fourth argument is the
2681 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002682
2683 Raises:
2684 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002685 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002686 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002687 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002688 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002689 try:
2690 footer = AvbFooter(image.read(AvbFooter.SIZE))
2691 except (LookupError, struct.error):
2692 # Nope, just seek back to the start.
2693 image.seek(0)
2694
2695 vbmeta_offset = 0
2696 if footer:
2697 vbmeta_offset = footer.vbmeta_offset
2698
2699 image.seek(vbmeta_offset)
2700 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2701
2702 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2703 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2704 desc_start_offset = aux_block_offset + h.descriptors_offset
2705 image.seek(desc_start_offset)
2706 descriptors = parse_descriptors(image.read(h.descriptors_size))
2707
David Zeuthen09692692016-09-30 16:16:40 -04002708 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002709
David Zeuthenb1b994d2017-03-06 18:01:31 -05002710 def _load_vbmeta_blob(self, image):
2711 """Gets the vbmeta struct and associated sections.
2712
2713 The image can either be a vbmeta.img or an image with a footer.
2714
2715 Arguments:
2716 image: An ImageHandler (vbmeta or footer).
2717
2718 Returns:
2719 A blob with the vbmeta struct and other sections.
2720 """
2721 assert isinstance(image, ImageHandler)
2722 footer = None
2723 image.seek(image.image_size - AvbFooter.SIZE)
2724 try:
2725 footer = AvbFooter(image.read(AvbFooter.SIZE))
2726 except (LookupError, struct.error):
2727 # Nope, just seek back to the start.
2728 image.seek(0)
2729
2730 vbmeta_offset = 0
2731 if footer:
2732 vbmeta_offset = footer.vbmeta_offset
2733
2734 image.seek(vbmeta_offset)
2735 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2736
2737 image.seek(vbmeta_offset)
2738 data_size = AvbVBMetaHeader.SIZE
2739 data_size += h.authentication_data_block_size
2740 data_size += h.auxiliary_data_block_size
2741 return image.read(data_size)
2742
David Zeuthen73f2afa2017-05-17 16:54:11 -04002743 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002744 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002745
2746 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002747 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002748
2749 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002750 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2751 instructions. There is one for when hashtree is not disabled and one for
2752 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002753
David Zeuthen21e95262016-07-27 17:58:40 -04002754 """
David Zeuthen21e95262016-07-27 17:58:40 -04002755 c = 'dm="1 vroot none ro 1,'
Jan Monsch25040d92020-04-22 22:48:20 +02002756 c += '0' # start
2757 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
2758 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2759 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2760 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2761 c += ' {}'.format(ht.data_block_size) # data_block
2762 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002763 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2764 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
Jan Monsch25040d92020-04-22 22:48:20 +02002765 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2766 c += ' {}'.format(ht.root_digest.hex()) # root_digest
2767 c += ' {}'.format(ht.salt.hex()) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002768 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002769 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002770 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002771 c += ' ignore_zero_blocks'
2772 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2773 c += ' fec_roots {}'.format(ht.fec_num_roots)
2774 # Note that fec_blocks is the size that FEC covers, *not* the
2775 # size of the FEC data. Since we use FEC for everything up until
2776 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002777 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2778 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002779 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002780 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002781 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002782 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002783 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002784
David Zeuthenfd41eb92016-11-17 12:24:47 -05002785 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002786 desc = AvbKernelCmdlineDescriptor()
2787 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002788 desc.flags = (
2789 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2790
2791 # The descriptor for when hashtree verification is disabled is a lot
2792 # simpler - we just set the root to the partition.
2793 desc_no_ht = AvbKernelCmdlineDescriptor()
2794 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2795 desc_no_ht.flags = (
2796 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2797
2798 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002799
David Zeuthen73f2afa2017-05-17 16:54:11 -04002800 def _get_cmdline_descriptors_for_dm_verity(self, image):
2801 """Generate kernel cmdline descriptors for dm-verity.
2802
2803 Arguments:
2804 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2805
2806 Returns:
2807 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2808 instructions. There is one for when hashtree is not disabled and one for
2809 when it is.
2810
2811 Raises:
2812 AvbError: If |image| doesn't have a hashtree descriptor.
2813
2814 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002815 (_, _, descriptors, _) = self._parse_image(image)
2816
2817 ht = None
2818 for desc in descriptors:
2819 if isinstance(desc, AvbHashtreeDescriptor):
2820 ht = desc
2821 break
2822
2823 if not ht:
2824 raise AvbError('No hashtree descriptor in given image')
2825
2826 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2827
David Zeuthen21e95262016-07-27 17:58:40 -04002828 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002829 key_path, public_key_metadata_path, rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07002830 flags, rollback_index_location,
2831 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002832 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002833 include_descriptors_from_image,
2834 signing_helper,
2835 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002836 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002837 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002838 print_required_libavb_version,
2839 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002840 """Implements the 'make_vbmeta_image' command.
2841
2842 Arguments:
2843 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002844 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002845 algorithm_name: Name of algorithm to use.
2846 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002847 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002848 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002849 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002850 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002851 props: Properties to insert (list of strings of the form 'key:value').
2852 props_from_file: Properties to insert (list of strings 'key:<path>').
2853 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002854 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002855 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002856 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002857 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002858 release_string: None or avbtool release string to use instead of default.
2859 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002860 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002861 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002862
2863 Raises:
2864 AvbError: If a chained partition is malformed.
2865 """
David Zeuthen1097a782017-05-31 15:53:17 -04002866 # If we're asked to calculate minimum required libavb version, we're done.
Varun Sharmade538272020-04-10 15:22:31 -07002867 tmp_header = AvbVBMetaHeader()
2868 if rollback_index_location > 0:
2869 tmp_header.bump_required_libavb_version_minor(2)
2870 if include_descriptors_from_image:
2871 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2872 # version of all included descriptors.
2873 for image in include_descriptors_from_image:
2874 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2875 tmp_header.bump_required_libavb_version_minor(
2876 image_header.required_libavb_version_minor)
2877
David Zeuthen1097a782017-05-31 15:53:17 -04002878 if print_required_libavb_version:
Varun Sharmade538272020-04-10 15:22:31 -07002879 print('1.{}'.format(tmp_header.required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04002880 return
2881
2882 if not output:
2883 raise AvbError('No output file given')
2884
David Zeuthen21e95262016-07-27 17:58:40 -04002885 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002886 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002887 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002888 algorithm_name, key_path, public_key_metadata_path, descriptors,
Varun Sharmade538272020-04-10 15:22:31 -07002889 chain_partitions, rollback_index, flags, rollback_index_location,
2890 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002891 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002892 include_descriptors_from_image, signing_helper,
2893 signing_helper_with_files, release_string,
Varun Sharmade538272020-04-10 15:22:31 -07002894 append_to_release_string, tmp_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002895
2896 # Write entire vbmeta blob (header, authentication, auxiliary).
2897 output.seek(0)
2898 output.write(vbmeta_blob)
2899
David Zeuthen97cb5802017-06-01 16:14:05 -04002900 if padding_size > 0:
2901 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2902 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002903 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002904
David Zeuthen18666ab2016-11-15 11:18:05 -05002905 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2906 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002907 chain_partitions,
Varun Sharmade538272020-04-10 15:22:31 -07002908 rollback_index, flags, rollback_index_location,
2909 props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002910 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002911 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002912 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002913 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002914 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002915 release_string, append_to_release_string,
2916 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002917 """Generates a VBMeta blob.
2918
2919 This blob contains the header (struct AvbVBMetaHeader), the
2920 authentication data block (which contains the hash and signature
2921 for the header and auxiliary block), and the auxiliary block
2922 (which contains descriptors, the public key used, and other data).
2923
2924 The |key| parameter can |None| only if the |algorithm_name| is
2925 'NONE'.
2926
2927 Arguments:
2928 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2929 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002930 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002931 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002932 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002933 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002934 flags: Flags to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002935 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002936 props: Properties to insert (List of strings of the form 'key:value').
2937 props_from_file: Properties to insert (List of strings 'key:<path>').
2938 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002939 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002940 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002941 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2942 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002943 include_descriptors_from_image: List of file objects for which
2944 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002945 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002946 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002947 release_string: None or avbtool release string.
2948 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002949 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002950
2951 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02002952 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002953
2954 Raises:
2955 Exception: If the |algorithm_name| is not found, if no key has
2956 been given and the given algorithm requires one, or the key is
2957 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04002958 """
2959 try:
2960 alg = ALGORITHMS[algorithm_name]
2961 except KeyError:
2962 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2963
David Zeuthena5fd3a42017-02-27 16:38:54 -05002964 if not descriptors:
2965 descriptors = []
2966
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002967 h = AvbVBMetaHeader()
2968 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2969
David Zeuthena5fd3a42017-02-27 16:38:54 -05002970 # Insert chained partition descriptors, if any
2971 if chain_partitions:
Varun Sharmade538272020-04-10 15:22:31 -07002972 used_locations = {rollback_index_location: True}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002973 for cp in chain_partitions:
2974 cp_tokens = cp.split(':')
2975 if len(cp_tokens) != 3:
2976 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002977 partition_name = cp_tokens[0]
Varun Sharmade538272020-04-10 15:22:31 -07002978 chained_rollback_index_location = int(cp_tokens[1])
David Zeuthend8e48582017-04-21 11:31:51 -04002979 file_path = cp_tokens[2]
2980 # Check that the same rollback location isn't being used by
2981 # multiple chained partitions.
Varun Sharmade538272020-04-10 15:22:31 -07002982 if used_locations.get(chained_rollback_index_location):
David Zeuthend8e48582017-04-21 11:31:51 -04002983 raise AvbError('Rollback Index Location {} is already in use.'.format(
Varun Sharmade538272020-04-10 15:22:31 -07002984 chained_rollback_index_location))
2985 used_locations[chained_rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002986 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002987 desc.partition_name = partition_name
Varun Sharmade538272020-04-10 15:22:31 -07002988 desc.rollback_index_location = chained_rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002989 if desc.rollback_index_location < 1:
2990 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02002991 with open(file_path, 'rb') as f:
2992 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002993 descriptors.append(desc)
2994
David Zeuthen21e95262016-07-27 17:58:40 -04002995 # Descriptors.
2996 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002997 for desc in descriptors:
2998 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002999
3000 # Add properties.
3001 if props:
3002 for prop in props:
3003 idx = prop.find(':')
3004 if idx == -1:
3005 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003006 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003007 desc = AvbPropertyDescriptor()
3008 desc.key = prop[0:idx]
Jan Monschee6fccd2020-04-09 19:36:13 +02003009 desc.value = prop[(idx + 1):].encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04003010 encoded_descriptors.extend(desc.encode())
3011 if props_from_file:
3012 for prop in props_from_file:
3013 idx = prop.find(':')
3014 if idx == -1:
3015 raise AvbError('Malformed property "{}".'.format(prop))
3016 desc = AvbPropertyDescriptor()
3017 desc.key = prop[0:idx]
David Zeuthen21e95262016-07-27 17:58:40 -04003018 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02003019 with open(file_path, 'rb') as f:
Jan Monsch9c130122020-04-14 13:43:51 +02003020 # pylint: disable=attribute-defined-outside-init
Jan Monschb1d920f2020-04-09 12:59:28 +02003021 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04003022 encoded_descriptors.extend(desc.encode())
3023
David Zeuthen73f2afa2017-05-17 16:54:11 -04003024 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003025 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003026 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003027 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003028 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3029 encoded_descriptors.extend(cmdline_desc[0].encode())
3030 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003031
David Zeuthen73f2afa2017-05-17 16:54:11 -04003032 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3033 if ht_desc_to_setup:
3034 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3035 ht_desc_to_setup)
3036 encoded_descriptors.extend(cmdline_desc[0].encode())
3037 encoded_descriptors.extend(cmdline_desc[1].encode())
3038
David Zeuthen21e95262016-07-27 17:58:40 -04003039 # Add kernel command-lines.
3040 if kernel_cmdlines:
3041 for i in kernel_cmdlines:
3042 desc = AvbKernelCmdlineDescriptor()
3043 desc.kernel_cmdline = i
3044 encoded_descriptors.extend(desc.encode())
3045
3046 # Add descriptors from other images.
3047 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003048 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003049 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003050 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003051 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3052 image_handler)
3053 # Bump the required libavb version to support all included descriptors.
3054 h.bump_required_libavb_version_minor(
3055 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003056 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003057 # The --include_descriptors_from_image option is used in some setups
3058 # with images A and B where both A and B contain a descriptor
3059 # for a partition with the same name. Since it's not meaningful
3060 # to include both descriptors, only include the last seen descriptor.
3061 # See bug 76386656 for details.
3062 if hasattr(desc, 'partition_name'):
3063 key = type(desc).__name__ + '_' + desc.partition_name
3064 descriptors_dict[key] = desc.encode()
3065 else:
3066 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003067 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003068 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003069
David Zeuthen18666ab2016-11-15 11:18:05 -05003070 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003071 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05003072 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003073 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05003074 pkmd_blob = f.read()
3075
David Zeuthen21e95262016-07-27 17:58:40 -04003076 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02003077 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003078 if alg.public_key_num_bytes > 0:
3079 if not key_path:
3080 raise AvbError('Key is required for algorithm {}'.format(
3081 algorithm_name))
Jan Monsch9c130122020-04-14 13:43:51 +02003082 encoded_key = RSAPublicKey(key_path).encode()
David Zeuthen21e95262016-07-27 17:58:40 -04003083 if len(encoded_key) != alg.public_key_num_bytes:
3084 raise AvbError('Key is wrong size for algorithm {}'.format(
3085 algorithm_name))
3086
David Zeuthene3cadca2017-02-22 21:25:46 -05003087 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003088 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003089 h.release_string = release_string
3090
3091 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003092 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003093 h.release_string += ' ' + append_to_release_string
3094
David Zeuthen18666ab2016-11-15 11:18:05 -05003095 # For the Auxiliary data block, descriptors are stored at offset 0,
3096 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003097 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003098 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003099 h.descriptors_offset = 0
3100 h.descriptors_size = len(encoded_descriptors)
3101 h.public_key_offset = h.descriptors_size
3102 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003103 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3104 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003105
3106 # For the Authentication data block, the hash is first and then
3107 # the signature.
3108 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003109 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003110 h.algorithm_type = alg.algorithm_type
3111 h.hash_offset = 0
3112 h.hash_size = alg.hash_num_bytes
3113 # Signature offset and size - it's stored right after the hash
3114 # (in Authentication data block).
3115 h.signature_offset = alg.hash_num_bytes
3116 h.signature_size = alg.signature_num_bytes
3117
3118 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003119 h.flags = flags
Varun Sharmade538272020-04-10 15:22:31 -07003120 h.rollback_index_location = rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04003121
3122 # Generate Header data block.
3123 header_data_blob = h.encode()
3124
3125 # Generate Auxiliary data block.
3126 aux_data_blob = bytearray()
3127 aux_data_blob.extend(encoded_descriptors)
3128 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003129 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003130 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003131 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003132
3133 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003134 binary_hash = b''
3135 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003136 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003137 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003138 ha.update(header_data_blob)
3139 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003140 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003141
3142 # Calculate the signature.
Jan Monsch9c130122020-04-14 13:43:51 +02003143 rsa_key = RSAPublicKey(key_path)
3144 data_to_sign = header_data_blob + aux_data_blob
3145 binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
3146 signing_helper, signing_helper_with_files)
David Zeuthen21e95262016-07-27 17:58:40 -04003147
3148 # Generate Authentication data block.
3149 auth_data_blob = bytearray()
3150 auth_data_blob.extend(binary_hash)
3151 auth_data_blob.extend(binary_signature)
3152 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003153 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003154
Jan Monschb1d920f2020-04-09 12:59:28 +02003155 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003156
3157 def extract_public_key(self, key_path, output):
3158 """Implements the 'extract_public_key' command.
3159
3160 Arguments:
3161 key_path: The path to a RSA private key file.
3162 output: The file to write to.
Jan Monsch9c130122020-04-14 13:43:51 +02003163
3164 Raises:
3165 AvbError: If the public key could not be extracted.
David Zeuthen21e95262016-07-27 17:58:40 -04003166 """
Jan Monsch9c130122020-04-14 13:43:51 +02003167 output.write(RSAPublicKey(key_path).encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003168
David Zeuthenb1b994d2017-03-06 18:01:31 -05003169 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3170 partition_size):
3171 """Implementation of the append_vbmeta_image command.
3172
3173 Arguments:
3174 image_filename: File to add the footer to.
3175 vbmeta_image_filename: File to get vbmeta struct from.
3176 partition_size: Size of partition.
3177
3178 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003179 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003180 """
3181 image = ImageHandler(image_filename)
3182
3183 if partition_size % image.block_size != 0:
3184 raise AvbError('Partition size of {} is not a multiple of the image '
3185 'block size {}.'.format(partition_size,
3186 image.block_size))
3187
3188 # If there's already a footer, truncate the image to its original
3189 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003190 if image.image_size >= AvbFooter.SIZE:
3191 image.seek(image.image_size - AvbFooter.SIZE)
3192 try:
3193 footer = AvbFooter(image.read(AvbFooter.SIZE))
3194 # Existing footer found. Just truncate.
3195 original_image_size = footer.original_image_size
3196 image.truncate(footer.original_image_size)
3197 except (LookupError, struct.error):
3198 original_image_size = image.image_size
3199 else:
3200 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003201 original_image_size = image.image_size
3202
3203 # If anything goes wrong from here-on, restore the image back to
3204 # its original size.
3205 try:
3206 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3207 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3208
3209 # If the image isn't sparse, its size might not be a multiple of
3210 # the block size. This will screw up padding later so just grow it.
3211 if image.image_size % image.block_size != 0:
3212 assert not image.is_sparse
3213 padding_needed = image.block_size - (image.image_size%image.block_size)
3214 image.truncate(image.image_size + padding_needed)
3215
3216 # The append_raw() method requires content with size being a
3217 # multiple of |block_size| so add padding as needed. Also record
3218 # where this is written to since we'll need to put that in the
3219 # footer.
3220 vbmeta_offset = image.image_size
3221 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3222 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003223 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003224
3225 # Append vbmeta blob and footer
3226 image.append_raw(vbmeta_blob_with_padding)
3227 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3228
3229 # Now insert a DONT_CARE chunk with enough bytes such that the
3230 # final Footer block is at the end of partition_size..
3231 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003232 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003233
3234 # Generate the Footer that tells where the VBMeta footer
3235 # is. Also put enough padding in the front of the footer since
3236 # we'll write out an entire block.
3237 footer = AvbFooter()
3238 footer.original_image_size = original_image_size
3239 footer.vbmeta_offset = vbmeta_offset
3240 footer.vbmeta_size = len(vbmeta_blob)
3241 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003242 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003243 footer_blob)
3244 image.append_raw(footer_blob_with_padding)
3245
Jan Monschb1d920f2020-04-09 12:59:28 +02003246 except Exception as e:
3247 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003248 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003249 raise AvbError('Appending VBMeta image failed: {}.'.format(e))
David Zeuthenb1b994d2017-03-06 18:01:31 -05003250
David Zeuthena4fee8b2016-08-22 15:20:43 -04003251 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003252 hash_algorithm, salt, chain_partitions, algorithm_name,
3253 key_path,
Varun Sharmade538272020-04-10 15:22:31 -07003254 public_key_metadata_path, rollback_index, flags,
3255 rollback_index_location, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003256 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003257 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003258 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003259 signing_helper, signing_helper_with_files,
3260 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003261 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003262 print_required_libavb_version, use_persistent_digest,
3263 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003264 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003265
3266 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003267 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003268 partition_size: Size of partition.
3269 partition_name: Name of partition (without A/B suffix).
3270 hash_algorithm: Hash algorithm to use.
3271 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003272 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003273 algorithm_name: Name of algorithm to use.
3274 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003275 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003276 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003277 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003278 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003279 props: Properties to insert (List of strings of the form 'key:value').
3280 props_from_file: Properties to insert (List of strings 'key:<path>').
3281 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003282 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003283 dm-verity kernel cmdline from.
3284 include_descriptors_from_image: List of file objects for which
3285 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003286 calc_max_image_size: Don't store the footer - instead calculate the
3287 maximum image size leaving enough room for metadata with the
3288 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003289 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003290 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003291 release_string: None or avbtool release string.
3292 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003293 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3294 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003295 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003296 use_persistent_digest: Use a persistent digest on device.
3297 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003298
3299 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003300 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003301 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003302 required_libavb_version_minor = 0
3303 if use_persistent_digest or do_not_use_ab:
3304 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003305 if rollback_index_location > 0:
3306 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003307
David Zeuthen1097a782017-05-31 15:53:17 -04003308 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003309 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003310 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003311 return
3312
David Zeuthenbf562452017-05-17 18:04:43 -04003313 # First, calculate the maximum image size such that an image
3314 # this size + metadata (footer + vbmeta struct) fits in
3315 # |partition_size|.
3316 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003317 if partition_size < max_metadata_size:
3318 raise AvbError('Parition size of {} is too small. '
3319 'Needs to be at least {}'.format(
3320 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003321 max_image_size = partition_size - max_metadata_size
3322
3323 # If we're asked to only calculate the maximum image size, we're done.
3324 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003325 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003326 return
3327
David Zeuthena4fee8b2016-08-22 15:20:43 -04003328 image = ImageHandler(image_filename)
3329
3330 if partition_size % image.block_size != 0:
3331 raise AvbError('Partition size of {} is not a multiple of the image '
3332 'block size {}.'.format(partition_size,
3333 image.block_size))
3334
David Zeuthen21e95262016-07-27 17:58:40 -04003335 # If there's already a footer, truncate the image to its original
3336 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3337 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003338 if image.image_size >= AvbFooter.SIZE:
3339 image.seek(image.image_size - AvbFooter.SIZE)
3340 try:
3341 footer = AvbFooter(image.read(AvbFooter.SIZE))
3342 # Existing footer found. Just truncate.
3343 original_image_size = footer.original_image_size
3344 image.truncate(footer.original_image_size)
3345 except (LookupError, struct.error):
3346 original_image_size = image.image_size
3347 else:
3348 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003349 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003350
3351 # If anything goes wrong from here-on, restore the image back to
3352 # its original size.
3353 try:
David Zeuthen09692692016-09-30 16:16:40 -04003354 # If image size exceeds the maximum image size, fail.
3355 if image.image_size > max_image_size:
3356 raise AvbError('Image size of {} exceeds maximum image '
3357 'size of {} in order to fit in a partition '
3358 'size of {}.'.format(image.image_size, max_image_size,
3359 partition_size))
3360
Jan Monsch6f27bb12020-04-07 07:33:26 +02003361 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003362 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003363 salt = binascii.unhexlify(salt)
3364 elif salt is None and not use_persistent_digest:
3365 # If salt is not explicitly specified, choose a hash that's the same
3366 # size as the hash size. Don't populate a random salt if this
3367 # descriptor is being created to use a persistent digest on device.
3368 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003369 with open('/dev/urandom', 'rb') as f:
3370 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003371 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003372 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003373
Jan Monsch6f27bb12020-04-07 07:33:26 +02003374 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003375 # TODO(zeuthen): might want to read this in chunks to avoid
3376 # memory pressure, then again, this is only supposed to be used
3377 # on kernel/initramfs partitions. Possible optimization.
3378 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003379 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003380 digest = hasher.digest()
3381
3382 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003383 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003384 h_desc.hash_algorithm = hash_algorithm
3385 h_desc.partition_name = partition_name
3386 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003387 h_desc.flags = 0
3388 if do_not_use_ab:
3389 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3390 if not use_persistent_digest:
3391 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003392
3393 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003394 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003395 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003396 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003397 chain_partitions, rollback_index, flags, rollback_index_location,
3398 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003399 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003400 include_descriptors_from_image, signing_helper,
3401 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003402 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003403
David Zeuthend247fcb2017-02-16 12:09:27 -05003404 # Write vbmeta blob, if requested.
3405 if output_vbmeta_image:
3406 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003407
David Zeuthend247fcb2017-02-16 12:09:27 -05003408 # Append vbmeta blob and footer, unless requested not to.
3409 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003410 # If the image isn't sparse, its size might not be a multiple of
3411 # the block size. This will screw up padding later so just grow it.
3412 if image.image_size % image.block_size != 0:
3413 assert not image.is_sparse
3414 padding_needed = image.block_size - (
3415 image.image_size % image.block_size)
3416 image.truncate(image.image_size + padding_needed)
3417
3418 # The append_raw() method requires content with size being a
3419 # multiple of |block_size| so add padding as needed. Also record
3420 # where this is written to since we'll need to put that in the
3421 # footer.
3422 vbmeta_offset = image.image_size
3423 padding_needed = (
3424 round_to_multiple(len(vbmeta_blob), image.block_size) -
3425 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003426 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003427
David Zeuthend247fcb2017-02-16 12:09:27 -05003428 image.append_raw(vbmeta_blob_with_padding)
3429 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3430
3431 # Now insert a DONT_CARE chunk with enough bytes such that the
3432 # final Footer block is at the end of partition_size..
3433 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003434 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003435
3436 # Generate the Footer that tells where the VBMeta footer
3437 # is. Also put enough padding in the front of the footer since
3438 # we'll write out an entire block.
3439 footer = AvbFooter()
3440 footer.original_image_size = original_image_size
3441 footer.vbmeta_offset = vbmeta_offset
3442 footer.vbmeta_size = len(vbmeta_blob)
3443 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003444 footer_blob_with_padding = (
3445 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003446 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003447 except Exception as e:
3448 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003449 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003450 raise AvbError('Adding hash_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003451
David Zeuthena4fee8b2016-08-22 15:20:43 -04003452 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003453 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003454 block_size, salt, chain_partitions, algorithm_name,
3455 key_path,
3456 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003457 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003458 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003459 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003460 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003461 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003462 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003463 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003464 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003465 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003466 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003467 use_persistent_root_digest, do_not_use_ab,
3468 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003469 """Implements the 'add_hashtree_footer' command.
3470
3471 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3472 more information about dm-verity and these hashes.
3473
3474 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003475 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003476 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003477 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003478 generate_fec: If True, generate FEC codes.
3479 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003480 hash_algorithm: Hash algorithm to use.
3481 block_size: Block size to use.
3482 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003483 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003484 algorithm_name: Name of algorithm to use.
3485 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003486 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003487 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003488 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003489 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003490 props: Properties to insert (List of strings of the form 'key:value').
3491 props_from_file: Properties to insert (List of strings 'key:<path>').
3492 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003493 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003494 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003495 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3496 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003497 include_descriptors_from_image: List of file objects for which
3498 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003499 calc_max_image_size: Don't store the hashtree or footer - instead
3500 calculate the maximum image size leaving enough room for hashtree
3501 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003502 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003503 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003504 release_string: None or avbtool release string.
3505 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003506 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3507 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003508 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003509 use_persistent_root_digest: Use a persistent root digest on device.
3510 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003511 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003512
3513 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003514 AvbError: If an argument is incorrect or adding the hashtree footer
3515 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003516 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003517 required_libavb_version_minor = 0
3518 if use_persistent_root_digest or do_not_use_ab:
3519 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003520 if rollback_index_location > 0:
3521 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003522
David Zeuthen1097a782017-05-31 15:53:17 -04003523 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003524 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003525 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003526 return
3527
Jan Monsch6f27bb12020-04-07 07:33:26 +02003528 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen09692692016-09-30 16:16:40 -04003529 digest_padding = round_to_pow2(digest_size) - digest_size
3530
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003531 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3532 # size such that an image this size + the hashtree + metadata (footer +
3533 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3534 # for metadata.
3535 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003536 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003537 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003538 if not no_hashtree:
3539 (_, max_tree_size) = calc_hash_level_offsets(
3540 partition_size, block_size, digest_size + digest_padding)
3541 if generate_fec:
3542 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003543 max_metadata_size = (max_fec_size + max_tree_size +
3544 self.MAX_VBMETA_SIZE +
3545 self.MAX_FOOTER_SIZE)
3546 max_image_size = partition_size - max_metadata_size
3547 else:
3548 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003549
3550 # If we're asked to only calculate the maximum image size, we're done.
3551 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003552 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003553 return
3554
David Zeuthena4fee8b2016-08-22 15:20:43 -04003555 image = ImageHandler(image_filename)
3556
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003557 if partition_size > 0:
3558 if partition_size % image.block_size != 0:
3559 raise AvbError('Partition size of {} is not a multiple of the image '
3560 'block size {}.'.format(partition_size,
3561 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003562 elif image.image_size % image.block_size != 0:
3563 raise AvbError('File size of {} is not a multiple of the image '
3564 'block size {}.'.format(image.image_size,
3565 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003566
David Zeuthen21e95262016-07-27 17:58:40 -04003567 # If there's already a footer, truncate the image to its original
3568 # size. This way 'avbtool add_hashtree_footer' is idempotent
3569 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003570 if image.image_size >= AvbFooter.SIZE:
3571 image.seek(image.image_size - AvbFooter.SIZE)
3572 try:
3573 footer = AvbFooter(image.read(AvbFooter.SIZE))
3574 # Existing footer found. Just truncate.
3575 original_image_size = footer.original_image_size
3576 image.truncate(footer.original_image_size)
3577 except (LookupError, struct.error):
3578 original_image_size = image.image_size
3579 else:
3580 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003581 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003582
3583 # If anything goes wrong from here-on, restore the image back to
3584 # its original size.
3585 try:
3586 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003587 rounded_image_size = round_to_multiple(image.image_size, block_size)
3588 if rounded_image_size > image.image_size:
3589 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003590
David Zeuthen09692692016-09-30 16:16:40 -04003591 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003592 if partition_size > 0:
3593 if image.image_size > max_image_size:
3594 raise AvbError('Image size of {} exceeds maximum image '
3595 'size of {} in order to fit in a partition '
3596 'size of {}.'.format(image.image_size, max_image_size,
3597 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003598
3599 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003600 salt = binascii.unhexlify(salt)
3601 elif salt is None and not use_persistent_root_digest:
3602 # If salt is not explicitly specified, choose a hash that's the same
3603 # size as the hash size. Don't populate a random salt if this
3604 # descriptor is being created to use a persistent digest on device.
3605 hash_size = digest_size
Jan Monsch13efb5f2020-04-22 17:34:24 +02003606 with open('/dev/urandom', 'rb') as f:
Jan Monschb1d920f2020-04-09 12:59:28 +02003607 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003608 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003609 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003610
David Zeuthena4fee8b2016-08-22 15:20:43 -04003611 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003612 # offsets in advance.
3613 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003614 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003615
David Zeuthena4fee8b2016-08-22 15:20:43 -04003616 # If the image isn't sparse, its size might not be a multiple of
3617 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003618 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003619 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003620 padding_needed = image.block_size - (image.image_size%image.block_size)
3621 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003622
David Zeuthena4fee8b2016-08-22 15:20:43 -04003623 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003624 tree_offset = image.image_size
3625 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003626 block_size,
3627 hash_algorithm, salt,
3628 digest_padding,
3629 hash_level_offsets,
3630 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003631
3632 # Generate HashtreeDescriptor with details about the tree we
3633 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003634 if no_hashtree:
3635 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003636 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003637 ht_desc = AvbHashtreeDescriptor()
3638 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003639 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003640 ht_desc.tree_offset = tree_offset
3641 ht_desc.tree_size = tree_size
3642 ht_desc.data_block_size = block_size
3643 ht_desc.hash_block_size = block_size
3644 ht_desc.hash_algorithm = hash_algorithm
3645 ht_desc.partition_name = partition_name
3646 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003647 if do_not_use_ab:
3648 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3649 if not use_persistent_root_digest:
3650 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003651
David Zeuthen09692692016-09-30 16:16:40 -04003652 # Write the hash tree
3653 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3654 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003655 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003656 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003657 len_hashtree_and_fec = len(hash_tree_with_padding)
3658
3659 # Generate FEC codes, if requested.
3660 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003661 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003662 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003663 else:
3664 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003665 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3666 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003667 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003668 fec_offset = image.image_size
3669 image.append_raw(fec_data_with_padding)
3670 len_hashtree_and_fec += len(fec_data_with_padding)
3671 # Update the hashtree descriptor.
3672 ht_desc.fec_num_roots = fec_num_roots
3673 ht_desc.fec_offset = fec_offset
3674 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003675
David Zeuthen73f2afa2017-05-17 16:54:11 -04003676 ht_desc_to_setup = None
3677 if setup_as_rootfs_from_kernel:
3678 ht_desc_to_setup = ht_desc
3679
David Zeuthena4fee8b2016-08-22 15:20:43 -04003680 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003681 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003682 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003683 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003684 chain_partitions, rollback_index, flags, rollback_index_location,
3685 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003686 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003687 include_descriptors_from_image, signing_helper,
3688 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003689 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003690 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3691 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003692 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003693
David Zeuthend247fcb2017-02-16 12:09:27 -05003694 # Write vbmeta blob, if requested.
3695 if output_vbmeta_image:
3696 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003697
David Zeuthend247fcb2017-02-16 12:09:27 -05003698 # Append vbmeta blob and footer, unless requested not to.
3699 if not do_not_append_vbmeta_image:
3700 image.append_raw(vbmeta_blob_with_padding)
3701
3702 # Now insert a DONT_CARE chunk with enough bytes such that the
3703 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003704 if partition_size > 0:
3705 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003706 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003707
3708 # Generate the Footer that tells where the VBMeta footer
3709 # is. Also put enough padding in the front of the footer since
3710 # we'll write out an entire block.
3711 footer = AvbFooter()
3712 footer.original_image_size = original_image_size
3713 footer.vbmeta_offset = vbmeta_offset
3714 footer.vbmeta_size = len(vbmeta_blob)
3715 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003716 footer_blob_with_padding = (
3717 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003718 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003719
Jan Monschb1d920f2020-04-09 12:59:28 +02003720 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003721 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003722 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003723 raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003724
David Zeuthenc68f0822017-03-31 17:22:35 -04003725 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003726 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003727 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003728 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003729 """Implements the 'make_atx_certificate' command.
3730
3731 Android Things certificates are required for Android Things public key
3732 metadata. They chain the vbmeta signing key for a particular product back to
3733 a fused, permanent root key. These certificates are fixed-length and fixed-
3734 format with the explicit goal of not parsing ASN.1 in bootloader code.
3735
3736 Arguments:
3737 output: Certificate will be written to this file on success.
3738 authority_key_path: A PEM file path with the authority private key.
3739 If None, then a certificate will be created without a
3740 signature. The signature can be created out-of-band
3741 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003742 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003743 subject_key_version: A 64-bit version value. If this is None, the number
3744 of seconds since the epoch is used.
3745 subject: A subject identifier. For Product Signing Key certificates this
3746 should be the same Product ID found in the permanent attributes.
3747 is_intermediate_authority: True if the certificate is for an intermediate
3748 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003749 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003750 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003751 signing_helper_with_files: Same as signing_helper but uses files instead.
Jan Monsch9c130122020-04-14 13:43:51 +02003752
3753 Raises:
3754 AvbError: If there an error during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -08003755 """
3756 signed_data = bytearray()
3757 signed_data.extend(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003758 signed_data.extend(RSAPublicKey(subject_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003759 hasher = hashlib.sha256()
3760 hasher.update(subject)
3761 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003762 if not usage:
3763 usage = 'com.google.android.things.vboot'
3764 if is_intermediate_authority:
3765 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003766 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003767 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003768 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003769 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003770 subject_key_version = int(time.time())
3771 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003772 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003773 if authority_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003774 rsa_key = RSAPublicKey(authority_key_path)
Darren Krahn43e12d82017-02-24 16:26:31 -08003775 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003776 signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3777 signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08003778 output.write(signed_data)
3779 output.write(signature)
3780
David Zeuthenc68f0822017-03-31 17:22:35 -04003781 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003782 product_id):
3783 """Implements the 'make_atx_permanent_attributes' command.
3784
3785 Android Things permanent attributes are designed to be permanent for a
3786 particular product and a hash of these attributes should be fused into
3787 hardware to enforce this.
3788
3789 Arguments:
3790 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003791 root_authority_key_path: Path to a PEM or DER public key for
3792 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003793 product_id: A 16-byte Product ID.
3794
3795 Raises:
3796 AvbError: If an argument is incorrect.
3797 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003798 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003799 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003800 raise AvbError('Invalid Product ID length.')
3801 output.write(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003802 output.write(RSAPublicKey(root_authority_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003803 output.write(product_id)
3804
3805 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003806 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003807 """Implements the 'make_atx_metadata' command.
3808
3809 Android Things metadata are included in vbmeta images to facilitate
3810 verification. The output of this command can be used as the
3811 public_key_metadata argument to other commands.
3812
3813 Arguments:
3814 output: Metadata will be written to this file on success.
3815 intermediate_key_certificate: A certificate file as output by
3816 make_atx_certificate with
3817 is_intermediate_authority set to true.
3818 product_key_certificate: A certificate file as output by
3819 make_atx_certificate with
3820 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003821
3822 Raises:
3823 AvbError: If an argument is incorrect.
3824 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003825 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003826 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003827 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003828 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003829 raise AvbError('Invalid product key certificate length.')
3830 output.write(struct.pack('<I', 1)) # Format Version
3831 output.write(intermediate_key_certificate)
3832 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003833
Darren Krahnfccd64e2018-01-16 17:39:35 -08003834 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3835 unlock_key_certificate, challenge_path,
3836 unlock_key_path, signing_helper,
3837 signing_helper_with_files):
3838 """Implements the 'make_atx_unlock_credential' command.
3839
3840 Android Things unlock credentials can be used to authorize the unlock of AVB
3841 on a device. These credentials are presented to an Android Things bootloader
3842 via the fastboot interface in response to a 16-byte challenge. This method
3843 creates all fields of the credential except the challenge signature field
3844 (which is the last field) and can optionally create the challenge signature
3845 field as well if a challenge and the unlock_key_path is provided.
3846
3847 Arguments:
3848 output: The credential will be written to this file on success.
3849 intermediate_key_certificate: A certificate file as output by
3850 make_atx_certificate with
3851 is_intermediate_authority set to true.
3852 unlock_key_certificate: A certificate file as output by
3853 make_atx_certificate with
3854 is_intermediate_authority set to false and the
3855 usage set to
3856 'com.google.android.things.vboot.unlock'.
3857 challenge_path: [optional] A path to the challenge to sign.
3858 unlock_key_path: [optional] A PEM file path with the unlock private key.
3859 signing_helper: Program which signs a hash and returns the signature.
3860 signing_helper_with_files: Same as signing_helper but uses files instead.
3861
3862 Raises:
Jan Monsch9c130122020-04-14 13:43:51 +02003863 AvbError: If an argument is incorrect or an error occurs during signing.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003864 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003865 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3866 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003867 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3868 raise AvbError('Invalid intermediate key certificate length.')
3869 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3870 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003871 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003872 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003873 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003874 challenge = f.read()
3875 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3876 raise AvbError('Invalid unlock challenge length.')
3877 output.write(struct.pack('<I', 1)) # Format Version
3878 output.write(intermediate_key_certificate)
3879 output.write(unlock_key_certificate)
3880 if challenge_path and unlock_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003881 rsa_key = RSAPublicKey(unlock_key_path)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003882 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003883 signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3884 signing_helper_with_files)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003885 output.write(signature)
3886
David Zeuthen21e95262016-07-27 17:58:40 -04003887
3888def calc_hash_level_offsets(image_size, block_size, digest_size):
3889 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3890
3891 Arguments:
3892 image_size: The size of the image to calculate a Merkle-tree for.
3893 block_size: The block size, e.g. 4096.
3894 digest_size: The size of each hash, e.g. 32 for SHA-256.
3895
3896 Returns:
3897 A tuple where the first argument is an array of offsets and the
3898 second is size of the tree, in bytes.
3899 """
3900 level_offsets = []
3901 level_sizes = []
3902 tree_size = 0
3903
3904 num_levels = 0
3905 size = image_size
3906 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003907 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003908 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3909
3910 level_sizes.append(level_size)
3911 tree_size += level_size
3912 num_levels += 1
3913
3914 size = level_size
3915
3916 for n in range(0, num_levels):
3917 offset = 0
3918 for m in range(n + 1, num_levels):
3919 offset += level_sizes[m]
3920 level_offsets.append(offset)
3921
David Zeuthena4fee8b2016-08-22 15:20:43 -04003922 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003923
3924
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003925# See system/extras/libfec/include/fec/io.h for these definitions.
3926FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3927FEC_MAGIC = 0xfecfecfe
3928
3929
3930def calc_fec_data_size(image_size, num_roots):
3931 """Calculates how much space FEC data will take.
3932
Jan Monschfe00c0a2019-12-11 11:19:40 +01003933 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003934 image_size: The size of the image.
3935 num_roots: Number of roots.
3936
3937 Returns:
3938 The number of bytes needed for FEC for an image of the given size
3939 and with the requested number of FEC roots.
3940
3941 Raises:
3942 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003943 """
3944 p = subprocess.Popen(
3945 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3946 stdout=subprocess.PIPE,
3947 stderr=subprocess.PIPE)
3948 (pout, perr) = p.communicate()
3949 retcode = p.wait()
3950 if retcode != 0:
3951 raise ValueError('Error invoking fec: {}'.format(perr))
3952 return int(pout)
3953
3954
3955def generate_fec_data(image_filename, num_roots):
3956 """Generate FEC codes for an image.
3957
Jan Monschfe00c0a2019-12-11 11:19:40 +01003958 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003959 image_filename: The filename of the image.
3960 num_roots: Number of roots.
3961
3962 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003963 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003964
3965 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003966 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003967 """
Jan Monschb1d920f2020-04-09 12:59:28 +02003968 with tempfile.NamedTemporaryFile() as fec_tmpfile:
3969 try:
3970 subprocess.check_call(
3971 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3972 fec_tmpfile.name],
3973 stderr=open(os.devnull, 'wb'))
3974 except subprocess.CalledProcessError as e:
3975 raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
3976 fec_data = fec_tmpfile.read()
3977
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003978 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3979 footer_data = fec_data[-footer_size:]
3980 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3981 footer_data)
3982 if magic != FEC_MAGIC:
3983 raise ValueError('Unexpected magic in FEC footer')
3984 return fec_data[0:fec_size]
3985
3986
David Zeuthen21e95262016-07-27 17:58:40 -04003987def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003988 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003989 """Generates a Merkle-tree for a file.
3990
Jan Monschfe00c0a2019-12-11 11:19:40 +01003991 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003992 image: The image, as a file.
3993 image_size: The size of the image.
3994 block_size: The block size, e.g. 4096.
3995 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3996 salt: The salt to use.
3997 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003998 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003999 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004000
4001 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004002 A tuple where the first element is the top-level hash as bytes and the
4003 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004004 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004005 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004006 hash_src_offset = 0
4007 hash_src_size = image_size
4008 level_num = 0
4009 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08004010 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04004011 remaining = hash_src_size
4012 while remaining > 0:
Jan Monsch6f27bb12020-04-07 07:33:26 +02004013 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004014 # Only read from the file for the first level - for subsequent
4015 # levels, access the array we're building.
4016 if level_num == 0:
4017 image.seek(hash_src_offset + hash_src_size - remaining)
4018 data = image.read(min(remaining, block_size))
4019 else:
4020 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4021 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004022 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004023
4024 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004025 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02004026 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08004027 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04004028 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02004029 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08004030
Jan Monschb1d920f2020-04-09 12:59:28 +02004031 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04004032
4033 padding_needed = (round_to_multiple(
4034 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02004035 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004036
David Zeuthena4fee8b2016-08-22 15:20:43 -04004037 # Copy level-output into resulting tree.
4038 offset = hash_level_offsets[level_num]
4039 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004040
David Zeuthena4fee8b2016-08-22 15:20:43 -04004041 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004042 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004043 level_num += 1
4044
Jan Monsch6f27bb12020-04-07 07:33:26 +02004045 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04004046 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02004047 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04004048
4049
4050class AvbTool(object):
4051 """Object for avbtool command-line tool."""
4052
4053 def __init__(self):
4054 """Initializer method."""
4055 self.avb = Avb()
4056
4057 def _add_common_args(self, sub_parser):
4058 """Adds arguments used by several sub-commands.
4059
4060 Arguments:
4061 sub_parser: The parser to add arguments to.
4062 """
4063 sub_parser.add_argument('--algorithm',
4064 help='Algorithm to use (default: NONE)',
4065 metavar='ALGORITHM',
4066 default='NONE')
4067 sub_parser.add_argument('--key',
4068 help='Path to RSA private key file',
4069 metavar='KEY',
4070 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004071 sub_parser.add_argument('--signing_helper',
4072 help='Path to helper used for signing',
4073 metavar='APP',
4074 default=None,
4075 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004076 sub_parser.add_argument('--signing_helper_with_files',
4077 help='Path to helper used for signing using files',
4078 metavar='APP',
4079 default=None,
4080 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004081 sub_parser.add_argument('--public_key_metadata',
4082 help='Path to public key metadata file',
4083 metavar='KEY_METADATA',
4084 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004085 sub_parser.add_argument('--rollback_index',
4086 help='Rollback Index',
4087 type=parse_number,
4088 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004089 sub_parser.add_argument('--rollback_index_location',
4090 help='Location of main vbmeta Rollback Index',
4091 type=parse_number,
4092 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004093 # This is used internally for unit tests. Do not include in --help output.
4094 sub_parser.add_argument('--internal_release_string',
4095 help=argparse.SUPPRESS)
4096 sub_parser.add_argument('--append_to_release_string',
4097 help='Text to append to release string',
4098 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004099 sub_parser.add_argument('--prop',
4100 help='Add property',
4101 metavar='KEY:VALUE',
4102 action='append')
4103 sub_parser.add_argument('--prop_from_file',
4104 help='Add property from file',
4105 metavar='KEY:PATH',
4106 action='append')
4107 sub_parser.add_argument('--kernel_cmdline',
4108 help='Add kernel cmdline',
4109 metavar='CMDLINE',
4110 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004111 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4112 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4113 # at some future point.
4114 sub_parser.add_argument('--setup_rootfs_from_kernel',
4115 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004116 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004117 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004118 type=argparse.FileType('rb'))
4119 sub_parser.add_argument('--include_descriptors_from_image',
4120 help='Include descriptors from image',
4121 metavar='IMAGE',
4122 action='append',
4123 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004124 sub_parser.add_argument('--print_required_libavb_version',
4125 help=('Don\'t store the footer - '
4126 'instead calculate the required libavb '
4127 'version for the given options.'),
4128 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004129 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4130 sub_parser.add_argument('--chain_partition',
4131 help='Allow signed integrity-data for partition',
4132 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4133 action='append')
4134 sub_parser.add_argument('--flags',
4135 help='VBMeta flags',
4136 type=parse_number,
4137 default=0)
4138 sub_parser.add_argument('--set_hashtree_disabled_flag',
4139 help='Set the HASHTREE_DISABLED flag',
4140 action='store_true')
4141
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004142 def _add_common_footer_args(self, sub_parser):
4143 """Adds arguments used by add_*_footer sub-commands.
4144
4145 Arguments:
4146 sub_parser: The parser to add arguments to.
4147 """
4148 sub_parser.add_argument('--use_persistent_digest',
4149 help='Use a persistent digest on device instead of '
4150 'storing the digest in the descriptor. This '
4151 'cannot be used with A/B so must be combined '
4152 'with --do_not_use_ab when an A/B suffix is '
4153 'expected at runtime.',
4154 action='store_true')
4155 sub_parser.add_argument('--do_not_use_ab',
4156 help='The partition does not use A/B even when an '
4157 'A/B suffix is present. This must not be used '
4158 'for vbmeta or chained partitions.',
4159 action='store_true')
4160
David Zeuthena5fd3a42017-02-27 16:38:54 -05004161 def _fixup_common_args(self, args):
4162 """Common fixups needed by subcommands.
4163
4164 Arguments:
4165 args: Arguments to modify.
4166
4167 Returns:
4168 The modified arguments.
4169 """
4170 if args.set_hashtree_disabled_flag:
4171 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4172 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004173
4174 def run(self, argv):
4175 """Command-line processor.
4176
4177 Arguments:
4178 argv: Pass sys.argv from main.
4179 """
4180 parser = argparse.ArgumentParser()
4181 subparsers = parser.add_subparsers(title='subcommands')
4182
Jan Monsch2c7be992020-04-03 14:37:13 +02004183 sub_parser = subparsers.add_parser(
4184 'generate_test_image',
4185 help=('Generates a test image with a known pattern for testing: '
4186 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4187 sub_parser.add_argument('--image_size',
4188 help='Size of image to generate.',
4189 type=parse_number,
4190 required=True)
4191 sub_parser.add_argument('--start_byte',
4192 help='Integer for the start byte of the pattern.',
4193 type=parse_number,
4194 default=0)
4195 sub_parser.add_argument('--output',
4196 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004197 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004198 default=sys.stdout)
4199 sub_parser.set_defaults(func=self.generate_test_image)
4200
David Zeuthen21e95262016-07-27 17:58:40 -04004201 sub_parser = subparsers.add_parser('version',
4202 help='Prints version of avbtool.')
4203 sub_parser.set_defaults(func=self.version)
4204
4205 sub_parser = subparsers.add_parser('extract_public_key',
4206 help='Extract public key.')
4207 sub_parser.add_argument('--key',
4208 help='Path to RSA private key file',
4209 required=True)
4210 sub_parser.add_argument('--output',
4211 help='Output file name',
4212 type=argparse.FileType('wb'),
4213 required=True)
4214 sub_parser.set_defaults(func=self.extract_public_key)
4215
4216 sub_parser = subparsers.add_parser('make_vbmeta_image',
4217 help='Makes a vbmeta image.')
4218 sub_parser.add_argument('--output',
4219 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004220 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004221 sub_parser.add_argument('--padding_size',
4222 metavar='NUMBER',
4223 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004224 'its size is a multiple of NUMBER '
4225 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004226 type=parse_number,
4227 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004228 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004229 sub_parser.set_defaults(func=self.make_vbmeta_image)
4230
4231 sub_parser = subparsers.add_parser('add_hash_footer',
4232 help='Add hashes and footer to image.')
4233 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004234 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004235 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004236 sub_parser.add_argument('--partition_size',
4237 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004238 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004239 sub_parser.add_argument('--partition_name',
4240 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004241 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004242 sub_parser.add_argument('--hash_algorithm',
4243 help='Hash algorithm to use (default: sha256)',
4244 default='sha256')
4245 sub_parser.add_argument('--salt',
4246 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004247 sub_parser.add_argument('--calc_max_image_size',
4248 help=('Don\'t store the footer - '
4249 'instead calculate the maximum image size '
4250 'leaving enough room for metadata with '
4251 'the given partition size.'),
4252 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004253 sub_parser.add_argument('--output_vbmeta_image',
4254 help='Also write vbmeta struct to file',
4255 type=argparse.FileType('wb'))
4256 sub_parser.add_argument('--do_not_append_vbmeta_image',
4257 help=('Do not append vbmeta struct or footer '
4258 'to the image'),
4259 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004260 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004261 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004262 sub_parser.set_defaults(func=self.add_hash_footer)
4263
David Zeuthenb1b994d2017-03-06 18:01:31 -05004264 sub_parser = subparsers.add_parser('append_vbmeta_image',
4265 help='Append vbmeta image to image.')
4266 sub_parser.add_argument('--image',
4267 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004268 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004269 sub_parser.add_argument('--partition_size',
4270 help='Partition size',
4271 type=parse_number,
4272 required=True)
4273 sub_parser.add_argument('--vbmeta_image',
4274 help='Image with vbmeta blob to append',
4275 type=argparse.FileType('rb'))
4276 sub_parser.set_defaults(func=self.append_vbmeta_image)
4277
Jan Monscheeb28b62019-12-05 16:17:09 +01004278 sub_parser = subparsers.add_parser(
4279 'add_hashtree_footer',
4280 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004281 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004282 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004283 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004284 sub_parser.add_argument('--partition_size',
4285 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004286 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004287 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004288 sub_parser.add_argument('--partition_name',
4289 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004290 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004291 sub_parser.add_argument('--hash_algorithm',
4292 help='Hash algorithm to use (default: sha1)',
4293 default='sha1')
4294 sub_parser.add_argument('--salt',
4295 help='Salt in hex (default: /dev/urandom)')
4296 sub_parser.add_argument('--block_size',
4297 help='Block size (default: 4096)',
4298 type=parse_number,
4299 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004300 # TODO(zeuthen): The --generate_fec option was removed when we
4301 # moved to generating FEC by default. To avoid breaking existing
4302 # users needing to transition we simply just print a warning below
4303 # in add_hashtree_footer(). Remove this option and the warning at
4304 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004305 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004306 help=argparse.SUPPRESS,
4307 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004308 sub_parser.add_argument(
4309 '--do_not_generate_fec',
4310 help='Do not generate forward-error-correction codes',
4311 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004312 sub_parser.add_argument('--fec_num_roots',
4313 help='Number of roots for FEC (default: 2)',
4314 type=parse_number,
4315 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004316 sub_parser.add_argument('--calc_max_image_size',
4317 help=('Don\'t store the hashtree or footer - '
4318 'instead calculate the maximum image size '
4319 'leaving enough room for hashtree '
4320 'and metadata with the given partition '
4321 'size.'),
4322 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004323 sub_parser.add_argument('--output_vbmeta_image',
4324 help='Also write vbmeta struct to file',
4325 type=argparse.FileType('wb'))
4326 sub_parser.add_argument('--do_not_append_vbmeta_image',
4327 help=('Do not append vbmeta struct or footer '
4328 'to the image'),
4329 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004330 # This is different from --setup_rootfs_from_kernel insofar that
4331 # it doesn't take an IMAGE, the generated cmdline will be for the
4332 # hashtree we're adding.
4333 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4334 action='store_true',
4335 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004336 sub_parser.add_argument('--no_hashtree',
4337 action='store_true',
4338 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004339 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004340 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004341 sub_parser.set_defaults(func=self.add_hashtree_footer)
4342
4343 sub_parser = subparsers.add_parser('erase_footer',
4344 help='Erase footer from an image.')
4345 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004346 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004347 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004348 required=True)
4349 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004350 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004351 action='store_true')
4352 sub_parser.set_defaults(func=self.erase_footer)
4353
David Zeuthen1394f762019-04-30 10:20:11 -04004354 sub_parser = subparsers.add_parser('zero_hashtree',
4355 help='Zero out hashtree and FEC data.')
4356 sub_parser.add_argument('--image',
4357 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004358 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004359 required=True)
4360 sub_parser.set_defaults(func=self.zero_hashtree)
4361
Jan Monscheeb28b62019-12-05 16:17:09 +01004362 sub_parser = subparsers.add_parser(
4363 'extract_vbmeta_image',
4364 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004365 sub_parser.add_argument('--image',
4366 help='Image with footer',
4367 type=argparse.FileType('rb'),
4368 required=True)
4369 sub_parser.add_argument('--output',
4370 help='Output file name',
4371 type=argparse.FileType('wb'))
4372 sub_parser.add_argument('--padding_size',
4373 metavar='NUMBER',
4374 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004375 'its size is a multiple of NUMBER '
4376 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004377 type=parse_number,
4378 default=0)
4379 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4380
David Zeuthen2bc232b2017-04-19 14:25:19 -04004381 sub_parser = subparsers.add_parser('resize_image',
4382 help='Resize image with a footer.')
4383 sub_parser.add_argument('--image',
4384 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004385 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004386 required=True)
4387 sub_parser.add_argument('--partition_size',
4388 help='New partition size',
4389 type=parse_number)
4390 sub_parser.set_defaults(func=self.resize_image)
4391
David Zeuthen21e95262016-07-27 17:58:40 -04004392 sub_parser = subparsers.add_parser(
4393 'info_image',
4394 help='Show information about vbmeta or footer.')
4395 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004396 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004397 type=argparse.FileType('rb'),
4398 required=True)
4399 sub_parser.add_argument('--output',
4400 help='Write info to file',
4401 type=argparse.FileType('wt'),
4402 default=sys.stdout)
4403 sub_parser.set_defaults(func=self.info_image)
4404
David Zeuthenb623d8b2017-04-04 16:05:53 -04004405 sub_parser = subparsers.add_parser(
4406 'verify_image',
4407 help='Verify an image.')
4408 sub_parser.add_argument('--image',
4409 help='Image to verify',
4410 type=argparse.FileType('rb'),
4411 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004412 sub_parser.add_argument('--key',
4413 help='Check embedded public key matches KEY',
4414 metavar='KEY',
4415 required=False)
4416 sub_parser.add_argument('--expected_chain_partition',
4417 help='Expected chain partition',
4418 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4419 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004420 sub_parser.add_argument(
4421 '--follow_chain_partitions',
4422 help=('Follows chain partitions even when not '
4423 'specified with the --expected_chain_partition option'),
4424 action='store_true')
4425 sub_parser.add_argument(
4426 '--accept_zeroed_hashtree',
4427 help=('Accept images where the hashtree or FEC data is zeroed out'),
4428 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004429 sub_parser.set_defaults(func=self.verify_image)
4430
David Zeuthenb8643c02018-05-17 17:21:18 -04004431 sub_parser = subparsers.add_parser(
David Zeuthen34b6b492020-04-13 14:45:02 -04004432 'print_partition_digests',
4433 help='Prints partition digests.')
4434 sub_parser.add_argument('--image',
4435 help='Image to print partition digests from',
4436 type=argparse.FileType('rb'),
4437 required=True)
4438 sub_parser.add_argument('--output',
4439 help='Write info to file',
4440 type=argparse.FileType('wt'),
4441 default=sys.stdout)
4442 sub_parser.add_argument('--json',
4443 help=('Print output as JSON'),
4444 action='store_true')
4445 sub_parser.set_defaults(func=self.print_partition_digests)
4446
4447 sub_parser = subparsers.add_parser(
David Zeuthenb8643c02018-05-17 17:21:18 -04004448 'calculate_vbmeta_digest',
4449 help='Calculate vbmeta digest.')
4450 sub_parser.add_argument('--image',
4451 help='Image to calculate digest for',
4452 type=argparse.FileType('rb'),
4453 required=True)
4454 sub_parser.add_argument('--hash_algorithm',
4455 help='Hash algorithm to use (default: sha256)',
4456 default='sha256')
4457 sub_parser.add_argument('--output',
4458 help='Write hex digest to file (default: stdout)',
4459 type=argparse.FileType('wt'),
4460 default=sys.stdout)
4461 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4462
David Zeuthenf7d2e752018-09-20 13:30:41 -04004463 sub_parser = subparsers.add_parser(
4464 'calculate_kernel_cmdline',
4465 help='Calculate kernel cmdline.')
4466 sub_parser.add_argument('--image',
4467 help='Image to calculate kernel cmdline for',
4468 type=argparse.FileType('rb'),
4469 required=True)
4470 sub_parser.add_argument('--hashtree_disabled',
4471 help='Return the cmdline for hashtree disabled',
4472 action='store_true')
4473 sub_parser.add_argument('--output',
4474 help='Write cmdline to file (default: stdout)',
4475 type=argparse.FileType('wt'),
4476 default=sys.stdout)
4477 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4478
David Zeuthen8b6973b2016-09-20 12:39:49 -04004479 sub_parser = subparsers.add_parser('set_ab_metadata',
4480 help='Set A/B metadata.')
4481 sub_parser.add_argument('--misc_image',
4482 help=('The misc image to modify. If the image does '
4483 'not exist, it will be created.'),
4484 type=argparse.FileType('r+b'),
4485 required=True)
4486 sub_parser.add_argument('--slot_data',
4487 help=('Slot data of the form "priority", '
4488 '"tries_remaining", "sucessful_boot" for '
4489 'slot A followed by the same for slot B, '
4490 'separated by colons. The default value '
4491 'is 15:7:0:14:7:0.'),
4492 default='15:7:0:14:7:0')
4493 sub_parser.set_defaults(func=self.set_ab_metadata)
4494
Darren Krahn147b08d2016-12-20 16:38:29 -08004495 sub_parser = subparsers.add_parser(
4496 'make_atx_certificate',
4497 help='Create an Android Things eXtension (ATX) certificate.')
4498 sub_parser.add_argument('--output',
4499 help='Write certificate to file',
4500 type=argparse.FileType('wb'),
4501 default=sys.stdout)
4502 sub_parser.add_argument('--subject',
4503 help=('Path to subject file'),
4504 type=argparse.FileType('rb'),
4505 required=True)
4506 sub_parser.add_argument('--subject_key',
4507 help=('Path to subject RSA public key file'),
4508 type=argparse.FileType('rb'),
4509 required=True)
4510 sub_parser.add_argument('--subject_key_version',
4511 help=('Version of the subject key'),
4512 type=parse_number,
4513 required=False)
4514 sub_parser.add_argument('--subject_is_intermediate_authority',
4515 help=('Generate an intermediate authority '
4516 'certificate'),
4517 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004518 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004519 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004520 'string'),
4521 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004522 sub_parser.add_argument('--authority_key',
4523 help='Path to authority RSA private key file',
4524 required=False)
4525 sub_parser.add_argument('--signing_helper',
4526 help='Path to helper used for signing',
4527 metavar='APP',
4528 default=None,
4529 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004530 sub_parser.add_argument('--signing_helper_with_files',
4531 help='Path to helper used for signing using files',
4532 metavar='APP',
4533 default=None,
4534 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004535 sub_parser.set_defaults(func=self.make_atx_certificate)
4536
4537 sub_parser = subparsers.add_parser(
4538 'make_atx_permanent_attributes',
4539 help='Create Android Things eXtension (ATX) permanent attributes.')
4540 sub_parser.add_argument('--output',
4541 help='Write attributes to file',
4542 type=argparse.FileType('wb'),
4543 default=sys.stdout)
4544 sub_parser.add_argument('--root_authority_key',
4545 help='Path to authority RSA public key file',
4546 type=argparse.FileType('rb'),
4547 required=True)
4548 sub_parser.add_argument('--product_id',
4549 help=('Path to Product ID file'),
4550 type=argparse.FileType('rb'),
4551 required=True)
4552 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4553
4554 sub_parser = subparsers.add_parser(
4555 'make_atx_metadata',
4556 help='Create Android Things eXtension (ATX) metadata.')
4557 sub_parser.add_argument('--output',
4558 help='Write metadata to file',
4559 type=argparse.FileType('wb'),
4560 default=sys.stdout)
4561 sub_parser.add_argument('--intermediate_key_certificate',
4562 help='Path to intermediate key certificate file',
4563 type=argparse.FileType('rb'),
4564 required=True)
4565 sub_parser.add_argument('--product_key_certificate',
4566 help='Path to product key certificate file',
4567 type=argparse.FileType('rb'),
4568 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004569 sub_parser.set_defaults(func=self.make_atx_metadata)
4570
Darren Krahnfccd64e2018-01-16 17:39:35 -08004571 sub_parser = subparsers.add_parser(
4572 'make_atx_unlock_credential',
4573 help='Create an Android Things eXtension (ATX) unlock credential.')
4574 sub_parser.add_argument('--output',
4575 help='Write credential to file',
4576 type=argparse.FileType('wb'),
4577 default=sys.stdout)
4578 sub_parser.add_argument('--intermediate_key_certificate',
4579 help='Path to intermediate key certificate file',
4580 type=argparse.FileType('rb'),
4581 required=True)
4582 sub_parser.add_argument('--unlock_key_certificate',
4583 help='Path to unlock key certificate file',
4584 type=argparse.FileType('rb'),
4585 required=True)
4586 sub_parser.add_argument('--challenge',
4587 help='Path to the challenge to sign (optional). If '
4588 'this is not provided the challenge signature '
4589 'field is omitted and can be concatenated '
4590 'later.',
4591 required=False)
4592 sub_parser.add_argument('--unlock_key',
4593 help='Path to unlock key (optional). Must be '
4594 'provided if using --challenge.',
4595 required=False)
4596 sub_parser.add_argument('--signing_helper',
4597 help='Path to helper used for signing',
4598 metavar='APP',
4599 default=None,
4600 required=False)
4601 sub_parser.add_argument('--signing_helper_with_files',
4602 help='Path to helper used for signing using files',
4603 metavar='APP',
4604 default=None,
4605 required=False)
4606 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4607
David Zeuthen21e95262016-07-27 17:58:40 -04004608 args = parser.parse_args(argv[1:])
4609 try:
4610 args.func(args)
Jan Monschcc9939a2020-04-16 09:15:20 +02004611 except AttributeError:
4612 # This error gets raised when the command line tool is called without any
4613 # arguments. It mimics the original Python 2 behavior.
4614 parser.print_usage()
4615 print('avbtool: error: too few arguments')
4616 sys.exit(2)
David Zeuthen21e95262016-07-27 17:58:40 -04004617 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004618 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004619 sys.exit(1)
4620
4621 def version(self, _):
4622 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004623 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004624
Jan Monsch2c7be992020-04-03 14:37:13 +02004625 def generate_test_image(self, args):
4626 """Implements the 'generate_test_image' sub-command."""
4627 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4628
David Zeuthen21e95262016-07-27 17:58:40 -04004629 def extract_public_key(self, args):
4630 """Implements the 'extract_public_key' sub-command."""
4631 self.avb.extract_public_key(args.key, args.output)
4632
4633 def make_vbmeta_image(self, args):
4634 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004635 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004636 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004637 args.algorithm, args.key,
4638 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004639 args.flags, args.rollback_index_location,
4640 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004641 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004642 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004643 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004644 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004645 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004646 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004647 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004648 args.print_required_libavb_version,
4649 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004650
David Zeuthenb1b994d2017-03-06 18:01:31 -05004651 def append_vbmeta_image(self, args):
4652 """Implements the 'append_vbmeta_image' sub-command."""
4653 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4654 args.partition_size)
4655
David Zeuthen21e95262016-07-27 17:58:40 -04004656 def add_hash_footer(self, args):
4657 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004658 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004659 self.avb.add_hash_footer(args.image.name if args.image else None,
4660 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004661 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004662 args.salt, args.chain_partition, args.algorithm,
4663 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004664 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004665 args.flags, args.rollback_index_location,
4666 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004667 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004668 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004669 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004670 args.calc_max_image_size,
4671 args.signing_helper,
4672 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004673 args.internal_release_string,
4674 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004675 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004676 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004677 args.print_required_libavb_version,
4678 args.use_persistent_digest,
4679 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004680
4681 def add_hashtree_footer(self, args):
4682 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004683 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004684 # TODO(zeuthen): Remove when removing support for the
4685 # '--generate_fec' option above.
4686 if args.generate_fec:
4687 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4688 'is now generated by default. Use the option '
4689 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004690 self.avb.add_hashtree_footer(
4691 args.image.name if args.image else None,
4692 args.partition_size,
4693 args.partition_name,
4694 not args.do_not_generate_fec, args.fec_num_roots,
4695 args.hash_algorithm, args.block_size,
4696 args.salt, args.chain_partition, args.algorithm,
4697 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004698 args.rollback_index, args.flags,
4699 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004700 args.prop_from_file,
4701 args.kernel_cmdline,
4702 args.setup_rootfs_from_kernel,
4703 args.setup_as_rootfs_from_kernel,
4704 args.include_descriptors_from_image,
4705 args.calc_max_image_size,
4706 args.signing_helper,
4707 args.signing_helper_with_files,
4708 args.internal_release_string,
4709 args.append_to_release_string,
4710 args.output_vbmeta_image,
4711 args.do_not_append_vbmeta_image,
4712 args.print_required_libavb_version,
4713 args.use_persistent_digest,
4714 args.do_not_use_ab,
4715 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004716
David Zeuthen21e95262016-07-27 17:58:40 -04004717 def erase_footer(self, args):
4718 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004719 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004720
David Zeuthen1394f762019-04-30 10:20:11 -04004721 def zero_hashtree(self, args):
4722 """Implements the 'zero_hashtree' sub-command."""
4723 self.avb.zero_hashtree(args.image.name)
4724
David Zeuthen49936b42018-08-07 17:38:58 -04004725 def extract_vbmeta_image(self, args):
4726 """Implements the 'extract_vbmeta_image' sub-command."""
4727 self.avb.extract_vbmeta_image(args.output, args.image.name,
4728 args.padding_size)
4729
David Zeuthen2bc232b2017-04-19 14:25:19 -04004730 def resize_image(self, args):
4731 """Implements the 'resize_image' sub-command."""
4732 self.avb.resize_image(args.image.name, args.partition_size)
4733
David Zeuthen8b6973b2016-09-20 12:39:49 -04004734 def set_ab_metadata(self, args):
4735 """Implements the 'set_ab_metadata' sub-command."""
4736 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4737
David Zeuthen21e95262016-07-27 17:58:40 -04004738 def info_image(self, args):
4739 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004740 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004741
David Zeuthenb623d8b2017-04-04 16:05:53 -04004742 def verify_image(self, args):
4743 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004744 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004745 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004746 args.follow_chain_partitions,
4747 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004748
David Zeuthen34b6b492020-04-13 14:45:02 -04004749 def print_partition_digests(self, args):
4750 """Implements the 'print_partition_digests' sub-command."""
4751 self.avb.print_partition_digests(args.image.name, args.output, args.json)
4752
David Zeuthenb8643c02018-05-17 17:21:18 -04004753 def calculate_vbmeta_digest(self, args):
4754 """Implements the 'calculate_vbmeta_digest' sub-command."""
4755 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4756 args.output)
4757
David Zeuthenf7d2e752018-09-20 13:30:41 -04004758 def calculate_kernel_cmdline(self, args):
4759 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004760 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4761 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004762
Darren Krahn147b08d2016-12-20 16:38:29 -08004763 def make_atx_certificate(self, args):
4764 """Implements the 'make_atx_certificate' sub-command."""
4765 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004766 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004767 args.subject_key_version,
4768 args.subject.read(),
4769 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004770 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004771 args.signing_helper,
4772 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004773
4774 def make_atx_permanent_attributes(self, args):
4775 """Implements the 'make_atx_permanent_attributes' sub-command."""
4776 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004777 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004778 args.product_id.read())
4779
4780 def make_atx_metadata(self, args):
4781 """Implements the 'make_atx_metadata' sub-command."""
4782 self.avb.make_atx_metadata(args.output,
4783 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004784 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004785
Darren Krahnfccd64e2018-01-16 17:39:35 -08004786 def make_atx_unlock_credential(self, args):
4787 """Implements the 'make_atx_unlock_credential' sub-command."""
4788 self.avb.make_atx_unlock_credential(
4789 args.output,
4790 args.intermediate_key_certificate.read(),
4791 args.unlock_key_certificate.read(),
4792 args.challenge,
4793 args.unlock_key,
4794 args.signing_helper,
4795 args.signing_helper_with_files)
4796
David Zeuthen21e95262016-07-27 17:58:40 -04004797
4798if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004799 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004800 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4801 log.write(' '.join(sys.argv))
4802 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004803
David Zeuthen21e95262016-07-27 17:58:40 -04004804 tool = AvbTool()
4805 tool.run(sys.argv)