blob: 85f8ca1bd04bf6beb25ca7525d0cdda3ddc24f6d [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthen34b6b492020-04-13 14:45:02 -040033import json
David Zeuthenc68f0822017-03-31 17:22:35 -040034import math
David Zeuthen21e95262016-07-27 17:58:40 -040035import os
36import struct
37import subprocess
38import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040039import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080040import time
David Zeuthen21e95262016-07-27 17:58:40 -040041
David Zeuthene3cadca2017-02-22 21:25:46 -050042# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040043AVB_VERSION_MAJOR = 1
Varun Sharmade538272020-04-10 15:22:31 -070044AVB_VERSION_MINOR = 2
David Zeuthene3cadca2017-02-22 21:25:46 -050045AVB_VERSION_SUB = 0
46
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080047# Keep in sync with libavb/avb_footer.h.
48AVB_FOOTER_VERSION_MAJOR = 1
49AVB_FOOTER_VERSION_MINOR = 0
50
David Zeuthen58305522017-01-11 17:42:47 -050051AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040052
Jan Monsch2c7be992020-04-03 14:37:13 +020053# Configuration for enabling logging of calls to avbtool.
54AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
55
David Zeuthene3cadca2017-02-22 21:25:46 -050056
David Zeuthen21e95262016-07-27 17:58:40 -040057class AvbError(Exception):
58 """Application-specific errors.
59
60 These errors represent issues for which a stack-trace should not be
61 presented.
62
63 Attributes:
64 message: Error message.
65 """
66
67 def __init__(self, message):
68 Exception.__init__(self, message)
69
70
71class Algorithm(object):
72 """Contains details about an algorithm.
73
Tao Bao80418a52018-07-20 11:41:22 -070074 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040075
76 The constant |ALGORITHMS| is a dictionary from human-readable
77 names (e.g 'SHA256_RSA2048') to instances of this class.
78
79 Attributes:
80 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040081 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040082 hash_num_bytes: Number of bytes used to store the hash.
83 signature_num_bytes: Number of bytes used to store the signature.
84 public_key_num_bytes: Number of bytes used to store the public key.
Jan Monsch9c130122020-04-14 13:43:51 +020085 padding: Padding used for signature as bytes, if any.
David Zeuthen21e95262016-07-27 17:58:40 -040086 """
87
David Zeuthenb623d8b2017-04-04 16:05:53 -040088 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
89 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040090 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040091 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040092 self.hash_num_bytes = hash_num_bytes
93 self.signature_num_bytes = signature_num_bytes
94 self.public_key_num_bytes = public_key_num_bytes
95 self.padding = padding
96
David Zeuthenb623d8b2017-04-04 16:05:53 -040097
David Zeuthen21e95262016-07-27 17:58:40 -040098# This must be kept in sync with the avb_crypto.h file.
99#
100# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
101# obtained from section 5.2.2 of RFC 4880.
102ALGORITHMS = {
103 'NONE': Algorithm(
104 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400105 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400106 hash_num_bytes=0,
107 signature_num_bytes=0,
108 public_key_num_bytes=0,
Jan Monsch9c130122020-04-14 13:43:51 +0200109 padding=b''),
David Zeuthen21e95262016-07-27 17:58:40 -0400110 'SHA256_RSA2048': Algorithm(
111 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400112 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400113 hash_num_bytes=32,
114 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100115 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200116 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400117 # PKCS1-v1_5 padding
118 0x00, 0x01] + [0xff]*202 + [0x00] + [
119 # ASN.1 header
120 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
121 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
122 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200123 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400124 'SHA256_RSA4096': Algorithm(
125 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400126 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400127 hash_num_bytes=32,
128 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100129 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200130 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400131 # PKCS1-v1_5 padding
132 0x00, 0x01] + [0xff]*458 + [0x00] + [
133 # ASN.1 header
134 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
135 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
136 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200137 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400138 'SHA256_RSA8192': Algorithm(
139 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400140 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400141 hash_num_bytes=32,
142 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100143 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200144 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400145 # PKCS1-v1_5 padding
146 0x00, 0x01] + [0xff]*970 + [0x00] + [
147 # ASN.1 header
148 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
149 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
150 0x00, 0x04, 0x20,
Jan Monsch9c130122020-04-14 13:43:51 +0200151 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400152 'SHA512_RSA2048': Algorithm(
153 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400154 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400155 hash_num_bytes=64,
156 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100157 public_key_num_bytes=8 + 2*2048//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200158 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400159 # PKCS1-v1_5 padding
160 0x00, 0x01] + [0xff]*170 + [0x00] + [
161 # ASN.1 header
162 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
163 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
164 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200165 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400166 'SHA512_RSA4096': Algorithm(
167 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400168 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400169 hash_num_bytes=64,
170 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100171 public_key_num_bytes=8 + 2*4096//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200172 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400173 # PKCS1-v1_5 padding
174 0x00, 0x01] + [0xff]*426 + [0x00] + [
175 # ASN.1 header
176 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
177 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
178 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200179 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400180 'SHA512_RSA8192': Algorithm(
181 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400182 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400183 hash_num_bytes=64,
184 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100185 public_key_num_bytes=8 + 2*8192//8,
Jan Monsch9c130122020-04-14 13:43:51 +0200186 padding=bytes(bytearray([
David Zeuthen21e95262016-07-27 17:58:40 -0400187 # PKCS1-v1_5 padding
188 0x00, 0x01] + [0xff]*938 + [0x00] + [
189 # ASN.1 header
190 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
191 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
192 0x00, 0x04, 0x40
Jan Monsch9c130122020-04-14 13:43:51 +0200193 ]))),
David Zeuthen21e95262016-07-27 17:58:40 -0400194}
195
196
David Zeuthene3cadca2017-02-22 21:25:46 -0500197def get_release_string():
198 """Calculates the release string to use in the VBMeta struct."""
199 # Keep in sync with libavb/avb_version.c:avb_version_string().
200 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
201 AVB_VERSION_MINOR,
202 AVB_VERSION_SUB)
203
204
David Zeuthen21e95262016-07-27 17:58:40 -0400205def round_to_multiple(number, size):
206 """Rounds a number up to nearest multiple of another number.
207
Jan Monschfe00c0a2019-12-11 11:19:40 +0100208 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400209 number: The number to round up.
210 size: The multiple to round up to.
211
212 Returns:
213 If |number| is a multiple of |size|, returns |number|, otherwise
214 returns |number| + |size|.
215 """
216 remainder = number % size
217 if remainder == 0:
218 return number
219 return number + size - remainder
220
221
222def round_to_pow2(number):
223 """Rounds a number up to the next power of 2.
224
Jan Monschfe00c0a2019-12-11 11:19:40 +0100225 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400226 number: The number to round up.
227
228 Returns:
229 If |number| is already a power of 2 then |number| is
230 returned. Otherwise the smallest power of 2 greater than |number|
231 is returned.
232 """
233 return 2**((number - 1).bit_length())
234
235
David Zeuthen21e95262016-07-27 17:58:40 -0400236def encode_long(num_bits, value):
237 """Encodes a long to a bytearray() using a given amount of bits.
238
239 This number is written big-endian, e.g. with the most significant
240 bit first.
241
David Zeuthenb623d8b2017-04-04 16:05:53 -0400242 This is the reverse of decode_long().
243
David Zeuthen21e95262016-07-27 17:58:40 -0400244 Arguments:
245 num_bits: The number of bits to write, e.g. 2048.
246 value: The value to write.
247
248 Returns:
249 A bytearray() with the encoded long.
250 """
251 ret = bytearray()
252 for bit_pos in range(num_bits, 0, -8):
253 octet = (value >> (bit_pos - 8)) & 0xff
254 ret.extend(struct.pack('!B', octet))
255 return ret
256
257
David Zeuthenb623d8b2017-04-04 16:05:53 -0400258def decode_long(blob):
259 """Decodes a long from a bytearray() using a given amount of bits.
260
261 This number is expected to be in big-endian, e.g. with the most
262 significant bit first.
263
264 This is the reverse of encode_long().
265
266 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100267 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400268
269 Returns:
270 The decoded value.
271 """
272 ret = 0
273 for b in bytearray(blob):
274 ret *= 256
275 ret += b
276 return ret
277
278
David Zeuthen21e95262016-07-27 17:58:40 -0400279def egcd(a, b):
280 """Calculate greatest common divisor of two numbers.
281
282 This implementation uses a recursive version of the extended
283 Euclidian algorithm.
284
285 Arguments:
286 a: First number.
287 b: Second number.
288
289 Returns:
290 A tuple (gcd, x, y) that where |gcd| is the greatest common
291 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
292 """
293 if a == 0:
294 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100295 g, y, x = egcd(b % a, a)
296 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400297
298
299def modinv(a, m):
300 """Calculate modular multiplicative inverse of |a| modulo |m|.
301
302 This calculates the number |x| such that |a| * |x| == 1 (modulo
303 |m|). This number only exists if |a| and |m| are co-prime - |None|
304 is returned if this isn't true.
305
306 Arguments:
307 a: The number to calculate a modular inverse of.
308 m: The modulo to use.
309
310 Returns:
311 The modular multiplicative inverse of |a| and |m| or |None| if
312 these numbers are not co-prime.
313 """
314 gcd, x, _ = egcd(a, m)
315 if gcd != 1:
316 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100317 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400318
319
320def parse_number(string):
321 """Parse a string as a number.
322
323 This is just a short-hand for int(string, 0) suitable for use in the
324 |type| parameter of |ArgumentParser|'s add_argument() function. An
325 improvement to just using type=int is that this function supports
326 numbers in other bases, e.g. "0x1234".
327
328 Arguments:
329 string: The string to parse.
330
331 Returns:
332 The parsed integer.
333
334 Raises:
335 ValueError: If the number could not be parsed.
336 """
337 return int(string, 0)
338
339
David Zeuthenc68f0822017-03-31 17:22:35 -0400340class RSAPublicKey(object):
341 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400342
David Zeuthenc68f0822017-03-31 17:22:35 -0400343 Attributes:
344 exponent: The key exponent.
345 modulus: The key modulus.
346 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400347 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400348
Jan Monsch38865f22020-04-08 09:32:58 +0200349 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400350
351 def __init__(self, key_path):
352 """Loads and parses an RSA key from either a private or public key file.
353
354 Arguments:
355 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100356
357 Raises:
358 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400359 """
360 # We used to have something as simple as this:
361 #
362 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
363 # self.exponent = key.e
364 # self.modulus = key.n
365 # self.num_bits = key.size() + 1
366 #
367 # but unfortunately PyCrypto is not available in the builder. So
368 # instead just parse openssl(1) output to get this
369 # information. It's ugly but...
370 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
371 p = subprocess.Popen(args,
372 stdin=subprocess.PIPE,
373 stdout=subprocess.PIPE,
374 stderr=subprocess.PIPE)
375 (pout, perr) = p.communicate()
376 if p.wait() != 0:
377 # Could be just a public key is passed, try that.
378 args.append('-pubin')
379 p = subprocess.Popen(args,
380 stdin=subprocess.PIPE,
381 stdout=subprocess.PIPE,
382 stderr=subprocess.PIPE)
383 (pout, perr) = p.communicate()
384 if p.wait() != 0:
385 raise AvbError('Error getting public key: {}'.format(perr))
386
387 if not pout.lower().startswith(self.MODULUS_PREFIX):
388 raise AvbError('Unexpected modulus output')
389
390 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
391
392 # The exponent is assumed to always be 65537 and the number of
393 # bits can be derived from the modulus by rounding up to the
394 # nearest power of 2.
Jan Monsch9c130122020-04-14 13:43:51 +0200395 self.key_path = key_path
David Zeuthenc68f0822017-03-31 17:22:35 -0400396 self.modulus = int(modulus_hexstr, 16)
397 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
398 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400399
Jan Monsch9c130122020-04-14 13:43:51 +0200400 def encode(self):
401 """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
David Zeuthen21e95262016-07-27 17:58:40 -0400402
Jan Monsch9c130122020-04-14 13:43:51 +0200403 This creates a |AvbRSAPublicKeyHeader| as well as the two large
404 numbers (|key_num_bits| bits long) following it.
David Zeuthen21e95262016-07-27 17:58:40 -0400405
Jan Monsch9c130122020-04-14 13:43:51 +0200406 Returns:
407 The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -0400408
Jan Monsch9c130122020-04-14 13:43:51 +0200409 Raises:
410 AvbError: If given RSA key exponent is not 65537.
411 """
412 if self.exponent != 65537:
413 raise AvbError('Only RSA keys with exponent 65537 are supported.')
414 ret = bytearray()
415 # Calculate n0inv = -1/n[0] (mod 2^32)
416 b = 2 ** 32
417 n0inv = b - modinv(self.modulus, b)
418 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
419 r = 2 ** self.modulus.bit_length()
420 rrmodn = r * r % self.modulus
421 ret.extend(struct.pack('!II', self.num_bits, n0inv))
422 ret.extend(encode_long(self.num_bits, self.modulus))
423 ret.extend(encode_long(self.num_bits, rrmodn))
424 return bytes(ret)
David Zeuthen21e95262016-07-27 17:58:40 -0400425
Jan Monsch9c130122020-04-14 13:43:51 +0200426 def sign(self, algorithm_name, data_to_sign, signing_helper=None,
427 signing_helper_with_files=None):
428 """Sign given data using |signing_helper| or openssl.
Jan Monsch77cd2022019-12-10 17:18:04 +0100429
Jan Monsch9c130122020-04-14 13:43:51 +0200430 openssl is used if neither the parameters signing_helper nor
431 signing_helper_with_files are given.
432
433 Arguments:
434 algorithm_name: The algorithm name as per the ALGORITHMS dict.
435 data_to_sign: Data to sign as bytes or bytearray.
436 signing_helper: Program which signs a hash and returns the signature.
437 signing_helper_with_files: Same as signing_helper but uses files instead.
438
439 Returns:
440 The signature as bytes.
441
442 Raises:
443 AvbError: If an error occurred during signing.
444 """
445 # Checks requested algorithm for validity.
446 algorithm = ALGORITHMS.get(algorithm_name)
447 if not algorithm:
448 raise AvbError('Algorithm with name {} is not supported.'
449 .format(algorithm_name))
450
451 if self.num_bits != (algorithm.signature_num_bytes * 8):
452 raise AvbError('Key size of key ({} bits) does not match key size '
453 '({} bits) of given algorithm {}.'
454 .format(self.num_bits, algorithm.signature_num_bytes * 8,
455 algorithm_name))
456
457 # Hashes the data.
458 hasher = hashlib.new(algorithm.hash_name)
459 hasher.update(data_to_sign)
460 digest = hasher.digest()
461
462 # Calculates the signature.
463 padding_and_hash = algorithm.padding + digest
464 p = None
465 if signing_helper_with_files is not None:
466 with tempfile.NamedTemporaryFile() as signing_file:
467 signing_file.write(padding_and_hash)
468 signing_file.flush()
469 p = subprocess.Popen([signing_helper_with_files, algorithm_name,
470 self.key_path, signing_file.name])
471 retcode = p.wait()
472 if retcode != 0:
473 raise AvbError('Error signing')
474 signing_file.seek(0)
475 signature = signing_file.read()
476 else:
477 if signing_helper is not None:
478 p = subprocess.Popen(
479 [signing_helper, algorithm_name, self.key_path],
480 stdin=subprocess.PIPE,
481 stdout=subprocess.PIPE,
482 stderr=subprocess.PIPE)
483 else:
484 p = subprocess.Popen(
485 ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
486 stdin=subprocess.PIPE,
487 stdout=subprocess.PIPE,
488 stderr=subprocess.PIPE)
489 (pout, perr) = p.communicate(padding_and_hash)
490 retcode = p.wait()
491 if retcode != 0:
492 raise AvbError('Error signing: {}'.format(perr))
493 signature = pout
494 if len(signature) != algorithm.signature_num_bytes:
495 raise AvbError('Error signing: Invalid length of signature')
496 return signature
David Zeuthen21e95262016-07-27 17:58:40 -0400497
498
499def lookup_algorithm_by_type(alg_type):
500 """Looks up algorithm by type.
501
502 Arguments:
503 alg_type: The integer representing the type.
504
505 Returns:
506 A tuple with the algorithm name and an |Algorithm| instance.
507
508 Raises:
509 Exception: If the algorithm cannot be found
510 """
511 for alg_name in ALGORITHMS:
512 alg_data = ALGORITHMS[alg_name]
513 if alg_data.algorithm_type == alg_type:
514 return (alg_name, alg_data)
515 raise AvbError('Unknown algorithm type {}'.format(alg_type))
516
Jan Monsch77cd2022019-12-10 17:18:04 +0100517
Dan Austina7bc4962019-12-02 13:26:08 -0800518def lookup_hash_size_by_type(alg_type):
519 """Looks up hash size by type.
520
521 Arguments:
522 alg_type: The integer representing the type.
523
524 Returns:
525 The corresponding hash size.
526
527 Raises:
528 AvbError: If the algorithm cannot be found.
529 """
530 for alg_name in ALGORITHMS:
531 alg_data = ALGORITHMS[alg_name]
532 if alg_data.algorithm_type == alg_type:
533 return alg_data.hash_num_bytes
534 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400535
Jan Monsch77cd2022019-12-10 17:18:04 +0100536
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100538 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400539
540 Arguments:
541 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200542 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
543 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400544
545 Returns:
546 True if the signature is valid and corresponds to the embedded
547 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100548
549 Raises:
550 AvbError: If there errors calling out to openssl command during
551 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400552 """
553 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100554 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400555 return True
556 header_blob = vbmeta_blob[0:256]
557 auth_offset = 256
558 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
559 aux_size = vbmeta_header.auxiliary_data_block_size
560 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
561 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
562 pubkey_size = vbmeta_header.public_key_size
563 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
564
565 digest_offset = auth_offset + vbmeta_header.hash_offset
566 digest_size = vbmeta_header.hash_size
567 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
568
569 sig_offset = auth_offset + vbmeta_header.signature_offset
570 sig_size = vbmeta_header.signature_size
571 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
572
573 # Now that we've got the stored digest, public key, and signature
574 # all we need to do is to verify. This is the exactly the same
575 # steps as performed in the avb_vbmeta_image_verify() function in
576 # libavb/avb_vbmeta_image.c.
577
578 ha = hashlib.new(alg.hash_name)
579 ha.update(header_blob)
580 ha.update(aux_blob)
581 computed_digest = ha.digest()
582
583 if computed_digest != digest_blob:
584 return False
585
Jan Monsch9c130122020-04-14 13:43:51 +0200586 padding_and_digest = alg.padding + computed_digest
David Zeuthenb623d8b2017-04-04 16:05:53 -0400587
588 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100589 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400590 modulus = decode_long(modulus_blob)
591 exponent = 65537
592
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500593 # We used to have this:
594 #
595 # import Crypto.PublicKey.RSA
596 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
597 # if not key.verify(decode_long(padding_and_digest),
598 # (decode_long(sig_blob), None)):
599 # return False
600 # return True
601 #
602 # but since 'avbtool verify_image' is used on the builders we don't want
603 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
604 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
605 '\n'
606 '[pubkeyinfo]\n'
607 'algorithm=SEQUENCE:rsa_alg\n'
608 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
609 '\n'
610 '[rsa_alg]\n'
611 'algorithm=OID:rsaEncryption\n'
612 'parameter=NULL\n'
613 '\n'
614 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200615 'n=INTEGER:{}\n'
616 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
617 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500618
Jan Monsch38865f22020-04-08 09:32:58 +0200619 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
620 asn1_tmpfile.write(asn1_str.encode('ascii'))
621 asn1_tmpfile.flush()
622
623 with tempfile.NamedTemporaryFile() as der_tmpfile:
624 p = subprocess.Popen(
625 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
626 der_tmpfile.name, '-noout'])
627 retcode = p.wait()
628 if retcode != 0:
629 raise AvbError('Error generating DER file')
630
631 p = subprocess.Popen(
632 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
633 '-keyform', 'DER', '-raw'],
634 stdin=subprocess.PIPE,
635 stdout=subprocess.PIPE,
636 stderr=subprocess.PIPE)
637 (pout, perr) = p.communicate(sig_blob)
638 retcode = p.wait()
639 if retcode != 0:
640 raise AvbError('Error verifying data: {}'.format(perr))
641 if pout != padding_and_digest:
642 sys.stderr.write('Signature not correct\n')
643 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400644 return True
645
646
David Zeuthena4fee8b2016-08-22 15:20:43 -0400647class ImageChunk(object):
648 """Data structure used for representing chunks in Android sparse files.
649
650 Attributes:
651 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
652 chunk_offset: Offset in the sparse file where this chunk begins.
653 output_offset: Offset in de-sparsified file where output begins.
654 output_size: Number of bytes in output.
655 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
656 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
657 """
658
659 FORMAT = '<2H2I'
660 TYPE_RAW = 0xcac1
661 TYPE_FILL = 0xcac2
662 TYPE_DONT_CARE = 0xcac3
663 TYPE_CRC32 = 0xcac4
664
665 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
666 input_offset, fill_data):
667 """Initializes an ImageChunk object.
668
669 Arguments:
670 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
671 chunk_offset: Offset in the sparse file where this chunk begins.
672 output_offset: Offset in de-sparsified file.
673 output_size: Number of bytes in output.
674 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200675 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400676
677 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200678 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400679 """
680 self.chunk_type = chunk_type
681 self.chunk_offset = chunk_offset
682 self.output_offset = output_offset
683 self.output_size = output_size
684 self.input_offset = input_offset
685 self.fill_data = fill_data
686 # Check invariants.
687 if self.chunk_type == self.TYPE_RAW:
688 if self.fill_data is not None:
689 raise ValueError('RAW chunk cannot have fill_data set.')
690 if not self.input_offset:
691 raise ValueError('RAW chunk must have input_offset set.')
692 elif self.chunk_type == self.TYPE_FILL:
693 if self.fill_data is None:
694 raise ValueError('FILL chunk must have fill_data set.')
695 if self.input_offset:
696 raise ValueError('FILL chunk cannot have input_offset set.')
697 elif self.chunk_type == self.TYPE_DONT_CARE:
698 if self.fill_data is not None:
699 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
700 if self.input_offset:
701 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
702 else:
703 raise ValueError('Invalid chunk type')
704
705
706class ImageHandler(object):
707 """Abstraction for image I/O with support for Android sparse images.
708
709 This class provides an interface for working with image files that
710 may be using the Android Sparse Image format. When an instance is
711 constructed, we test whether it's an Android sparse file. If so,
712 operations will be on the sparse file by interpreting the sparse
713 format, otherwise they will be directly on the file. Either way the
714 operations do the same.
715
716 For reading, this interface mimics a file object - it has seek(),
717 tell(), and read() methods. For writing, only truncation
718 (truncate()) and appending is supported (append_raw() and
719 append_dont_care()). Additionally, data can only be written in units
720 of the block size.
721
722 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400723 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400724 is_sparse: Whether the file being operated on is sparse.
725 block_size: The block size, typically 4096.
726 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400727 """
728 # See system/core/libsparse/sparse_format.h for details.
729 MAGIC = 0xed26ff3a
730 HEADER_FORMAT = '<I4H4I'
731
732 # These are formats and offset of just the |total_chunks| and
733 # |total_blocks| fields.
734 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
735 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
736
737 def __init__(self, image_filename):
738 """Initializes an image handler.
739
740 Arguments:
741 image_filename: The name of the file to operate on.
742
743 Raises:
744 ValueError: If data in the file is invalid.
745 """
David Zeuthen49936b42018-08-07 17:38:58 -0400746 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100747 self._num_total_blocks = 0
748 self._num_total_chunks = 0
749 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400750 self._read_header()
751
752 def _read_header(self):
753 """Initializes internal data structures used for reading file.
754
755 This may be called multiple times and is typically called after
756 modifying the file (e.g. appending, truncation).
757
758 Raises:
759 ValueError: If data in the file is invalid.
760 """
761 self.is_sparse = False
762 self.block_size = 4096
763 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400764 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400765 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400766 self.image_size = self._image.tell()
767
768 self._image.seek(0, os.SEEK_SET)
769 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
770 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
771 block_size, self._num_total_blocks, self._num_total_chunks,
772 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
773 if magic != self.MAGIC:
774 # Not a sparse image, our job here is done.
775 return
776 if not (major_version == 1 and minor_version == 0):
777 raise ValueError('Encountered sparse image format version {}.{} but '
778 'only 1.0 is supported'.format(major_version,
779 minor_version))
780 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
781 raise ValueError('Unexpected file_hdr_sz value {}.'.
782 format(file_hdr_sz))
783 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
784 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
785 format(chunk_hdr_sz))
786
787 self.block_size = block_size
788
789 # Build an list of chunks by parsing the file.
790 self._chunks = []
791
792 # Find the smallest offset where only "Don't care" chunks
793 # follow. This will be the size of the content in the sparse
794 # image.
795 offset = 0
796 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100797 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400798 chunk_offset = self._image.tell()
799
800 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
801 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
802 header_bin)
803 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
804
David Zeuthena4fee8b2016-08-22 15:20:43 -0400805 if chunk_type == ImageChunk.TYPE_RAW:
806 if data_sz != (chunk_sz * self.block_size):
807 raise ValueError('Raw chunk input size ({}) does not match output '
808 'size ({})'.
809 format(data_sz, chunk_sz*self.block_size))
810 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
811 chunk_offset,
812 output_offset,
813 chunk_sz*self.block_size,
814 self._image.tell(),
815 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700816 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400817
818 elif chunk_type == ImageChunk.TYPE_FILL:
819 if data_sz != 4:
820 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
821 'has {}'.format(data_sz))
822 fill_data = self._image.read(4)
823 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
824 chunk_offset,
825 output_offset,
826 chunk_sz*self.block_size,
827 None,
828 fill_data))
829 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
830 if data_sz != 0:
831 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
832 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400833 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
834 chunk_offset,
835 output_offset,
836 chunk_sz*self.block_size,
837 None,
838 None))
839 elif chunk_type == ImageChunk.TYPE_CRC32:
840 if data_sz != 4:
841 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
842 'this has {}'.format(data_sz))
843 self._image.read(4)
844 else:
845 raise ValueError('Unknown chunk type {}'.format(chunk_type))
846
847 offset += chunk_sz
848 output_offset += chunk_sz*self.block_size
849
850 # Record where sparse data end.
851 self._sparse_end = self._image.tell()
852
853 # Now that we've traversed all chunks, sanity check.
854 if self._num_total_blocks != offset:
855 raise ValueError('The header said we should have {} output blocks, '
856 'but we saw {}'.format(self._num_total_blocks, offset))
857 junk_len = len(self._image.read())
858 if junk_len > 0:
859 raise ValueError('There were {} bytes of extra data at the end of the '
860 'file.'.format(junk_len))
861
David Zeuthen09692692016-09-30 16:16:40 -0400862 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400863 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400864
865 # This is used when bisecting in read() to find the initial slice.
866 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
867
868 self.is_sparse = True
869
870 def _update_chunks_and_blocks(self):
871 """Helper function to update the image header.
872
873 The the |total_chunks| and |total_blocks| fields in the header
874 will be set to value of the |_num_total_blocks| and
875 |_num_total_chunks| attributes.
876
877 """
878 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
879 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
880 self._num_total_blocks,
881 self._num_total_chunks))
882
883 def append_dont_care(self, num_bytes):
884 """Appends a DONT_CARE chunk to the sparse file.
885
886 The given number of bytes must be a multiple of the block size.
887
888 Arguments:
889 num_bytes: Size in number of bytes of the DONT_CARE chunk.
890 """
891 assert num_bytes % self.block_size == 0
892
893 if not self.is_sparse:
894 self._image.seek(0, os.SEEK_END)
895 # This is more efficient that writing NUL bytes since it'll add
896 # a hole on file systems that support sparse files (native
897 # sparse, not Android sparse).
898 self._image.truncate(self._image.tell() + num_bytes)
899 self._read_header()
900 return
901
902 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100903 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400904 self._update_chunks_and_blocks()
905
906 self._image.seek(self._sparse_end, os.SEEK_SET)
907 self._image.write(struct.pack(ImageChunk.FORMAT,
908 ImageChunk.TYPE_DONT_CARE,
909 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100910 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400911 struct.calcsize(ImageChunk.FORMAT)))
912 self._read_header()
913
914 def append_raw(self, data):
915 """Appends a RAW chunk to the sparse file.
916
917 The length of the given data must be a multiple of the block size.
918
919 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200920 data: Data to append as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400921 """
922 assert len(data) % self.block_size == 0
923
924 if not self.is_sparse:
925 self._image.seek(0, os.SEEK_END)
926 self._image.write(data)
927 self._read_header()
928 return
929
930 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100931 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400932 self._update_chunks_and_blocks()
933
934 self._image.seek(self._sparse_end, os.SEEK_SET)
935 self._image.write(struct.pack(ImageChunk.FORMAT,
936 ImageChunk.TYPE_RAW,
937 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100938 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400939 len(data) +
940 struct.calcsize(ImageChunk.FORMAT)))
941 self._image.write(data)
942 self._read_header()
943
944 def append_fill(self, fill_data, size):
945 """Appends a fill chunk to the sparse file.
946
947 The total length of the fill data must be a multiple of the block size.
948
949 Arguments:
950 fill_data: Fill data to append - must be four bytes.
951 size: Number of chunk - must be a multiple of four and the block size.
952 """
953 assert len(fill_data) == 4
954 assert size % 4 == 0
955 assert size % self.block_size == 0
956
957 if not self.is_sparse:
958 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100959 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400960 self._read_header()
961 return
962
963 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100964 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400965 self._update_chunks_and_blocks()
966
967 self._image.seek(self._sparse_end, os.SEEK_SET)
968 self._image.write(struct.pack(ImageChunk.FORMAT,
969 ImageChunk.TYPE_FILL,
970 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100971 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400972 4 + struct.calcsize(ImageChunk.FORMAT)))
973 self._image.write(fill_data)
974 self._read_header()
975
976 def seek(self, offset):
977 """Sets the cursor position for reading from unsparsified file.
978
979 Arguments:
980 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100981
982 Raises:
983 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400984 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700985 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +0200986 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400987 self._file_pos = offset
988
989 def read(self, size):
990 """Reads data from the unsparsified file.
991
992 This method may return fewer than |size| bytes of data if the end
993 of the file was encountered.
994
995 The file cursor for reading is advanced by the number of bytes
996 read.
997
998 Arguments:
999 size: Number of bytes to read.
1000
1001 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +02001002 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -04001003 """
1004 if not self.is_sparse:
1005 self._image.seek(self._file_pos)
1006 data = self._image.read(size)
1007 self._file_pos += len(data)
1008 return data
1009
1010 # Iterate over all chunks.
1011 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1012 self._file_pos) - 1
1013 data = bytearray()
1014 to_go = size
1015 while to_go > 0:
1016 chunk = self._chunks[chunk_idx]
1017 chunk_pos_offset = self._file_pos - chunk.output_offset
1018 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1019
1020 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1021 self._image.seek(chunk.input_offset + chunk_pos_offset)
1022 data.extend(self._image.read(chunk_pos_to_go))
1023 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001024 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001025 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1026 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1027 else:
1028 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001029 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001030
1031 to_go -= chunk_pos_to_go
1032 self._file_pos += chunk_pos_to_go
1033 chunk_idx += 1
1034 # Generate partial read in case of EOF.
1035 if chunk_idx >= len(self._chunks):
1036 break
1037
Jan Monsch8347da92020-04-08 12:41:49 +02001038 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001039
1040 def tell(self):
1041 """Returns the file cursor position for reading from unsparsified file.
1042
1043 Returns:
1044 The file cursor position for reading.
1045 """
1046 return self._file_pos
1047
1048 def truncate(self, size):
1049 """Truncates the unsparsified file.
1050
1051 Arguments:
1052 size: Desired size of unsparsified file.
1053
1054 Raises:
1055 ValueError: If desired size isn't a multiple of the block size.
1056 """
1057 if not self.is_sparse:
1058 self._image.truncate(size)
1059 self._read_header()
1060 return
1061
1062 if size % self.block_size != 0:
1063 raise ValueError('Cannot truncate to a size which is not a multiple '
1064 'of the block size')
1065
1066 if size == self.image_size:
1067 # Trivial where there's nothing to do.
1068 return
Jan Monsch9c130122020-04-14 13:43:51 +02001069
1070 if size < self.image_size:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001071 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1072 chunk = self._chunks[chunk_idx]
1073 if chunk.output_offset != size:
1074 # Truncation in the middle of a trunk - need to keep the chunk
1075 # and modify it.
1076 chunk_idx_for_update = chunk_idx + 1
1077 num_to_keep = size - chunk.output_offset
1078 assert num_to_keep % self.block_size == 0
1079 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1080 truncate_at = (chunk.chunk_offset +
1081 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1082 data_sz = num_to_keep
1083 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1084 truncate_at = (chunk.chunk_offset +
1085 struct.calcsize(ImageChunk.FORMAT) + 4)
1086 data_sz = 4
1087 else:
1088 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1089 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1090 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001091 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001092 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1093 self._image.seek(chunk.chunk_offset)
1094 self._image.write(struct.pack(ImageChunk.FORMAT,
1095 chunk.chunk_type,
1096 0, # Reserved
1097 chunk_sz,
1098 total_sz))
1099 chunk.output_size = num_to_keep
1100 else:
1101 # Truncation at trunk boundary.
1102 truncate_at = chunk.chunk_offset
1103 chunk_idx_for_update = chunk_idx
1104
1105 self._num_total_chunks = chunk_idx_for_update
1106 self._num_total_blocks = 0
1107 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001108 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001109 self._update_chunks_and_blocks()
1110 self._image.truncate(truncate_at)
1111
1112 # We've modified the file so re-read all data.
1113 self._read_header()
1114 else:
1115 # Truncating to grow - just add a DONT_CARE section.
1116 self.append_dont_care(size - self.image_size)
1117
1118
David Zeuthen21e95262016-07-27 17:58:40 -04001119class AvbDescriptor(object):
1120 """Class for AVB descriptor.
1121
1122 See the |AvbDescriptor| C struct for more information.
1123
1124 Attributes:
1125 tag: The tag identifying what kind of descriptor this is.
1126 data: The data in the descriptor.
1127 """
1128
1129 SIZE = 16
1130 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1131
1132 def __init__(self, data):
1133 """Initializes a new property descriptor.
1134
1135 Arguments:
1136 data: If not None, must be a bytearray().
1137
1138 Raises:
1139 LookupError: If the given descriptor is malformed.
1140 """
1141 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1142
1143 if data:
1144 (self.tag, num_bytes_following) = (
1145 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1146 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1147 else:
1148 self.tag = None
1149 self.data = None
1150
1151 def print_desc(self, o):
1152 """Print the descriptor.
1153
1154 Arguments:
1155 o: The object to write the output to.
1156 """
1157 o.write(' Unknown descriptor:\n')
1158 o.write(' Tag: {}\n'.format(self.tag))
1159 if len(self.data) < 256:
1160 o.write(' Data: {} ({} bytes)\n'.format(
1161 repr(str(self.data)), len(self.data)))
1162 else:
1163 o.write(' Data: {} bytes\n'.format(len(self.data)))
1164
1165 def encode(self):
1166 """Serializes the descriptor.
1167
1168 Returns:
1169 A bytearray() with the descriptor data.
1170 """
1171 num_bytes_following = len(self.data)
1172 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1173 padding_size = nbf_with_padding - num_bytes_following
1174 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1175 padding = struct.pack(str(padding_size) + 'x')
1176 ret = desc + self.data + padding
1177 return bytearray(ret)
1178
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001179 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001180 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001181 """Verifies contents of the descriptor - used in verify_image sub-command.
1182
1183 Arguments:
1184 image_dir: The directory of the file being verified.
1185 image_ext: The extension of the file being verified (e.g. '.img').
1186 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001187 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001188 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001189 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1190 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001191
1192 Returns:
1193 True if the descriptor verifies, False otherwise.
1194 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001195 # Deletes unused parameters to prevent pylint warning unused-argument.
1196 del image_dir, image_ext, expected_chain_partitions_map
1197 del image_containing_descriptor, accept_zeroed_hashtree
1198
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001199 # Nothing to do.
1200 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001201
Jan Monscheeb28b62019-12-05 16:17:09 +01001202
David Zeuthen21e95262016-07-27 17:58:40 -04001203class AvbPropertyDescriptor(AvbDescriptor):
1204 """A class for property descriptors.
1205
1206 See the |AvbPropertyDescriptor| C struct for more information.
1207
1208 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001209 key: The key as string.
1210 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001211 """
1212
1213 TAG = 0
1214 SIZE = 32
1215 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001216 'Q' # key size (bytes)
1217 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001218
1219 def __init__(self, data=None):
1220 """Initializes a new property descriptor.
1221
1222 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001223 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001224
1225 Raises:
1226 LookupError: If the given descriptor is malformed.
1227 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001228 super(AvbPropertyDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001229 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1230
1231 if data:
1232 (tag, num_bytes_following, key_size,
1233 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1234 expected_size = round_to_multiple(
1235 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1236 if tag != self.TAG or num_bytes_following != expected_size:
1237 raise LookupError('Given data does not look like a property '
1238 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001239 try:
1240 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1241 except UnicodeDecodeError as e:
1242 raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001243 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1244 value_size)]
1245 else:
1246 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001247 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001248
1249 def print_desc(self, o):
1250 """Print the descriptor.
1251
1252 Arguments:
1253 o: The object to write the output to.
1254 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001255 # Go forward with python 3, bytes are represented with the 'b' prefix,
1256 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1257 # the same between python 2 and python 3.
1258 printable_value = repr(self.value)
1259 if printable_value.startswith('b\''):
1260 printable_value = printable_value[1:]
1261
David Zeuthen21e95262016-07-27 17:58:40 -04001262 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001263 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001264 else:
1265 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1266
1267 def encode(self):
1268 """Serializes the descriptor.
1269
1270 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001271 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001272 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001273 key_encoded = self.key.encode('utf-8')
1274 num_bytes_following = (
1275 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001276 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1277 padding_size = nbf_with_padding - num_bytes_following
1278 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001279 len(key_encoded), len(self.value))
1280 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1281 padding_size * b'\0')
1282 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001283
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001284 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001285 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001286 """Verifies contents of the descriptor - used in verify_image sub-command.
1287
1288 Arguments:
1289 image_dir: The directory of the file being verified.
1290 image_ext: The extension of the file being verified (e.g. '.img').
1291 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001292 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001293 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001294 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1295 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001296
1297 Returns:
1298 True if the descriptor verifies, False otherwise.
1299 """
1300 # Nothing to do.
1301 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001302
Jan Monscheeb28b62019-12-05 16:17:09 +01001303
David Zeuthen21e95262016-07-27 17:58:40 -04001304class AvbHashtreeDescriptor(AvbDescriptor):
1305 """A class for hashtree descriptors.
1306
1307 See the |AvbHashtreeDescriptor| C struct for more information.
1308
1309 Attributes:
1310 dm_verity_version: dm-verity version used.
1311 image_size: Size of the image, after rounding up to |block_size|.
1312 tree_offset: Offset of the hash tree in the file.
1313 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001314 data_block_size: Data block size.
1315 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001316 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1317 fec_offset: Offset of FEC data (0 if FEC is not used).
1318 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001319 hash_algorithm: Hash algorithm used as string.
1320 partition_name: Partition name as string.
1321 salt: Salt used as bytes.
1322 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001323 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001324 """
1325
1326 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001327 RESERVED = 60
1328 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001329 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001330 'L' # dm-verity version used
1331 'Q' # image size (bytes)
1332 'Q' # tree offset (bytes)
1333 'Q' # tree size (bytes)
1334 'L' # data block size (bytes)
1335 'L' # hash block size (bytes)
1336 'L' # FEC number of roots
1337 'Q' # FEC offset (bytes)
1338 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001339 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001340 'L' # partition name (bytes)
1341 'L' # salt length (bytes)
1342 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001343 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001344 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001345
1346 def __init__(self, data=None):
1347 """Initializes a new hashtree descriptor.
1348
1349 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001350 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001351
1352 Raises:
1353 LookupError: If the given descriptor is malformed.
1354 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001355 super(AvbHashtreeDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001356 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1357
1358 if data:
1359 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1360 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001361 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1362 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001363 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1364 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001365 expected_size = round_to_multiple(
1366 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1367 if tag != self.TAG or num_bytes_following != expected_size:
1368 raise LookupError('Given data does not look like a hashtree '
1369 'descriptor.')
1370 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001371 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001372 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001373 try:
1374 self.partition_name = data[
1375 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1376 ].decode('utf-8')
1377 except UnicodeDecodeError as e:
1378 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1379 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001380 o += partition_name_len
1381 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1382 o += salt_len
1383 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001384 if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001385 if root_digest_len != 0:
1386 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001387
1388 else:
1389 self.dm_verity_version = 0
1390 self.image_size = 0
1391 self.tree_offset = 0
1392 self.tree_size = 0
1393 self.data_block_size = 0
1394 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001395 self.fec_num_roots = 0
1396 self.fec_offset = 0
1397 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001398 self.hash_algorithm = ''
1399 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001400 self.salt = b''
1401 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001402 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001403
1404 def print_desc(self, o):
1405 """Print the descriptor.
1406
1407 Arguments:
1408 o: The object to write the output to.
1409 """
1410 o.write(' Hashtree descriptor:\n')
1411 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1412 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1413 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1414 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1415 o.write(' Data Block Size: {} bytes\n'.format(
1416 self.data_block_size))
1417 o.write(' Hash Block Size: {} bytes\n'.format(
1418 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001419 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1420 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1421 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001422 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1423 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001424 o.write(' Salt: {}\n'.format(
1425 binascii.hexlify(self.salt).decode('ascii')))
1426 o.write(' Root Digest: {}\n'.format(
1427 binascii.hexlify(self.root_digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001428 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001429
1430 def encode(self):
1431 """Serializes the descriptor.
1432
1433 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001434 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001435 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001436 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1437 partition_name_encoded = self.partition_name.encode('utf-8')
1438 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1439 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001440 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1441 padding_size = nbf_with_padding - num_bytes_following
1442 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1443 self.dm_verity_version, self.image_size,
1444 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001445 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001446 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1447 len(partition_name_encoded), len(self.salt),
1448 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1449 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1450 padding_size * b'\0')
1451 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001452
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001453 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001454 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001455 """Verifies contents of the descriptor - used in verify_image sub-command.
1456
1457 Arguments:
1458 image_dir: The directory of the file being verified.
1459 image_ext: The extension of the file being verified (e.g. '.img').
1460 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001461 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001462 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001463 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1464 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001465
1466 Returns:
1467 True if the descriptor verifies, False otherwise.
1468 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001469 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001470 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001471 image = image_containing_descriptor
1472 else:
1473 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1474 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001475 # Generate the hashtree and checks that it matches what's in the file.
Jan Monsch6f27bb12020-04-07 07:33:26 +02001476 digest_size = len(hashlib.new(self.hash_algorithm).digest())
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001477 digest_padding = round_to_pow2(digest_size) - digest_size
1478 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001479 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001480 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1481 self.data_block_size,
1482 self.hash_algorithm, self.salt,
1483 digest_padding,
1484 hash_level_offsets,
1485 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001486 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001487 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001488 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1489 format(image_filename))
1490 return False
1491 # ... also check that the on-disk hashtree matches
1492 image.seek(self.tree_offset)
1493 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001494 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001495 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001496 print('{}: skipping verification since hashtree is zeroed and '
1497 '--accept_zeroed_hashtree was given'
1498 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001499 else:
1500 if hash_tree != hash_tree_ondisk:
1501 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001502 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001503 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001504 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1505 .format(self.partition_name, self.hash_algorithm, image.filename,
1506 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001507 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1508 # correct but this a) currently requires the 'fec' binary; and b) takes a
1509 # long time; and c) is not strictly needed for verification purposes as
1510 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001511 return True
1512
David Zeuthen21e95262016-07-27 17:58:40 -04001513
1514class AvbHashDescriptor(AvbDescriptor):
1515 """A class for hash descriptors.
1516
1517 See the |AvbHashDescriptor| C struct for more information.
1518
1519 Attributes:
1520 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001521 hash_algorithm: Hash algorithm used as string.
1522 partition_name: Partition name as string.
1523 salt: Salt used as bytes.
1524 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001525 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001526 """
1527
1528 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001529 RESERVED = 60
1530 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001531 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001532 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001533 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001534 'L' # partition name (bytes)
1535 'L' # salt length (bytes)
1536 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001537 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001538 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001539
1540 def __init__(self, data=None):
1541 """Initializes a new hash descriptor.
1542
1543 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001544 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001545
1546 Raises:
1547 LookupError: If the given descriptor is malformed.
1548 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001549 super(AvbHashDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001550 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1551
1552 if data:
1553 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1554 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001555 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1556 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001557 expected_size = round_to_multiple(
1558 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1559 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001560 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001561 # Nuke NUL-bytes at the end.
Jan Monschee6fccd2020-04-09 19:36:13 +02001562 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001563 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001564 try:
1565 self.partition_name = data[
1566 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1567 ].decode('utf-8')
1568 except UnicodeDecodeError as e:
1569 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1570 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001571 o += partition_name_len
1572 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1573 o += salt_len
1574 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001575 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001576 if digest_len != 0:
1577 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001578
1579 else:
1580 self.image_size = 0
1581 self.hash_algorithm = ''
1582 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001583 self.salt = b''
1584 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001585 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001586
1587 def print_desc(self, o):
1588 """Print the descriptor.
1589
1590 Arguments:
1591 o: The object to write the output to.
1592 """
1593 o.write(' Hash descriptor:\n')
1594 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1595 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1596 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001597 o.write(' Salt: {}\n'.format(
1598 binascii.hexlify(self.salt).decode('ascii')))
1599 o.write(' Digest: {}\n'.format(
1600 binascii.hexlify(self.digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001601 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001602
1603 def encode(self):
1604 """Serializes the descriptor.
1605
1606 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001607 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001608 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001609 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1610 partition_name_encoded = self.partition_name.encode('utf-8')
1611 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1612 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001613 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1614 padding_size = nbf_with_padding - num_bytes_following
1615 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001616 self.image_size, hash_algorithm_encoded,
1617 len(partition_name_encoded), len(self.salt),
1618 len(self.digest), self.flags, self.RESERVED * b'\0')
1619 ret = (desc + partition_name_encoded + self.salt + self.digest +
1620 padding_size * b'\0')
1621 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001622
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001623 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001624 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001625 """Verifies contents of the descriptor - used in verify_image sub-command.
1626
1627 Arguments:
1628 image_dir: The directory of the file being verified.
1629 image_ext: The extension of the file being verified (e.g. '.img').
1630 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001631 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001632 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001633 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1634 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001635
1636 Returns:
1637 True if the descriptor verifies, False otherwise.
1638 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001639 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001640 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001641 image = image_containing_descriptor
1642 else:
1643 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1644 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001645 data = image.read(self.image_size)
1646 ha = hashlib.new(self.hash_algorithm)
1647 ha.update(self.salt)
1648 ha.update(data)
1649 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001650 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001651 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001652 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1653 format(self.hash_algorithm, image_filename))
1654 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001655 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1656 .format(self.partition_name, self.hash_algorithm, image.filename,
1657 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001658 return True
1659
David Zeuthen21e95262016-07-27 17:58:40 -04001660
1661class AvbKernelCmdlineDescriptor(AvbDescriptor):
1662 """A class for kernel command-line descriptors.
1663
1664 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1665
1666 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001667 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001668 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001669 """
1670
1671 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001672 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001673 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001674 'L' # flags
1675 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001676
David Zeuthenfd41eb92016-11-17 12:24:47 -05001677 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1678 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1679
David Zeuthen21e95262016-07-27 17:58:40 -04001680 def __init__(self, data=None):
1681 """Initializes a new kernel cmdline descriptor.
1682
1683 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001684 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001685
1686 Raises:
1687 LookupError: If the given descriptor is malformed.
1688 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001689 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001690 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1691
1692 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001693 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001694 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1695 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1696 8)
1697 if tag != self.TAG or num_bytes_following != expected_size:
1698 raise LookupError('Given data does not look like a kernel cmdline '
1699 'descriptor.')
1700 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001701 try:
1702 self.kernel_cmdline = data[
1703 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1704 except UnicodeDecodeError as e:
1705 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1706 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001707 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001708 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001709 self.kernel_cmdline = ''
1710
1711 def print_desc(self, o):
1712 """Print the descriptor.
1713
1714 Arguments:
1715 o: The object to write the output to.
1716 """
1717 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001718 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001719 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001720
1721 def encode(self):
1722 """Serializes the descriptor.
1723
1724 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001725 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001726 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001727 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1728 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001729 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1730 padding_size = nbf_with_padding - num_bytes_following
1731 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001732 self.flags, len(kernel_cmd_encoded))
1733 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1734 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001735
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001736 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001737 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001738 """Verifies contents of the descriptor - used in verify_image sub-command.
1739
1740 Arguments:
1741 image_dir: The directory of the file being verified.
1742 image_ext: The extension of the file being verified (e.g. '.img').
1743 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001744 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001745 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001746 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1747 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001748
1749 Returns:
1750 True if the descriptor verifies, False otherwise.
1751 """
1752 # Nothing to verify.
1753 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001754
Jan Monscheeb28b62019-12-05 16:17:09 +01001755
David Zeuthen21e95262016-07-27 17:58:40 -04001756class AvbChainPartitionDescriptor(AvbDescriptor):
1757 """A class for chained partition descriptors.
1758
1759 See the |AvbChainPartitionDescriptor| C struct for more information.
1760
1761 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001762 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001763 partition_name: Partition name as string.
1764 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001765 """
1766
1767 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001768 RESERVED = 64
1769 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001770 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001771 'L' # rollback_index_location
1772 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001773 'L' + # public_key_size (bytes)
1774 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001775
1776 def __init__(self, data=None):
1777 """Initializes a new chain partition descriptor.
1778
1779 Arguments:
1780 data: If not None, must be a bytearray of size |SIZE|.
1781
1782 Raises:
1783 LookupError: If the given descriptor is malformed.
1784 """
1785 AvbDescriptor.__init__(self, None)
1786 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1787
1788 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001789 (tag, num_bytes_following, self.rollback_index_location,
1790 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001791 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001792 expected_size = round_to_multiple(
1793 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1794 if tag != self.TAG or num_bytes_following != expected_size:
1795 raise LookupError('Given data does not look like a chain partition '
1796 'descriptor.')
1797 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001798 try:
1799 self.partition_name = data[
1800 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1801 ].decode('utf-8')
1802 except UnicodeDecodeError as e:
1803 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1804 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001805 o += partition_name_len
1806 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1807
1808 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001809 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001810 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001811 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001812
1813 def print_desc(self, o):
1814 """Print the descriptor.
1815
1816 Arguments:
1817 o: The object to write the output to.
1818 """
1819 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001820 o.write(' Partition Name: {}\n'.format(self.partition_name))
1821 o.write(' Rollback Index Location: {}\n'.format(
1822 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001823 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001824 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1825 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001826
1827 def encode(self):
1828 """Serializes the descriptor.
1829
1830 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001831 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001832 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001833 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001834 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001835 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001836 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1837 padding_size = nbf_with_padding - num_bytes_following
1838 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001839 self.rollback_index_location,
1840 len(partition_name_encoded), len(self.public_key),
1841 self.RESERVED * b'\0')
1842 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1843 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001844
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001845 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001846 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001847 """Verifies contents of the descriptor - used in verify_image sub-command.
1848
1849 Arguments:
1850 image_dir: The directory of the file being verified.
1851 image_ext: The extension of the file being verified (e.g. '.img').
1852 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001853 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001854 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001855 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1856 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001857
1858 Returns:
1859 True if the descriptor verifies, False otherwise.
1860 """
1861 value = expected_chain_partitions_map.get(self.partition_name)
1862 if not value:
1863 sys.stderr.write('No expected chain partition for partition {}. Use '
1864 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001865 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001866 format(self.partition_name))
1867 return False
1868 rollback_index_location, pk_blob = value
1869
1870 if self.rollback_index_location != rollback_index_location:
1871 sys.stderr.write('Expected rollback_index_location {} does not '
1872 'match {} in descriptor for partition {}\n'.
1873 format(rollback_index_location,
1874 self.rollback_index_location,
1875 self.partition_name))
1876 return False
1877
1878 if self.public_key != pk_blob:
1879 sys.stderr.write('Expected public key blob does not match public '
1880 'key blob in descriptor for partition {}\n'.
1881 format(self.partition_name))
1882 return False
1883
Jan Monsch23e0c622019-12-11 11:23:58 +01001884 print('{}: Successfully verified chain partition descriptor matches '
1885 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001886
1887 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001888
1889DESCRIPTOR_CLASSES = [
1890 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1891 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1892]
1893
1894
1895def parse_descriptors(data):
1896 """Parses a blob of data into descriptors.
1897
1898 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001899 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001900
1901 Returns:
1902 A list of instances of objects derived from AvbDescriptor. For
1903 unknown descriptors, the class AvbDescriptor is used.
1904 """
1905 o = 0
1906 ret = []
1907 while o < len(data):
1908 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1909 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001910 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001911 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001912 clazz = AvbDescriptor
1913 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001914 o += 16 + nb_following
1915 return ret
1916
1917
1918class AvbFooter(object):
1919 """A class for parsing and writing footers.
1920
1921 Footers are stored at the end of partitions and point to where the
1922 AvbVBMeta blob is located. They also contain the original size of
1923 the image before AVB information was added.
1924
1925 Attributes:
1926 magic: Magic for identifying the footer, see |MAGIC|.
1927 version_major: The major version of avbtool that wrote the footer.
1928 version_minor: The minor version of avbtool that wrote the footer.
1929 original_image_size: Original image size.
1930 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1931 vbmeta_size: Size of the AvbVBMeta blob.
1932 """
1933
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001934 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001935 SIZE = 64
1936 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001937 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1938 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001939 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001940 'Q' # Original image size.
1941 'Q' # Offset of VBMeta blob.
1942 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001943 str(RESERVED) + 'x') # padding for reserved bytes
1944
1945 def __init__(self, data=None):
1946 """Initializes a new footer object.
1947
1948 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001949 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001950
1951 Raises:
1952 LookupError: If the given footer is malformed.
1953 struct.error: If the given data has no footer.
1954 """
1955 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1956
1957 if data:
1958 (self.magic, self.version_major, self.version_minor,
1959 self.original_image_size, self.vbmeta_offset,
1960 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1961 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001962 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001963 else:
1964 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001965 self.version_major = self.FOOTER_VERSION_MAJOR
1966 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001967 self.original_image_size = 0
1968 self.vbmeta_offset = 0
1969 self.vbmeta_size = 0
1970
David Zeuthena4fee8b2016-08-22 15:20:43 -04001971 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001972 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001973
David Zeuthena4fee8b2016-08-22 15:20:43 -04001974 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001975 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001976 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001977 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1978 self.version_minor, self.original_image_size,
1979 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001980
1981
1982class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001983 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001984
Jan Monschfe00c0a2019-12-11 11:19:40 +01001985 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1986 avb_vbmeta_image.h.
1987
David Zeuthen21e95262016-07-27 17:58:40 -04001988 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001989 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1990 required_libavb_version_major: The major version of libavb required for this
1991 header.
1992 required_libavb_version_minor: The minor version of libavb required for this
1993 header.
1994 authentication_data_block_size: The size of the signature block.
1995 auxiliary_data_block_size: The size of the auxiliary data block.
1996 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1997 enum.
1998 hash_offset: Offset into the "Authentication data" block of hash data.
1999 hash_size: Length of the hash data.
2000 signature_offset: Offset into the "Authentication data" block of signature
2001 data.
2002 signature_size: Length of the signature data.
2003 public_key_offset: Offset into the "Auxiliary data" block of public key
2004 data.
2005 public_key_size: Length of the public key data.
2006 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2007 key metadata.
2008 public_key_metadata_size: Length of the public key metadata. Must be set to
2009 zero if there is no public key metadata.
2010 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2011 data.
2012 descriptors_size: Length of descriptor data.
2013 rollback_index: The rollback index which can be used to prevent rollback to
2014 older versions.
2015 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2016 zero if the vbmeta image is not a top-level image.
Varun Sharmade538272020-04-10 15:22:31 -07002017 rollback_index_location: The location of the rollback index defined in this
2018 header. Only valid for the main vbmeta. For chained partitions, the
2019 rollback index location must be specified in the
2020 AvbChainPartitionDescriptor and this value must be set to 0.
Jan Monschfe00c0a2019-12-11 11:19:40 +01002021 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2022 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2023 terminated. Applications must not make assumptions about how this
2024 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002025 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002026 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002027 SIZE = 256
2028
Varun Sharmade538272020-04-10 15:22:31 -07002029 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthene3cadca2017-02-22 21:25:46 -05002030 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002031
2032 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002033 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2034 '2Q' # 2 x block size
2035 'L' # algorithm type
2036 '2Q' # offset, size (hash)
2037 '2Q' # offset, size (signature)
2038 '2Q' # offset, size (public key)
2039 '2Q' # offset, size (public key metadata)
2040 '2Q' # offset, size (descriptors)
2041 'Q' # rollback_index
Varun Sharmade538272020-04-10 15:22:31 -07002042 'L' # flags
2043 'L' # rollback_index_location
David Zeuthene3cadca2017-02-22 21:25:46 -05002044 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002045 str(RESERVED) + 'x') # padding for reserved bytes
2046
2047 def __init__(self, data=None):
2048 """Initializes a new header object.
2049
2050 Arguments:
2051 data: If not None, must be a bytearray of size 8192.
2052
2053 Raises:
2054 Exception: If the given data is malformed.
2055 """
2056 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2057
2058 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002059 (self.magic, self.required_libavb_version_major,
2060 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002061 self.authentication_data_block_size, self.auxiliary_data_block_size,
2062 self.algorithm_type, self.hash_offset, self.hash_size,
2063 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002064 self.public_key_size, self.public_key_metadata_offset,
2065 self.public_key_metadata_size, self.descriptors_offset,
2066 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002067 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002068 self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002069 self.rollback_index_location,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002070 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002071 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002072 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002073 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002074 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002075 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002076 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002077 # Start by just requiring version 1.0. Code that adds features
2078 # in a future version can use bump_required_libavb_version_minor() to
2079 # bump the minor.
2080 self.required_libavb_version_major = AVB_VERSION_MAJOR
2081 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002082 self.authentication_data_block_size = 0
2083 self.auxiliary_data_block_size = 0
2084 self.algorithm_type = 0
2085 self.hash_offset = 0
2086 self.hash_size = 0
2087 self.signature_offset = 0
2088 self.signature_size = 0
2089 self.public_key_offset = 0
2090 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002091 self.public_key_metadata_offset = 0
2092 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002093 self.descriptors_offset = 0
2094 self.descriptors_size = 0
2095 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002096 self.flags = 0
Varun Sharmade538272020-04-10 15:22:31 -07002097 self.rollback_index_location = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002098 self.release_string = get_release_string()
2099
2100 def bump_required_libavb_version_minor(self, minor):
2101 """Function to bump required_libavb_version_minor.
2102
2103 Call this when writing data that requires a specific libavb
2104 version to parse it.
2105
2106 Arguments:
2107 minor: The minor version of libavb that has support for the feature.
2108 """
2109 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002110 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002111
David Zeuthen21e95262016-07-27 17:58:40 -04002112 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002113 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002114
2115 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002116 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002117 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002118 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002119 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002120 self.required_libavb_version_major,
2121 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002122 self.authentication_data_block_size,
2123 self.auxiliary_data_block_size, self.algorithm_type,
2124 self.hash_offset, self.hash_size, self.signature_offset,
2125 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002126 self.public_key_size, self.public_key_metadata_offset,
2127 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002128 self.descriptors_size, self.rollback_index, self.flags,
Varun Sharmade538272020-04-10 15:22:31 -07002129 self.rollback_index_location, release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002130
2131
2132class Avb(object):
2133 """Business logic for avbtool command-line tool."""
2134
David Zeuthen8b6973b2016-09-20 12:39:49 -04002135 # Keep in sync with avb_ab_flow.h.
2136 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
Jan Monschb1d920f2020-04-09 12:59:28 +02002137 AB_MAGIC = b'\0AB0'
David Zeuthen8b6973b2016-09-20 12:39:49 -04002138 AB_MAJOR_VERSION = 1
2139 AB_MINOR_VERSION = 0
2140 AB_MISC_METADATA_OFFSET = 2048
2141
David Zeuthen09692692016-09-30 16:16:40 -04002142 # Constants for maximum metadata size. These are used to give
2143 # meaningful errors if the value passed in via --partition_size is
2144 # too small and when --calc_max_image_size is used. We use
2145 # conservative figures.
2146 MAX_VBMETA_SIZE = 64 * 1024
2147 MAX_FOOTER_SIZE = 4096
2148
Jan Monsch2c7be992020-04-03 14:37:13 +02002149 def generate_test_image(self, output, image_size, start_byte):
2150 """Generates a test image for testing avbtool with known content.
2151
2152 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2153
2154 Arguments:
2155 output: Write test image to this file.
2156 image_size: The size of the requested file in bytes.
2157 start_byte: The integer value of the start byte to use for pattern
2158 generation.
2159 """
2160 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2161 buf = bytearray()
2162 c = int(math.ceil(image_size / 256.0))
2163 for _ in range(0, c):
2164 buf.extend(pattern)
2165 output.write(buf[0:image_size])
2166
David Zeuthen49936b42018-08-07 17:38:58 -04002167 def extract_vbmeta_image(self, output, image_filename, padding_size):
2168 """Implements the 'extract_vbmeta_image' command.
2169
2170 Arguments:
2171 output: Write vbmeta struct to this file.
2172 image_filename: File to extract vbmeta data from (with a footer).
2173 padding_size: If not 0, pads output so size is a multiple of the number.
2174
2175 Raises:
2176 AvbError: If there's no footer in the image.
2177 """
2178 image = ImageHandler(image_filename)
David Zeuthen49936b42018-08-07 17:38:58 -04002179 (footer, _, _, _) = self._parse_image(image)
David Zeuthen49936b42018-08-07 17:38:58 -04002180 if not footer:
2181 raise AvbError('Given image does not have a footer.')
2182
2183 image.seek(footer.vbmeta_offset)
2184 vbmeta_blob = image.read(footer.vbmeta_size)
2185 output.write(vbmeta_blob)
2186
2187 if padding_size > 0:
2188 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2189 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002190 output.write(b'\0' * padding_needed)
David Zeuthen49936b42018-08-07 17:38:58 -04002191
David Zeuthena4fee8b2016-08-22 15:20:43 -04002192 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002193 """Implements the 'erase_footer' command.
2194
2195 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002196 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002197 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002198
2199 Raises:
2200 AvbError: If there's no footer in the image.
2201 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002202 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002203 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen21e95262016-07-27 17:58:40 -04002204 if not footer:
2205 raise AvbError('Given image does not have a footer.')
2206
2207 new_image_size = None
2208 if not keep_hashtree:
2209 new_image_size = footer.original_image_size
2210 else:
2211 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002212 # descriptor to figure out the location and size of the hashtree
2213 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002214 for desc in descriptors:
2215 if isinstance(desc, AvbHashtreeDescriptor):
2216 # The hashtree is always just following the main data so the
2217 # new size is easily derived.
2218 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002219 # If the image has FEC codes, also keep those.
2220 if desc.fec_offset > 0:
2221 fec_end = desc.fec_offset + desc.fec_size
2222 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002223 break
2224 if not new_image_size:
2225 raise AvbError('Requested to keep hashtree but no hashtree '
2226 'descriptor was found.')
2227
2228 # And cut...
2229 image.truncate(new_image_size)
2230
David Zeuthen1394f762019-04-30 10:20:11 -04002231 def zero_hashtree(self, image_filename):
2232 """Implements the 'zero_hashtree' command.
2233
2234 Arguments:
2235 image_filename: File to zero hashtree and FEC data from.
2236
2237 Raises:
2238 AvbError: If there's no footer in the image.
2239 """
David Zeuthen1394f762019-04-30 10:20:11 -04002240 image = ImageHandler(image_filename)
David Zeuthen1394f762019-04-30 10:20:11 -04002241 (footer, _, descriptors, _) = self._parse_image(image)
David Zeuthen1394f762019-04-30 10:20:11 -04002242 if not footer:
2243 raise AvbError('Given image does not have a footer.')
2244
2245 # Search for a hashtree descriptor to figure out the location and
2246 # size of the hashtree and FEC.
2247 ht_desc = None
2248 for desc in descriptors:
2249 if isinstance(desc, AvbHashtreeDescriptor):
2250 ht_desc = desc
2251 break
2252
2253 if not ht_desc:
2254 raise AvbError('No hashtree descriptor was found.')
2255
2256 zero_ht_start_offset = ht_desc.tree_offset
2257 zero_ht_num_bytes = ht_desc.tree_size
2258 zero_fec_start_offset = None
2259 zero_fec_num_bytes = 0
2260 if ht_desc.fec_offset > 0:
2261 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2262 raise AvbError('Hash-tree and FEC data must be adjacent.')
2263 zero_fec_start_offset = ht_desc.fec_offset
2264 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002265 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2266 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002267 image.seek(zero_end_offset)
2268 data = image.read(image.image_size - zero_end_offset)
2269
2270 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2271 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2272 # beginning of both hashtree and FEC. (That way, in the future we can add
2273 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2274 #
2275 # Applications can use these markers to detect that the hashtree and/or
2276 # FEC needs to be recomputed.
2277 image.truncate(zero_ht_start_offset)
Jan Monschb1d920f2020-04-09 12:59:28 +02002278 data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
David Zeuthen1394f762019-04-30 10:20:11 -04002279 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002280 image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002281 if zero_fec_start_offset:
2282 image.append_raw(data_zeroed_firstblock)
Jan Monschb1d920f2020-04-09 12:59:28 +02002283 image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
David Zeuthen1394f762019-04-30 10:20:11 -04002284 image.append_raw(data)
2285
David Zeuthen2bc232b2017-04-19 14:25:19 -04002286 def resize_image(self, image_filename, partition_size):
2287 """Implements the 'resize_image' command.
2288
2289 Arguments:
2290 image_filename: File with footer to resize.
2291 partition_size: The new size of the image.
2292
2293 Raises:
2294 AvbError: If there's no footer in the image.
2295 """
2296
2297 image = ImageHandler(image_filename)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002298 if partition_size % image.block_size != 0:
2299 raise AvbError('Partition size of {} is not a multiple of the image '
2300 'block size {}.'.format(partition_size,
2301 image.block_size))
Jan Monsch77cd2022019-12-10 17:18:04 +01002302 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002303 if not footer:
2304 raise AvbError('Given image does not have a footer.')
2305
2306 # The vbmeta blob is always at the end of the data so resizing an
2307 # image amounts to just moving the footer around.
David Zeuthen2bc232b2017-04-19 14:25:19 -04002308 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2309 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002310 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2311 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002312
Jan Monschb1d920f2020-04-09 12:59:28 +02002313 if partition_size < vbmeta_end_offset + 1 * image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002314 raise AvbError('Requested size of {} is too small for an image '
2315 'of size {}.'
2316 .format(partition_size,
Jan Monschb1d920f2020-04-09 12:59:28 +02002317 vbmeta_end_offset + 1 * image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002318
2319 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2320 # with enough bytes such that the final Footer block is at the end
2321 # of partition_size.
2322 image.truncate(vbmeta_end_offset)
2323 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02002324 1 * image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002325
2326 # Just reuse the same footer - only difference is that we're
2327 # writing it in a different place.
2328 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02002329 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthen2bc232b2017-04-19 14:25:19 -04002330 footer_blob)
2331 image.append_raw(footer_blob_with_padding)
2332
David Zeuthen8b6973b2016-09-20 12:39:49 -04002333 def set_ab_metadata(self, misc_image, slot_data):
2334 """Implements the 'set_ab_metadata' command.
2335
2336 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2337 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2338
2339 Arguments:
2340 misc_image: The misc image to write to.
2341 slot_data: Slot data as a string
2342
2343 Raises:
2344 AvbError: If slot data is malformed.
2345 """
2346 tokens = slot_data.split(':')
2347 if len(tokens) != 6:
2348 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2349 a_priority = int(tokens[0])
2350 a_tries_remaining = int(tokens[1])
Jan Monsch9c130122020-04-14 13:43:51 +02002351 a_success = int(tokens[2]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002352 b_priority = int(tokens[3])
2353 b_tries_remaining = int(tokens[4])
Jan Monsch9c130122020-04-14 13:43:51 +02002354 b_success = int(tokens[5]) != 0
David Zeuthen8b6973b2016-09-20 12:39:49 -04002355
2356 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2357 self.AB_MAGIC,
2358 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2359 a_priority, a_tries_remaining, a_success,
2360 b_priority, b_tries_remaining, b_success)
2361 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2362 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2363 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2364 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2365 misc_image.write(ab_data)
2366
David Zeuthena4fee8b2016-08-22 15:20:43 -04002367 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002368 """Implements the 'info_image' command.
2369
2370 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002371 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002372 output: Output file to write human-readable information to (file object).
2373 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002374 image = ImageHandler(image_filename)
David Zeuthen21e95262016-07-27 17:58:40 -04002375 o = output
David Zeuthen21e95262016-07-27 17:58:40 -04002376 (footer, header, descriptors, image_size) = self._parse_image(image)
2377
Bowgo Tsaid7145942020-03-20 17:03:51 +08002378 # To show the SHA1 of the public key.
2379 vbmeta_blob = self._load_vbmeta_blob(image)
2380 key_offset = (header.SIZE +
2381 header.authentication_data_block_size +
2382 header.public_key_offset)
2383 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2384
David Zeuthen21e95262016-07-27 17:58:40 -04002385 if footer:
2386 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2387 footer.version_minor))
2388 o.write('Image size: {} bytes\n'.format(image_size))
2389 o.write('Original image size: {} bytes\n'.format(
2390 footer.original_image_size))
2391 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2392 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2393 o.write('--\n')
2394
2395 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2396
David Zeuthene3cadca2017-02-22 21:25:46 -05002397 o.write('Minimum libavb version: {}.{}{}\n'.format(
2398 header.required_libavb_version_major,
2399 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002400 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002401 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2402 o.write('Authentication Block: {} bytes\n'.format(
2403 header.authentication_data_block_size))
2404 o.write('Auxiliary Block: {} bytes\n'.format(
2405 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002406 if key_blob:
2407 hexdig = hashlib.sha1(key_blob).hexdigest()
2408 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002409 o.write('Algorithm: {}\n'.format(alg_name))
2410 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002411 o.write('Flags: {}\n'.format(header.flags))
Jan Monscha18b2ec2020-04-22 11:43:35 +02002412 o.write('Rollback Index Location: {}\n'.format(
2413 header.rollback_index_location))
Jan Monschb1d920f2020-04-09 12:59:28 +02002414 o.write('Release String: \'{}\'\n'.format(header.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002415
2416 # Print descriptors.
2417 num_printed = 0
2418 o.write('Descriptors:\n')
2419 for desc in descriptors:
2420 desc.print_desc(o)
2421 num_printed += 1
2422 if num_printed == 0:
2423 o.write(' (none)\n')
2424
Jan Monscheeb28b62019-12-05 16:17:09 +01002425 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2426 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002427 """Implements the 'verify_image' command.
2428
2429 Arguments:
2430 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002431 key_path: None or check that embedded public key matches key at given
2432 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002433 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002434 follow_chain_partitions:
2435 If True, will follows chain partitions even when not specified with
2436 the --expected_chain_partition option
2437 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2438 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002439
2440 Raises:
2441 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002442 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002443 expected_chain_partitions_map = {}
2444 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002445 for cp in expected_chain_partitions:
2446 cp_tokens = cp.split(':')
2447 if len(cp_tokens) != 3:
2448 raise AvbError('Malformed chained partition "{}".'.format(cp))
2449 partition_name = cp_tokens[0]
2450 rollback_index_location = int(cp_tokens[1])
2451 file_path = cp_tokens[2]
Jan Monschb1d920f2020-04-09 12:59:28 +02002452 with open(file_path, 'rb') as f:
2453 pk_blob = f.read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002454 expected_chain_partitions_map[partition_name] = (
2455 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002456
2457 image_dir = os.path.dirname(image_filename)
2458 image_ext = os.path.splitext(image_filename)[1]
2459
2460 key_blob = None
2461 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002462 print('Verifying image {} using key at {}'.format(image_filename,
2463 key_path))
Jan Monsch9c130122020-04-14 13:43:51 +02002464 key_blob = RSAPublicKey(key_path).encode()
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002465 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002466 print('Verifying image {} using embedded public key'.format(
2467 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002468
David Zeuthenb623d8b2017-04-04 16:05:53 -04002469 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002470 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002471 offset = 0
2472 if footer:
2473 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002474
David Zeuthenb623d8b2017-04-04 16:05:53 -04002475 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002476 vbmeta_blob = image.read(header.SIZE
2477 + header.authentication_data_block_size
2478 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002479
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002480 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002481 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002482 raise AvbError('Signature check failed for {} vbmeta struct {}'
2483 .format(alg_name, image_filename))
2484
2485 if key_blob:
2486 # The embedded public key is in the auxiliary block at an offset.
2487 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002488 key_offset += header.authentication_data_block_size
2489 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002490 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2491 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002492 if key_blob != key_blob_in_vbmeta:
2493 raise AvbError('Embedded public key does not match given key.')
2494
2495 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002496 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2497 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002498 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002499 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2500 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002501
2502 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002503 if (isinstance(desc, AvbChainPartitionDescriptor)
2504 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002505 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002506 # In this case we're processing a chain descriptor but don't have a
2507 # --expect_chain_partition ... however --follow_chain_partitions was
2508 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002509 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2510 'and KEY (which has sha1 {}) not specified'
2511 .format(desc.partition_name, desc.rollback_index_location,
2512 hashlib.sha1(desc.public_key).hexdigest()))
2513 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002514 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002515 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002516 # Honor --follow_chain_partitions - add '--' to make the output more
2517 # readable.
2518 if (isinstance(desc, AvbChainPartitionDescriptor)
2519 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002520 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002521 chained_image_filename = os.path.join(image_dir,
2522 desc.partition_name + image_ext)
2523 self.verify_image(chained_image_filename, key_path, None, False,
2524 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002525
David Zeuthen34b6b492020-04-13 14:45:02 -04002526 def print_partition_digests(self, image_filename, output, as_json):
2527 """Implements the 'print_partition_digests' command.
2528
2529 Arguments:
2530 image_filename: Image file to get information from (file object).
2531 output: Output file to write human-readable information to (file object).
2532 as_json: If True, print information as JSON
2533
2534 Raises:
2535 AvbError: If getting the partition digests from the image fails.
2536 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002537 image_dir = os.path.dirname(image_filename)
2538 image_ext = os.path.splitext(image_filename)[1]
2539 json_partitions = None
2540 if as_json:
2541 json_partitions = []
Jan Monschcc9939a2020-04-16 09:15:20 +02002542 self._print_partition_digests(
2543 image_filename, output, json_partitions, image_dir, image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002544 if as_json:
2545 output.write(json.dumps({'partitions': json_partitions}, indent=2))
2546
Jan Monschcc9939a2020-04-16 09:15:20 +02002547 def _print_partition_digests(self, image_filename, output, json_partitions,
2548 image_dir, image_ext):
David Zeuthen34b6b492020-04-13 14:45:02 -04002549 """Helper for printing partitions.
2550
2551 Arguments:
2552 image_filename: Image file to get information from (file object).
2553 output: Output file to write human-readable information to (file object).
Jan Monschcc9939a2020-04-16 09:15:20 +02002554 json_partitions: If not None, don't print to output, instead add partition
2555 information to this list.
David Zeuthen34b6b492020-04-13 14:45:02 -04002556 image_dir: The directory to use when looking for chained partition files.
2557 image_ext: The extension to use for chained partition files.
2558
2559 Raises:
2560 AvbError: If getting the partition digests from the image fails.
2561 """
David Zeuthen34b6b492020-04-13 14:45:02 -04002562 image = ImageHandler(image_filename)
2563 (_, _, descriptors, _) = self._parse_image(image)
2564
2565 for desc in descriptors:
2566 if isinstance(desc, AvbHashDescriptor):
2567 digest = binascii.hexlify(desc.digest).decode('ascii')
Jan Monschcc9939a2020-04-16 09:15:20 +02002568 if json_partitions is not None:
2569 json_partitions.append({'name': desc.partition_name,
2570 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002571 else:
2572 output.write('{}: {}\n'.format(desc.partition_name, digest))
2573 elif isinstance(desc, AvbHashtreeDescriptor):
2574 digest = binascii.hexlify(desc.root_digest).decode('ascii')
Jan Monschcc9939a2020-04-16 09:15:20 +02002575 if json_partitions is not None:
2576 json_partitions.append({'name': desc.partition_name,
2577 'digest': digest})
David Zeuthen34b6b492020-04-13 14:45:02 -04002578 else:
2579 output.write('{}: {}\n'.format(desc.partition_name, digest))
2580 elif isinstance(desc, AvbChainPartitionDescriptor):
2581 chained_image_filename = os.path.join(image_dir,
2582 desc.partition_name + image_ext)
Jan Monschcc9939a2020-04-16 09:15:20 +02002583 self._print_partition_digests(
2584 chained_image_filename, output, json_partitions, image_dir,
2585 image_ext)
David Zeuthen34b6b492020-04-13 14:45:02 -04002586
David Zeuthenb8643c02018-05-17 17:21:18 -04002587 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2588 """Implements the 'calculate_vbmeta_digest' command.
2589
2590 Arguments:
2591 image_filename: Image file to get information from (file object).
2592 hash_algorithm: Hash algorithm used.
2593 output: Output file to write human-readable information to (file object).
2594 """
2595
2596 image_dir = os.path.dirname(image_filename)
2597 image_ext = os.path.splitext(image_filename)[1]
2598
2599 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002600 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002601 offset = 0
2602 if footer:
2603 offset = footer.vbmeta_offset
2604 size = (header.SIZE + header.authentication_data_block_size +
2605 header.auxiliary_data_block_size)
2606 image.seek(offset)
2607 vbmeta_blob = image.read(size)
2608
Jan Monsch6f27bb12020-04-07 07:33:26 +02002609 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002610 hasher.update(vbmeta_blob)
2611
2612 for desc in descriptors:
2613 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002614 ch_image_filename = os.path.join(image_dir,
2615 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002616 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002617 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002618 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002619 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2620 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002621 if ch_footer:
2622 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002623 ch_image.seek(ch_offset)
2624 ch_vbmeta_blob = ch_image.read(ch_size)
2625 hasher.update(ch_vbmeta_blob)
2626
2627 digest = hasher.digest()
Jan Monschb1d920f2020-04-09 12:59:28 +02002628 output.write('{}\n'.format(binascii.hexlify(digest).decode('ascii')))
David Zeuthenb8643c02018-05-17 17:21:18 -04002629
David Zeuthenf7d2e752018-09-20 13:30:41 -04002630 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2631 """Implements the 'calculate_kernel_cmdline' command.
2632
2633 Arguments:
2634 image_filename: Image file to get information from (file object).
2635 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2636 output: Output file to write human-readable information to (file object).
2637 """
2638
2639 image = ImageHandler(image_filename)
2640 _, _, descriptors, _ = self._parse_image(image)
2641
2642 image_dir = os.path.dirname(image_filename)
2643 image_ext = os.path.splitext(image_filename)[1]
2644
2645 cmdline_descriptors = []
2646 for desc in descriptors:
2647 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002648 ch_image_filename = os.path.join(image_dir,
2649 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002650 ch_image = ImageHandler(ch_image_filename)
2651 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2652 for ch_desc in ch_descriptors:
2653 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2654 cmdline_descriptors.append(ch_desc)
2655 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2656 cmdline_descriptors.append(desc)
2657
2658 kernel_cmdline_snippets = []
2659 for desc in cmdline_descriptors:
2660 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002661 if ((desc.flags &
2662 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2663 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002664 if hashtree_disabled:
2665 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002666 if (desc.flags &
2667 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002668 if not hashtree_disabled:
2669 use_cmdline = False
2670 if use_cmdline:
2671 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2672 output.write(' '.join(kernel_cmdline_snippets))
2673
David Zeuthen21e95262016-07-27 17:58:40 -04002674 def _parse_image(self, image):
2675 """Gets information about an image.
2676
2677 The image can either be a vbmeta or an image with a footer.
2678
2679 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002680 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002681
2682 Returns:
2683 A tuple where the first argument is a AvbFooter (None if there
2684 is no footer on the image), the second argument is a
2685 AvbVBMetaHeader, the third argument is a list of
2686 AvbDescriptor-derived instances, and the fourth argument is the
2687 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002688
2689 Raises:
2690 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002691 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002692 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002693 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002694 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002695 try:
2696 footer = AvbFooter(image.read(AvbFooter.SIZE))
2697 except (LookupError, struct.error):
2698 # Nope, just seek back to the start.
2699 image.seek(0)
2700
2701 vbmeta_offset = 0
2702 if footer:
2703 vbmeta_offset = footer.vbmeta_offset
2704
2705 image.seek(vbmeta_offset)
2706 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2707
2708 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2709 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2710 desc_start_offset = aux_block_offset + h.descriptors_offset
2711 image.seek(desc_start_offset)
2712 descriptors = parse_descriptors(image.read(h.descriptors_size))
2713
David Zeuthen09692692016-09-30 16:16:40 -04002714 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002715
David Zeuthenb1b994d2017-03-06 18:01:31 -05002716 def _load_vbmeta_blob(self, image):
2717 """Gets the vbmeta struct and associated sections.
2718
2719 The image can either be a vbmeta.img or an image with a footer.
2720
2721 Arguments:
2722 image: An ImageHandler (vbmeta or footer).
2723
2724 Returns:
2725 A blob with the vbmeta struct and other sections.
2726 """
2727 assert isinstance(image, ImageHandler)
2728 footer = None
2729 image.seek(image.image_size - AvbFooter.SIZE)
2730 try:
2731 footer = AvbFooter(image.read(AvbFooter.SIZE))
2732 except (LookupError, struct.error):
2733 # Nope, just seek back to the start.
2734 image.seek(0)
2735
2736 vbmeta_offset = 0
2737 if footer:
2738 vbmeta_offset = footer.vbmeta_offset
2739
2740 image.seek(vbmeta_offset)
2741 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2742
2743 image.seek(vbmeta_offset)
2744 data_size = AvbVBMetaHeader.SIZE
2745 data_size += h.authentication_data_block_size
2746 data_size += h.auxiliary_data_block_size
2747 return image.read(data_size)
2748
David Zeuthen73f2afa2017-05-17 16:54:11 -04002749 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002750 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002751
2752 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002753 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002754
2755 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002756 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2757 instructions. There is one for when hashtree is not disabled and one for
2758 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002759
David Zeuthen21e95262016-07-27 17:58:40 -04002760 """
David Zeuthen21e95262016-07-27 17:58:40 -04002761 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002762 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002763 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002764 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2765 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2766 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2767 c += ' {}'.format(ht.data_block_size) # data_block
2768 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002769 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2770 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002771 c += ' {}'.format(ht.hash_algorithm) # hash_alg
Jan Monschb1d920f2020-04-09 12:59:28 +02002772 c += ' {}'.format(
2773 binascii.hexlify(ht.root_digest).decode('ascii')) # root_digest
2774 c += ' {}'.format(binascii.hexlify(ht.salt).decode('ascii')) # salt
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002775 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002776 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002777 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002778 c += ' ignore_zero_blocks'
2779 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2780 c += ' fec_roots {}'.format(ht.fec_num_roots)
2781 # Note that fec_blocks is the size that FEC covers, *not* the
2782 # size of the FEC data. Since we use FEC for everything up until
2783 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002784 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2785 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002786 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002787 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002788 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002789 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002790 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002791
David Zeuthenfd41eb92016-11-17 12:24:47 -05002792 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002793 desc = AvbKernelCmdlineDescriptor()
2794 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002795 desc.flags = (
2796 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2797
2798 # The descriptor for when hashtree verification is disabled is a lot
2799 # simpler - we just set the root to the partition.
2800 desc_no_ht = AvbKernelCmdlineDescriptor()
2801 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2802 desc_no_ht.flags = (
2803 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2804
2805 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002806
David Zeuthen73f2afa2017-05-17 16:54:11 -04002807 def _get_cmdline_descriptors_for_dm_verity(self, image):
2808 """Generate kernel cmdline descriptors for dm-verity.
2809
2810 Arguments:
2811 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2812
2813 Returns:
2814 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2815 instructions. There is one for when hashtree is not disabled and one for
2816 when it is.
2817
2818 Raises:
2819 AvbError: If |image| doesn't have a hashtree descriptor.
2820
2821 """
David Zeuthen73f2afa2017-05-17 16:54:11 -04002822 (_, _, descriptors, _) = self._parse_image(image)
2823
2824 ht = None
2825 for desc in descriptors:
2826 if isinstance(desc, AvbHashtreeDescriptor):
2827 ht = desc
2828 break
2829
2830 if not ht:
2831 raise AvbError('No hashtree descriptor in given image')
2832
2833 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2834
David Zeuthen21e95262016-07-27 17:58:40 -04002835 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002836 key_path, public_key_metadata_path, rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07002837 flags, rollback_index_location,
2838 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002839 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002840 include_descriptors_from_image,
2841 signing_helper,
2842 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002843 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002844 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002845 print_required_libavb_version,
2846 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002847 """Implements the 'make_vbmeta_image' command.
2848
2849 Arguments:
2850 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002851 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002852 algorithm_name: Name of algorithm to use.
2853 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002854 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002855 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002856 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002857 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002858 props: Properties to insert (list of strings of the form 'key:value').
2859 props_from_file: Properties to insert (list of strings 'key:<path>').
2860 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002861 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002862 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002863 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002864 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002865 release_string: None or avbtool release string to use instead of default.
2866 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002867 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002868 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002869
2870 Raises:
2871 AvbError: If a chained partition is malformed.
2872 """
David Zeuthen1097a782017-05-31 15:53:17 -04002873 # If we're asked to calculate minimum required libavb version, we're done.
Varun Sharmade538272020-04-10 15:22:31 -07002874 tmp_header = AvbVBMetaHeader()
2875 if rollback_index_location > 0:
2876 tmp_header.bump_required_libavb_version_minor(2)
2877 if include_descriptors_from_image:
2878 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2879 # version of all included descriptors.
2880 for image in include_descriptors_from_image:
2881 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2882 tmp_header.bump_required_libavb_version_minor(
2883 image_header.required_libavb_version_minor)
2884
David Zeuthen1097a782017-05-31 15:53:17 -04002885 if print_required_libavb_version:
Varun Sharmade538272020-04-10 15:22:31 -07002886 print('1.{}'.format(tmp_header.required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04002887 return
2888
2889 if not output:
2890 raise AvbError('No output file given')
2891
David Zeuthen21e95262016-07-27 17:58:40 -04002892 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002893 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002894 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002895 algorithm_name, key_path, public_key_metadata_path, descriptors,
Varun Sharmade538272020-04-10 15:22:31 -07002896 chain_partitions, rollback_index, flags, rollback_index_location,
2897 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002898 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002899 include_descriptors_from_image, signing_helper,
2900 signing_helper_with_files, release_string,
Varun Sharmade538272020-04-10 15:22:31 -07002901 append_to_release_string, tmp_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002902
2903 # Write entire vbmeta blob (header, authentication, auxiliary).
2904 output.seek(0)
2905 output.write(vbmeta_blob)
2906
David Zeuthen97cb5802017-06-01 16:14:05 -04002907 if padding_size > 0:
2908 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2909 padding_needed = padded_size - len(vbmeta_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02002910 output.write(b'\0' * padding_needed)
David Zeuthen97cb5802017-06-01 16:14:05 -04002911
David Zeuthen18666ab2016-11-15 11:18:05 -05002912 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2913 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002914 chain_partitions,
Varun Sharmade538272020-04-10 15:22:31 -07002915 rollback_index, flags, rollback_index_location,
2916 props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002917 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002918 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002919 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002920 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002921 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002922 release_string, append_to_release_string,
2923 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002924 """Generates a VBMeta blob.
2925
2926 This blob contains the header (struct AvbVBMetaHeader), the
2927 authentication data block (which contains the hash and signature
2928 for the header and auxiliary block), and the auxiliary block
2929 (which contains descriptors, the public key used, and other data).
2930
2931 The |key| parameter can |None| only if the |algorithm_name| is
2932 'NONE'.
2933
2934 Arguments:
2935 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2936 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002937 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002938 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002939 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002940 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002941 flags: Flags to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07002942 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04002943 props: Properties to insert (List of strings of the form 'key:value').
2944 props_from_file: Properties to insert (List of strings 'key:<path>').
2945 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002946 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002947 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002948 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2949 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002950 include_descriptors_from_image: List of file objects for which
2951 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002952 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002953 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002954 release_string: None or avbtool release string.
2955 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002956 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002957
2958 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02002959 The VBMeta blob as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002960
2961 Raises:
2962 Exception: If the |algorithm_name| is not found, if no key has
2963 been given and the given algorithm requires one, or the key is
2964 of the wrong size.
David Zeuthen21e95262016-07-27 17:58:40 -04002965 """
2966 try:
2967 alg = ALGORITHMS[algorithm_name]
2968 except KeyError:
2969 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2970
David Zeuthena5fd3a42017-02-27 16:38:54 -05002971 if not descriptors:
2972 descriptors = []
2973
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002974 h = AvbVBMetaHeader()
2975 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2976
David Zeuthena5fd3a42017-02-27 16:38:54 -05002977 # Insert chained partition descriptors, if any
2978 if chain_partitions:
Varun Sharmade538272020-04-10 15:22:31 -07002979 used_locations = {rollback_index_location: True}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002980 for cp in chain_partitions:
2981 cp_tokens = cp.split(':')
2982 if len(cp_tokens) != 3:
2983 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002984 partition_name = cp_tokens[0]
Varun Sharmade538272020-04-10 15:22:31 -07002985 chained_rollback_index_location = int(cp_tokens[1])
David Zeuthend8e48582017-04-21 11:31:51 -04002986 file_path = cp_tokens[2]
2987 # Check that the same rollback location isn't being used by
2988 # multiple chained partitions.
Varun Sharmade538272020-04-10 15:22:31 -07002989 if used_locations.get(chained_rollback_index_location):
David Zeuthend8e48582017-04-21 11:31:51 -04002990 raise AvbError('Rollback Index Location {} is already in use.'.format(
Varun Sharmade538272020-04-10 15:22:31 -07002991 chained_rollback_index_location))
2992 used_locations[chained_rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002993 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002994 desc.partition_name = partition_name
Varun Sharmade538272020-04-10 15:22:31 -07002995 desc.rollback_index_location = chained_rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002996 if desc.rollback_index_location < 1:
2997 raise AvbError('Rollback index location must be 1 or larger.')
Jan Monschb1d920f2020-04-09 12:59:28 +02002998 with open(file_path, 'rb') as f:
2999 desc.public_key = f.read()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003000 descriptors.append(desc)
3001
David Zeuthen21e95262016-07-27 17:58:40 -04003002 # Descriptors.
3003 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003004 for desc in descriptors:
3005 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003006
3007 # Add properties.
3008 if props:
3009 for prop in props:
3010 idx = prop.find(':')
3011 if idx == -1:
3012 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003013 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003014 desc = AvbPropertyDescriptor()
3015 desc.key = prop[0:idx]
Jan Monschee6fccd2020-04-09 19:36:13 +02003016 desc.value = prop[(idx + 1):].encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04003017 encoded_descriptors.extend(desc.encode())
3018 if props_from_file:
3019 for prop in props_from_file:
3020 idx = prop.find(':')
3021 if idx == -1:
3022 raise AvbError('Malformed property "{}".'.format(prop))
3023 desc = AvbPropertyDescriptor()
3024 desc.key = prop[0:idx]
David Zeuthen21e95262016-07-27 17:58:40 -04003025 file_path = prop[(idx + 1):]
Jan Monschb1d920f2020-04-09 12:59:28 +02003026 with open(file_path, 'rb') as f:
Jan Monsch9c130122020-04-14 13:43:51 +02003027 # pylint: disable=attribute-defined-outside-init
Jan Monschb1d920f2020-04-09 12:59:28 +02003028 desc.value = f.read()
David Zeuthen21e95262016-07-27 17:58:40 -04003029 encoded_descriptors.extend(desc.encode())
3030
David Zeuthen73f2afa2017-05-17 16:54:11 -04003031 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003032 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003033 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003034 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003035 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3036 encoded_descriptors.extend(cmdline_desc[0].encode())
3037 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003038
David Zeuthen73f2afa2017-05-17 16:54:11 -04003039 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3040 if ht_desc_to_setup:
3041 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3042 ht_desc_to_setup)
3043 encoded_descriptors.extend(cmdline_desc[0].encode())
3044 encoded_descriptors.extend(cmdline_desc[1].encode())
3045
David Zeuthen21e95262016-07-27 17:58:40 -04003046 # Add kernel command-lines.
3047 if kernel_cmdlines:
3048 for i in kernel_cmdlines:
3049 desc = AvbKernelCmdlineDescriptor()
3050 desc.kernel_cmdline = i
3051 encoded_descriptors.extend(desc.encode())
3052
3053 # Add descriptors from other images.
3054 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003055 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003056 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003057 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003058 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3059 image_handler)
3060 # Bump the required libavb version to support all included descriptors.
3061 h.bump_required_libavb_version_minor(
3062 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003063 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003064 # The --include_descriptors_from_image option is used in some setups
3065 # with images A and B where both A and B contain a descriptor
3066 # for a partition with the same name. Since it's not meaningful
3067 # to include both descriptors, only include the last seen descriptor.
3068 # See bug 76386656 for details.
3069 if hasattr(desc, 'partition_name'):
3070 key = type(desc).__name__ + '_' + desc.partition_name
3071 descriptors_dict[key] = desc.encode()
3072 else:
3073 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003074 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003075 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003076
David Zeuthen18666ab2016-11-15 11:18:05 -05003077 # Load public key metadata blob, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003078 pkmd_blob = b''
David Zeuthen18666ab2016-11-15 11:18:05 -05003079 if public_key_metadata_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003080 with open(public_key_metadata_path, 'rb') as f:
David Zeuthen18666ab2016-11-15 11:18:05 -05003081 pkmd_blob = f.read()
3082
David Zeuthen21e95262016-07-27 17:58:40 -04003083 key = None
Jan Monschb1d920f2020-04-09 12:59:28 +02003084 encoded_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003085 if alg.public_key_num_bytes > 0:
3086 if not key_path:
3087 raise AvbError('Key is required for algorithm {}'.format(
3088 algorithm_name))
Jan Monsch9c130122020-04-14 13:43:51 +02003089 encoded_key = RSAPublicKey(key_path).encode()
David Zeuthen21e95262016-07-27 17:58:40 -04003090 if len(encoded_key) != alg.public_key_num_bytes:
3091 raise AvbError('Key is wrong size for algorithm {}'.format(
3092 algorithm_name))
3093
David Zeuthene3cadca2017-02-22 21:25:46 -05003094 # Override release string, if requested.
Jan Monschb1d920f2020-04-09 12:59:28 +02003095 if isinstance(release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003096 h.release_string = release_string
3097
3098 # Append to release string, if requested. Also insert a space before.
Jan Monschb1d920f2020-04-09 12:59:28 +02003099 if isinstance(append_to_release_string, str):
David Zeuthene3cadca2017-02-22 21:25:46 -05003100 h.release_string += ' ' + append_to_release_string
3101
David Zeuthen18666ab2016-11-15 11:18:05 -05003102 # For the Auxiliary data block, descriptors are stored at offset 0,
3103 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003104 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003105 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003106 h.descriptors_offset = 0
3107 h.descriptors_size = len(encoded_descriptors)
3108 h.public_key_offset = h.descriptors_size
3109 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003110 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3111 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003112
3113 # For the Authentication data block, the hash is first and then
3114 # the signature.
3115 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003116 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003117 h.algorithm_type = alg.algorithm_type
3118 h.hash_offset = 0
3119 h.hash_size = alg.hash_num_bytes
3120 # Signature offset and size - it's stored right after the hash
3121 # (in Authentication data block).
3122 h.signature_offset = alg.hash_num_bytes
3123 h.signature_size = alg.signature_num_bytes
3124
3125 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003126 h.flags = flags
Varun Sharmade538272020-04-10 15:22:31 -07003127 h.rollback_index_location = rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04003128
3129 # Generate Header data block.
3130 header_data_blob = h.encode()
3131
3132 # Generate Auxiliary data block.
3133 aux_data_blob = bytearray()
3134 aux_data_blob.extend(encoded_descriptors)
3135 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003136 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003137 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003138 aux_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003139
3140 # Calculate the hash.
Jan Monschb1d920f2020-04-09 12:59:28 +02003141 binary_hash = b''
3142 binary_signature = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003143 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003144 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003145 ha.update(header_data_blob)
3146 ha.update(aux_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003147 binary_hash = ha.digest()
David Zeuthen21e95262016-07-27 17:58:40 -04003148
3149 # Calculate the signature.
Jan Monsch9c130122020-04-14 13:43:51 +02003150 rsa_key = RSAPublicKey(key_path)
3151 data_to_sign = header_data_blob + aux_data_blob
3152 binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
3153 signing_helper, signing_helper_with_files)
David Zeuthen21e95262016-07-27 17:58:40 -04003154
3155 # Generate Authentication data block.
3156 auth_data_blob = bytearray()
3157 auth_data_blob.extend(binary_hash)
3158 auth_data_blob.extend(binary_signature)
3159 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
Jan Monschb1d920f2020-04-09 12:59:28 +02003160 auth_data_blob.extend(b'\0' * padding_bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04003161
Jan Monschb1d920f2020-04-09 12:59:28 +02003162 return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003163
3164 def extract_public_key(self, key_path, output):
3165 """Implements the 'extract_public_key' command.
3166
3167 Arguments:
3168 key_path: The path to a RSA private key file.
3169 output: The file to write to.
Jan Monsch9c130122020-04-14 13:43:51 +02003170
3171 Raises:
3172 AvbError: If the public key could not be extracted.
David Zeuthen21e95262016-07-27 17:58:40 -04003173 """
Jan Monsch9c130122020-04-14 13:43:51 +02003174 output.write(RSAPublicKey(key_path).encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003175
David Zeuthenb1b994d2017-03-06 18:01:31 -05003176 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3177 partition_size):
3178 """Implementation of the append_vbmeta_image command.
3179
3180 Arguments:
3181 image_filename: File to add the footer to.
3182 vbmeta_image_filename: File to get vbmeta struct from.
3183 partition_size: Size of partition.
3184
3185 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003186 AvbError: If an argument is incorrect or if appending VBMeta image fialed.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003187 """
3188 image = ImageHandler(image_filename)
3189
3190 if partition_size % image.block_size != 0:
3191 raise AvbError('Partition size of {} is not a multiple of the image '
3192 'block size {}.'.format(partition_size,
3193 image.block_size))
3194
3195 # If there's already a footer, truncate the image to its original
3196 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003197 if image.image_size >= AvbFooter.SIZE:
3198 image.seek(image.image_size - AvbFooter.SIZE)
3199 try:
3200 footer = AvbFooter(image.read(AvbFooter.SIZE))
3201 # Existing footer found. Just truncate.
3202 original_image_size = footer.original_image_size
3203 image.truncate(footer.original_image_size)
3204 except (LookupError, struct.error):
3205 original_image_size = image.image_size
3206 else:
3207 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003208 original_image_size = image.image_size
3209
3210 # If anything goes wrong from here-on, restore the image back to
3211 # its original size.
3212 try:
3213 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3214 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3215
3216 # If the image isn't sparse, its size might not be a multiple of
3217 # the block size. This will screw up padding later so just grow it.
3218 if image.image_size % image.block_size != 0:
3219 assert not image.is_sparse
3220 padding_needed = image.block_size - (image.image_size%image.block_size)
3221 image.truncate(image.image_size + padding_needed)
3222
3223 # The append_raw() method requires content with size being a
3224 # multiple of |block_size| so add padding as needed. Also record
3225 # where this is written to since we'll need to put that in the
3226 # footer.
3227 vbmeta_offset = image.image_size
3228 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3229 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003230 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthenb1b994d2017-03-06 18:01:31 -05003231
3232 # Append vbmeta blob and footer
3233 image.append_raw(vbmeta_blob_with_padding)
3234 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3235
3236 # Now insert a DONT_CARE chunk with enough bytes such that the
3237 # final Footer block is at the end of partition_size..
3238 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003239 1 * image.block_size)
David Zeuthenb1b994d2017-03-06 18:01:31 -05003240
3241 # Generate the Footer that tells where the VBMeta footer
3242 # is. Also put enough padding in the front of the footer since
3243 # we'll write out an entire block.
3244 footer = AvbFooter()
3245 footer.original_image_size = original_image_size
3246 footer.vbmeta_offset = vbmeta_offset
3247 footer.vbmeta_size = len(vbmeta_blob)
3248 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003249 footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
David Zeuthenb1b994d2017-03-06 18:01:31 -05003250 footer_blob)
3251 image.append_raw(footer_blob_with_padding)
3252
Jan Monschb1d920f2020-04-09 12:59:28 +02003253 except Exception as e:
3254 # Truncate back to original size, then re-raise.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003255 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003256 raise AvbError('Appending VBMeta image failed: {}.'.format(e))
David Zeuthenb1b994d2017-03-06 18:01:31 -05003257
David Zeuthena4fee8b2016-08-22 15:20:43 -04003258 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003259 hash_algorithm, salt, chain_partitions, algorithm_name,
3260 key_path,
Varun Sharmade538272020-04-10 15:22:31 -07003261 public_key_metadata_path, rollback_index, flags,
3262 rollback_index_location, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003263 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003264 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003265 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003266 signing_helper, signing_helper_with_files,
3267 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003268 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003269 print_required_libavb_version, use_persistent_digest,
3270 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003271 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003272
3273 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003274 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003275 partition_size: Size of partition.
3276 partition_name: Name of partition (without A/B suffix).
3277 hash_algorithm: Hash algorithm to use.
3278 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003279 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003280 algorithm_name: Name of algorithm to use.
3281 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003282 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003283 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003284 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003285 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003286 props: Properties to insert (List of strings of the form 'key:value').
3287 props_from_file: Properties to insert (List of strings 'key:<path>').
3288 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003289 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003290 dm-verity kernel cmdline from.
3291 include_descriptors_from_image: List of file objects for which
3292 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003293 calc_max_image_size: Don't store the footer - instead calculate the
3294 maximum image size leaving enough room for metadata with the
3295 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003296 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003297 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003298 release_string: None or avbtool release string.
3299 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003300 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3301 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003302 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003303 use_persistent_digest: Use a persistent digest on device.
3304 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003305
3306 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003307 AvbError: If an argument is incorrect of if adding of hash_footer failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003308 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003309 required_libavb_version_minor = 0
3310 if use_persistent_digest or do_not_use_ab:
3311 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003312 if rollback_index_location > 0:
3313 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003314
David Zeuthen1097a782017-05-31 15:53:17 -04003315 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003316 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003317 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003318 return
3319
David Zeuthenbf562452017-05-17 18:04:43 -04003320 # First, calculate the maximum image size such that an image
3321 # this size + metadata (footer + vbmeta struct) fits in
3322 # |partition_size|.
3323 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003324 if partition_size < max_metadata_size:
3325 raise AvbError('Parition size of {} is too small. '
3326 'Needs to be at least {}'.format(
3327 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003328 max_image_size = partition_size - max_metadata_size
3329
3330 # If we're asked to only calculate the maximum image size, we're done.
3331 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003332 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003333 return
3334
David Zeuthena4fee8b2016-08-22 15:20:43 -04003335 image = ImageHandler(image_filename)
3336
3337 if partition_size % image.block_size != 0:
3338 raise AvbError('Partition size of {} is not a multiple of the image '
3339 'block size {}.'.format(partition_size,
3340 image.block_size))
3341
David Zeuthen21e95262016-07-27 17:58:40 -04003342 # If there's already a footer, truncate the image to its original
3343 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3344 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003345 if image.image_size >= AvbFooter.SIZE:
3346 image.seek(image.image_size - AvbFooter.SIZE)
3347 try:
3348 footer = AvbFooter(image.read(AvbFooter.SIZE))
3349 # Existing footer found. Just truncate.
3350 original_image_size = footer.original_image_size
3351 image.truncate(footer.original_image_size)
3352 except (LookupError, struct.error):
3353 original_image_size = image.image_size
3354 else:
3355 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003356 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003357
3358 # If anything goes wrong from here-on, restore the image back to
3359 # its original size.
3360 try:
David Zeuthen09692692016-09-30 16:16:40 -04003361 # If image size exceeds the maximum image size, fail.
3362 if image.image_size > max_image_size:
3363 raise AvbError('Image size of {} exceeds maximum image '
3364 'size of {} in order to fit in a partition '
3365 'size of {}.'.format(image.image_size, max_image_size,
3366 partition_size))
3367
Jan Monsch6f27bb12020-04-07 07:33:26 +02003368 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003369 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003370 salt = binascii.unhexlify(salt)
3371 elif salt is None and not use_persistent_digest:
3372 # If salt is not explicitly specified, choose a hash that's the same
3373 # size as the hash size. Don't populate a random salt if this
3374 # descriptor is being created to use a persistent digest on device.
3375 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003376 with open('/dev/urandom', 'rb') as f:
3377 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003378 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003379 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003380
Jan Monsch6f27bb12020-04-07 07:33:26 +02003381 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003382 # TODO(zeuthen): might want to read this in chunks to avoid
3383 # memory pressure, then again, this is only supposed to be used
3384 # on kernel/initramfs partitions. Possible optimization.
3385 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003386 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003387 digest = hasher.digest()
3388
3389 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003390 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003391 h_desc.hash_algorithm = hash_algorithm
3392 h_desc.partition_name = partition_name
3393 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003394 h_desc.flags = 0
3395 if do_not_use_ab:
3396 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3397 if not use_persistent_digest:
3398 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003399
3400 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003401 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003402 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003403 algorithm_name, key_path, public_key_metadata_path, [h_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003404 chain_partitions, rollback_index, flags, rollback_index_location,
3405 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003406 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003407 include_descriptors_from_image, signing_helper,
3408 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003409 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003410
David Zeuthend247fcb2017-02-16 12:09:27 -05003411 # Write vbmeta blob, if requested.
3412 if output_vbmeta_image:
3413 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003414
David Zeuthend247fcb2017-02-16 12:09:27 -05003415 # Append vbmeta blob and footer, unless requested not to.
3416 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003417 # If the image isn't sparse, its size might not be a multiple of
3418 # the block size. This will screw up padding later so just grow it.
3419 if image.image_size % image.block_size != 0:
3420 assert not image.is_sparse
3421 padding_needed = image.block_size - (
3422 image.image_size % image.block_size)
3423 image.truncate(image.image_size + padding_needed)
3424
3425 # The append_raw() method requires content with size being a
3426 # multiple of |block_size| so add padding as needed. Also record
3427 # where this is written to since we'll need to put that in the
3428 # footer.
3429 vbmeta_offset = image.image_size
3430 padding_needed = (
3431 round_to_multiple(len(vbmeta_blob), image.block_size) -
3432 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003433 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003434
David Zeuthend247fcb2017-02-16 12:09:27 -05003435 image.append_raw(vbmeta_blob_with_padding)
3436 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3437
3438 # Now insert a DONT_CARE chunk with enough bytes such that the
3439 # final Footer block is at the end of partition_size..
3440 image.append_dont_care(partition_size - vbmeta_end_offset -
Jan Monschb1d920f2020-04-09 12:59:28 +02003441 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003442
3443 # Generate the Footer that tells where the VBMeta footer
3444 # is. Also put enough padding in the front of the footer since
3445 # we'll write out an entire block.
3446 footer = AvbFooter()
3447 footer.original_image_size = original_image_size
3448 footer.vbmeta_offset = vbmeta_offset
3449 footer.vbmeta_size = len(vbmeta_blob)
3450 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003451 footer_blob_with_padding = (
3452 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003453 image.append_raw(footer_blob_with_padding)
Jan Monschb1d920f2020-04-09 12:59:28 +02003454 except Exception as e:
3455 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003456 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003457 raise AvbError('Adding hash_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003458
David Zeuthena4fee8b2016-08-22 15:20:43 -04003459 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003460 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003461 block_size, salt, chain_partitions, algorithm_name,
3462 key_path,
3463 public_key_metadata_path, rollback_index, flags,
Varun Sharmade538272020-04-10 15:22:31 -07003464 rollback_index_location,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003465 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003466 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003467 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003468 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003469 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003470 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003471 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003472 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003473 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003474 use_persistent_root_digest, do_not_use_ab,
3475 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003476 """Implements the 'add_hashtree_footer' command.
3477
3478 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3479 more information about dm-verity and these hashes.
3480
3481 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003482 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003483 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003484 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003485 generate_fec: If True, generate FEC codes.
3486 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003487 hash_algorithm: Hash algorithm to use.
3488 block_size: Block size to use.
3489 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003490 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003491 algorithm_name: Name of algorithm to use.
3492 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003493 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003494 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003495 flags: Flags value to use in the image.
Varun Sharmade538272020-04-10 15:22:31 -07003496 rollback_index_location: Location of the main vbmeta rollback index.
David Zeuthen21e95262016-07-27 17:58:40 -04003497 props: Properties to insert (List of strings of the form 'key:value').
3498 props_from_file: Properties to insert (List of strings 'key:<path>').
3499 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003500 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003501 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003502 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3503 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003504 include_descriptors_from_image: List of file objects for which
3505 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003506 calc_max_image_size: Don't store the hashtree or footer - instead
3507 calculate the maximum image size leaving enough room for hashtree
3508 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003509 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003510 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003511 release_string: None or avbtool release string.
3512 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003513 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3514 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003515 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003516 use_persistent_root_digest: Use a persistent root digest on device.
3517 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003518 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003519
3520 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003521 AvbError: If an argument is incorrect or adding the hashtree footer
3522 failed.
David Zeuthen21e95262016-07-27 17:58:40 -04003523 """
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003524 required_libavb_version_minor = 0
3525 if use_persistent_root_digest or do_not_use_ab:
3526 required_libavb_version_minor = 1
Varun Sharmade538272020-04-10 15:22:31 -07003527 if rollback_index_location > 0:
3528 required_libavb_version_minor = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003529
David Zeuthen1097a782017-05-31 15:53:17 -04003530 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003531 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003532 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003533 return
3534
Jan Monsch6f27bb12020-04-07 07:33:26 +02003535 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen09692692016-09-30 16:16:40 -04003536 digest_padding = round_to_pow2(digest_size) - digest_size
3537
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003538 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3539 # size such that an image this size + the hashtree + metadata (footer +
3540 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3541 # for metadata.
3542 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003543 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003544 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003545 if not no_hashtree:
3546 (_, max_tree_size) = calc_hash_level_offsets(
3547 partition_size, block_size, digest_size + digest_padding)
3548 if generate_fec:
3549 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003550 max_metadata_size = (max_fec_size + max_tree_size +
3551 self.MAX_VBMETA_SIZE +
3552 self.MAX_FOOTER_SIZE)
3553 max_image_size = partition_size - max_metadata_size
3554 else:
3555 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003556
3557 # If we're asked to only calculate the maximum image size, we're done.
3558 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003559 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003560 return
3561
David Zeuthena4fee8b2016-08-22 15:20:43 -04003562 image = ImageHandler(image_filename)
3563
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003564 if partition_size > 0:
3565 if partition_size % image.block_size != 0:
3566 raise AvbError('Partition size of {} is not a multiple of the image '
3567 'block size {}.'.format(partition_size,
3568 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003569 elif image.image_size % image.block_size != 0:
3570 raise AvbError('File size of {} is not a multiple of the image '
3571 'block size {}.'.format(image.image_size,
3572 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003573
David Zeuthen21e95262016-07-27 17:58:40 -04003574 # If there's already a footer, truncate the image to its original
3575 # size. This way 'avbtool add_hashtree_footer' is idempotent
3576 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003577 if image.image_size >= AvbFooter.SIZE:
3578 image.seek(image.image_size - AvbFooter.SIZE)
3579 try:
3580 footer = AvbFooter(image.read(AvbFooter.SIZE))
3581 # Existing footer found. Just truncate.
3582 original_image_size = footer.original_image_size
3583 image.truncate(footer.original_image_size)
3584 except (LookupError, struct.error):
3585 original_image_size = image.image_size
3586 else:
3587 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003588 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003589
3590 # If anything goes wrong from here-on, restore the image back to
3591 # its original size.
3592 try:
3593 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003594 rounded_image_size = round_to_multiple(image.image_size, block_size)
3595 if rounded_image_size > image.image_size:
3596 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003597
David Zeuthen09692692016-09-30 16:16:40 -04003598 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003599 if partition_size > 0:
3600 if image.image_size > max_image_size:
3601 raise AvbError('Image size of {} exceeds maximum image '
3602 'size of {} in order to fit in a partition '
3603 'size of {}.'.format(image.image_size, max_image_size,
3604 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003605
3606 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003607 salt = binascii.unhexlify(salt)
3608 elif salt is None and not use_persistent_root_digest:
3609 # If salt is not explicitly specified, choose a hash that's the same
3610 # size as the hash size. Don't populate a random salt if this
3611 # descriptor is being created to use a persistent digest on device.
3612 hash_size = digest_size
Jan Monschb1d920f2020-04-09 12:59:28 +02003613 with open('/dev/urandom') as f:
3614 salt = f.read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003615 else:
Jan Monschb1d920f2020-04-09 12:59:28 +02003616 salt = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003617
David Zeuthena4fee8b2016-08-22 15:20:43 -04003618 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003619 # offsets in advance.
3620 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003621 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003622
David Zeuthena4fee8b2016-08-22 15:20:43 -04003623 # If the image isn't sparse, its size might not be a multiple of
3624 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003625 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003626 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003627 padding_needed = image.block_size - (image.image_size%image.block_size)
3628 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003629
David Zeuthena4fee8b2016-08-22 15:20:43 -04003630 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003631 tree_offset = image.image_size
3632 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003633 block_size,
3634 hash_algorithm, salt,
3635 digest_padding,
3636 hash_level_offsets,
3637 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003638
3639 # Generate HashtreeDescriptor with details about the tree we
3640 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003641 if no_hashtree:
3642 tree_size = 0
Jan Monschb1d920f2020-04-09 12:59:28 +02003643 hash_tree = b''
David Zeuthen21e95262016-07-27 17:58:40 -04003644 ht_desc = AvbHashtreeDescriptor()
3645 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003646 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003647 ht_desc.tree_offset = tree_offset
3648 ht_desc.tree_size = tree_size
3649 ht_desc.data_block_size = block_size
3650 ht_desc.hash_block_size = block_size
3651 ht_desc.hash_algorithm = hash_algorithm
3652 ht_desc.partition_name = partition_name
3653 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003654 if do_not_use_ab:
3655 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3656 if not use_persistent_root_digest:
3657 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003658
David Zeuthen09692692016-09-30 16:16:40 -04003659 # Write the hash tree
3660 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3661 len(hash_tree))
Jan Monschb1d920f2020-04-09 12:59:28 +02003662 hash_tree_with_padding = hash_tree + b'\0' * padding_needed
David Zeuthen09692692016-09-30 16:16:40 -04003663 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003664 len_hashtree_and_fec = len(hash_tree_with_padding)
3665
3666 # Generate FEC codes, if requested.
3667 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003668 if no_hashtree:
Jan Monschb1d920f2020-04-09 12:59:28 +02003669 fec_data = b''
Tao Bao868db2a2019-09-09 13:35:05 -07003670 else:
3671 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003672 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3673 len(fec_data))
Jan Monschb1d920f2020-04-09 12:59:28 +02003674 fec_data_with_padding = fec_data + b'\0' * padding_needed
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003675 fec_offset = image.image_size
3676 image.append_raw(fec_data_with_padding)
3677 len_hashtree_and_fec += len(fec_data_with_padding)
3678 # Update the hashtree descriptor.
3679 ht_desc.fec_num_roots = fec_num_roots
3680 ht_desc.fec_offset = fec_offset
3681 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003682
David Zeuthen73f2afa2017-05-17 16:54:11 -04003683 ht_desc_to_setup = None
3684 if setup_as_rootfs_from_kernel:
3685 ht_desc_to_setup = ht_desc
3686
David Zeuthena4fee8b2016-08-22 15:20:43 -04003687 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003688 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003689 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003690 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
Varun Sharmade538272020-04-10 15:22:31 -07003691 chain_partitions, rollback_index, flags, rollback_index_location,
3692 props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003693 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003694 include_descriptors_from_image, signing_helper,
3695 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003696 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003697 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3698 len(vbmeta_blob))
Jan Monschb1d920f2020-04-09 12:59:28 +02003699 vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003700
David Zeuthend247fcb2017-02-16 12:09:27 -05003701 # Write vbmeta blob, if requested.
3702 if output_vbmeta_image:
3703 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003704
David Zeuthend247fcb2017-02-16 12:09:27 -05003705 # Append vbmeta blob and footer, unless requested not to.
3706 if not do_not_append_vbmeta_image:
3707 image.append_raw(vbmeta_blob_with_padding)
3708
3709 # Now insert a DONT_CARE chunk with enough bytes such that the
3710 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003711 if partition_size > 0:
3712 image.append_dont_care(partition_size - image.image_size -
Jan Monschb1d920f2020-04-09 12:59:28 +02003713 1 * image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003714
3715 # Generate the Footer that tells where the VBMeta footer
3716 # is. Also put enough padding in the front of the footer since
3717 # we'll write out an entire block.
3718 footer = AvbFooter()
3719 footer.original_image_size = original_image_size
3720 footer.vbmeta_offset = vbmeta_offset
3721 footer.vbmeta_size = len(vbmeta_blob)
3722 footer_blob = footer.encode()
Jan Monschb1d920f2020-04-09 12:59:28 +02003723 footer_blob_with_padding = (
3724 b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
David Zeuthend247fcb2017-02-16 12:09:27 -05003725 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003726
Jan Monschb1d920f2020-04-09 12:59:28 +02003727 except Exception as e:
David Zeuthen09692692016-09-30 16:16:40 -04003728 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003729 image.truncate(original_image_size)
Jan Monschb1d920f2020-04-09 12:59:28 +02003730 raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04003731
David Zeuthenc68f0822017-03-31 17:22:35 -04003732 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003733 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003734 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003735 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003736 """Implements the 'make_atx_certificate' command.
3737
3738 Android Things certificates are required for Android Things public key
3739 metadata. They chain the vbmeta signing key for a particular product back to
3740 a fused, permanent root key. These certificates are fixed-length and fixed-
3741 format with the explicit goal of not parsing ASN.1 in bootloader code.
3742
3743 Arguments:
3744 output: Certificate will be written to this file on success.
3745 authority_key_path: A PEM file path with the authority private key.
3746 If None, then a certificate will be created without a
3747 signature. The signature can be created out-of-band
3748 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003749 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003750 subject_key_version: A 64-bit version value. If this is None, the number
3751 of seconds since the epoch is used.
3752 subject: A subject identifier. For Product Signing Key certificates this
3753 should be the same Product ID found in the permanent attributes.
3754 is_intermediate_authority: True if the certificate is for an intermediate
3755 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003756 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003757 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003758 signing_helper_with_files: Same as signing_helper but uses files instead.
Jan Monsch9c130122020-04-14 13:43:51 +02003759
3760 Raises:
3761 AvbError: If there an error during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -08003762 """
3763 signed_data = bytearray()
3764 signed_data.extend(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003765 signed_data.extend(RSAPublicKey(subject_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003766 hasher = hashlib.sha256()
3767 hasher.update(subject)
3768 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003769 if not usage:
3770 usage = 'com.google.android.things.vboot'
3771 if is_intermediate_authority:
3772 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003773 hasher = hashlib.sha256()
Jan Monschb1d920f2020-04-09 12:59:28 +02003774 hasher.update(usage.encode('ascii'))
Darren Krahn147b08d2016-12-20 16:38:29 -08003775 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003776 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003777 subject_key_version = int(time.time())
3778 signed_data.extend(struct.pack('<Q', subject_key_version))
Jan Monschb1d920f2020-04-09 12:59:28 +02003779 signature = b''
Darren Krahn147b08d2016-12-20 16:38:29 -08003780 if authority_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003781 rsa_key = RSAPublicKey(authority_key_path)
Darren Krahn43e12d82017-02-24 16:26:31 -08003782 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003783 signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3784 signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08003785 output.write(signed_data)
3786 output.write(signature)
3787
David Zeuthenc68f0822017-03-31 17:22:35 -04003788 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003789 product_id):
3790 """Implements the 'make_atx_permanent_attributes' command.
3791
3792 Android Things permanent attributes are designed to be permanent for a
3793 particular product and a hash of these attributes should be fused into
3794 hardware to enforce this.
3795
3796 Arguments:
3797 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003798 root_authority_key_path: Path to a PEM or DER public key for
3799 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003800 product_id: A 16-byte Product ID.
3801
3802 Raises:
3803 AvbError: If an argument is incorrect.
3804 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003805 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003806 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003807 raise AvbError('Invalid Product ID length.')
3808 output.write(struct.pack('<I', 1)) # Format Version
Jan Monsch9c130122020-04-14 13:43:51 +02003809 output.write(RSAPublicKey(root_authority_key_path).encode())
Darren Krahn147b08d2016-12-20 16:38:29 -08003810 output.write(product_id)
3811
3812 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003813 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003814 """Implements the 'make_atx_metadata' command.
3815
3816 Android Things metadata are included in vbmeta images to facilitate
3817 verification. The output of this command can be used as the
3818 public_key_metadata argument to other commands.
3819
3820 Arguments:
3821 output: Metadata will be written to this file on success.
3822 intermediate_key_certificate: A certificate file as output by
3823 make_atx_certificate with
3824 is_intermediate_authority set to true.
3825 product_key_certificate: A certificate file as output by
3826 make_atx_certificate with
3827 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003828
3829 Raises:
3830 AvbError: If an argument is incorrect.
3831 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003832 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003833 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003834 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003835 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003836 raise AvbError('Invalid product key certificate length.')
3837 output.write(struct.pack('<I', 1)) # Format Version
3838 output.write(intermediate_key_certificate)
3839 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003840
Darren Krahnfccd64e2018-01-16 17:39:35 -08003841 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3842 unlock_key_certificate, challenge_path,
3843 unlock_key_path, signing_helper,
3844 signing_helper_with_files):
3845 """Implements the 'make_atx_unlock_credential' command.
3846
3847 Android Things unlock credentials can be used to authorize the unlock of AVB
3848 on a device. These credentials are presented to an Android Things bootloader
3849 via the fastboot interface in response to a 16-byte challenge. This method
3850 creates all fields of the credential except the challenge signature field
3851 (which is the last field) and can optionally create the challenge signature
3852 field as well if a challenge and the unlock_key_path is provided.
3853
3854 Arguments:
3855 output: The credential will be written to this file on success.
3856 intermediate_key_certificate: A certificate file as output by
3857 make_atx_certificate with
3858 is_intermediate_authority set to true.
3859 unlock_key_certificate: A certificate file as output by
3860 make_atx_certificate with
3861 is_intermediate_authority set to false and the
3862 usage set to
3863 'com.google.android.things.vboot.unlock'.
3864 challenge_path: [optional] A path to the challenge to sign.
3865 unlock_key_path: [optional] A PEM file path with the unlock private key.
3866 signing_helper: Program which signs a hash and returns the signature.
3867 signing_helper_with_files: Same as signing_helper but uses files instead.
3868
3869 Raises:
Jan Monsch9c130122020-04-14 13:43:51 +02003870 AvbError: If an argument is incorrect or an error occurs during signing.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003871 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003872 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3873 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003874 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3875 raise AvbError('Invalid intermediate key certificate length.')
3876 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3877 raise AvbError('Invalid product key certificate length.')
Jan Monschb1d920f2020-04-09 12:59:28 +02003878 challenge = b''
Darren Krahnfccd64e2018-01-16 17:39:35 -08003879 if challenge_path:
Jan Monschb1d920f2020-04-09 12:59:28 +02003880 with open(challenge_path, 'rb') as f:
Darren Krahnfccd64e2018-01-16 17:39:35 -08003881 challenge = f.read()
3882 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3883 raise AvbError('Invalid unlock challenge length.')
3884 output.write(struct.pack('<I', 1)) # Format Version
3885 output.write(intermediate_key_certificate)
3886 output.write(unlock_key_certificate)
3887 if challenge_path and unlock_key_path:
Jan Monsch9c130122020-04-14 13:43:51 +02003888 rsa_key = RSAPublicKey(unlock_key_path)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003889 algorithm_name = 'SHA512_RSA4096'
Jan Monsch9c130122020-04-14 13:43:51 +02003890 signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3891 signing_helper_with_files)
Darren Krahnfccd64e2018-01-16 17:39:35 -08003892 output.write(signature)
3893
David Zeuthen21e95262016-07-27 17:58:40 -04003894
3895def calc_hash_level_offsets(image_size, block_size, digest_size):
3896 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3897
3898 Arguments:
3899 image_size: The size of the image to calculate a Merkle-tree for.
3900 block_size: The block size, e.g. 4096.
3901 digest_size: The size of each hash, e.g. 32 for SHA-256.
3902
3903 Returns:
3904 A tuple where the first argument is an array of offsets and the
3905 second is size of the tree, in bytes.
3906 """
3907 level_offsets = []
3908 level_sizes = []
3909 tree_size = 0
3910
3911 num_levels = 0
3912 size = image_size
3913 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003914 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003915 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3916
3917 level_sizes.append(level_size)
3918 tree_size += level_size
3919 num_levels += 1
3920
3921 size = level_size
3922
3923 for n in range(0, num_levels):
3924 offset = 0
3925 for m in range(n + 1, num_levels):
3926 offset += level_sizes[m]
3927 level_offsets.append(offset)
3928
David Zeuthena4fee8b2016-08-22 15:20:43 -04003929 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003930
3931
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003932# See system/extras/libfec/include/fec/io.h for these definitions.
3933FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3934FEC_MAGIC = 0xfecfecfe
3935
3936
3937def calc_fec_data_size(image_size, num_roots):
3938 """Calculates how much space FEC data will take.
3939
Jan Monschfe00c0a2019-12-11 11:19:40 +01003940 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003941 image_size: The size of the image.
3942 num_roots: Number of roots.
3943
3944 Returns:
3945 The number of bytes needed for FEC for an image of the given size
3946 and with the requested number of FEC roots.
3947
3948 Raises:
3949 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003950 """
3951 p = subprocess.Popen(
3952 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3953 stdout=subprocess.PIPE,
3954 stderr=subprocess.PIPE)
3955 (pout, perr) = p.communicate()
3956 retcode = p.wait()
3957 if retcode != 0:
3958 raise ValueError('Error invoking fec: {}'.format(perr))
3959 return int(pout)
3960
3961
3962def generate_fec_data(image_filename, num_roots):
3963 """Generate FEC codes for an image.
3964
Jan Monschfe00c0a2019-12-11 11:19:40 +01003965 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003966 image_filename: The filename of the image.
3967 num_roots: Number of roots.
3968
3969 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02003970 The FEC data blob as bytes.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003971
3972 Raises:
Jan Monschb1d920f2020-04-09 12:59:28 +02003973 ValueError: If calling the 'fec' tool failed or the output is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003974 """
Jan Monschb1d920f2020-04-09 12:59:28 +02003975 with tempfile.NamedTemporaryFile() as fec_tmpfile:
3976 try:
3977 subprocess.check_call(
3978 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3979 fec_tmpfile.name],
3980 stderr=open(os.devnull, 'wb'))
3981 except subprocess.CalledProcessError as e:
3982 raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
3983 fec_data = fec_tmpfile.read()
3984
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003985 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3986 footer_data = fec_data[-footer_size:]
3987 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3988 footer_data)
3989 if magic != FEC_MAGIC:
3990 raise ValueError('Unexpected magic in FEC footer')
3991 return fec_data[0:fec_size]
3992
3993
David Zeuthen21e95262016-07-27 17:58:40 -04003994def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003995 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003996 """Generates a Merkle-tree for a file.
3997
Jan Monschfe00c0a2019-12-11 11:19:40 +01003998 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003999 image: The image, as a file.
4000 image_size: The size of the image.
4001 block_size: The block size, e.g. 4096.
4002 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4003 salt: The salt to use.
4004 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004005 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004006 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004007
4008 Returns:
Jan Monschb1d920f2020-04-09 12:59:28 +02004009 A tuple where the first element is the top-level hash as bytes and the
4010 second element is the hash-tree as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004011 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004012 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004013 hash_src_offset = 0
4014 hash_src_size = image_size
4015 level_num = 0
4016 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08004017 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04004018 remaining = hash_src_size
4019 while remaining > 0:
Jan Monsch6f27bb12020-04-07 07:33:26 +02004020 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004021 # Only read from the file for the first level - for subsequent
4022 # levels, access the array we're building.
4023 if level_num == 0:
4024 image.seek(hash_src_offset + hash_src_size - remaining)
4025 data = image.read(min(remaining, block_size))
4026 else:
4027 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4028 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004029 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004030
4031 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004032 if len(data) < block_size:
Jan Monschb1d920f2020-04-09 12:59:28 +02004033 hasher.update(b'\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08004034 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04004035 if digest_padding > 0:
Jan Monschb1d920f2020-04-09 12:59:28 +02004036 level_output_list.append(b'\0' * digest_padding)
Colin Cross388338a2020-02-28 14:18:01 -08004037
Jan Monschb1d920f2020-04-09 12:59:28 +02004038 level_output = b''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04004039
4040 padding_needed = (round_to_multiple(
4041 len(level_output), block_size) - len(level_output))
Jan Monschb1d920f2020-04-09 12:59:28 +02004042 level_output += b'\0' * padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004043
David Zeuthena4fee8b2016-08-22 15:20:43 -04004044 # Copy level-output into resulting tree.
4045 offset = hash_level_offsets[level_num]
4046 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004047
David Zeuthena4fee8b2016-08-22 15:20:43 -04004048 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004049 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004050 level_num += 1
4051
Jan Monsch6f27bb12020-04-07 07:33:26 +02004052 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04004053 hasher.update(level_output)
Jan Monschb1d920f2020-04-09 12:59:28 +02004054 return hasher.digest(), bytes(hash_ret)
David Zeuthen21e95262016-07-27 17:58:40 -04004055
4056
4057class AvbTool(object):
4058 """Object for avbtool command-line tool."""
4059
4060 def __init__(self):
4061 """Initializer method."""
4062 self.avb = Avb()
4063
4064 def _add_common_args(self, sub_parser):
4065 """Adds arguments used by several sub-commands.
4066
4067 Arguments:
4068 sub_parser: The parser to add arguments to.
4069 """
4070 sub_parser.add_argument('--algorithm',
4071 help='Algorithm to use (default: NONE)',
4072 metavar='ALGORITHM',
4073 default='NONE')
4074 sub_parser.add_argument('--key',
4075 help='Path to RSA private key file',
4076 metavar='KEY',
4077 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004078 sub_parser.add_argument('--signing_helper',
4079 help='Path to helper used for signing',
4080 metavar='APP',
4081 default=None,
4082 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004083 sub_parser.add_argument('--signing_helper_with_files',
4084 help='Path to helper used for signing using files',
4085 metavar='APP',
4086 default=None,
4087 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004088 sub_parser.add_argument('--public_key_metadata',
4089 help='Path to public key metadata file',
4090 metavar='KEY_METADATA',
4091 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004092 sub_parser.add_argument('--rollback_index',
4093 help='Rollback Index',
4094 type=parse_number,
4095 default=0)
Varun Sharmade538272020-04-10 15:22:31 -07004096 sub_parser.add_argument('--rollback_index_location',
4097 help='Location of main vbmeta Rollback Index',
4098 type=parse_number,
4099 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004100 # This is used internally for unit tests. Do not include in --help output.
4101 sub_parser.add_argument('--internal_release_string',
4102 help=argparse.SUPPRESS)
4103 sub_parser.add_argument('--append_to_release_string',
4104 help='Text to append to release string',
4105 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004106 sub_parser.add_argument('--prop',
4107 help='Add property',
4108 metavar='KEY:VALUE',
4109 action='append')
4110 sub_parser.add_argument('--prop_from_file',
4111 help='Add property from file',
4112 metavar='KEY:PATH',
4113 action='append')
4114 sub_parser.add_argument('--kernel_cmdline',
4115 help='Add kernel cmdline',
4116 metavar='CMDLINE',
4117 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004118 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4119 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4120 # at some future point.
4121 sub_parser.add_argument('--setup_rootfs_from_kernel',
4122 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004123 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004124 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004125 type=argparse.FileType('rb'))
4126 sub_parser.add_argument('--include_descriptors_from_image',
4127 help='Include descriptors from image',
4128 metavar='IMAGE',
4129 action='append',
4130 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004131 sub_parser.add_argument('--print_required_libavb_version',
4132 help=('Don\'t store the footer - '
4133 'instead calculate the required libavb '
4134 'version for the given options.'),
4135 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004136 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4137 sub_parser.add_argument('--chain_partition',
4138 help='Allow signed integrity-data for partition',
4139 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4140 action='append')
4141 sub_parser.add_argument('--flags',
4142 help='VBMeta flags',
4143 type=parse_number,
4144 default=0)
4145 sub_parser.add_argument('--set_hashtree_disabled_flag',
4146 help='Set the HASHTREE_DISABLED flag',
4147 action='store_true')
4148
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004149 def _add_common_footer_args(self, sub_parser):
4150 """Adds arguments used by add_*_footer sub-commands.
4151
4152 Arguments:
4153 sub_parser: The parser to add arguments to.
4154 """
4155 sub_parser.add_argument('--use_persistent_digest',
4156 help='Use a persistent digest on device instead of '
4157 'storing the digest in the descriptor. This '
4158 'cannot be used with A/B so must be combined '
4159 'with --do_not_use_ab when an A/B suffix is '
4160 'expected at runtime.',
4161 action='store_true')
4162 sub_parser.add_argument('--do_not_use_ab',
4163 help='The partition does not use A/B even when an '
4164 'A/B suffix is present. This must not be used '
4165 'for vbmeta or chained partitions.',
4166 action='store_true')
4167
David Zeuthena5fd3a42017-02-27 16:38:54 -05004168 def _fixup_common_args(self, args):
4169 """Common fixups needed by subcommands.
4170
4171 Arguments:
4172 args: Arguments to modify.
4173
4174 Returns:
4175 The modified arguments.
4176 """
4177 if args.set_hashtree_disabled_flag:
4178 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4179 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004180
4181 def run(self, argv):
4182 """Command-line processor.
4183
4184 Arguments:
4185 argv: Pass sys.argv from main.
4186 """
4187 parser = argparse.ArgumentParser()
4188 subparsers = parser.add_subparsers(title='subcommands')
4189
Jan Monsch2c7be992020-04-03 14:37:13 +02004190 sub_parser = subparsers.add_parser(
4191 'generate_test_image',
4192 help=('Generates a test image with a known pattern for testing: '
4193 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4194 sub_parser.add_argument('--image_size',
4195 help='Size of image to generate.',
4196 type=parse_number,
4197 required=True)
4198 sub_parser.add_argument('--start_byte',
4199 help='Integer for the start byte of the pattern.',
4200 type=parse_number,
4201 default=0)
4202 sub_parser.add_argument('--output',
4203 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004204 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004205 default=sys.stdout)
4206 sub_parser.set_defaults(func=self.generate_test_image)
4207
David Zeuthen21e95262016-07-27 17:58:40 -04004208 sub_parser = subparsers.add_parser('version',
4209 help='Prints version of avbtool.')
4210 sub_parser.set_defaults(func=self.version)
4211
4212 sub_parser = subparsers.add_parser('extract_public_key',
4213 help='Extract public key.')
4214 sub_parser.add_argument('--key',
4215 help='Path to RSA private key file',
4216 required=True)
4217 sub_parser.add_argument('--output',
4218 help='Output file name',
4219 type=argparse.FileType('wb'),
4220 required=True)
4221 sub_parser.set_defaults(func=self.extract_public_key)
4222
4223 sub_parser = subparsers.add_parser('make_vbmeta_image',
4224 help='Makes a vbmeta image.')
4225 sub_parser.add_argument('--output',
4226 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004227 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004228 sub_parser.add_argument('--padding_size',
4229 metavar='NUMBER',
4230 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004231 'its size is a multiple of NUMBER '
4232 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004233 type=parse_number,
4234 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004235 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004236 sub_parser.set_defaults(func=self.make_vbmeta_image)
4237
4238 sub_parser = subparsers.add_parser('add_hash_footer',
4239 help='Add hashes and footer to image.')
4240 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004241 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004242 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004243 sub_parser.add_argument('--partition_size',
4244 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004245 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004246 sub_parser.add_argument('--partition_name',
4247 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004248 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004249 sub_parser.add_argument('--hash_algorithm',
4250 help='Hash algorithm to use (default: sha256)',
4251 default='sha256')
4252 sub_parser.add_argument('--salt',
4253 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004254 sub_parser.add_argument('--calc_max_image_size',
4255 help=('Don\'t store the footer - '
4256 'instead calculate the maximum image size '
4257 'leaving enough room for metadata with '
4258 'the given partition size.'),
4259 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004260 sub_parser.add_argument('--output_vbmeta_image',
4261 help='Also write vbmeta struct to file',
4262 type=argparse.FileType('wb'))
4263 sub_parser.add_argument('--do_not_append_vbmeta_image',
4264 help=('Do not append vbmeta struct or footer '
4265 'to the image'),
4266 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004267 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004268 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004269 sub_parser.set_defaults(func=self.add_hash_footer)
4270
David Zeuthenb1b994d2017-03-06 18:01:31 -05004271 sub_parser = subparsers.add_parser('append_vbmeta_image',
4272 help='Append vbmeta image to image.')
4273 sub_parser.add_argument('--image',
4274 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004275 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004276 sub_parser.add_argument('--partition_size',
4277 help='Partition size',
4278 type=parse_number,
4279 required=True)
4280 sub_parser.add_argument('--vbmeta_image',
4281 help='Image with vbmeta blob to append',
4282 type=argparse.FileType('rb'))
4283 sub_parser.set_defaults(func=self.append_vbmeta_image)
4284
Jan Monscheeb28b62019-12-05 16:17:09 +01004285 sub_parser = subparsers.add_parser(
4286 'add_hashtree_footer',
4287 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004288 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004289 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004290 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004291 sub_parser.add_argument('--partition_size',
4292 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004293 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004294 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004295 sub_parser.add_argument('--partition_name',
4296 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004297 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004298 sub_parser.add_argument('--hash_algorithm',
4299 help='Hash algorithm to use (default: sha1)',
4300 default='sha1')
4301 sub_parser.add_argument('--salt',
4302 help='Salt in hex (default: /dev/urandom)')
4303 sub_parser.add_argument('--block_size',
4304 help='Block size (default: 4096)',
4305 type=parse_number,
4306 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004307 # TODO(zeuthen): The --generate_fec option was removed when we
4308 # moved to generating FEC by default. To avoid breaking existing
4309 # users needing to transition we simply just print a warning below
4310 # in add_hashtree_footer(). Remove this option and the warning at
4311 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004312 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004313 help=argparse.SUPPRESS,
4314 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004315 sub_parser.add_argument(
4316 '--do_not_generate_fec',
4317 help='Do not generate forward-error-correction codes',
4318 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004319 sub_parser.add_argument('--fec_num_roots',
4320 help='Number of roots for FEC (default: 2)',
4321 type=parse_number,
4322 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004323 sub_parser.add_argument('--calc_max_image_size',
4324 help=('Don\'t store the hashtree or footer - '
4325 'instead calculate the maximum image size '
4326 'leaving enough room for hashtree '
4327 'and metadata with the given partition '
4328 'size.'),
4329 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004330 sub_parser.add_argument('--output_vbmeta_image',
4331 help='Also write vbmeta struct to file',
4332 type=argparse.FileType('wb'))
4333 sub_parser.add_argument('--do_not_append_vbmeta_image',
4334 help=('Do not append vbmeta struct or footer '
4335 'to the image'),
4336 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004337 # This is different from --setup_rootfs_from_kernel insofar that
4338 # it doesn't take an IMAGE, the generated cmdline will be for the
4339 # hashtree we're adding.
4340 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4341 action='store_true',
4342 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004343 sub_parser.add_argument('--no_hashtree',
4344 action='store_true',
4345 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004346 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004347 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004348 sub_parser.set_defaults(func=self.add_hashtree_footer)
4349
4350 sub_parser = subparsers.add_parser('erase_footer',
4351 help='Erase footer from an image.')
4352 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004353 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004354 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004355 required=True)
4356 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004357 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004358 action='store_true')
4359 sub_parser.set_defaults(func=self.erase_footer)
4360
David Zeuthen1394f762019-04-30 10:20:11 -04004361 sub_parser = subparsers.add_parser('zero_hashtree',
4362 help='Zero out hashtree and FEC data.')
4363 sub_parser.add_argument('--image',
4364 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004365 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004366 required=True)
4367 sub_parser.set_defaults(func=self.zero_hashtree)
4368
Jan Monscheeb28b62019-12-05 16:17:09 +01004369 sub_parser = subparsers.add_parser(
4370 'extract_vbmeta_image',
4371 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004372 sub_parser.add_argument('--image',
4373 help='Image with footer',
4374 type=argparse.FileType('rb'),
4375 required=True)
4376 sub_parser.add_argument('--output',
4377 help='Output file name',
4378 type=argparse.FileType('wb'))
4379 sub_parser.add_argument('--padding_size',
4380 metavar='NUMBER',
4381 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004382 'its size is a multiple of NUMBER '
4383 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004384 type=parse_number,
4385 default=0)
4386 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4387
David Zeuthen2bc232b2017-04-19 14:25:19 -04004388 sub_parser = subparsers.add_parser('resize_image',
4389 help='Resize image with a footer.')
4390 sub_parser.add_argument('--image',
4391 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004392 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004393 required=True)
4394 sub_parser.add_argument('--partition_size',
4395 help='New partition size',
4396 type=parse_number)
4397 sub_parser.set_defaults(func=self.resize_image)
4398
David Zeuthen21e95262016-07-27 17:58:40 -04004399 sub_parser = subparsers.add_parser(
4400 'info_image',
4401 help='Show information about vbmeta or footer.')
4402 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004403 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004404 type=argparse.FileType('rb'),
4405 required=True)
4406 sub_parser.add_argument('--output',
4407 help='Write info to file',
4408 type=argparse.FileType('wt'),
4409 default=sys.stdout)
4410 sub_parser.set_defaults(func=self.info_image)
4411
David Zeuthenb623d8b2017-04-04 16:05:53 -04004412 sub_parser = subparsers.add_parser(
4413 'verify_image',
4414 help='Verify an image.')
4415 sub_parser.add_argument('--image',
4416 help='Image to verify',
4417 type=argparse.FileType('rb'),
4418 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004419 sub_parser.add_argument('--key',
4420 help='Check embedded public key matches KEY',
4421 metavar='KEY',
4422 required=False)
4423 sub_parser.add_argument('--expected_chain_partition',
4424 help='Expected chain partition',
4425 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4426 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004427 sub_parser.add_argument(
4428 '--follow_chain_partitions',
4429 help=('Follows chain partitions even when not '
4430 'specified with the --expected_chain_partition option'),
4431 action='store_true')
4432 sub_parser.add_argument(
4433 '--accept_zeroed_hashtree',
4434 help=('Accept images where the hashtree or FEC data is zeroed out'),
4435 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004436 sub_parser.set_defaults(func=self.verify_image)
4437
David Zeuthenb8643c02018-05-17 17:21:18 -04004438 sub_parser = subparsers.add_parser(
David Zeuthen34b6b492020-04-13 14:45:02 -04004439 'print_partition_digests',
4440 help='Prints partition digests.')
4441 sub_parser.add_argument('--image',
4442 help='Image to print partition digests from',
4443 type=argparse.FileType('rb'),
4444 required=True)
4445 sub_parser.add_argument('--output',
4446 help='Write info to file',
4447 type=argparse.FileType('wt'),
4448 default=sys.stdout)
4449 sub_parser.add_argument('--json',
4450 help=('Print output as JSON'),
4451 action='store_true')
4452 sub_parser.set_defaults(func=self.print_partition_digests)
4453
4454 sub_parser = subparsers.add_parser(
David Zeuthenb8643c02018-05-17 17:21:18 -04004455 'calculate_vbmeta_digest',
4456 help='Calculate vbmeta digest.')
4457 sub_parser.add_argument('--image',
4458 help='Image to calculate digest for',
4459 type=argparse.FileType('rb'),
4460 required=True)
4461 sub_parser.add_argument('--hash_algorithm',
4462 help='Hash algorithm to use (default: sha256)',
4463 default='sha256')
4464 sub_parser.add_argument('--output',
4465 help='Write hex digest to file (default: stdout)',
4466 type=argparse.FileType('wt'),
4467 default=sys.stdout)
4468 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4469
David Zeuthenf7d2e752018-09-20 13:30:41 -04004470 sub_parser = subparsers.add_parser(
4471 'calculate_kernel_cmdline',
4472 help='Calculate kernel cmdline.')
4473 sub_parser.add_argument('--image',
4474 help='Image to calculate kernel cmdline for',
4475 type=argparse.FileType('rb'),
4476 required=True)
4477 sub_parser.add_argument('--hashtree_disabled',
4478 help='Return the cmdline for hashtree disabled',
4479 action='store_true')
4480 sub_parser.add_argument('--output',
4481 help='Write cmdline to file (default: stdout)',
4482 type=argparse.FileType('wt'),
4483 default=sys.stdout)
4484 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4485
David Zeuthen8b6973b2016-09-20 12:39:49 -04004486 sub_parser = subparsers.add_parser('set_ab_metadata',
4487 help='Set A/B metadata.')
4488 sub_parser.add_argument('--misc_image',
4489 help=('The misc image to modify. If the image does '
4490 'not exist, it will be created.'),
4491 type=argparse.FileType('r+b'),
4492 required=True)
4493 sub_parser.add_argument('--slot_data',
4494 help=('Slot data of the form "priority", '
4495 '"tries_remaining", "sucessful_boot" for '
4496 'slot A followed by the same for slot B, '
4497 'separated by colons. The default value '
4498 'is 15:7:0:14:7:0.'),
4499 default='15:7:0:14:7:0')
4500 sub_parser.set_defaults(func=self.set_ab_metadata)
4501
Darren Krahn147b08d2016-12-20 16:38:29 -08004502 sub_parser = subparsers.add_parser(
4503 'make_atx_certificate',
4504 help='Create an Android Things eXtension (ATX) certificate.')
4505 sub_parser.add_argument('--output',
4506 help='Write certificate to file',
4507 type=argparse.FileType('wb'),
4508 default=sys.stdout)
4509 sub_parser.add_argument('--subject',
4510 help=('Path to subject file'),
4511 type=argparse.FileType('rb'),
4512 required=True)
4513 sub_parser.add_argument('--subject_key',
4514 help=('Path to subject RSA public key file'),
4515 type=argparse.FileType('rb'),
4516 required=True)
4517 sub_parser.add_argument('--subject_key_version',
4518 help=('Version of the subject key'),
4519 type=parse_number,
4520 required=False)
4521 sub_parser.add_argument('--subject_is_intermediate_authority',
4522 help=('Generate an intermediate authority '
4523 'certificate'),
4524 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004525 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004526 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004527 'string'),
4528 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004529 sub_parser.add_argument('--authority_key',
4530 help='Path to authority RSA private key file',
4531 required=False)
4532 sub_parser.add_argument('--signing_helper',
4533 help='Path to helper used for signing',
4534 metavar='APP',
4535 default=None,
4536 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004537 sub_parser.add_argument('--signing_helper_with_files',
4538 help='Path to helper used for signing using files',
4539 metavar='APP',
4540 default=None,
4541 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004542 sub_parser.set_defaults(func=self.make_atx_certificate)
4543
4544 sub_parser = subparsers.add_parser(
4545 'make_atx_permanent_attributes',
4546 help='Create Android Things eXtension (ATX) permanent attributes.')
4547 sub_parser.add_argument('--output',
4548 help='Write attributes to file',
4549 type=argparse.FileType('wb'),
4550 default=sys.stdout)
4551 sub_parser.add_argument('--root_authority_key',
4552 help='Path to authority RSA public key file',
4553 type=argparse.FileType('rb'),
4554 required=True)
4555 sub_parser.add_argument('--product_id',
4556 help=('Path to Product ID file'),
4557 type=argparse.FileType('rb'),
4558 required=True)
4559 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4560
4561 sub_parser = subparsers.add_parser(
4562 'make_atx_metadata',
4563 help='Create Android Things eXtension (ATX) metadata.')
4564 sub_parser.add_argument('--output',
4565 help='Write metadata to file',
4566 type=argparse.FileType('wb'),
4567 default=sys.stdout)
4568 sub_parser.add_argument('--intermediate_key_certificate',
4569 help='Path to intermediate key certificate file',
4570 type=argparse.FileType('rb'),
4571 required=True)
4572 sub_parser.add_argument('--product_key_certificate',
4573 help='Path to product key certificate file',
4574 type=argparse.FileType('rb'),
4575 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004576 sub_parser.set_defaults(func=self.make_atx_metadata)
4577
Darren Krahnfccd64e2018-01-16 17:39:35 -08004578 sub_parser = subparsers.add_parser(
4579 'make_atx_unlock_credential',
4580 help='Create an Android Things eXtension (ATX) unlock credential.')
4581 sub_parser.add_argument('--output',
4582 help='Write credential to file',
4583 type=argparse.FileType('wb'),
4584 default=sys.stdout)
4585 sub_parser.add_argument('--intermediate_key_certificate',
4586 help='Path to intermediate key certificate file',
4587 type=argparse.FileType('rb'),
4588 required=True)
4589 sub_parser.add_argument('--unlock_key_certificate',
4590 help='Path to unlock key certificate file',
4591 type=argparse.FileType('rb'),
4592 required=True)
4593 sub_parser.add_argument('--challenge',
4594 help='Path to the challenge to sign (optional). If '
4595 'this is not provided the challenge signature '
4596 'field is omitted and can be concatenated '
4597 'later.',
4598 required=False)
4599 sub_parser.add_argument('--unlock_key',
4600 help='Path to unlock key (optional). Must be '
4601 'provided if using --challenge.',
4602 required=False)
4603 sub_parser.add_argument('--signing_helper',
4604 help='Path to helper used for signing',
4605 metavar='APP',
4606 default=None,
4607 required=False)
4608 sub_parser.add_argument('--signing_helper_with_files',
4609 help='Path to helper used for signing using files',
4610 metavar='APP',
4611 default=None,
4612 required=False)
4613 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4614
David Zeuthen21e95262016-07-27 17:58:40 -04004615 args = parser.parse_args(argv[1:])
4616 try:
4617 args.func(args)
Jan Monschcc9939a2020-04-16 09:15:20 +02004618 except AttributeError:
4619 # This error gets raised when the command line tool is called without any
4620 # arguments. It mimics the original Python 2 behavior.
4621 parser.print_usage()
4622 print('avbtool: error: too few arguments')
4623 sys.exit(2)
David Zeuthen21e95262016-07-27 17:58:40 -04004624 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004625 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004626 sys.exit(1)
4627
4628 def version(self, _):
4629 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004630 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004631
Jan Monsch2c7be992020-04-03 14:37:13 +02004632 def generate_test_image(self, args):
4633 """Implements the 'generate_test_image' sub-command."""
4634 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4635
David Zeuthen21e95262016-07-27 17:58:40 -04004636 def extract_public_key(self, args):
4637 """Implements the 'extract_public_key' sub-command."""
4638 self.avb.extract_public_key(args.key, args.output)
4639
4640 def make_vbmeta_image(self, args):
4641 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004642 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004643 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004644 args.algorithm, args.key,
4645 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004646 args.flags, args.rollback_index_location,
4647 args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004648 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004649 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004650 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004651 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004652 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004653 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004654 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004655 args.print_required_libavb_version,
4656 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004657
David Zeuthenb1b994d2017-03-06 18:01:31 -05004658 def append_vbmeta_image(self, args):
4659 """Implements the 'append_vbmeta_image' sub-command."""
4660 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4661 args.partition_size)
4662
David Zeuthen21e95262016-07-27 17:58:40 -04004663 def add_hash_footer(self, args):
4664 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004665 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004666 self.avb.add_hash_footer(args.image.name if args.image else None,
4667 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004668 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004669 args.salt, args.chain_partition, args.algorithm,
4670 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004671 args.public_key_metadata, args.rollback_index,
Varun Sharmade538272020-04-10 15:22:31 -07004672 args.flags, args.rollback_index_location,
4673 args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004674 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004675 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004676 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004677 args.calc_max_image_size,
4678 args.signing_helper,
4679 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004680 args.internal_release_string,
4681 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004682 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004683 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004684 args.print_required_libavb_version,
4685 args.use_persistent_digest,
4686 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004687
4688 def add_hashtree_footer(self, args):
4689 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004690 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004691 # TODO(zeuthen): Remove when removing support for the
4692 # '--generate_fec' option above.
4693 if args.generate_fec:
4694 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4695 'is now generated by default. Use the option '
4696 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004697 self.avb.add_hashtree_footer(
4698 args.image.name if args.image else None,
4699 args.partition_size,
4700 args.partition_name,
4701 not args.do_not_generate_fec, args.fec_num_roots,
4702 args.hash_algorithm, args.block_size,
4703 args.salt, args.chain_partition, args.algorithm,
4704 args.key, args.public_key_metadata,
Varun Sharmade538272020-04-10 15:22:31 -07004705 args.rollback_index, args.flags,
4706 args.rollback_index_location, args.prop,
Jan Monscheeb28b62019-12-05 16:17:09 +01004707 args.prop_from_file,
4708 args.kernel_cmdline,
4709 args.setup_rootfs_from_kernel,
4710 args.setup_as_rootfs_from_kernel,
4711 args.include_descriptors_from_image,
4712 args.calc_max_image_size,
4713 args.signing_helper,
4714 args.signing_helper_with_files,
4715 args.internal_release_string,
4716 args.append_to_release_string,
4717 args.output_vbmeta_image,
4718 args.do_not_append_vbmeta_image,
4719 args.print_required_libavb_version,
4720 args.use_persistent_digest,
4721 args.do_not_use_ab,
4722 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004723
David Zeuthen21e95262016-07-27 17:58:40 -04004724 def erase_footer(self, args):
4725 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004726 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004727
David Zeuthen1394f762019-04-30 10:20:11 -04004728 def zero_hashtree(self, args):
4729 """Implements the 'zero_hashtree' sub-command."""
4730 self.avb.zero_hashtree(args.image.name)
4731
David Zeuthen49936b42018-08-07 17:38:58 -04004732 def extract_vbmeta_image(self, args):
4733 """Implements the 'extract_vbmeta_image' sub-command."""
4734 self.avb.extract_vbmeta_image(args.output, args.image.name,
4735 args.padding_size)
4736
David Zeuthen2bc232b2017-04-19 14:25:19 -04004737 def resize_image(self, args):
4738 """Implements the 'resize_image' sub-command."""
4739 self.avb.resize_image(args.image.name, args.partition_size)
4740
David Zeuthen8b6973b2016-09-20 12:39:49 -04004741 def set_ab_metadata(self, args):
4742 """Implements the 'set_ab_metadata' sub-command."""
4743 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4744
David Zeuthen21e95262016-07-27 17:58:40 -04004745 def info_image(self, args):
4746 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004747 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004748
David Zeuthenb623d8b2017-04-04 16:05:53 -04004749 def verify_image(self, args):
4750 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004751 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004752 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004753 args.follow_chain_partitions,
4754 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004755
David Zeuthen34b6b492020-04-13 14:45:02 -04004756 def print_partition_digests(self, args):
4757 """Implements the 'print_partition_digests' sub-command."""
4758 self.avb.print_partition_digests(args.image.name, args.output, args.json)
4759
David Zeuthenb8643c02018-05-17 17:21:18 -04004760 def calculate_vbmeta_digest(self, args):
4761 """Implements the 'calculate_vbmeta_digest' sub-command."""
4762 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4763 args.output)
4764
David Zeuthenf7d2e752018-09-20 13:30:41 -04004765 def calculate_kernel_cmdline(self, args):
4766 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004767 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4768 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004769
Darren Krahn147b08d2016-12-20 16:38:29 -08004770 def make_atx_certificate(self, args):
4771 """Implements the 'make_atx_certificate' sub-command."""
4772 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004773 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004774 args.subject_key_version,
4775 args.subject.read(),
4776 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004777 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004778 args.signing_helper,
4779 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004780
4781 def make_atx_permanent_attributes(self, args):
4782 """Implements the 'make_atx_permanent_attributes' sub-command."""
4783 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004784 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004785 args.product_id.read())
4786
4787 def make_atx_metadata(self, args):
4788 """Implements the 'make_atx_metadata' sub-command."""
4789 self.avb.make_atx_metadata(args.output,
4790 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004791 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004792
Darren Krahnfccd64e2018-01-16 17:39:35 -08004793 def make_atx_unlock_credential(self, args):
4794 """Implements the 'make_atx_unlock_credential' sub-command."""
4795 self.avb.make_atx_unlock_credential(
4796 args.output,
4797 args.intermediate_key_certificate.read(),
4798 args.unlock_key_certificate.read(),
4799 args.challenge,
4800 args.unlock_key,
4801 args.signing_helper,
4802 args.signing_helper_with_files)
4803
David Zeuthen21e95262016-07-27 17:58:40 -04004804
4805if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004806 if AVB_INVOCATION_LOGFILE:
Jan Monschb1d920f2020-04-09 12:59:28 +02004807 with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4808 log.write(' '.join(sys.argv))
4809 log.write('\n')
Jan Monsch2c7be992020-04-03 14:37:13 +02004810
David Zeuthen21e95262016-07-27 17:58:40 -04004811 tool = AvbTool()
4812 tool.run(sys.argv)