blob: 5b20c8858f1661f3de8c13c089322427e6f1d384 [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040033import math
David Zeuthen21e95262016-07-27 17:58:40 -040034import os
35import struct
36import subprocess
37import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040038import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080039import time
David Zeuthen21e95262016-07-27 17:58:40 -040040
David Zeuthene3cadca2017-02-22 21:25:46 -050041# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040042AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080043AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
David Zeuthene3cadca2017-02-22 21:25:46 -050052
David Zeuthen21e95262016-07-27 17:58:40 -040053class AvbError(Exception):
54 """Application-specific errors.
55
56 These errors represent issues for which a stack-trace should not be
57 presented.
58
59 Attributes:
60 message: Error message.
61 """
62
63 def __init__(self, message):
64 Exception.__init__(self, message)
65
66
67class Algorithm(object):
68 """Contains details about an algorithm.
69
Tao Bao80418a52018-07-20 11:41:22 -070070 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040071
72 The constant |ALGORITHMS| is a dictionary from human-readable
73 names (e.g 'SHA256_RSA2048') to instances of this class.
74
75 Attributes:
76 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040077 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040078 hash_num_bytes: Number of bytes used to store the hash.
79 signature_num_bytes: Number of bytes used to store the signature.
80 public_key_num_bytes: Number of bytes used to store the public key.
81 padding: Padding used for signature, if any.
82 """
83
David Zeuthenb623d8b2017-04-04 16:05:53 -040084 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
85 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040088 self.hash_num_bytes = hash_num_bytes
89 self.signature_num_bytes = signature_num_bytes
90 self.public_key_num_bytes = public_key_num_bytes
91 self.padding = padding
92
David Zeuthenb623d8b2017-04-04 16:05:53 -040093
David Zeuthen21e95262016-07-27 17:58:40 -040094# This must be kept in sync with the avb_crypto.h file.
95#
96# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
97# obtained from section 5.2.2 of RFC 4880.
98ALGORITHMS = {
99 'NONE': Algorithm(
100 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400101 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400102 hash_num_bytes=0,
103 signature_num_bytes=0,
104 public_key_num_bytes=0,
105 padding=[]),
106 'SHA256_RSA2048': Algorithm(
107 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400108 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400109 hash_num_bytes=32,
110 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100111 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400112 padding=[
113 # PKCS1-v1_5 padding
114 0x00, 0x01] + [0xff]*202 + [0x00] + [
115 # ASN.1 header
116 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
117 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
118 0x00, 0x04, 0x20,
119 ]),
120 'SHA256_RSA4096': Algorithm(
121 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400122 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400123 hash_num_bytes=32,
124 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100125 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400126 padding=[
127 # PKCS1-v1_5 padding
128 0x00, 0x01] + [0xff]*458 + [0x00] + [
129 # ASN.1 header
130 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
131 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
132 0x00, 0x04, 0x20,
133 ]),
134 'SHA256_RSA8192': Algorithm(
135 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400136 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400137 hash_num_bytes=32,
138 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100139 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400140 padding=[
141 # PKCS1-v1_5 padding
142 0x00, 0x01] + [0xff]*970 + [0x00] + [
143 # ASN.1 header
144 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
145 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
146 0x00, 0x04, 0x20,
147 ]),
148 'SHA512_RSA2048': Algorithm(
149 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400150 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400151 hash_num_bytes=64,
152 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100153 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400154 padding=[
155 # PKCS1-v1_5 padding
156 0x00, 0x01] + [0xff]*170 + [0x00] + [
157 # ASN.1 header
158 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
159 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
160 0x00, 0x04, 0x40
161 ]),
162 'SHA512_RSA4096': Algorithm(
163 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400164 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400165 hash_num_bytes=64,
166 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100167 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400168 padding=[
169 # PKCS1-v1_5 padding
170 0x00, 0x01] + [0xff]*426 + [0x00] + [
171 # ASN.1 header
172 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
173 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
174 0x00, 0x04, 0x40
175 ]),
176 'SHA512_RSA8192': Algorithm(
177 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400178 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400179 hash_num_bytes=64,
180 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100181 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400182 padding=[
183 # PKCS1-v1_5 padding
184 0x00, 0x01] + [0xff]*938 + [0x00] + [
185 # ASN.1 header
186 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
187 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
188 0x00, 0x04, 0x40
189 ]),
190}
191
192
David Zeuthene3cadca2017-02-22 21:25:46 -0500193def get_release_string():
194 """Calculates the release string to use in the VBMeta struct."""
195 # Keep in sync with libavb/avb_version.c:avb_version_string().
196 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
197 AVB_VERSION_MINOR,
198 AVB_VERSION_SUB)
199
200
David Zeuthen21e95262016-07-27 17:58:40 -0400201def round_to_multiple(number, size):
202 """Rounds a number up to nearest multiple of another number.
203
Jan Monschfe00c0a2019-12-11 11:19:40 +0100204 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400205 number: The number to round up.
206 size: The multiple to round up to.
207
208 Returns:
209 If |number| is a multiple of |size|, returns |number|, otherwise
210 returns |number| + |size|.
211 """
212 remainder = number % size
213 if remainder == 0:
214 return number
215 return number + size - remainder
216
217
218def round_to_pow2(number):
219 """Rounds a number up to the next power of 2.
220
Jan Monschfe00c0a2019-12-11 11:19:40 +0100221 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400222 number: The number to round up.
223
224 Returns:
225 If |number| is already a power of 2 then |number| is
226 returned. Otherwise the smallest power of 2 greater than |number|
227 is returned.
228 """
229 return 2**((number - 1).bit_length())
230
231
David Zeuthen21e95262016-07-27 17:58:40 -0400232def encode_long(num_bits, value):
233 """Encodes a long to a bytearray() using a given amount of bits.
234
235 This number is written big-endian, e.g. with the most significant
236 bit first.
237
David Zeuthenb623d8b2017-04-04 16:05:53 -0400238 This is the reverse of decode_long().
239
David Zeuthen21e95262016-07-27 17:58:40 -0400240 Arguments:
241 num_bits: The number of bits to write, e.g. 2048.
242 value: The value to write.
243
244 Returns:
245 A bytearray() with the encoded long.
246 """
247 ret = bytearray()
248 for bit_pos in range(num_bits, 0, -8):
249 octet = (value >> (bit_pos - 8)) & 0xff
250 ret.extend(struct.pack('!B', octet))
251 return ret
252
253
David Zeuthenb623d8b2017-04-04 16:05:53 -0400254def decode_long(blob):
255 """Decodes a long from a bytearray() using a given amount of bits.
256
257 This number is expected to be in big-endian, e.g. with the most
258 significant bit first.
259
260 This is the reverse of encode_long().
261
262 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100263 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400264
265 Returns:
266 The decoded value.
267 """
268 ret = 0
269 for b in bytearray(blob):
270 ret *= 256
271 ret += b
272 return ret
273
274
David Zeuthen21e95262016-07-27 17:58:40 -0400275def egcd(a, b):
276 """Calculate greatest common divisor of two numbers.
277
278 This implementation uses a recursive version of the extended
279 Euclidian algorithm.
280
281 Arguments:
282 a: First number.
283 b: Second number.
284
285 Returns:
286 A tuple (gcd, x, y) that where |gcd| is the greatest common
287 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
288 """
289 if a == 0:
290 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100291 g, y, x = egcd(b % a, a)
292 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400293
294
295def modinv(a, m):
296 """Calculate modular multiplicative inverse of |a| modulo |m|.
297
298 This calculates the number |x| such that |a| * |x| == 1 (modulo
299 |m|). This number only exists if |a| and |m| are co-prime - |None|
300 is returned if this isn't true.
301
302 Arguments:
303 a: The number to calculate a modular inverse of.
304 m: The modulo to use.
305
306 Returns:
307 The modular multiplicative inverse of |a| and |m| or |None| if
308 these numbers are not co-prime.
309 """
310 gcd, x, _ = egcd(a, m)
311 if gcd != 1:
312 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100313 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400314
315
316def parse_number(string):
317 """Parse a string as a number.
318
319 This is just a short-hand for int(string, 0) suitable for use in the
320 |type| parameter of |ArgumentParser|'s add_argument() function. An
321 improvement to just using type=int is that this function supports
322 numbers in other bases, e.g. "0x1234".
323
324 Arguments:
325 string: The string to parse.
326
327 Returns:
328 The parsed integer.
329
330 Raises:
331 ValueError: If the number could not be parsed.
332 """
333 return int(string, 0)
334
335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336class RSAPublicKey(object):
337 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339 Attributes:
340 exponent: The key exponent.
341 modulus: The key modulus.
342 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400343 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400344
345 MODULUS_PREFIX = 'modulus='
346
347 def __init__(self, key_path):
348 """Loads and parses an RSA key from either a private or public key file.
349
350 Arguments:
351 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100352
353 Raises:
354 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400355 """
356 # We used to have something as simple as this:
357 #
358 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
359 # self.exponent = key.e
360 # self.modulus = key.n
361 # self.num_bits = key.size() + 1
362 #
363 # but unfortunately PyCrypto is not available in the builder. So
364 # instead just parse openssl(1) output to get this
365 # information. It's ugly but...
366 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
367 p = subprocess.Popen(args,
368 stdin=subprocess.PIPE,
369 stdout=subprocess.PIPE,
370 stderr=subprocess.PIPE)
371 (pout, perr) = p.communicate()
372 if p.wait() != 0:
373 # Could be just a public key is passed, try that.
374 args.append('-pubin')
375 p = subprocess.Popen(args,
376 stdin=subprocess.PIPE,
377 stdout=subprocess.PIPE,
378 stderr=subprocess.PIPE)
379 (pout, perr) = p.communicate()
380 if p.wait() != 0:
381 raise AvbError('Error getting public key: {}'.format(perr))
382
383 if not pout.lower().startswith(self.MODULUS_PREFIX):
384 raise AvbError('Unexpected modulus output')
385
386 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
387
388 # The exponent is assumed to always be 65537 and the number of
389 # bits can be derived from the modulus by rounding up to the
390 # nearest power of 2.
391 self.modulus = int(modulus_hexstr, 16)
392 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
393 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400394
395
Dan Austinb12b2c12019-12-15 20:28:02 -0800396# TODO(danielaustin): Should this be moved into the RSAPublicKey class?
397def rsa_key_read_pem_bytes(key_path):
398 """Reads the bytes out of the passed in PEM file.
399
400 Arguments:
401 key_path: A string containing the path to the PEM file.
402
403 Returns:
404 A bytearray containing the bytes in the PEM file.
405
406 Raises:
407 AvbError: If openssl cannot decode the PEM file.
408 """
409 # Use openssl to decode the PEM file.
410 args = ['openssl', 'rsa', '-in', key_path, '-pubout', '-outform', 'DER']
411 p = subprocess.Popen(args,
412 stdin=subprocess.PIPE,
413 stdout=subprocess.PIPE,
414 stderr=subprocess.PIPE)
415 (pout, perr) = p.communicate()
416 retcode = p.wait()
417 if retcode != 0:
418 raise AvbError('Error decoding: {}'.format(perr))
419 return bytearray(pout)
420
421
David Zeuthenc68f0822017-03-31 17:22:35 -0400422def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400423 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
424
425 This creates a |AvbRSAPublicKeyHeader| as well as the two large
426 numbers (|key_num_bits| bits long) following it.
427
428 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400429 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400430
431 Returns:
432 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100433
434 Raises:
435 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400436 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400437 key = RSAPublicKey(key_path)
438 if key.exponent != 65537:
439 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400440 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400441 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monsch23e0c622019-12-11 11:23:58 +0100442 b = 2L**32 # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400443 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400444 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monsch23e0c622019-12-11 11:23:58 +0100445 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400446 rrmodn = r * r % key.modulus
447 ret.extend(struct.pack('!II', key.num_bits, n0inv))
448 ret.extend(encode_long(key.num_bits, key.modulus))
449 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400450 return ret
451
452
453def lookup_algorithm_by_type(alg_type):
454 """Looks up algorithm by type.
455
456 Arguments:
457 alg_type: The integer representing the type.
458
459 Returns:
460 A tuple with the algorithm name and an |Algorithm| instance.
461
462 Raises:
463 Exception: If the algorithm cannot be found
464 """
465 for alg_name in ALGORITHMS:
466 alg_data = ALGORITHMS[alg_name]
467 if alg_data.algorithm_type == alg_type:
468 return (alg_name, alg_data)
469 raise AvbError('Unknown algorithm type {}'.format(alg_type))
470
Jan Monsch77cd2022019-12-10 17:18:04 +0100471
Dan Austina7bc4962019-12-02 13:26:08 -0800472def lookup_hash_size_by_type(alg_type):
473 """Looks up hash size by type.
474
475 Arguments:
476 alg_type: The integer representing the type.
477
478 Returns:
479 The corresponding hash size.
480
481 Raises:
482 AvbError: If the algorithm cannot be found.
483 """
484 for alg_name in ALGORITHMS:
485 alg_data = ALGORITHMS[alg_name]
486 if alg_data.algorithm_type == alg_type:
487 return alg_data.hash_num_bytes
488 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400489
Jan Monsch77cd2022019-12-10 17:18:04 +0100490
David Zeuthena156d3d2017-06-01 12:08:09 -0400491def raw_sign(signing_helper, signing_helper_with_files,
492 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900493 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800494 """Computes a raw RSA signature using |signing_helper| or openssl.
495
496 Arguments:
497 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400498 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800499 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900500 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800501 key_path: Path to the private key file. Must be PEM format.
502 raw_data_to_sign: Data to sign (bytearray or str expected).
503
504 Returns:
505 A bytearray containing the signature.
506
507 Raises:
508 Exception: If an error occurs.
509 """
510 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400511 if signing_helper_with_files is not None:
512 signing_file = tempfile.NamedTemporaryFile()
513 signing_file.write(str(raw_data_to_sign))
514 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100515 p = subprocess.Popen([
516 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400517 retcode = p.wait()
518 if retcode != 0:
519 raise AvbError('Error signing')
520 signing_file.seek(0)
521 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800522 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400523 if signing_helper is not None:
524 p = subprocess.Popen(
525 [signing_helper, algorithm_name, key_path],
526 stdin=subprocess.PIPE,
527 stdout=subprocess.PIPE,
528 stderr=subprocess.PIPE)
529 else:
530 p = subprocess.Popen(
531 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
532 stdin=subprocess.PIPE,
533 stdout=subprocess.PIPE,
534 stderr=subprocess.PIPE)
535 (pout, perr) = p.communicate(str(raw_data_to_sign))
536 retcode = p.wait()
537 if retcode != 0:
538 raise AvbError('Error signing: {}'.format(perr))
539 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900540 if len(signature) != signature_num_bytes:
541 raise AvbError('Error signing: Invalid length of signature')
542 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800543
544
David Zeuthenb623d8b2017-04-04 16:05:53 -0400545def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100546 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400547
548 Arguments:
549 vbmeta_header: A AvbVBMetaHeader.
550 vbmeta_blob: The whole vbmeta blob, including the header.
551
552 Returns:
553 True if the signature is valid and corresponds to the embedded
554 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100555
556 Raises:
557 AvbError: If there errors calling out to openssl command during
558 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400559 """
560 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100561 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400562 return True
563 header_blob = vbmeta_blob[0:256]
564 auth_offset = 256
565 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
566 aux_size = vbmeta_header.auxiliary_data_block_size
567 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
568 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
569 pubkey_size = vbmeta_header.public_key_size
570 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
571
572 digest_offset = auth_offset + vbmeta_header.hash_offset
573 digest_size = vbmeta_header.hash_size
574 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
575
576 sig_offset = auth_offset + vbmeta_header.signature_offset
577 sig_size = vbmeta_header.signature_size
578 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
579
580 # Now that we've got the stored digest, public key, and signature
581 # all we need to do is to verify. This is the exactly the same
582 # steps as performed in the avb_vbmeta_image_verify() function in
583 # libavb/avb_vbmeta_image.c.
584
585 ha = hashlib.new(alg.hash_name)
586 ha.update(header_blob)
587 ha.update(aux_blob)
588 computed_digest = ha.digest()
589
590 if computed_digest != digest_blob:
591 return False
592
593 padding_and_digest = bytearray(alg.padding)
594 padding_and_digest.extend(computed_digest)
595
596 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100597 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400598 modulus = decode_long(modulus_blob)
599 exponent = 65537
600
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500601 # We used to have this:
602 #
603 # import Crypto.PublicKey.RSA
604 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
605 # if not key.verify(decode_long(padding_and_digest),
606 # (decode_long(sig_blob), None)):
607 # return False
608 # return True
609 #
610 # but since 'avbtool verify_image' is used on the builders we don't want
611 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
612 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
613 '\n'
614 '[pubkeyinfo]\n'
615 'algorithm=SEQUENCE:rsa_alg\n'
616 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
617 '\n'
618 '[rsa_alg]\n'
619 'algorithm=OID:rsaEncryption\n'
620 'parameter=NULL\n'
621 '\n'
622 '[rsapubkey]\n'
623 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100624 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
625 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500626 asn1_tmpfile = tempfile.NamedTemporaryFile()
627 asn1_tmpfile.write(asn1_str)
628 asn1_tmpfile.flush()
629 der_tmpfile = tempfile.NamedTemporaryFile()
630 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100631 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
632 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500633 retcode = p.wait()
634 if retcode != 0:
635 raise AvbError('Error generating DER file')
636
637 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100638 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
639 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500640 stdin=subprocess.PIPE,
641 stdout=subprocess.PIPE,
642 stderr=subprocess.PIPE)
643 (pout, perr) = p.communicate(str(sig_blob))
644 retcode = p.wait()
645 if retcode != 0:
646 raise AvbError('Error verifying data: {}'.format(perr))
647 recovered_data = bytearray(pout)
648 if recovered_data != padding_and_digest:
649 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400650 return False
651 return True
652
653
David Zeuthena4fee8b2016-08-22 15:20:43 -0400654class ImageChunk(object):
655 """Data structure used for representing chunks in Android sparse files.
656
657 Attributes:
658 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
659 chunk_offset: Offset in the sparse file where this chunk begins.
660 output_offset: Offset in de-sparsified file where output begins.
661 output_size: Number of bytes in output.
662 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
663 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
664 """
665
666 FORMAT = '<2H2I'
667 TYPE_RAW = 0xcac1
668 TYPE_FILL = 0xcac2
669 TYPE_DONT_CARE = 0xcac3
670 TYPE_CRC32 = 0xcac4
671
672 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
673 input_offset, fill_data):
674 """Initializes an ImageChunk object.
675
676 Arguments:
677 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
678 chunk_offset: Offset in the sparse file where this chunk begins.
679 output_offset: Offset in de-sparsified file.
680 output_size: Number of bytes in output.
681 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
682 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
683
684 Raises:
685 ValueError: If data is not well-formed.
686 """
687 self.chunk_type = chunk_type
688 self.chunk_offset = chunk_offset
689 self.output_offset = output_offset
690 self.output_size = output_size
691 self.input_offset = input_offset
692 self.fill_data = fill_data
693 # Check invariants.
694 if self.chunk_type == self.TYPE_RAW:
695 if self.fill_data is not None:
696 raise ValueError('RAW chunk cannot have fill_data set.')
697 if not self.input_offset:
698 raise ValueError('RAW chunk must have input_offset set.')
699 elif self.chunk_type == self.TYPE_FILL:
700 if self.fill_data is None:
701 raise ValueError('FILL chunk must have fill_data set.')
702 if self.input_offset:
703 raise ValueError('FILL chunk cannot have input_offset set.')
704 elif self.chunk_type == self.TYPE_DONT_CARE:
705 if self.fill_data is not None:
706 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
707 if self.input_offset:
708 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
709 else:
710 raise ValueError('Invalid chunk type')
711
712
713class ImageHandler(object):
714 """Abstraction for image I/O with support for Android sparse images.
715
716 This class provides an interface for working with image files that
717 may be using the Android Sparse Image format. When an instance is
718 constructed, we test whether it's an Android sparse file. If so,
719 operations will be on the sparse file by interpreting the sparse
720 format, otherwise they will be directly on the file. Either way the
721 operations do the same.
722
723 For reading, this interface mimics a file object - it has seek(),
724 tell(), and read() methods. For writing, only truncation
725 (truncate()) and appending is supported (append_raw() and
726 append_dont_care()). Additionally, data can only be written in units
727 of the block size.
728
729 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400730 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400731 is_sparse: Whether the file being operated on is sparse.
732 block_size: The block size, typically 4096.
733 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400734 """
735 # See system/core/libsparse/sparse_format.h for details.
736 MAGIC = 0xed26ff3a
737 HEADER_FORMAT = '<I4H4I'
738
739 # These are formats and offset of just the |total_chunks| and
740 # |total_blocks| fields.
741 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
742 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
743
744 def __init__(self, image_filename):
745 """Initializes an image handler.
746
747 Arguments:
748 image_filename: The name of the file to operate on.
749
750 Raises:
751 ValueError: If data in the file is invalid.
752 """
David Zeuthen49936b42018-08-07 17:38:58 -0400753 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100754 self._num_total_blocks = 0
755 self._num_total_chunks = 0
756 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400757 self._read_header()
758
759 def _read_header(self):
760 """Initializes internal data structures used for reading file.
761
762 This may be called multiple times and is typically called after
763 modifying the file (e.g. appending, truncation).
764
765 Raises:
766 ValueError: If data in the file is invalid.
767 """
768 self.is_sparse = False
769 self.block_size = 4096
770 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400771 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400772 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400773 self.image_size = self._image.tell()
774
775 self._image.seek(0, os.SEEK_SET)
776 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
777 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
778 block_size, self._num_total_blocks, self._num_total_chunks,
779 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
780 if magic != self.MAGIC:
781 # Not a sparse image, our job here is done.
782 return
783 if not (major_version == 1 and minor_version == 0):
784 raise ValueError('Encountered sparse image format version {}.{} but '
785 'only 1.0 is supported'.format(major_version,
786 minor_version))
787 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
788 raise ValueError('Unexpected file_hdr_sz value {}.'.
789 format(file_hdr_sz))
790 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
791 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
792 format(chunk_hdr_sz))
793
794 self.block_size = block_size
795
796 # Build an list of chunks by parsing the file.
797 self._chunks = []
798
799 # Find the smallest offset where only "Don't care" chunks
800 # follow. This will be the size of the content in the sparse
801 # image.
802 offset = 0
803 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100804 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400805 chunk_offset = self._image.tell()
806
807 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
808 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
809 header_bin)
810 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
811
David Zeuthena4fee8b2016-08-22 15:20:43 -0400812 if chunk_type == ImageChunk.TYPE_RAW:
813 if data_sz != (chunk_sz * self.block_size):
814 raise ValueError('Raw chunk input size ({}) does not match output '
815 'size ({})'.
816 format(data_sz, chunk_sz*self.block_size))
817 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
818 chunk_offset,
819 output_offset,
820 chunk_sz*self.block_size,
821 self._image.tell(),
822 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700823 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400824
825 elif chunk_type == ImageChunk.TYPE_FILL:
826 if data_sz != 4:
827 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
828 'has {}'.format(data_sz))
829 fill_data = self._image.read(4)
830 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
831 chunk_offset,
832 output_offset,
833 chunk_sz*self.block_size,
834 None,
835 fill_data))
836 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
837 if data_sz != 0:
838 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
839 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400840 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
841 chunk_offset,
842 output_offset,
843 chunk_sz*self.block_size,
844 None,
845 None))
846 elif chunk_type == ImageChunk.TYPE_CRC32:
847 if data_sz != 4:
848 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
849 'this has {}'.format(data_sz))
850 self._image.read(4)
851 else:
852 raise ValueError('Unknown chunk type {}'.format(chunk_type))
853
854 offset += chunk_sz
855 output_offset += chunk_sz*self.block_size
856
857 # Record where sparse data end.
858 self._sparse_end = self._image.tell()
859
860 # Now that we've traversed all chunks, sanity check.
861 if self._num_total_blocks != offset:
862 raise ValueError('The header said we should have {} output blocks, '
863 'but we saw {}'.format(self._num_total_blocks, offset))
864 junk_len = len(self._image.read())
865 if junk_len > 0:
866 raise ValueError('There were {} bytes of extra data at the end of the '
867 'file.'.format(junk_len))
868
David Zeuthen09692692016-09-30 16:16:40 -0400869 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400870 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400871
872 # This is used when bisecting in read() to find the initial slice.
873 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
874
875 self.is_sparse = True
876
877 def _update_chunks_and_blocks(self):
878 """Helper function to update the image header.
879
880 The the |total_chunks| and |total_blocks| fields in the header
881 will be set to value of the |_num_total_blocks| and
882 |_num_total_chunks| attributes.
883
884 """
885 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
886 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
887 self._num_total_blocks,
888 self._num_total_chunks))
889
890 def append_dont_care(self, num_bytes):
891 """Appends a DONT_CARE chunk to the sparse file.
892
893 The given number of bytes must be a multiple of the block size.
894
895 Arguments:
896 num_bytes: Size in number of bytes of the DONT_CARE chunk.
897 """
898 assert num_bytes % self.block_size == 0
899
900 if not self.is_sparse:
901 self._image.seek(0, os.SEEK_END)
902 # This is more efficient that writing NUL bytes since it'll add
903 # a hole on file systems that support sparse files (native
904 # sparse, not Android sparse).
905 self._image.truncate(self._image.tell() + num_bytes)
906 self._read_header()
907 return
908
909 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100910 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400911 self._update_chunks_and_blocks()
912
913 self._image.seek(self._sparse_end, os.SEEK_SET)
914 self._image.write(struct.pack(ImageChunk.FORMAT,
915 ImageChunk.TYPE_DONT_CARE,
916 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100917 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400918 struct.calcsize(ImageChunk.FORMAT)))
919 self._read_header()
920
921 def append_raw(self, data):
922 """Appends a RAW chunk to the sparse file.
923
924 The length of the given data must be a multiple of the block size.
925
926 Arguments:
927 data: Data to append.
928 """
929 assert len(data) % self.block_size == 0
930
931 if not self.is_sparse:
932 self._image.seek(0, os.SEEK_END)
933 self._image.write(data)
934 self._read_header()
935 return
936
937 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100938 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400939 self._update_chunks_and_blocks()
940
941 self._image.seek(self._sparse_end, os.SEEK_SET)
942 self._image.write(struct.pack(ImageChunk.FORMAT,
943 ImageChunk.TYPE_RAW,
944 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 len(data) +
947 struct.calcsize(ImageChunk.FORMAT)))
948 self._image.write(data)
949 self._read_header()
950
951 def append_fill(self, fill_data, size):
952 """Appends a fill chunk to the sparse file.
953
954 The total length of the fill data must be a multiple of the block size.
955
956 Arguments:
957 fill_data: Fill data to append - must be four bytes.
958 size: Number of chunk - must be a multiple of four and the block size.
959 """
960 assert len(fill_data) == 4
961 assert size % 4 == 0
962 assert size % self.block_size == 0
963
964 if not self.is_sparse:
965 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100966 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400967 self._read_header()
968 return
969
970 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100971 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400972 self._update_chunks_and_blocks()
973
974 self._image.seek(self._sparse_end, os.SEEK_SET)
975 self._image.write(struct.pack(ImageChunk.FORMAT,
976 ImageChunk.TYPE_FILL,
977 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100978 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400979 4 + struct.calcsize(ImageChunk.FORMAT)))
980 self._image.write(fill_data)
981 self._read_header()
982
983 def seek(self, offset):
984 """Sets the cursor position for reading from unsparsified file.
985
986 Arguments:
987 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100988
989 Raises:
990 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400991 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700992 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100993 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400994 self._file_pos = offset
995
996 def read(self, size):
997 """Reads data from the unsparsified file.
998
999 This method may return fewer than |size| bytes of data if the end
1000 of the file was encountered.
1001
1002 The file cursor for reading is advanced by the number of bytes
1003 read.
1004
1005 Arguments:
1006 size: Number of bytes to read.
1007
1008 Returns:
1009 The data.
1010
1011 """
1012 if not self.is_sparse:
1013 self._image.seek(self._file_pos)
1014 data = self._image.read(size)
1015 self._file_pos += len(data)
1016 return data
1017
1018 # Iterate over all chunks.
1019 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1020 self._file_pos) - 1
1021 data = bytearray()
1022 to_go = size
1023 while to_go > 0:
1024 chunk = self._chunks[chunk_idx]
1025 chunk_pos_offset = self._file_pos - chunk.output_offset
1026 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1027
1028 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1029 self._image.seek(chunk.input_offset + chunk_pos_offset)
1030 data.extend(self._image.read(chunk_pos_to_go))
1031 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001032 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001033 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1034 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1035 else:
1036 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1037 data.extend('\0' * chunk_pos_to_go)
1038
1039 to_go -= chunk_pos_to_go
1040 self._file_pos += chunk_pos_to_go
1041 chunk_idx += 1
1042 # Generate partial read in case of EOF.
1043 if chunk_idx >= len(self._chunks):
1044 break
1045
1046 return data
1047
1048 def tell(self):
1049 """Returns the file cursor position for reading from unsparsified file.
1050
1051 Returns:
1052 The file cursor position for reading.
1053 """
1054 return self._file_pos
1055
1056 def truncate(self, size):
1057 """Truncates the unsparsified file.
1058
1059 Arguments:
1060 size: Desired size of unsparsified file.
1061
1062 Raises:
1063 ValueError: If desired size isn't a multiple of the block size.
1064 """
1065 if not self.is_sparse:
1066 self._image.truncate(size)
1067 self._read_header()
1068 return
1069
1070 if size % self.block_size != 0:
1071 raise ValueError('Cannot truncate to a size which is not a multiple '
1072 'of the block size')
1073
1074 if size == self.image_size:
1075 # Trivial where there's nothing to do.
1076 return
1077 elif size < self.image_size:
1078 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1079 chunk = self._chunks[chunk_idx]
1080 if chunk.output_offset != size:
1081 # Truncation in the middle of a trunk - need to keep the chunk
1082 # and modify it.
1083 chunk_idx_for_update = chunk_idx + 1
1084 num_to_keep = size - chunk.output_offset
1085 assert num_to_keep % self.block_size == 0
1086 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1087 truncate_at = (chunk.chunk_offset +
1088 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1089 data_sz = num_to_keep
1090 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1091 truncate_at = (chunk.chunk_offset +
1092 struct.calcsize(ImageChunk.FORMAT) + 4)
1093 data_sz = 4
1094 else:
1095 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1096 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1097 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001098 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001099 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1100 self._image.seek(chunk.chunk_offset)
1101 self._image.write(struct.pack(ImageChunk.FORMAT,
1102 chunk.chunk_type,
1103 0, # Reserved
1104 chunk_sz,
1105 total_sz))
1106 chunk.output_size = num_to_keep
1107 else:
1108 # Truncation at trunk boundary.
1109 truncate_at = chunk.chunk_offset
1110 chunk_idx_for_update = chunk_idx
1111
1112 self._num_total_chunks = chunk_idx_for_update
1113 self._num_total_blocks = 0
1114 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001115 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001116 self._update_chunks_and_blocks()
1117 self._image.truncate(truncate_at)
1118
1119 # We've modified the file so re-read all data.
1120 self._read_header()
1121 else:
1122 # Truncating to grow - just add a DONT_CARE section.
1123 self.append_dont_care(size - self.image_size)
1124
1125
David Zeuthen21e95262016-07-27 17:58:40 -04001126class AvbDescriptor(object):
1127 """Class for AVB descriptor.
1128
1129 See the |AvbDescriptor| C struct for more information.
1130
1131 Attributes:
1132 tag: The tag identifying what kind of descriptor this is.
1133 data: The data in the descriptor.
1134 """
1135
1136 SIZE = 16
1137 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1138
1139 def __init__(self, data):
1140 """Initializes a new property descriptor.
1141
1142 Arguments:
1143 data: If not None, must be a bytearray().
1144
1145 Raises:
1146 LookupError: If the given descriptor is malformed.
1147 """
1148 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1149
1150 if data:
1151 (self.tag, num_bytes_following) = (
1152 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1153 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1154 else:
1155 self.tag = None
1156 self.data = None
1157
1158 def print_desc(self, o):
1159 """Print the descriptor.
1160
1161 Arguments:
1162 o: The object to write the output to.
1163 """
1164 o.write(' Unknown descriptor:\n')
1165 o.write(' Tag: {}\n'.format(self.tag))
1166 if len(self.data) < 256:
1167 o.write(' Data: {} ({} bytes)\n'.format(
1168 repr(str(self.data)), len(self.data)))
1169 else:
1170 o.write(' Data: {} bytes\n'.format(len(self.data)))
1171
1172 def encode(self):
1173 """Serializes the descriptor.
1174
1175 Returns:
1176 A bytearray() with the descriptor data.
1177 """
1178 num_bytes_following = len(self.data)
1179 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1180 padding_size = nbf_with_padding - num_bytes_following
1181 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1182 padding = struct.pack(str(padding_size) + 'x')
1183 ret = desc + self.data + padding
1184 return bytearray(ret)
1185
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001186 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001187 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001188 """Verifies contents of the descriptor - used in verify_image sub-command.
1189
1190 Arguments:
1191 image_dir: The directory of the file being verified.
1192 image_ext: The extension of the file being verified (e.g. '.img').
1193 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001194 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001195 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001196 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1197 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001198
1199 Returns:
1200 True if the descriptor verifies, False otherwise.
1201 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001202 # Deletes unused parameters to prevent pylint warning unused-argument.
1203 del image_dir, image_ext, expected_chain_partitions_map
1204 del image_containing_descriptor, accept_zeroed_hashtree
1205
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001206 # Nothing to do.
1207 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001208
Jan Monscheeb28b62019-12-05 16:17:09 +01001209
David Zeuthen21e95262016-07-27 17:58:40 -04001210class AvbPropertyDescriptor(AvbDescriptor):
1211 """A class for property descriptors.
1212
1213 See the |AvbPropertyDescriptor| C struct for more information.
1214
1215 Attributes:
1216 key: The key.
1217 value: The key.
1218 """
1219
1220 TAG = 0
1221 SIZE = 32
1222 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1223 'Q' # key size (bytes)
1224 'Q') # value size (bytes)
1225
1226 def __init__(self, data=None):
1227 """Initializes a new property descriptor.
1228
1229 Arguments:
1230 data: If not None, must be a bytearray of size |SIZE|.
1231
1232 Raises:
1233 LookupError: If the given descriptor is malformed.
1234 """
1235 AvbDescriptor.__init__(self, None)
1236 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1237
1238 if data:
1239 (tag, num_bytes_following, key_size,
1240 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1241 expected_size = round_to_multiple(
1242 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1243 if tag != self.TAG or num_bytes_following != expected_size:
1244 raise LookupError('Given data does not look like a property '
1245 'descriptor.')
1246 self.key = data[self.SIZE:(self.SIZE + key_size)]
1247 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1248 value_size)]
1249 else:
1250 self.key = ''
1251 self.value = ''
1252
1253 def print_desc(self, o):
1254 """Print the descriptor.
1255
1256 Arguments:
1257 o: The object to write the output to.
1258 """
1259 if len(self.value) < 256:
1260 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1261 else:
1262 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1263
1264 def encode(self):
1265 """Serializes the descriptor.
1266
1267 Returns:
1268 A bytearray() with the descriptor data.
1269 """
1270 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1271 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1272 padding_size = nbf_with_padding - num_bytes_following
1273 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1274 len(self.key), len(self.value))
1275 padding = struct.pack(str(padding_size) + 'x')
1276 ret = desc + self.key + '\0' + self.value + '\0' + padding
1277 return bytearray(ret)
1278
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001279 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001280 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001281 """Verifies contents of the descriptor - used in verify_image sub-command.
1282
1283 Arguments:
1284 image_dir: The directory of the file being verified.
1285 image_ext: The extension of the file being verified (e.g. '.img').
1286 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001287 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001288 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001289 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1290 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001291
1292 Returns:
1293 True if the descriptor verifies, False otherwise.
1294 """
1295 # Nothing to do.
1296 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001297
Jan Monscheeb28b62019-12-05 16:17:09 +01001298
David Zeuthen21e95262016-07-27 17:58:40 -04001299class AvbHashtreeDescriptor(AvbDescriptor):
1300 """A class for hashtree descriptors.
1301
1302 See the |AvbHashtreeDescriptor| C struct for more information.
1303
1304 Attributes:
1305 dm_verity_version: dm-verity version used.
1306 image_size: Size of the image, after rounding up to |block_size|.
1307 tree_offset: Offset of the hash tree in the file.
1308 tree_size: Size of the tree.
1309 data_block_size: Data block size
1310 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001311 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1312 fec_offset: Offset of FEC data (0 if FEC is not used).
1313 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001314 hash_algorithm: Hash algorithm used.
1315 partition_name: Partition name.
1316 salt: Salt used.
1317 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001318 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001319 """
1320
1321 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001322 RESERVED = 60
1323 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001324 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1325 'L' # dm-verity version used
1326 'Q' # image size (bytes)
1327 'Q' # tree offset (bytes)
1328 'Q' # tree size (bytes)
1329 'L' # data block size (bytes)
1330 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001331 'L' # FEC number of roots
1332 'Q' # FEC offset (bytes)
1333 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001334 '32s' # hash algorithm used
1335 'L' # partition name (bytes)
1336 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001337 'L' # root digest length (bytes)
1338 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001339 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001340
1341 def __init__(self, data=None):
1342 """Initializes a new hashtree descriptor.
1343
1344 Arguments:
1345 data: If not None, must be a bytearray of size |SIZE|.
1346
1347 Raises:
1348 LookupError: If the given descriptor is malformed.
1349 """
1350 AvbDescriptor.__init__(self, None)
1351 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1352
1353 if data:
1354 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1355 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001356 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1357 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001358 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1359 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001360 expected_size = round_to_multiple(
1361 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1362 if tag != self.TAG or num_bytes_following != expected_size:
1363 raise LookupError('Given data does not look like a hashtree '
1364 'descriptor.')
1365 # Nuke NUL-bytes at the end.
1366 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1367 o = 0
1368 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1369 partition_name_len)])
1370 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1371 self.partition_name.decode('utf-8')
1372 o += partition_name_len
1373 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1374 o += salt_len
1375 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1376 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001377 if root_digest_len != 0:
1378 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001379
1380 else:
1381 self.dm_verity_version = 0
1382 self.image_size = 0
1383 self.tree_offset = 0
1384 self.tree_size = 0
1385 self.data_block_size = 0
1386 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001387 self.fec_num_roots = 0
1388 self.fec_offset = 0
1389 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001390 self.hash_algorithm = ''
1391 self.partition_name = ''
1392 self.salt = bytearray()
1393 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001394 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001395
1396 def print_desc(self, o):
1397 """Print the descriptor.
1398
1399 Arguments:
1400 o: The object to write the output to.
1401 """
1402 o.write(' Hashtree descriptor:\n')
1403 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1404 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1405 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1406 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1407 o.write(' Data Block Size: {} bytes\n'.format(
1408 self.data_block_size))
1409 o.write(' Hash Block Size: {} bytes\n'.format(
1410 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001411 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1412 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1413 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001414 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1415 o.write(' Partition Name: {}\n'.format(self.partition_name))
1416 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1417 'hex')))
1418 o.write(' Root Digest: {}\n'.format(str(
1419 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001420 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001421
1422 def encode(self):
1423 """Serializes the descriptor.
1424
1425 Returns:
1426 A bytearray() with the descriptor data.
1427 """
1428 encoded_name = self.partition_name.encode('utf-8')
1429 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1430 len(self.root_digest) - 16)
1431 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1432 padding_size = nbf_with_padding - num_bytes_following
1433 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1434 self.dm_verity_version, self.image_size,
1435 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001436 self.hash_block_size, self.fec_num_roots,
1437 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001438 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001439 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001440 padding = struct.pack(str(padding_size) + 'x')
1441 ret = desc + encoded_name + self.salt + self.root_digest + padding
1442 return bytearray(ret)
1443
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001444 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001445 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001446 """Verifies contents of the descriptor - used in verify_image sub-command.
1447
1448 Arguments:
1449 image_dir: The directory of the file being verified.
1450 image_ext: The extension of the file being verified (e.g. '.img').
1451 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001452 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001453 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001454 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1455 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001456
1457 Returns:
1458 True if the descriptor verifies, False otherwise.
1459 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001460 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001461 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001462 image = image_containing_descriptor
1463 else:
1464 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1465 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001466 # Generate the hashtree and checks that it matches what's in the file.
1467 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1468 digest_padding = round_to_pow2(digest_size) - digest_size
1469 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001470 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001471 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1472 self.data_block_size,
1473 self.hash_algorithm, self.salt,
1474 digest_padding,
1475 hash_level_offsets,
1476 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001477 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001478 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001479 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1480 format(image_filename))
1481 return False
1482 # ... also check that the on-disk hashtree matches
1483 image.seek(self.tree_offset)
1484 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001485 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001486 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001487 print('{}: skipping verification since hashtree is zeroed and '
1488 '--accept_zeroed_hashtree was given'
1489 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001490 else:
1491 if hash_tree != hash_tree_ondisk:
1492 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001493 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001494 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001495 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1496 .format(self.partition_name, self.hash_algorithm, image.filename,
1497 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001498 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1499 # correct but this a) currently requires the 'fec' binary; and b) takes a
1500 # long time; and c) is not strictly needed for verification purposes as
1501 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001502 return True
1503
David Zeuthen21e95262016-07-27 17:58:40 -04001504
1505class AvbHashDescriptor(AvbDescriptor):
1506 """A class for hash descriptors.
1507
1508 See the |AvbHashDescriptor| C struct for more information.
1509
1510 Attributes:
1511 image_size: Image size, in bytes.
1512 hash_algorithm: Hash algorithm used.
1513 partition_name: Partition name.
1514 salt: Salt used.
1515 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001516 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001517 """
1518
1519 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001520 RESERVED = 60
1521 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001522 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1523 'Q' # image size (bytes)
1524 '32s' # hash algorithm used
1525 'L' # partition name (bytes)
1526 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001527 'L' # digest length (bytes)
1528 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001529 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001530
1531 def __init__(self, data=None):
1532 """Initializes a new hash descriptor.
1533
1534 Arguments:
1535 data: If not None, must be a bytearray of size |SIZE|.
1536
1537 Raises:
1538 LookupError: If the given descriptor is malformed.
1539 """
1540 AvbDescriptor.__init__(self, None)
1541 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1542
1543 if data:
1544 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1545 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001546 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1547 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001548 expected_size = round_to_multiple(
1549 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1550 if tag != self.TAG or num_bytes_following != expected_size:
1551 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1552 # Nuke NUL-bytes at the end.
1553 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1554 o = 0
1555 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1556 partition_name_len)])
1557 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1558 self.partition_name.decode('utf-8')
1559 o += partition_name_len
1560 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1561 o += salt_len
1562 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1563 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001564 if digest_len != 0:
1565 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001566
1567 else:
1568 self.image_size = 0
1569 self.hash_algorithm = ''
1570 self.partition_name = ''
1571 self.salt = bytearray()
1572 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001573 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001574
1575 def print_desc(self, o):
1576 """Print the descriptor.
1577
1578 Arguments:
1579 o: The object to write the output to.
1580 """
1581 o.write(' Hash descriptor:\n')
1582 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1583 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1584 o.write(' Partition Name: {}\n'.format(self.partition_name))
1585 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1586 'hex')))
1587 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1588 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001589 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001590
1591 def encode(self):
1592 """Serializes the descriptor.
1593
1594 Returns:
1595 A bytearray() with the descriptor data.
1596 """
1597 encoded_name = self.partition_name.encode('utf-8')
1598 num_bytes_following = (
1599 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1600 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1601 padding_size = nbf_with_padding - num_bytes_following
1602 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1603 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001604 len(self.salt), len(self.digest), self.flags,
1605 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001606 padding = struct.pack(str(padding_size) + 'x')
1607 ret = desc + encoded_name + self.salt + self.digest + padding
1608 return bytearray(ret)
1609
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001610 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001611 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001612 """Verifies contents of the descriptor - used in verify_image sub-command.
1613
1614 Arguments:
1615 image_dir: The directory of the file being verified.
1616 image_ext: The extension of the file being verified (e.g. '.img').
1617 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001618 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001619 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001620 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1621 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001622
1623 Returns:
1624 True if the descriptor verifies, False otherwise.
1625 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001626 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001627 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001628 image = image_containing_descriptor
1629 else:
1630 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1631 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001632 data = image.read(self.image_size)
1633 ha = hashlib.new(self.hash_algorithm)
1634 ha.update(self.salt)
1635 ha.update(data)
1636 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001637 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001638 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001639 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1640 format(self.hash_algorithm, image_filename))
1641 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001642 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1643 .format(self.partition_name, self.hash_algorithm, image.filename,
1644 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001645 return True
1646
David Zeuthen21e95262016-07-27 17:58:40 -04001647
1648class AvbKernelCmdlineDescriptor(AvbDescriptor):
1649 """A class for kernel command-line descriptors.
1650
1651 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1652
1653 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001654 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001655 kernel_cmdline: The kernel command-line.
1656 """
1657
1658 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001659 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001660 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001661 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001662 'L') # cmdline length (bytes)
1663
David Zeuthenfd41eb92016-11-17 12:24:47 -05001664 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1665 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1666
David Zeuthen21e95262016-07-27 17:58:40 -04001667 def __init__(self, data=None):
1668 """Initializes a new kernel cmdline descriptor.
1669
1670 Arguments:
1671 data: If not None, must be a bytearray of size |SIZE|.
1672
1673 Raises:
1674 LookupError: If the given descriptor is malformed.
1675 """
1676 AvbDescriptor.__init__(self, None)
1677 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1678
1679 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001680 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001681 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1682 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1683 8)
1684 if tag != self.TAG or num_bytes_following != expected_size:
1685 raise LookupError('Given data does not look like a kernel cmdline '
1686 'descriptor.')
1687 # Nuke NUL-bytes at the end.
1688 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1689 kernel_cmdline_length)])
1690 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1691 self.kernel_cmdline.decode('utf-8')
1692 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001693 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.kernel_cmdline = ''
1695
1696 def print_desc(self, o):
1697 """Print the descriptor.
1698
1699 Arguments:
1700 o: The object to write the output to.
1701 """
1702 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001703 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001704 o.write(' Kernel Cmdline: {}\n'.format(repr(
1705 self.kernel_cmdline)))
1706
1707 def encode(self):
1708 """Serializes the descriptor.
1709
1710 Returns:
1711 A bytearray() with the descriptor data.
1712 """
1713 encoded_str = self.kernel_cmdline.encode('utf-8')
1714 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1715 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1716 padding_size = nbf_with_padding - num_bytes_following
1717 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001718 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001719 padding = struct.pack(str(padding_size) + 'x')
1720 ret = desc + encoded_str + padding
1721 return bytearray(ret)
1722
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001723 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001724 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001725 """Verifies contents of the descriptor - used in verify_image sub-command.
1726
1727 Arguments:
1728 image_dir: The directory of the file being verified.
1729 image_ext: The extension of the file being verified (e.g. '.img').
1730 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001731 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001732 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001733 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1734 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001735
1736 Returns:
1737 True if the descriptor verifies, False otherwise.
1738 """
1739 # Nothing to verify.
1740 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001741
Jan Monscheeb28b62019-12-05 16:17:09 +01001742
David Zeuthen21e95262016-07-27 17:58:40 -04001743class AvbChainPartitionDescriptor(AvbDescriptor):
1744 """A class for chained partition descriptors.
1745
1746 See the |AvbChainPartitionDescriptor| C struct for more information.
1747
1748 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001749 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001750 partition_name: Partition name.
1751 public_key: Bytes for the public key.
1752 """
1753
1754 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001755 RESERVED = 64
1756 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001757 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001758 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001759 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001760 'L' + # public_key_size (bytes)
1761 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001762
1763 def __init__(self, data=None):
1764 """Initializes a new chain partition descriptor.
1765
1766 Arguments:
1767 data: If not None, must be a bytearray of size |SIZE|.
1768
1769 Raises:
1770 LookupError: If the given descriptor is malformed.
1771 """
1772 AvbDescriptor.__init__(self, None)
1773 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1774
1775 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001776 (tag, num_bytes_following, self.rollback_index_location,
1777 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001778 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001779 expected_size = round_to_multiple(
1780 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1781 if tag != self.TAG or num_bytes_following != expected_size:
1782 raise LookupError('Given data does not look like a chain partition '
1783 'descriptor.')
1784 o = 0
1785 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1786 partition_name_len)])
1787 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1788 self.partition_name.decode('utf-8')
1789 o += partition_name_len
1790 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1791
1792 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001793 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001794 self.partition_name = ''
1795 self.public_key = bytearray()
1796
1797 def print_desc(self, o):
1798 """Print the descriptor.
1799
1800 Arguments:
1801 o: The object to write the output to.
1802 """
1803 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001804 o.write(' Partition Name: {}\n'.format(self.partition_name))
1805 o.write(' Rollback Index Location: {}\n'.format(
1806 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001807 # Just show the SHA1 of the key, for size reasons.
1808 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001809 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001810
1811 def encode(self):
1812 """Serializes the descriptor.
1813
1814 Returns:
1815 A bytearray() with the descriptor data.
1816 """
1817 encoded_name = self.partition_name.encode('utf-8')
1818 num_bytes_following = (
1819 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1820 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1821 padding_size = nbf_with_padding - num_bytes_following
1822 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001823 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001824 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001825 padding = struct.pack(str(padding_size) + 'x')
1826 ret = desc + encoded_name + self.public_key + padding
1827 return bytearray(ret)
1828
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001829 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001830 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001831 """Verifies contents of the descriptor - used in verify_image sub-command.
1832
1833 Arguments:
1834 image_dir: The directory of the file being verified.
1835 image_ext: The extension of the file being verified (e.g. '.img').
1836 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001837 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001838 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001839 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1840 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001841
1842 Returns:
1843 True if the descriptor verifies, False otherwise.
1844 """
1845 value = expected_chain_partitions_map.get(self.partition_name)
1846 if not value:
1847 sys.stderr.write('No expected chain partition for partition {}. Use '
1848 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001849 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001850 format(self.partition_name))
1851 return False
1852 rollback_index_location, pk_blob = value
1853
1854 if self.rollback_index_location != rollback_index_location:
1855 sys.stderr.write('Expected rollback_index_location {} does not '
1856 'match {} in descriptor for partition {}\n'.
1857 format(rollback_index_location,
1858 self.rollback_index_location,
1859 self.partition_name))
1860 return False
1861
1862 if self.public_key != pk_blob:
1863 sys.stderr.write('Expected public key blob does not match public '
1864 'key blob in descriptor for partition {}\n'.
1865 format(self.partition_name))
1866 return False
1867
Jan Monsch23e0c622019-12-11 11:23:58 +01001868 print('{}: Successfully verified chain partition descriptor matches '
1869 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001870
1871 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001872
1873DESCRIPTOR_CLASSES = [
1874 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1875 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1876]
1877
1878
1879def parse_descriptors(data):
1880 """Parses a blob of data into descriptors.
1881
1882 Arguments:
1883 data: A bytearray() with encoded descriptors.
1884
1885 Returns:
1886 A list of instances of objects derived from AvbDescriptor. For
1887 unknown descriptors, the class AvbDescriptor is used.
1888 """
1889 o = 0
1890 ret = []
1891 while o < len(data):
1892 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1893 if tag < len(DESCRIPTOR_CLASSES):
1894 c = DESCRIPTOR_CLASSES[tag]
1895 else:
1896 c = AvbDescriptor
1897 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1898 o += 16 + nb_following
1899 return ret
1900
1901
1902class AvbFooter(object):
1903 """A class for parsing and writing footers.
1904
1905 Footers are stored at the end of partitions and point to where the
1906 AvbVBMeta blob is located. They also contain the original size of
1907 the image before AVB information was added.
1908
1909 Attributes:
1910 magic: Magic for identifying the footer, see |MAGIC|.
1911 version_major: The major version of avbtool that wrote the footer.
1912 version_minor: The minor version of avbtool that wrote the footer.
1913 original_image_size: Original image size.
1914 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1915 vbmeta_size: Size of the AvbVBMeta blob.
1916 """
1917
1918 MAGIC = 'AVBf'
1919 SIZE = 64
1920 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001921 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1922 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001923 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1924 'Q' # Original image size.
1925 'Q' # Offset of VBMeta blob.
1926 'Q' + # Size of VBMeta blob.
1927 str(RESERVED) + 'x') # padding for reserved bytes
1928
1929 def __init__(self, data=None):
1930 """Initializes a new footer object.
1931
1932 Arguments:
1933 data: If not None, must be a bytearray of size 4096.
1934
1935 Raises:
1936 LookupError: If the given footer is malformed.
1937 struct.error: If the given data has no footer.
1938 """
1939 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1940
1941 if data:
1942 (self.magic, self.version_major, self.version_minor,
1943 self.original_image_size, self.vbmeta_offset,
1944 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1945 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001946 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001947 else:
1948 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001949 self.version_major = self.FOOTER_VERSION_MAJOR
1950 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001951 self.original_image_size = 0
1952 self.vbmeta_offset = 0
1953 self.vbmeta_size = 0
1954
David Zeuthena4fee8b2016-08-22 15:20:43 -04001955 def encode(self):
1956 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001957
David Zeuthena4fee8b2016-08-22 15:20:43 -04001958 Returns:
1959 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001960 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001961 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1962 self.version_minor, self.original_image_size,
1963 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001964
1965
Dan Austina7bc4962019-12-02 13:26:08 -08001966# Android Firmware Transparency Log Data Structures
1967
Jan Monsch77cd2022019-12-10 17:18:04 +01001968
Dan Austina7bc4962019-12-02 13:26:08 -08001969class AvbIcpHeader(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01001970 """A class for the transparency log inclusion proof header.
Dan Austina7bc4962019-12-02 13:26:08 -08001971
1972 Attributes:
1973 magic: Magic for identifying the ICP header.
1974 required_icp_version_major: The major version of AVB that wrote the entry.
1975 required_icp_version_minor: The minor version of AVB that wrote the entry.
1976 algorithm: Hash algorithm used. ID is defined in ALGORITHMS.
1977 icp_count: Number of inclusion proofs represented in this structure.
1978 """
1979
1980 SIZE = 18 # The size of the structure, in bytes
1981 MAGIC = 'AFTL'
Jan Monsch77cd2022019-12-10 17:18:04 +01001982 FORMAT_STRING = ('!4s2L' # magic, major & minor version
1983 'L' # algorithm type for transparency log
1984 'H') # number of inclusion proof entries
Dan Austina7bc4962019-12-02 13:26:08 -08001985
1986 def __init__(self, data=None):
1987 """Initializes a new transparency header object.
1988
1989 Arguments:
1990 data: If not None, must be a bytearray of size == 18.
Dan Austin4e6615a2019-12-03 15:19:36 -08001991
1992 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08001993 AvbError: If invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08001994 """
1995 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1996
1997 if data:
Jan Monsch77cd2022019-12-10 17:18:04 +01001998 (self.magic, self.required_icp_version_major,
1999 self.required_icp_version_minor, self.algorithm,
2000 self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08002001 else:
2002 self.magic = self.MAGIC
2003 self.required_icp_version_major = AVB_VERSION_MAJOR
2004 self.required_icp_version_minor = AVB_VERSION_MINOR
2005 self.algorithm = 0
2006 self.icp_count = 0
Dan Austin4e6615a2019-12-03 15:19:36 -08002007 if not self.is_valid():
2008 raise AvbError('Invalid structure for AvbIcpHeader')
Dan Austina7bc4962019-12-02 13:26:08 -08002009
2010 def save(self, output):
2011 """Serializes the transparency header (18) to disk.
2012
2013 Arguments:
2014 output: The object to write the header to.
Dan Austin4e6615a2019-12-03 15:19:36 -08002015
2016 Raises:
2017 AvbError if invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08002018 """
2019 output.write(self.encode())
2020
2021 def encode(self):
2022 """Serializes the header (18) to a bytearray().
2023
2024 Returns:
2025 A bytearray() with the encoded header.
Dan Austin4e6615a2019-12-03 15:19:36 -08002026
2027 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08002028 AvbError: If invalid structure for AvbIcpHeader.
Dan Austina7bc4962019-12-02 13:26:08 -08002029 """
Dan Austin4e6615a2019-12-03 15:19:36 -08002030 if not self.is_valid():
2031 raise AvbError('Invalid structure for AvbIcpHeader')
Dan Austina7bc4962019-12-02 13:26:08 -08002032 return struct.pack(self.FORMAT_STRING, self.magic,
2033 self.required_icp_version_major,
2034 self.required_icp_version_minor,
2035 self.algorithm, self.icp_count)
Jan Monsch77cd2022019-12-10 17:18:04 +01002036
Dan Austina7bc4962019-12-02 13:26:08 -08002037 def is_valid(self):
2038 """Ensures that values in an AvbIcpHeader structure are sane.
2039
2040 Returns:
2041 True if the values in the AvbIcpHeader are sane, False otherwise.
2042 """
2043 if self.magic != AvbIcpHeader.MAGIC:
Jan Monsch77cd2022019-12-10 17:18:04 +01002044 sys.stderr.write(
2045 'ICP Header: magic value mismatch: {}\n'.format(self.magic))
Dan Austina7bc4962019-12-02 13:26:08 -08002046 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002047
Dan Austina7bc4962019-12-02 13:26:08 -08002048 if self.required_icp_version_major > AVB_VERSION_MAJOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002049 sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
2050 self.required_icp_version_major))
Dan Austina7bc4962019-12-02 13:26:08 -08002051 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002052
Dan Austina7bc4962019-12-02 13:26:08 -08002053 if self.required_icp_version_minor > AVB_VERSION_MINOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002054 sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
2055 self.required_icp_version_minor))
Dan Austina7bc4962019-12-02 13:26:08 -08002056 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002057
Dan Austina7bc4962019-12-02 13:26:08 -08002058 if self.algorithm < 0 or self.algorithm >= len(ALGORITHMS):
Jan Monsch77cd2022019-12-10 17:18:04 +01002059 sys.stderr.write(
2060 'ICP header: algorithm identifier out of range: {}\n'.format(
2061 self.algorithm))
Dan Austina7bc4962019-12-02 13:26:08 -08002062 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002063
Dan Austina7bc4962019-12-02 13:26:08 -08002064 if self.icp_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002065 sys.stderr.write(
2066 'ICP header: ICP entry count out of range: {}\n'.format(
2067 self.icp_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002068 return False
2069 return True
2070
Dan Austinb12b2c12019-12-15 20:28:02 -08002071def check_signature(log_root, log_root_sig,
2072 transparency_log_pub_key):
2073 """Validates the signature provided by the transparency log.
2074
2075 Arguments:
2076 log_root_hash: The root hash for the transparency log's Merkle tree.
2077 log_root_sig: The signature of the root hash for the transparency log.
2078 transparency_log_pub_key: The trusted public key of the transparency log.
2079 algorithm: The algorithm ID given by the AvbIcpHeader to determine hash
2080 and signature size.
2081 Returns:
2082 True if the signature check passes, otherwise False.
2083 """
2084
2085 # TODO(danielaustin): Figure out why validation is not working.
2086 logsig_tmp = tempfile.NamedTemporaryFile()
2087 logsig_tmp.write(log_root_sig)
2088 logsig_tmp.flush()
2089 logroot_tmp = tempfile.NamedTemporaryFile()
2090 logroot_tmp.write(log_root)
2091 logroot_tmp.flush()
2092
2093 p = subprocess.Popen(['openssl', 'dgst', '-sha256', '-verify',
2094 transparency_log_pub_key,
2095 '-signature', logsig_tmp.name, logroot_tmp.name],
2096 stdin=subprocess.PIPE,
2097 stdout=subprocess.PIPE,
2098 stderr=subprocess.PIPE)
2099
2100 (_, openssl_err) = p.communicate()
2101 retcode = p.wait()
2102 if not retcode:
2103 return True
2104 sys.stderr.write('openssl status {}'.format(openssl_err))
2105 return False
2106
2107class AvbIcpSignedRootBlob(object):
2108 """A class for the components required to validate the incusion proof.
2109
2110 This class contains the signed tree root components required to verify
2111 an inclusion proof given a list of hashes.
2112
2113 Attributes:
2114 leaf_hash: The hash of the leaf corresponding with this log entry.
2115 tree_size: The size of the Merkle tree.
2116 root_hash: The calculated root hash of the Merkle tree.
2117 log_root_sig: The signed root hash. Used to verify the ICP.
2118 """
2119 #TODO(danielaustin): Match hash and signature size to algorithm value.
2120 SIZE = 645
2121 FORMAT_STRING = ('!32s' # The leaf hash corresponding to this vbmeta.
2122 'Q' # The Merkle tree size
2123 '61s' # The log_root structure that is signed
2124 '32s' # The Merkle tree root hash.
2125 '512s') # The log_root signed with the transparency log key.
2126
2127 def __init__(self, data=None):
2128 """Initializes a new signed_root_blob structure.
2129
2130 Arguments:
2131 data: If not None, must be a bytearray of size |SIZE|.
2132
2133 Raises:
2134 AvbError: If data does not represent a well-formed AvbIcpSignedRootBlob.
2135 """
2136 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2137
2138 if data:
2139 (self.leaf_hash, self.tree_size, self.log_root,
2140 self.root_hash, self.log_root_sig) = struct.unpack(
2141 self.FORMAT_STRING, data)
2142 else:
2143 self.leaf_hash = bytearray()
2144 self.tree_size = 0
2145 self.log_root = bytearray()
2146 self.root_hash = bytearray()
2147 self.log_root_sig = ''
2148
2149 if not self.is_valid():
2150 raise AvbError('Invalid structure for AvbIcpSignedBlob')
2151
2152 def translate_afi_response(self, afi_response):
2153 """Translates an AddFirmwareImageResponse message to AvbIcpSignedRootBlob.
2154
2155 Arguments:
2156 afi_response: An AddFirmwareImageResponse proto message.
2157
2158 Raises:
2159 AvbError: If unsupported hash size is detected.
2160 """
2161 # Do the hash calculation
2162 self.leaf_hash = rfc6962_hash_leaf(afi_response.vbmeta_leaf)
2163 self.log_root = afi_response.vbmeta_proof.sth.log_root
2164 self.log_root_sig = str(afi_response.vbmeta_proof.sth.log_root_signature)
2165 # Partial format string to extract the tree_size and root_hash from
2166 # the log_root. THis structure is defined:
2167 # https://github.com/google/trillian/blob/master/trillian.proto#L255
2168
2169 # TODO(danielaustin): Make this into a class.
2170 partial_log_format_string = ('!H' # Version
2171 'Q' # tree_size
2172 'B' # hash_size, verify this is 32 for now
2173 '32s') # The root_hash
2174
2175 (log_root_version, self.tree_size, root_hash_size,
2176 self.root_hash) = struct.unpack(partial_log_format_string,
2177 self.log_root[0:43])
2178 if log_root_version != 1:
2179 raise AvbError('Unsupported log root version: {}'.format(
2180 log_root_version))
2181 if not len(self.root_hash) == root_hash_size:
2182 raise AvbError('Unsupported hash size.')
2183
2184 def encode(self):
2185 """Serializes the AvbSignedRootBlob structure (584) to a bytearray.
2186
2187 Returns:
2188 A bytearray with the AvbSignedRootBlob.
2189
2190 Raises:
2191 AvbError: If data does not represent a well-formed AvbIcpSignedRootBlob.
2192 """
2193 if not self.is_valid():
2194 raise AvbError('Invalid structure for AvbIcpSignedRootBlob')
2195
2196 return struct.pack(self.FORMAT_STRING,
2197 str(self.leaf_hash),
2198 self.tree_size,
2199 str(self.log_root),
2200 str(self.root_hash),
2201 str(self.log_root_sig))
2202 def is_valid(self):
2203 """Ensures that values in the AvbIcpSignedRootBlob are sane.
2204
2205 Returns:
2206 True if the values in the AvbIcpSignedRootBlob are sane, False otherwise.
2207 """
2208 #TODO(danielaustin): match these up with algorithm instead of defaults.
2209 # All structures being of size 0 is valid
2210 if not self.leaf_hash and self.tree_size == 0 and \
2211 not self.root_hash and not self.log_root_sig:
2212 return True
2213 if not len(self.leaf_hash) == 32:
2214 sys.stderr.write("AvbIcpSignedRootBlob: Bad leaf_hash size {}".format(
2215 len(self.leaf_hash)))
2216 return False
2217 if self.tree_size < 0:
2218 sys.stderr.write("AvbIcpSignedRootBlob: Bad tree_size value {}".format(
2219 self.tree_size))
2220 return False
2221 if not len(self.root_hash) == 32:
2222 sys.stderr.write("AvbIcpSignedRootBlob: Bad root_hash size {}".format(
2223 len(self.root_hash)))
2224 return False
2225 if not len(self.log_root_sig) == 512:
2226 sys.stderr.write("AvbIcpSignedRootBlob: Bad log_root_sig size {}".format(
2227 len(self.log_root_sig)))
2228 return False
2229 return True
Dan Austina7bc4962019-12-02 13:26:08 -08002230
2231class AvbIcpEntry(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01002232 """A class for the transparency log inclusion proof entries.
Dan Austina7bc4962019-12-02 13:26:08 -08002233
Jan Monsch77cd2022019-12-10 17:18:04 +01002234 The data that represents each of the components of the ICP entry are stored
2235 immediately following the ICP entry header. The format is log_url,
2236 SignedLogRoot, and inclusion proof hashes.
Dan Austina7bc4962019-12-02 13:26:08 -08002237
2238 Attributes:
2239 log_url_size: Length of the string representing the transparency log URL.
2240 leaf_index: Leaf index in the transparency log representing this entry.
Jan Monsch77cd2022019-12-10 17:18:04 +01002241 signed_root_blob_size: Size of the SignedLogRoot for the transparency log;
2242 treat as an opaque blob for now.
Dan Austina7bc4962019-12-02 13:26:08 -08002243 proof_hash_count: Number of hashes comprising the inclusion proof.
2244 proof_size: The total size of the inclusion proof, in bytes.
2245 next_entry: 1 if there is a next entry, 0 otherwise.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002246 log_url: The URL for the transparency log that generated this inclusion
2247 proof.
Dan Austin4e6615a2019-12-03 15:19:36 -08002248 signed_root_blob: The data comprising the signed tree head structure.
2249 proofs: The hashes comprising the inclusion proof.
2250
Dan Austina7bc4962019-12-02 13:26:08 -08002251 """
2252 SIZE = 22 # The size of the structure, in bytes
2253 FORMAT_STRING = ('!L' # transparency log server url size
2254 'Q' # leaf index
2255 'L' # signed tree root blob size
2256 'B' # number of hashes in the inclusion proof
2257 'L' # size of the inclusion proof in bytes
2258 'B') # next entry marker
Dan Austin4e6615a2019-12-03 15:19:36 -08002259 # These are used to capture the log_url, signed_root_blob,
2260 # and the proofs elements for the encode & save function.
Dan Austina7bc4962019-12-02 13:26:08 -08002261
2262 def __init__(self, data=None):
2263 """Initializes a new ICP entry object.
2264
2265 Arguments:
Dan Austin4e6615a2019-12-03 15:19:36 -08002266 data: If not None, must be a bytearray of size >= 22.
2267
2268 Raises:
2269 AvbError: If data does not represent a well-formed AvbIcpEntry.
Dan Austina7bc4962019-12-02 13:26:08 -08002270 """
Dan Austin4e6615a2019-12-03 15:19:36 -08002271 # Assert the header structure is of a sane size.
Dan Austina7bc4962019-12-02 13:26:08 -08002272 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2273
2274 if data:
Dan Austin4e6615a2019-12-03 15:19:36 -08002275 # Deserialize the header from the data blob.
Dan Austina7bc4962019-12-02 13:26:08 -08002276 (self.log_url_size, self.leaf_index, self.signed_root_blob_size,
Jan Monsch5ac429e2019-12-11 19:04:26 +01002277 self.proof_hash_count, self.proof_size, self.next_entry) = struct.unpack(
2278 self.FORMAT_STRING, data[0:self.SIZE])
Dan Austin4e6615a2019-12-03 15:19:36 -08002279 if len(data) >= self.SIZE:
2280 # There's more data. Ensure the data entry size is valid.
2281 if len(data) != self.get_expected_size():
2282 if self.next_entry == 0:
Jan Monsch5ac429e2019-12-11 19:04:26 +01002283 raise AvbError('ICP entry size is not valid {}/{}.'
2284 .format(len(data), self.get_expected_size()))
Dan Austin4e6615a2019-12-03 15:19:36 -08002285 # Deserialize ICP entry components from the data blob.
Dan Austinb12b2c12019-12-15 20:28:02 -08002286 expected_format_string = '{}s{}s{}s'.format(
2287 self.log_url_size,
2288 AvbIcpSignedRootBlob.SIZE,
2289 self.proof_size)
2290
2291 (self.log_url, signed_root_blob_bytes, proof_bytes) = struct.unpack(
2292 expected_format_string, data[self.SIZE:self.get_expected_size()])
2293 self.signed_root_blob = AvbIcpSignedRootBlob(signed_root_blob_bytes)
2294 self.proofs = []
2295 if self.proof_hash_count > 0:
2296 proof_idx = 0
2297 hash_size = self.proof_size // self.proof_hash_count
2298 for _ in range(self.proof_hash_count):
2299 proof = proof_bytes[proof_idx:(proof_idx+hash_size)]
2300 self.proofs.append(proof)
2301 proof_idx += hash_size
Dan Austina7bc4962019-12-02 13:26:08 -08002302 else:
2303 self.log_url_size = 0
2304 self.leaf_index = 0
2305 self.signed_root_blob_size = 0
2306 self.proof_hash_count = 0
2307 self.proof_size = 0
2308 self.next_entry = 0
Dan Austinb12b2c12019-12-15 20:28:02 -08002309 self.log_url = ''
2310 self.signed_root_blob = AvbIcpSignedRootBlob()
2311 self.proofs = []
Dan Austin4e6615a2019-12-03 15:19:36 -08002312 if not self.is_valid():
2313 raise AvbError('Invalid structure for AvbIcpEntry')
2314
2315 def set_log_url(self, log_url):
2316 """Sets the log_url and log_url_size elements in the AvbIcpEntry.
2317
2318 Arguments:
2319 log_url: The string representing the transparency log URL.
2320 """
2321 self.log_url = log_url
2322 self.log_url_size = len(log_url)
2323
2324 def set_signed_root_blob(self, signed_root_blob):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002325 """Sets signed_root_blob and signed_root_blob_size.
Dan Austin4e6615a2019-12-03 15:19:36 -08002326
2327 Arguments:
Dan Austinb12b2c12019-12-15 20:28:02 -08002328 signed_root_blob: An AvbIcpSignedRootBlob containing the SignedLogRoot
2329 for the transparency log.
Dan Austin4e6615a2019-12-03 15:19:36 -08002330 """
2331 self.signed_root_blob = signed_root_blob
Dan Austinb12b2c12019-12-15 20:28:02 -08002332 self.signed_root_blob_size = signed_root_blob.SIZE
Dan Austin4e6615a2019-12-03 15:19:36 -08002333
Dan Austinb12b2c12019-12-15 20:28:02 -08002334 def set_proofs(self, proofs):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002335 """Sets the proof_hash_count, proofs, and proof_size.
Dan Austin4e6615a2019-12-03 15:19:36 -08002336
2337 Arguments:
Dan Austin4e6615a2019-12-03 15:19:36 -08002338 proofs: A bytearray of concatenated hashes comprising the inclusion proof.
2339 """
Dan Austinb12b2c12019-12-15 20:28:02 -08002340 self.proof_hash_count = 0
Dan Austin4e6615a2019-12-03 15:19:36 -08002341 self.proofs = proofs
Dan Austinb12b2c12019-12-15 20:28:02 -08002342 proof_size = 0
2343 for proof in proofs:
2344 proof_size += len(proof)
2345 self.proof_hash_count += 1
2346 self.proof_size = proof_size
2347
2348 def verify_icp(self, transparency_log_pub_key):
2349 """Verifies the contained inclusion proof given the public log key.
2350
2351 Arguments:
2352 transparency_log_pub_key: The trusted public key for the log.
2353
2354 Returns:
2355 True if the calculated signature matches AvbIcpEntry's. False otherwise.
2356 """
2357 calc_root = root_from_icp(self.leaf_index, self.signed_root_blob.tree_size,
2358 self.proofs, self.signed_root_blob.leaf_hash)
2359 if (calc_root == self.signed_root_blob.root_hash) and check_signature(
2360 self.signed_root_blob.log_root, self.signed_root_blob.log_root_sig,
2361 transparency_log_pub_key):
2362 return True
2363 return False
Dan Austina7bc4962019-12-02 13:26:08 -08002364
2365 def save(self, output):
Dan Austin4e6615a2019-12-03 15:19:36 -08002366 """Serializes the transparency header (22) and data to disk.
Dan Austina7bc4962019-12-02 13:26:08 -08002367
2368 Arguments:
2369 output: The object to write the header to.
Dan Austin4e6615a2019-12-03 15:19:36 -08002370
2371 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08002372 AvbError: If invalid entry structure.
Dan Austina7bc4962019-12-02 13:26:08 -08002373 """
2374 output.write(self.encode())
2375
2376 def encode(self):
Dan Austin4e6615a2019-12-03 15:19:36 -08002377 """Serializes the header (22) and data to a bytearray().
Dan Austina7bc4962019-12-02 13:26:08 -08002378
2379 Returns:
2380 A bytearray() with the encoded header.
Dan Austin4e6615a2019-12-03 15:19:36 -08002381
2382 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08002383 AvbError: If invalid entry structure.
Dan Austina7bc4962019-12-02 13:26:08 -08002384 """
Dan Austinb12b2c12019-12-15 20:28:02 -08002385 proof_bytes = bytearray()
Dan Austin4e6615a2019-12-03 15:19:36 -08002386 if not self.is_valid():
2387 raise AvbError('Invalid AvbIcpEntry structure')
Dan Austinb12b2c12019-12-15 20:28:02 -08002388 expected_format_string = '{}{}s{}s{}s'.format(
2389 self.FORMAT_STRING, self.log_url_size,
2390 self.signed_root_blob.SIZE,
2391 self.proof_size)
2392
2393 for proof in self.proofs:
2394 proof_bytes.extend(proof)
2395
Dan Austin4e6615a2019-12-03 15:19:36 -08002396 return struct.pack(expected_format_string,
2397 self.log_url_size, self.leaf_index,
2398 self.signed_root_blob_size, self.proof_hash_count,
2399 self.proof_size, self.next_entry, self.log_url,
Dan Austinb12b2c12019-12-15 20:28:02 -08002400 self.signed_root_blob.encode(),
2401 str(proof_bytes))
2402
2403 # TODO(danielaustin): Add unit test.
2404 def translate_response(self, transparency_log, afi_response):
2405 """Takes an AddFirmwareInfoResponse object and translates to an AvbIcpEntry.
2406
2407 Arguments:
2408 transparency_log: String representing the transparency log URL.
2409 afi_response: The AddFirmwareResponse object to translate.
2410 """
2411 self.set_log_url(transparency_log)
2412 self.leaf_index = afi_response.vbmeta_proof.proof.leaf_index
2413 self.signed_root_blob = AvbIcpSignedRootBlob()
2414 self.signed_root_blob.translate_afi_response(afi_response)
2415 self.signed_root_blob_size = self.signed_root_blob.SIZE
2416 # Calculate the number of hashes.
2417 proof_hashes = afi_response.vbmeta_proof.proof.hashes
2418 self.set_proofs(proof_hashes)
Dan Austin4e6615a2019-12-03 15:19:36 -08002419
2420 def get_expected_size(self):
2421 """Gets the expected size of the full entry out of the header.
2422
2423 Returns:
2424 The expected size of the AvbIcpEntry from the header.
2425 """
2426 return (self.SIZE + self.log_url_size +
Jan Monsch5ac429e2019-12-11 19:04:26 +01002427 self.signed_root_blob_size + self.proof_size)
Dan Austina7bc4962019-12-02 13:26:08 -08002428
2429 def is_valid(self):
2430 """Ensures that values in an AvbIcpEntry structure are sane.
2431
2432 Returns:
2433 True if the values in the AvbIcpEntry are sane, False otherwise.
2434 """
Jan Monsch5ac429e2019-12-11 19:04:26 +01002435 if ((self.log_url and self.log_url_size != len(self.log_url))
2436 or (not self.log_url and self.log_url_size != 0)):
2437 sys.stderr.write('ICP entry: invalid URL size: {}\n'
2438 .format(self.log_url_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002439 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002440
Dan Austina7bc4962019-12-02 13:26:08 -08002441 if self.leaf_index < 0:
Jan Monsch5ac429e2019-12-11 19:04:26 +01002442 sys.stderr.write('ICP entry: leaf index out of range: '
2443 '{}\n'.format(self.leaf_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002444 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002445
Dan Austinb12b2c12019-12-15 20:28:02 -08002446 if not self.signed_root_blob or not self.signed_root_blob.is_valid():
2447 sys.stderr.write('ICP entry: invalid AvbIcpSignedRootBlob\n')
2448 return False
2449
2450 if (self.signed_root_blob_size != 0) and (
2451 not self.signed_root_blob_size == self.signed_root_blob.SIZE):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002452 sys.stderr.write('ICP entry: invalid signed root blob size: '
Dan Austinb12b2c12019-12-15 20:28:02 -08002453 '{}, should be {}\n'.format(
2454 self.signed_root_blob_size,
2455 self.signed_root_blob.SIZE))
Dan Austina7bc4962019-12-02 13:26:08 -08002456 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002457
Dan Austina7bc4962019-12-02 13:26:08 -08002458 if self.proof_hash_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002459 sys.stderr.write('ICP entry: invalid proof count: {}\n'.format(
2460 self.proof_hash_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002461 return False
Jan Monsch5ac429e2019-12-11 19:04:26 +01002462
Dan Austinb12b2c12019-12-15 20:28:02 -08002463 proof_size = 0
2464 if self.proofs:
2465 for proof in self.proofs:
2466 proof_size += len(proof)
2467 if self.proof_size != proof_size:
2468 sys.stderr.write('ICP entry: invalid transparency log proof size: ')
2469 sys.stderr.write('{}, calculated {}\n'.format(self.proof_size,
2470 proof_size))
2471 return False
2472 elif self.proof_size != 0:
2473 sys.stderr.write('ICP entry: invalid transparency log proof size '
2474 '(should be 0): {}'.format(self.proof_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002475 return False
2476 if self.next_entry != 0 and self.next_entry != 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002477 sys.stderr.write('ICP entry: invalid next entry value: {}\n'.format(
2478 self.next_entry))
Dan Austina7bc4962019-12-02 13:26:08 -08002479 return False
2480 return True
2481
Jan Monsch5ac429e2019-12-11 19:04:26 +01002482
Dan Austin4e6615a2019-12-03 15:19:36 -08002483class AvbIcpBlob(object):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002484 """A class for the transparency log inclusion proof blob.
2485
2486 This encapsulates an AFTL ICP section with all information required to
2487 validate an inclusion proof.
Jan Monsch77cd2022019-12-10 17:18:04 +01002488
Dan Austin4e6615a2019-12-03 15:19:36 -08002489 Attributes:
2490 icp_header: A header for the section.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002491 icp_entries: A list of AvbIcpEntry objects representing the inclusion
2492 proofs.
Dan Austina7bc4962019-12-02 13:26:08 -08002493 """
Dan Austina7bc4962019-12-02 13:26:08 -08002494
Dan Austin4e6615a2019-12-03 15:19:36 -08002495 def __init__(self, data=None):
2496 """Initializes a new AvbIcpBlob section.
Dan Austina7bc4962019-12-02 13:26:08 -08002497
Dan Austin4e6615a2019-12-03 15:19:36 -08002498 Arguments:
2499 data: If not None, must be a bytearray representing an AvbIcpBlob.
Dan Austina7bc4962019-12-02 13:26:08 -08002500
Dan Austin4e6615a2019-12-03 15:19:36 -08002501 Raises:
2502 AvbError: If the data does not represent a well-formed AvbIcpBlob.
2503 """
2504 if data:
2505 icp_header_bytes = data[0:AvbIcpHeader.SIZE]
2506 self.icp_header = AvbIcpHeader(icp_header_bytes)
2507 if not self.icp_header.is_valid():
2508 raise AvbError('Invalid ICP header.')
2509 icp_count = self.icp_header.icp_count
2510 algorithm_id = self.icp_header.algorithm
Jan Monsch5ac429e2019-12-11 19:04:26 +01002511 # TODO(danielaustin): make use of proof_hash_size.
2512 # pylint: disable=unused-variable
Dan Austin4e6615a2019-12-03 15:19:36 -08002513 proof_hash_size = lookup_hash_size_by_type(algorithm_id)
2514
2515 # Jump past the header for entry deserialization.
2516 icp_index = AvbIcpHeader.SIZE
2517 # Validate each entry.
2518 self.icp_entries = []
Jan Monsch5ac429e2019-12-11 19:04:26 +01002519 # Add_icp_entry updates entries and header, so set header count to
2520 # compensate.
Dan Austin4e6615a2019-12-03 15:19:36 -08002521 self.icp_header.icp_count = 0
2522 for i in range(icp_count):
2523 # Get the entry header from the ICP blob.
2524 cur_icp_entry = AvbIcpEntry(data[icp_index:])
2525 cur_icp_entry_size = cur_icp_entry.get_expected_size()
2526 # Now validate the entry structure.
2527 if not cur_icp_entry.is_valid():
2528 raise AvbError('Validation of ICP entry failed.')
2529 self.add_icp_entry(cur_icp_entry)
2530 # Check if there is a next entry.
2531 if cur_icp_entry.next_entry == 0:
2532 if i != icp_count - 1:
2533 raise AvbError('ICP entry count mismatch')
2534 break
2535 icp_index += cur_icp_entry_size
2536 else:
2537 self.icp_header = AvbIcpHeader()
2538 self.icp_entries = []
2539 if not self.is_valid():
2540 raise AvbError('Malformed ICP blob')
2541
2542 def set_algorithm(self, algorithm_id):
Jan Monsch5ac429e2019-12-11 19:04:26 +01002543 """Sets algorithm to be used by the inclusion proofs in AvbIcpBlob."""
Dan Austin4e6615a2019-12-03 15:19:36 -08002544 self.icp_header.algorithm = algorithm_id
2545
2546 def add_icp_entry(self, avb_icp_entry):
2547 """Adds a new AvbIcpEntry to the AvbIcpBlob, updating fields as necessary.
2548
2549 Arguments:
2550 avb_icp_entry: An AvbIcpEntry structure.
2551 """
2552
2553 # Set the next entry field to denote that a new ICP entry will follow.
Jan Monsch5ac429e2019-12-11 19:04:26 +01002554 if self.icp_entries:
Dan Austin4e6615a2019-12-03 15:19:36 -08002555 self.icp_entries[-1].next_entry = 1
2556 self.icp_entries.append(avb_icp_entry)
2557 self.icp_header.icp_count += 1
2558
2559 def save(self, output):
2560 """Serializes the AvbIcpBlob to disk.
2561
2562 Arguments:
2563 output: The object to write the blob to.
2564
2565 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08002566 AvbError: If invalid blob structure.
Dan Austin4e6615a2019-12-03 15:19:36 -08002567 """
2568 output.write(self.encode())
2569
2570 def encode(self):
Dan Austinb12b2c12019-12-15 20:28:02 -08002571 """Serialize the AvbIcpBlob to a bytearray().
Dan Austin4e6615a2019-12-03 15:19:36 -08002572
2573 Returns:
2574 A bytearray() with the encoded header.
2575
2576 Raises:
Dan Austinb12b2c12019-12-15 20:28:02 -08002577 AvbError: If invalid blob structure.
Dan Austin4e6615a2019-12-03 15:19:36 -08002578 """
2579 # The header and entries are guaranteed to be valid when encode is called.
2580 # Check the entire structure as a whole.
2581 if not self.is_valid():
2582 raise AvbError('Invalid AvbIcpBlob structure.')
2583
2584 icp_blob = bytearray()
2585 icp_blob.extend(self.icp_header.encode())
2586 for icp_entry in self.icp_entries:
2587 icp_blob.extend(icp_entry.encode())
2588 return icp_blob
2589
2590 def is_valid(self):
2591 """Ensures that values in the AvbIcpBlob are sane.
2592
2593 Returns:
2594 True if the values in the AvbIcpBlob are sane, False otherwise.
2595 """
2596 if not self.icp_header.is_valid():
Dan Austina7bc4962019-12-02 13:26:08 -08002597 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002598
2599 if self.icp_header.icp_count != len(self.icp_entries):
Dan Austina7bc4962019-12-02 13:26:08 -08002600 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002601
2602 for icp_entry in self.icp_entries:
2603 if not icp_entry.is_valid():
Dan Austina7bc4962019-12-02 13:26:08 -08002604 return False
Dan Austin4e6615a2019-12-03 15:19:36 -08002605 return True
Dan Austina7bc4962019-12-02 13:26:08 -08002606
Jan Monsch77cd2022019-12-10 17:18:04 +01002607
Dan Austinb12b2c12019-12-15 20:28:02 -08002608# AFTL Merkle Tree Functionality
2609# TODO(danielaustin): Encapsulate this behavior in a class.
2610def rfc6962_hash_leaf(leaf):
2611 """RFC6962 hashing function for hashing leaves of a Merkle tree.
2612
2613 Arguments:
2614 leaf: A bytearray containing the Merkle tree leaf to be hashed.
2615
2616 Returns:
2617 A bytearray containing the RFC6962 SHA256 hash of the leaf.
2618 """
2619 hasher = hashlib.sha256()
2620 # RFC6962 states a '0' byte should be prepended to the data.
2621 # This is done in conjunction with the '1' byte for non-leaf
2622 # nodes for 2nd preimage attack resistance.
2623 hasher.update(b'\x00')
2624 hasher.update(leaf)
2625 return hasher.digest()
2626
2627
2628def rfc6962_hash_children(l, r):
2629 """Calculates the inner Merkle tree node hash of child nodes l and r.
2630
2631 Arguments:
2632 l: A bytearray containing the left child node to be hashed.
2633 r: A bytearray containing the right child node to be hashed.
2634
2635 Returns:
2636 A bytearray containing the RFC6962 SHA256 hash of 1|l|r.
2637 """
2638 hasher = hashlib.sha256()
2639 # RFC6962 states a '1' byte should be prepended to the concatenated data.
2640 # This is done in conjunction with the '0' byte for leaf
2641 # nodes for 2nd preimage attack resistance.
2642 hasher.update(b'\x01')
2643 hasher.update(l)
2644 hasher.update(r)
2645 return hasher.digest()
2646
2647
2648def chain_border_right(seed, proof):
2649 """Computes a subtree hash along the left-side tree border.
2650
2651 Arguments:
2652 seed: A bytearray containing the starting hash.
2653 proof: A list of bytearrays representing the hashes in the inclusion proof.
2654
2655 Returns:
2656 A bytearray containing the left-side subtree hash.
2657 """
2658 for h in proof:
2659 seed = rfc6962_hash_children(h, seed)
2660 return seed
2661
2662
2663def chain_inner(seed, proof, leaf_index):
2664 """Computes a subtree hash on or below the tree's right border.
2665
2666 Arguments:
2667 seed: A bytearray containing the starting hash.
2668 proof: A list of bytearrays representing the hashes in the inclusion proof.
2669 leaf_index: The current leaf index.
2670
2671 Returns:
2672 A bytearray containing the subtree hash.
2673 """
2674 for i, h in enumerate(proof):
2675 if leaf_index >> i & 1 == 0:
2676 seed = rfc6962_hash_children(seed, h)
2677 else:
2678 seed = rfc6962_hash_children(h, seed)
2679 return seed
2680
2681
2682def root_from_icp(leaf_index, tree_size, proof, leaf_hash):
2683 """Calculates the expected Merkle tree root hash.
2684
2685 Arguments:
2686 leaf_index: The current leaf index.
2687 tree_size: The number of nodes in the Merkle tree.
2688 proof: A list of bytearrays containing the inclusion proof.
2689 leaf_hash: A bytearray containing the initial leaf hash.
2690
2691 Returns:
2692 A bytearray containing the calculated Merkle tree root hash.
2693
2694 Raises:
2695 AvbError: If invalid parameters are passed in.
2696 """
2697 if leaf_index < 0:
2698 raise AvbError('Invalid leaf_index value: {}'.format(leaf_index))
2699 if tree_size < 0:
2700 raise AvbError('Invalid tree_size value: {}'.format(tree_size))
2701 if leaf_index >= tree_size:
2702 err_str = 'leaf_index cannot be equal or larger than tree_size: {}, {}'
2703 raise AvbError(err_str.format(leaf_index, tree_size))
2704
2705 # Calculate the point to split the proof into two parts.
2706 # The split is where the paths to leaves diverge.
2707 inner = (leaf_index ^ (tree_size - 1)).bit_length()
2708 result = chain_inner(leaf_hash, proof[:inner], leaf_index)
2709 result = chain_border_right(result, proof[inner:])
2710 return result
2711
2712
David Zeuthen21e95262016-07-27 17:58:40 -04002713class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002714 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002715
Jan Monschfe00c0a2019-12-11 11:19:40 +01002716 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2717 avb_vbmeta_image.h.
2718
David Zeuthen21e95262016-07-27 17:58:40 -04002719 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002720 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2721 required_libavb_version_major: The major version of libavb required for this
2722 header.
2723 required_libavb_version_minor: The minor version of libavb required for this
2724 header.
2725 authentication_data_block_size: The size of the signature block.
2726 auxiliary_data_block_size: The size of the auxiliary data block.
2727 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2728 enum.
2729 hash_offset: Offset into the "Authentication data" block of hash data.
2730 hash_size: Length of the hash data.
2731 signature_offset: Offset into the "Authentication data" block of signature
2732 data.
2733 signature_size: Length of the signature data.
2734 public_key_offset: Offset into the "Auxiliary data" block of public key
2735 data.
2736 public_key_size: Length of the public key data.
2737 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2738 key metadata.
2739 public_key_metadata_size: Length of the public key metadata. Must be set to
2740 zero if there is no public key metadata.
2741 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2742 data.
2743 descriptors_size: Length of descriptor data.
2744 rollback_index: The rollback index which can be used to prevent rollback to
2745 older versions.
2746 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2747 zero if the vbmeta image is not a top-level image.
2748 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2749 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2750 terminated. Applications must not make assumptions about how this
2751 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002752 """
2753
2754 SIZE = 256
2755
David Zeuthene3cadca2017-02-22 21:25:46 -05002756 # Keep in sync with |reserved0| and |reserved| field of
2757 # |AvbVBMetaImageHeader|.
2758 RESERVED0 = 4
2759 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002760
2761 # Keep in sync with |AvbVBMetaImageHeader|.
2762 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2763 '2Q' # 2 x block size
2764 'L' # algorithm type
2765 '2Q' # offset, size (hash)
2766 '2Q' # offset, size (signature)
2767 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002768 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002769 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002770 'Q' # rollback_index
2771 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002772 str(RESERVED0) + 'x' + # padding for reserved bytes
2773 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002774 str(RESERVED) + 'x') # padding for reserved bytes
2775
2776 def __init__(self, data=None):
2777 """Initializes a new header object.
2778
2779 Arguments:
2780 data: If not None, must be a bytearray of size 8192.
2781
2782 Raises:
2783 Exception: If the given data is malformed.
2784 """
2785 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2786
2787 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002788 (self.magic, self.required_libavb_version_major,
2789 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002790 self.authentication_data_block_size, self.auxiliary_data_block_size,
2791 self.algorithm_type, self.hash_offset, self.hash_size,
2792 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002793 self.public_key_size, self.public_key_metadata_offset,
2794 self.public_key_metadata_size, self.descriptors_offset,
2795 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002796 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002797 self.flags,
2798 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002799 # Nuke NUL-bytes at the end of the string.
2800 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002801 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002802 else:
2803 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002804 # Start by just requiring version 1.0. Code that adds features
2805 # in a future version can use bump_required_libavb_version_minor() to
2806 # bump the minor.
2807 self.required_libavb_version_major = AVB_VERSION_MAJOR
2808 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002809 self.authentication_data_block_size = 0
2810 self.auxiliary_data_block_size = 0
2811 self.algorithm_type = 0
2812 self.hash_offset = 0
2813 self.hash_size = 0
2814 self.signature_offset = 0
2815 self.signature_size = 0
2816 self.public_key_offset = 0
2817 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002818 self.public_key_metadata_offset = 0
2819 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002820 self.descriptors_offset = 0
2821 self.descriptors_size = 0
2822 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002823 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002824 self.release_string = get_release_string()
2825
2826 def bump_required_libavb_version_minor(self, minor):
2827 """Function to bump required_libavb_version_minor.
2828
2829 Call this when writing data that requires a specific libavb
2830 version to parse it.
2831
2832 Arguments:
2833 minor: The minor version of libavb that has support for the feature.
2834 """
2835 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002836 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002837
2838 def save(self, output):
2839 """Serializes the header (256 bytes) to disk.
2840
2841 Arguments:
2842 output: The object to write the output to.
2843 """
2844 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002845 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2846 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002847 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2848 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002849 self.public_key_offset, self.public_key_size,
2850 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002851 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002852 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002853
2854 def encode(self):
2855 """Serializes the header (256) to a bytearray().
2856
2857 Returns:
2858 A bytearray() with the encoded header.
2859 """
2860 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002861 self.required_libavb_version_major,
2862 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002863 self.authentication_data_block_size,
2864 self.auxiliary_data_block_size, self.algorithm_type,
2865 self.hash_offset, self.hash_size, self.signature_offset,
2866 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002867 self.public_key_size, self.public_key_metadata_offset,
2868 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002869 self.descriptors_size, self.rollback_index, self.flags,
2870 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002871
2872
2873class Avb(object):
2874 """Business logic for avbtool command-line tool."""
2875
David Zeuthen8b6973b2016-09-20 12:39:49 -04002876 # Keep in sync with avb_ab_flow.h.
2877 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2878 AB_MAGIC = '\0AB0'
2879 AB_MAJOR_VERSION = 1
2880 AB_MINOR_VERSION = 0
2881 AB_MISC_METADATA_OFFSET = 2048
2882
David Zeuthen09692692016-09-30 16:16:40 -04002883 # Constants for maximum metadata size. These are used to give
2884 # meaningful errors if the value passed in via --partition_size is
2885 # too small and when --calc_max_image_size is used. We use
2886 # conservative figures.
2887 MAX_VBMETA_SIZE = 64 * 1024
2888 MAX_FOOTER_SIZE = 4096
2889
David Zeuthen49936b42018-08-07 17:38:58 -04002890 def extract_vbmeta_image(self, output, image_filename, padding_size):
2891 """Implements the 'extract_vbmeta_image' command.
2892
2893 Arguments:
2894 output: Write vbmeta struct to this file.
2895 image_filename: File to extract vbmeta data from (with a footer).
2896 padding_size: If not 0, pads output so size is a multiple of the number.
2897
2898 Raises:
2899 AvbError: If there's no footer in the image.
2900 """
2901 image = ImageHandler(image_filename)
2902
2903 (footer, _, _, _) = self._parse_image(image)
2904
2905 if not footer:
2906 raise AvbError('Given image does not have a footer.')
2907
2908 image.seek(footer.vbmeta_offset)
2909 vbmeta_blob = image.read(footer.vbmeta_size)
2910 output.write(vbmeta_blob)
2911
2912 if padding_size > 0:
2913 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2914 padding_needed = padded_size - len(vbmeta_blob)
2915 output.write('\0' * padding_needed)
2916
David Zeuthena4fee8b2016-08-22 15:20:43 -04002917 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002918 """Implements the 'erase_footer' command.
2919
2920 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002921 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002922 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002923
2924 Raises:
2925 AvbError: If there's no footer in the image.
2926 """
2927
David Zeuthena4fee8b2016-08-22 15:20:43 -04002928 image = ImageHandler(image_filename)
2929
David Zeuthen21e95262016-07-27 17:58:40 -04002930 (footer, _, descriptors, _) = self._parse_image(image)
2931
2932 if not footer:
2933 raise AvbError('Given image does not have a footer.')
2934
2935 new_image_size = None
2936 if not keep_hashtree:
2937 new_image_size = footer.original_image_size
2938 else:
2939 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002940 # descriptor to figure out the location and size of the hashtree
2941 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002942 for desc in descriptors:
2943 if isinstance(desc, AvbHashtreeDescriptor):
2944 # The hashtree is always just following the main data so the
2945 # new size is easily derived.
2946 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002947 # If the image has FEC codes, also keep those.
2948 if desc.fec_offset > 0:
2949 fec_end = desc.fec_offset + desc.fec_size
2950 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002951 break
2952 if not new_image_size:
2953 raise AvbError('Requested to keep hashtree but no hashtree '
2954 'descriptor was found.')
2955
2956 # And cut...
2957 image.truncate(new_image_size)
2958
David Zeuthen1394f762019-04-30 10:20:11 -04002959 def zero_hashtree(self, image_filename):
2960 """Implements the 'zero_hashtree' command.
2961
2962 Arguments:
2963 image_filename: File to zero hashtree and FEC data from.
2964
2965 Raises:
2966 AvbError: If there's no footer in the image.
2967 """
2968
2969 image = ImageHandler(image_filename)
2970
2971 (footer, _, descriptors, _) = self._parse_image(image)
2972
2973 if not footer:
2974 raise AvbError('Given image does not have a footer.')
2975
2976 # Search for a hashtree descriptor to figure out the location and
2977 # size of the hashtree and FEC.
2978 ht_desc = None
2979 for desc in descriptors:
2980 if isinstance(desc, AvbHashtreeDescriptor):
2981 ht_desc = desc
2982 break
2983
2984 if not ht_desc:
2985 raise AvbError('No hashtree descriptor was found.')
2986
2987 zero_ht_start_offset = ht_desc.tree_offset
2988 zero_ht_num_bytes = ht_desc.tree_size
2989 zero_fec_start_offset = None
2990 zero_fec_num_bytes = 0
2991 if ht_desc.fec_offset > 0:
2992 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2993 raise AvbError('Hash-tree and FEC data must be adjacent.')
2994 zero_fec_start_offset = ht_desc.fec_offset
2995 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002996 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2997 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002998 image.seek(zero_end_offset)
2999 data = image.read(image.image_size - zero_end_offset)
3000
3001 # Write zeroes all over hashtree and FEC, except for the first eight bytes
3002 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
3003 # beginning of both hashtree and FEC. (That way, in the future we can add
3004 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
3005 #
3006 # Applications can use these markers to detect that the hashtree and/or
3007 # FEC needs to be recomputed.
3008 image.truncate(zero_ht_start_offset)
3009 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
3010 image.append_raw(data_zeroed_firstblock)
3011 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
3012 if zero_fec_start_offset:
3013 image.append_raw(data_zeroed_firstblock)
3014 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
3015 image.append_raw(data)
3016
David Zeuthen2bc232b2017-04-19 14:25:19 -04003017 def resize_image(self, image_filename, partition_size):
3018 """Implements the 'resize_image' command.
3019
3020 Arguments:
3021 image_filename: File with footer to resize.
3022 partition_size: The new size of the image.
3023
3024 Raises:
3025 AvbError: If there's no footer in the image.
3026 """
3027
3028 image = ImageHandler(image_filename)
3029
3030 if partition_size % image.block_size != 0:
3031 raise AvbError('Partition size of {} is not a multiple of the image '
3032 'block size {}.'.format(partition_size,
3033 image.block_size))
3034
Jan Monsch77cd2022019-12-10 17:18:04 +01003035 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04003036
3037 if not footer:
3038 raise AvbError('Given image does not have a footer.')
3039
3040 # The vbmeta blob is always at the end of the data so resizing an
3041 # image amounts to just moving the footer around.
3042
3043 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
3044 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01003045 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
3046 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04003047
3048 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003049 raise AvbError('Requested size of {} is too small for an image '
3050 'of size {}.'
3051 .format(partition_size,
3052 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04003053
3054 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
3055 # with enough bytes such that the final Footer block is at the end
3056 # of partition_size.
3057 image.truncate(vbmeta_end_offset)
3058 image.append_dont_care(partition_size - vbmeta_end_offset -
3059 1*image.block_size)
3060
3061 # Just reuse the same footer - only difference is that we're
3062 # writing it in a different place.
3063 footer_blob = footer.encode()
3064 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3065 footer_blob)
3066 image.append_raw(footer_blob_with_padding)
3067
David Zeuthen8b6973b2016-09-20 12:39:49 -04003068 def set_ab_metadata(self, misc_image, slot_data):
3069 """Implements the 'set_ab_metadata' command.
3070
3071 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
3072 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
3073
3074 Arguments:
3075 misc_image: The misc image to write to.
3076 slot_data: Slot data as a string
3077
3078 Raises:
3079 AvbError: If slot data is malformed.
3080 """
3081 tokens = slot_data.split(':')
3082 if len(tokens) != 6:
3083 raise AvbError('Malformed slot data "{}".'.format(slot_data))
3084 a_priority = int(tokens[0])
3085 a_tries_remaining = int(tokens[1])
3086 a_success = True if int(tokens[2]) != 0 else False
3087 b_priority = int(tokens[3])
3088 b_tries_remaining = int(tokens[4])
3089 b_success = True if int(tokens[5]) != 0 else False
3090
3091 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
3092 self.AB_MAGIC,
3093 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
3094 a_priority, a_tries_remaining, a_success,
3095 b_priority, b_tries_remaining, b_success)
3096 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
3097 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
3098 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
3099 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
3100 misc_image.write(ab_data)
3101
David Zeuthena4fee8b2016-08-22 15:20:43 -04003102 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04003103 """Implements the 'info_image' command.
3104
3105 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003106 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04003107 output: Output file to write human-readable information to (file object).
3108 """
3109
David Zeuthena4fee8b2016-08-22 15:20:43 -04003110 image = ImageHandler(image_filename)
3111
David Zeuthen21e95262016-07-27 17:58:40 -04003112 o = output
3113
3114 (footer, header, descriptors, image_size) = self._parse_image(image)
3115
3116 if footer:
3117 o.write('Footer version: {}.{}\n'.format(footer.version_major,
3118 footer.version_minor))
3119 o.write('Image size: {} bytes\n'.format(image_size))
3120 o.write('Original image size: {} bytes\n'.format(
3121 footer.original_image_size))
3122 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
3123 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
3124 o.write('--\n')
3125
3126 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
3127
David Zeuthene3cadca2017-02-22 21:25:46 -05003128 o.write('Minimum libavb version: {}.{}{}\n'.format(
3129 header.required_libavb_version_major,
3130 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003131 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04003132 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
3133 o.write('Authentication Block: {} bytes\n'.format(
3134 header.authentication_data_block_size))
3135 o.write('Auxiliary Block: {} bytes\n'.format(
3136 header.auxiliary_data_block_size))
3137 o.write('Algorithm: {}\n'.format(alg_name))
3138 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05003139 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05003140 o.write('Release String: \'{}\'\n'.format(
3141 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04003142
3143 # Print descriptors.
3144 num_printed = 0
3145 o.write('Descriptors:\n')
3146 for desc in descriptors:
3147 desc.print_desc(o)
3148 num_printed += 1
3149 if num_printed == 0:
3150 o.write(' (none)\n')
3151
Jan Monscheeb28b62019-12-05 16:17:09 +01003152 def verify_image(self, image_filename, key_path, expected_chain_partitions,
3153 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04003154 """Implements the 'verify_image' command.
3155
3156 Arguments:
3157 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01003158 key_path: None or check that embedded public key matches key at given
3159 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003160 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01003161 follow_chain_partitions:
3162 If True, will follows chain partitions even when not specified with
3163 the --expected_chain_partition option
3164 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
3165 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01003166
3167 Raises:
3168 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04003169 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003170 expected_chain_partitions_map = {}
3171 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003172 for cp in expected_chain_partitions:
3173 cp_tokens = cp.split(':')
3174 if len(cp_tokens) != 3:
3175 raise AvbError('Malformed chained partition "{}".'.format(cp))
3176 partition_name = cp_tokens[0]
3177 rollback_index_location = int(cp_tokens[1])
3178 file_path = cp_tokens[2]
3179 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01003180 expected_chain_partitions_map[partition_name] = (
3181 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003182
3183 image_dir = os.path.dirname(image_filename)
3184 image_ext = os.path.splitext(image_filename)[1]
3185
3186 key_blob = None
3187 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01003188 print('Verifying image {} using key at {}'.format(image_filename,
3189 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003190 key_blob = encode_rsa_key(key_path)
3191 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003192 print('Verifying image {} using embedded public key'.format(
3193 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003194
David Zeuthenb623d8b2017-04-04 16:05:53 -04003195 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01003196 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04003197 offset = 0
3198 if footer:
3199 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04003200
David Zeuthenb623d8b2017-04-04 16:05:53 -04003201 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01003202 vbmeta_blob = image.read(header.SIZE
3203 + header.authentication_data_block_size
3204 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04003205
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003206 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04003207 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003208 raise AvbError('Signature check failed for {} vbmeta struct {}'
3209 .format(alg_name, image_filename))
3210
3211 if key_blob:
3212 # The embedded public key is in the auxiliary block at an offset.
3213 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04003214 key_offset += header.authentication_data_block_size
3215 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01003216 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
3217 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003218 if key_blob != key_blob_in_vbmeta:
3219 raise AvbError('Embedded public key does not match given key.')
3220
3221 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01003222 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
3223 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003224 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003225 print('vbmeta: Successfully verified {} vbmeta struct in {}'
3226 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003227
3228 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01003229 if (isinstance(desc, AvbChainPartitionDescriptor)
3230 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01003231 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05003232 # In this case we're processing a chain descriptor but don't have a
3233 # --expect_chain_partition ... however --follow_chain_partitions was
3234 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01003235 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
3236 'and KEY (which has sha1 {}) not specified'
3237 .format(desc.partition_name, desc.rollback_index_location,
3238 hashlib.sha1(desc.public_key).hexdigest()))
3239 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01003240 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01003241 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01003242 # Honor --follow_chain_partitions - add '--' to make the output more
3243 # readable.
3244 if (isinstance(desc, AvbChainPartitionDescriptor)
3245 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01003246 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01003247 chained_image_filename = os.path.join(image_dir,
3248 desc.partition_name + image_ext)
3249 self.verify_image(chained_image_filename, key_path, None, False,
3250 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04003251
David Zeuthenb8643c02018-05-17 17:21:18 -04003252 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
3253 """Implements the 'calculate_vbmeta_digest' command.
3254
3255 Arguments:
3256 image_filename: Image file to get information from (file object).
3257 hash_algorithm: Hash algorithm used.
3258 output: Output file to write human-readable information to (file object).
3259 """
3260
3261 image_dir = os.path.dirname(image_filename)
3262 image_ext = os.path.splitext(image_filename)[1]
3263
3264 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01003265 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04003266 offset = 0
3267 if footer:
3268 offset = footer.vbmeta_offset
3269 size = (header.SIZE + header.authentication_data_block_size +
3270 header.auxiliary_data_block_size)
3271 image.seek(offset)
3272 vbmeta_blob = image.read(size)
3273
3274 hasher = hashlib.new(name=hash_algorithm)
3275 hasher.update(vbmeta_blob)
3276
3277 for desc in descriptors:
3278 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01003279 ch_image_filename = os.path.join(image_dir,
3280 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04003281 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01003282 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04003283 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04003284 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
3285 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04003286 if ch_footer:
3287 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04003288 ch_image.seek(ch_offset)
3289 ch_vbmeta_blob = ch_image.read(ch_size)
3290 hasher.update(ch_vbmeta_blob)
3291
3292 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01003293 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04003294
David Zeuthenf7d2e752018-09-20 13:30:41 -04003295 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
3296 """Implements the 'calculate_kernel_cmdline' command.
3297
3298 Arguments:
3299 image_filename: Image file to get information from (file object).
3300 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
3301 output: Output file to write human-readable information to (file object).
3302 """
3303
3304 image = ImageHandler(image_filename)
3305 _, _, descriptors, _ = self._parse_image(image)
3306
3307 image_dir = os.path.dirname(image_filename)
3308 image_ext = os.path.splitext(image_filename)[1]
3309
3310 cmdline_descriptors = []
3311 for desc in descriptors:
3312 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01003313 ch_image_filename = os.path.join(image_dir,
3314 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04003315 ch_image = ImageHandler(ch_image_filename)
3316 _, _, ch_descriptors, _ = self._parse_image(ch_image)
3317 for ch_desc in ch_descriptors:
3318 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
3319 cmdline_descriptors.append(ch_desc)
3320 elif isinstance(desc, AvbKernelCmdlineDescriptor):
3321 cmdline_descriptors.append(desc)
3322
3323 kernel_cmdline_snippets = []
3324 for desc in cmdline_descriptors:
3325 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01003326 if ((desc.flags &
3327 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3328 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04003329 if hashtree_disabled:
3330 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01003331 if (desc.flags &
3332 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04003333 if not hashtree_disabled:
3334 use_cmdline = False
3335 if use_cmdline:
3336 kernel_cmdline_snippets.append(desc.kernel_cmdline)
3337 output.write(' '.join(kernel_cmdline_snippets))
3338
David Zeuthen21e95262016-07-27 17:58:40 -04003339 def _parse_image(self, image):
3340 """Gets information about an image.
3341
3342 The image can either be a vbmeta or an image with a footer.
3343
3344 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003345 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003346
3347 Returns:
3348 A tuple where the first argument is a AvbFooter (None if there
3349 is no footer on the image), the second argument is a
3350 AvbVBMetaHeader, the third argument is a list of
3351 AvbDescriptor-derived instances, and the fourth argument is the
3352 size of |image|.
3353 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003354 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04003355 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04003356 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04003357 try:
3358 footer = AvbFooter(image.read(AvbFooter.SIZE))
3359 except (LookupError, struct.error):
3360 # Nope, just seek back to the start.
3361 image.seek(0)
3362
3363 vbmeta_offset = 0
3364 if footer:
3365 vbmeta_offset = footer.vbmeta_offset
3366
3367 image.seek(vbmeta_offset)
3368 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3369
3370 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
3371 aux_block_offset = auth_block_offset + h.authentication_data_block_size
3372 desc_start_offset = aux_block_offset + h.descriptors_offset
3373 image.seek(desc_start_offset)
3374 descriptors = parse_descriptors(image.read(h.descriptors_size))
3375
David Zeuthen09692692016-09-30 16:16:40 -04003376 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003377
David Zeuthenb1b994d2017-03-06 18:01:31 -05003378 def _load_vbmeta_blob(self, image):
3379 """Gets the vbmeta struct and associated sections.
3380
3381 The image can either be a vbmeta.img or an image with a footer.
3382
3383 Arguments:
3384 image: An ImageHandler (vbmeta or footer).
3385
3386 Returns:
3387 A blob with the vbmeta struct and other sections.
3388 """
3389 assert isinstance(image, ImageHandler)
3390 footer = None
3391 image.seek(image.image_size - AvbFooter.SIZE)
3392 try:
3393 footer = AvbFooter(image.read(AvbFooter.SIZE))
3394 except (LookupError, struct.error):
3395 # Nope, just seek back to the start.
3396 image.seek(0)
3397
3398 vbmeta_offset = 0
3399 if footer:
3400 vbmeta_offset = footer.vbmeta_offset
3401
3402 image.seek(vbmeta_offset)
3403 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3404
3405 image.seek(vbmeta_offset)
3406 data_size = AvbVBMetaHeader.SIZE
3407 data_size += h.authentication_data_block_size
3408 data_size += h.auxiliary_data_block_size
3409 return image.read(data_size)
3410
David Zeuthen73f2afa2017-05-17 16:54:11 -04003411 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05003412 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04003413
3414 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04003415 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04003416
3417 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05003418 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3419 instructions. There is one for when hashtree is not disabled and one for
3420 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04003421
David Zeuthen21e95262016-07-27 17:58:40 -04003422 """
3423
David Zeuthen21e95262016-07-27 17:58:40 -04003424 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003425 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01003426 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003427 c += ' verity {}'.format(ht.dm_verity_version) # type and version
3428 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
3429 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
3430 c += ' {}'.format(ht.data_block_size) # data_block
3431 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01003432 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
3433 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003434 c += ' {}'.format(ht.hash_algorithm) # hash_alg
3435 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
3436 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
3437 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05003438 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003439 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003440 c += ' ignore_zero_blocks'
3441 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3442 c += ' fec_roots {}'.format(ht.fec_num_roots)
3443 # Note that fec_blocks is the size that FEC covers, *not* the
3444 # size of the FEC data. Since we use FEC for everything up until
3445 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01003446 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
3447 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003448 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05003449 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003450 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003451 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04003452 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04003453
David Zeuthenfd41eb92016-11-17 12:24:47 -05003454 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003455 desc = AvbKernelCmdlineDescriptor()
3456 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05003457 desc.flags = (
3458 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3459
3460 # The descriptor for when hashtree verification is disabled is a lot
3461 # simpler - we just set the root to the partition.
3462 desc_no_ht = AvbKernelCmdlineDescriptor()
3463 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3464 desc_no_ht.flags = (
3465 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
3466
3467 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04003468
David Zeuthen73f2afa2017-05-17 16:54:11 -04003469 def _get_cmdline_descriptors_for_dm_verity(self, image):
3470 """Generate kernel cmdline descriptors for dm-verity.
3471
3472 Arguments:
3473 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
3474
3475 Returns:
3476 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3477 instructions. There is one for when hashtree is not disabled and one for
3478 when it is.
3479
3480 Raises:
3481 AvbError: If |image| doesn't have a hashtree descriptor.
3482
3483 """
3484
3485 (_, _, descriptors, _) = self._parse_image(image)
3486
3487 ht = None
3488 for desc in descriptors:
3489 if isinstance(desc, AvbHashtreeDescriptor):
3490 ht = desc
3491 break
3492
3493 if not ht:
3494 raise AvbError('No hashtree descriptor in given image')
3495
3496 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
3497
Dan Austinb12b2c12019-12-15 20:28:02 -08003498 # TODO(danielaustin): Add unit tests.
3499 def request_inclusion_proof(self, transparency_log, vbmeta_blob,
3500 version_inc, manufacturer_key_path):
3501 """Packages and sends a request to the specified transparency log.
3502
3503 Arguments:
3504 transparency_log: String containing the URL of a transparency log server.
3505 vbmeta_blob: A bytearray with the vbmeta blob.
3506 version_inc: Subcomponent of the build fingerprint.
3507 manufacturer_key_path: Path to key used to sign messages sent to the
3508 transparency log servers.
3509
3510 Returns:
3511 An AvbIcpEntry with the inclusion proof for the log entry.
3512
3513 Raises:
3514 AvbError: If grpc or the proto modules cannot be loaded, if there is an
3515 error communicating with the log or if the manufacturer_key_path
3516 cannot be decoded.
3517 """
3518 # Import grpc and proto.api_pb2_grpc now to avoid global dependencies.
3519 try:
3520 import grpc
3521 import proto.api_pb2_grpc
3522 except ImportError as e:
3523 err_str = 'grpc can be installed with python pip install grpcio.\n'
3524 raise AvbError('Failed to import module: ({}).\n{}'.format(e, err_str))
3525
3526 # Set up the gRPC channel with the transparency log.
3527 sys.stdout.write('Preparing to request inclusion proof from {}. This could '
3528 'take ~30 seconds for the process to complete.\n'.format(
3529 transparency_log))
3530 channel = grpc.insecure_channel(transparency_log)
3531 stub = proto.api_pb2_grpc.AFTLogStub(channel)
3532
3533 # Calculate the hash of the vbmeta image.
3534 hasher = hashlib.sha256()
3535 hasher.update(vbmeta_blob)
3536 vbmeta_hash = hasher.digest()
3537 # Extract the key data from the PEM file.
3538 manufacturer_key_data = rsa_key_read_pem_bytes(manufacturer_key_path)
3539 # Calculate the hash of the manufacturer key data.
3540 hasher = hashlib.sha256()
3541 hasher.update(manufacturer_key_data)
3542 m_key_hash = hasher.digest()
3543 # Create an AddFirmwareInfoRequest protobuf for transmission to the
3544 # transparency log.
3545 fw_info = proto.aftl_pb2.FirmwareInfo(vbmeta_hash=vbmeta_hash,
3546 version_incremental=version_inc,
3547 manufacturer_key_hash=m_key_hash)
3548 # TODO(danielaustin): Sign the message with the manufacturer key.
3549 sfw_info = proto.aftl_pb2.SignedFirmwareInfo(info=fw_info)
3550 request = proto.api_pb2.AddFirmwareInfoRequest(vbmeta=bytes(
3551 str(vbmeta_blob)), fw_info=sfw_info)
3552 # Attempt to transmit to the transparency log.
3553 try:
3554 # TODO(danielaustin): Set a reasonable timeout deadline here.
3555 sys.stdout.write('ICP is about to be requested from transparency log '
3556 'with domain {}.\n'.format(transparency_log))
3557 response = stub.AddFirmwareInfo(request)
3558 except grpc.RpcError as e:
3559 raise AvbError('Error: grpc failure ({})'.format(e))
3560 # Return an AvbIcpEntry representing this response.
3561 icp_entry = AvbIcpEntry()
3562 icp_entry.translate_response(transparency_log, response)
3563 return icp_entry
3564
David Zeuthen21e95262016-07-27 17:58:40 -04003565 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05003566 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003567 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003568 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04003569 include_descriptors_from_image,
3570 signing_helper,
3571 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003572 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003573 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003574 print_required_libavb_version,
3575 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003576 """Implements the 'make_vbmeta_image' command.
3577
3578 Arguments:
3579 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003580 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003581 algorithm_name: Name of algorithm to use.
3582 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003583 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003584 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003585 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003586 props: Properties to insert (list of strings of the form 'key:value').
3587 props_from_file: Properties to insert (list of strings 'key:<path>').
3588 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003589 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04003590 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003591 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003592 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003593 release_string: None or avbtool release string to use instead of default.
3594 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04003595 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04003596 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04003597
3598 Raises:
3599 AvbError: If a chained partition is malformed.
3600 """
3601
David Zeuthen1097a782017-05-31 15:53:17 -04003602 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003603 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003604 if include_descriptors_from_image:
3605 # Use the bump logic in AvbVBMetaHeader to calculate the max required
3606 # version of all included descriptors.
3607 tmp_header = AvbVBMetaHeader()
3608 for image in include_descriptors_from_image:
3609 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
3610 tmp_header.bump_required_libavb_version_minor(
3611 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01003612 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003613 else:
3614 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01003615 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04003616 return
3617
3618 if not output:
3619 raise AvbError('No output file given')
3620
David Zeuthen21e95262016-07-27 17:58:40 -04003621 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04003622 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003623 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003624 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003625 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003626 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003627 include_descriptors_from_image, signing_helper,
3628 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003629 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04003630
3631 # Write entire vbmeta blob (header, authentication, auxiliary).
3632 output.seek(0)
3633 output.write(vbmeta_blob)
3634
David Zeuthen97cb5802017-06-01 16:14:05 -04003635 if padding_size > 0:
3636 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
3637 padding_needed = padded_size - len(vbmeta_blob)
3638 output.write('\0' * padding_needed)
3639
Dan Austinb12b2c12019-12-15 20:28:02 -08003640
3641 def make_icp_from_vbmeta(self, vbmeta_image_path, output, algorithm,
3642 signing_helper, signing_helper_with_files,
3643 version_incremental, transparency_log_servers,
3644 transparency_log_pub_keys, manufacturer_key,
3645 padding_size):
3646 """Generates a vbmeta image with inclusion proof given a vbmeta image.
3647
3648 This blob (struct AvbIcpBlob) contains the information required to
3649 validate an inclusion proof for a specific vbmeta image. It consists
3650 of a header (struct AvbIcpHeader) and zero or more entry structures
3651 (struct AvbIcpEntry) that contain the vbmeta leaf hash, tree size,
3652 root hash, inclusion proof hashes, and the signature for the root hash.
3653
3654 The vbmeta image, its hash, the version_incremental part of the build
3655 fingerprint, and the hash of the manufacturer key are sent to the
3656 transparency log, with the message signed by the manufacturer key.
3657 An inclusion proof is calculated and returned. This inclusion proof is
3658 then packaged in a AvbIcpBlob structure. The existing vbmeta data is
3659 copied to a new file, appended with the AvbIcpBlob data, and written to
3660 output. Validation of the inclusion proof does not require
3661 communication with the transparency log.
3662
3663 Arguments:
3664 vbmeta_image_path: Path to a vbmeta image file.
3665 output: File to write the results to.
3666 algorithm: The algorithm ID for signing and hashing (see ALGORITHMS). This
3667 will be used for hash and signature size calculation and padding.
3668 signing_helper: Program which signs a hash and returns a signature.
3669 signing_helper_with_files: Same as signing_helper but uses files instead.
3670 version_incremental: A string representing the subcomponent of the
3671 build fingerprint used to identify the vbmeta in the transparency log.
3672 transparency_log_servers: List of strings containing URLs of transparency
3673 log servers where inclusion proofs are requested from.
3674 transparency_log_pub_keys: List of paths to PEM files containing trusted
3675 public keys that correspond with the transparency_logs. There must be
3676 the same number of keys as log servers and they must be in the same
3677 order, that is, transparency_log_pub_keys[n] corresponds to
3678 transparency_log_servers[n].
3679 manufacturer_key: Path to PEM file containting the key file used to sign
3680 messages sent to the transparency log servers.
3681 padding_size: If not 0, pads output so size is a multiple of the number.
3682
3683 Returns:
3684 True if the inclusion proofs could be fetched from the transparency log
3685 servers and could be successfully validated, False otherwise.
3686
3687 Raises:
3688 AvbError: If any parameters are invalid, communication with the log
3689 fails or the structures are malformed.
3690 """
3691 # TODO(danielaustin): Determine the best way to handle chained vbmeta
3692 # structures. Currently, we only put the main one in the transparency
3693 # log.
3694
3695 # Validates command line parameters.
3696 if not vbmeta_image_path:
3697 raise AvbError('No vbmeta image path found.')
3698 if not transparency_log_servers:
3699 raise AvbError('No transparency log servers given.')
3700 if not transparency_log_pub_keys:
3701 raise AvbError('No transparency log public keys given.')
3702 if len(transparency_log_servers) != len(transparency_log_pub_keys):
3703 raise AvbError('Transparency log count and public key count mismatch: '
3704 '{} servers and {} public keys'.format(
3705 len(transparency_log_servers),
3706 len(transparency_log_pub_keys)))
3707 if not manufacturer_key:
3708 raise AvbError('No manufacturer key path given.')
3709
3710 # TODO(danielaustin): add support for signing_helper and
3711 # signing_helper_with_files
3712 if signing_helper is not None or signing_helper_with_files is not None:
3713 raise AvbError('signing_helper support not yet implemented for ICP.')
3714
3715 try:
3716 algorithm_id = ALGORITHMS[algorithm].algorithm_type
3717 except KeyError:
3718 raise AvbError('Unknown algorithm with name {}'.format(algorithm))
3719
3720 # Retrieves vbmeta structure from given partition image.
3721 image = ImageHandler(vbmeta_image_path)
3722 (footer, header, _, _) = self._parse_image(image)
3723 offset = 0
3724 if footer:
3725 offset = footer.vbmeta_offset
3726 image.seek(offset)
3727 vbmeta_blob = image.read(header.SIZE +
3728 header.authentication_data_block_size +
3729 header.auxiliary_data_block_size)
3730
3731 # Fetches inclusion proofs for vbmeta structure from all transparency logs.
3732 icp_entries = []
3733 for i, transparency_log in enumerate(transparency_log_servers):
3734 try:
3735 icp_entry = self.request_inclusion_proof(transparency_log, vbmeta_blob,
3736 version_incremental,
3737 manufacturer_key)
3738 if not icp_entry.verify_icp(transparency_log_pub_keys[i]):
3739 sys.stderr.write('The ICP from {} could not be verified\n'.format(
3740 transparency_log))
3741 icp_entries.append(icp_entry)
3742 except AvbError as e:
3743 sys.stderr.write('AvbError: {}'.format(e))
3744 # The inclusion proof request failed.
3745 # Continue and see if another will succeed.
3746 continue
3747 if not icp_entries:
3748 sys.stderr.write('No inclusion proofs could be validated from any log.\n')
3749 return False
3750
3751 # Prepares the inclusion proof blob to be appended to the vbmeta image.
3752 icp_blob = AvbIcpBlob()
3753 icp_blob.set_algorithm(algorithm_id)
3754 for icp_entry in icp_entries:
3755 icp_blob.add_icp_entry(icp_entry)
3756 if not icp_blob.is_valid():
3757 sys.stderr.write('Resulting AvbIcpBlob structure is malformed\n.')
3758 return False
3759
3760 # Write the original vbmeta blob, followed by the AvbIcpBlob.
3761 if footer: # Checks if it is a chained partition.
3762 # TODO(danielaustin): Add support for chained partitions like system.img
3763 # using similar functionality as implemented in append_vbmeta_image().
3764 sys.stderr.write('Image has a footer and ICP for this format is not '
3765 'implemented.')
3766 return False
3767
3768 # Writes vbmeta image with inclusion proof into a new vbmeta image.
3769 output.seek(0)
3770 output.write(vbmeta_blob)
3771 encoded_icp_blob = icp_blob.encode()
3772 output.write(encoded_icp_blob)
3773
3774 if padding_size > 0:
3775 blob_size = len(vbmeta_blob) + len(encoded_icp_blob)
3776 padded_size = round_to_multiple(blob_size, padding_size)
3777 padding_needed = padded_size - blob_size
3778 output.write('\0' * padding_needed)
3779
3780 return True
3781
David Zeuthen18666ab2016-11-15 11:18:05 -05003782 def _generate_vbmeta_blob(self, algorithm_name, key_path,
3783 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003784 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003785 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003786 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003787 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003788 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003789 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003790 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003791 release_string, append_to_release_string,
3792 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04003793 """Generates a VBMeta blob.
3794
3795 This blob contains the header (struct AvbVBMetaHeader), the
3796 authentication data block (which contains the hash and signature
3797 for the header and auxiliary block), and the auxiliary block
3798 (which contains descriptors, the public key used, and other data).
3799
3800 The |key| parameter can |None| only if the |algorithm_name| is
3801 'NONE'.
3802
3803 Arguments:
3804 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3805 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003806 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003807 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003808 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003809 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003810 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003811 props: Properties to insert (List of strings of the form 'key:value').
3812 props_from_file: Properties to insert (List of strings 'key:<path>').
3813 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003814 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003815 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003816 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3817 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003818 include_descriptors_from_image: List of file objects for which
3819 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003820 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003821 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003822 release_string: None or avbtool release string.
3823 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003824 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003825
3826 Returns:
3827 A bytearray() with the VBMeta blob.
3828
3829 Raises:
3830 Exception: If the |algorithm_name| is not found, if no key has
3831 been given and the given algorithm requires one, or the key is
3832 of the wrong size.
3833
3834 """
3835 try:
3836 alg = ALGORITHMS[algorithm_name]
3837 except KeyError:
3838 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3839
David Zeuthena5fd3a42017-02-27 16:38:54 -05003840 if not descriptors:
3841 descriptors = []
3842
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003843 h = AvbVBMetaHeader()
3844 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3845
David Zeuthena5fd3a42017-02-27 16:38:54 -05003846 # Insert chained partition descriptors, if any
3847 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04003848 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003849 for cp in chain_partitions:
3850 cp_tokens = cp.split(':')
3851 if len(cp_tokens) != 3:
3852 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003853 partition_name = cp_tokens[0]
3854 rollback_index_location = int(cp_tokens[1])
3855 file_path = cp_tokens[2]
3856 # Check that the same rollback location isn't being used by
3857 # multiple chained partitions.
3858 if used_locations.get(rollback_index_location):
3859 raise AvbError('Rollback Index Location {} is already in use.'.format(
3860 rollback_index_location))
3861 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003862 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003863 desc.partition_name = partition_name
3864 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003865 if desc.rollback_index_location < 1:
3866 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003867 desc.public_key = open(file_path, 'rb').read()
3868 descriptors.append(desc)
3869
David Zeuthen21e95262016-07-27 17:58:40 -04003870 # Descriptors.
3871 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003872 for desc in descriptors:
3873 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003874
3875 # Add properties.
3876 if props:
3877 for prop in props:
3878 idx = prop.find(':')
3879 if idx == -1:
3880 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01003881 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04003882 desc = AvbPropertyDescriptor()
3883 desc.key = prop[0:idx]
3884 desc.value = prop[(idx + 1):]
3885 encoded_descriptors.extend(desc.encode())
3886 if props_from_file:
3887 for prop in props_from_file:
3888 idx = prop.find(':')
3889 if idx == -1:
3890 raise AvbError('Malformed property "{}".'.format(prop))
3891 desc = AvbPropertyDescriptor()
3892 desc.key = prop[0:idx]
3893 desc.value = prop[(idx + 1):]
3894 file_path = prop[(idx + 1):]
3895 desc.value = open(file_path, 'rb').read()
3896 encoded_descriptors.extend(desc.encode())
3897
David Zeuthen73f2afa2017-05-17 16:54:11 -04003898 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003899 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003900 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003901 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003902 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3903 encoded_descriptors.extend(cmdline_desc[0].encode())
3904 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003905
David Zeuthen73f2afa2017-05-17 16:54:11 -04003906 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3907 if ht_desc_to_setup:
3908 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3909 ht_desc_to_setup)
3910 encoded_descriptors.extend(cmdline_desc[0].encode())
3911 encoded_descriptors.extend(cmdline_desc[1].encode())
3912
David Zeuthen21e95262016-07-27 17:58:40 -04003913 # Add kernel command-lines.
3914 if kernel_cmdlines:
3915 for i in kernel_cmdlines:
3916 desc = AvbKernelCmdlineDescriptor()
3917 desc.kernel_cmdline = i
3918 encoded_descriptors.extend(desc.encode())
3919
3920 # Add descriptors from other images.
3921 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003922 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003923 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003924 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003925 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3926 image_handler)
3927 # Bump the required libavb version to support all included descriptors.
3928 h.bump_required_libavb_version_minor(
3929 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003930 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003931 # The --include_descriptors_from_image option is used in some setups
3932 # with images A and B where both A and B contain a descriptor
3933 # for a partition with the same name. Since it's not meaningful
3934 # to include both descriptors, only include the last seen descriptor.
3935 # See bug 76386656 for details.
3936 if hasattr(desc, 'partition_name'):
3937 key = type(desc).__name__ + '_' + desc.partition_name
3938 descriptors_dict[key] = desc.encode()
3939 else:
3940 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003941 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003942 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003943
David Zeuthen18666ab2016-11-15 11:18:05 -05003944 # Load public key metadata blob, if requested.
3945 pkmd_blob = []
3946 if public_key_metadata_path:
3947 with open(public_key_metadata_path) as f:
3948 pkmd_blob = f.read()
3949
David Zeuthen21e95262016-07-27 17:58:40 -04003950 key = None
3951 encoded_key = bytearray()
3952 if alg.public_key_num_bytes > 0:
3953 if not key_path:
3954 raise AvbError('Key is required for algorithm {}'.format(
3955 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003956 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003957 if len(encoded_key) != alg.public_key_num_bytes:
3958 raise AvbError('Key is wrong size for algorithm {}'.format(
3959 algorithm_name))
3960
David Zeuthene3cadca2017-02-22 21:25:46 -05003961 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003962 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003963 if isinstance(release_string, (str, unicode)):
3964 h.release_string = release_string
3965
3966 # Append to release string, if requested. Also insert a space before.
3967 if isinstance(append_to_release_string, (str, unicode)):
3968 h.release_string += ' ' + append_to_release_string
3969
David Zeuthen18666ab2016-11-15 11:18:05 -05003970 # For the Auxiliary data block, descriptors are stored at offset 0,
3971 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003972 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003973 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003974 h.descriptors_offset = 0
3975 h.descriptors_size = len(encoded_descriptors)
3976 h.public_key_offset = h.descriptors_size
3977 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003978 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3979 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003980
3981 # For the Authentication data block, the hash is first and then
3982 # the signature.
3983 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003984 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003985 h.algorithm_type = alg.algorithm_type
3986 h.hash_offset = 0
3987 h.hash_size = alg.hash_num_bytes
3988 # Signature offset and size - it's stored right after the hash
3989 # (in Authentication data block).
3990 h.signature_offset = alg.hash_num_bytes
3991 h.signature_size = alg.signature_num_bytes
3992
3993 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003994 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003995
3996 # Generate Header data block.
3997 header_data_blob = h.encode()
3998
3999 # Generate Auxiliary data block.
4000 aux_data_blob = bytearray()
4001 aux_data_blob.extend(encoded_descriptors)
4002 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05004003 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04004004 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
4005 aux_data_blob.extend('\0' * padding_bytes)
4006
4007 # Calculate the hash.
4008 binary_hash = bytearray()
4009 binary_signature = bytearray()
4010 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04004011 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04004012 ha.update(header_data_blob)
4013 ha.update(aux_data_blob)
4014 binary_hash.extend(ha.digest())
4015
4016 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04004017 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04004018 binary_signature.extend(raw_sign(signing_helper,
4019 signing_helper_with_files,
4020 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004021 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004022 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04004023
4024 # Generate Authentication data block.
4025 auth_data_blob = bytearray()
4026 auth_data_blob.extend(binary_hash)
4027 auth_data_blob.extend(binary_signature)
4028 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
4029 auth_data_blob.extend('\0' * padding_bytes)
4030
4031 return header_data_blob + auth_data_blob + aux_data_blob
4032
4033 def extract_public_key(self, key_path, output):
4034 """Implements the 'extract_public_key' command.
4035
4036 Arguments:
4037 key_path: The path to a RSA private key file.
4038 output: The file to write to.
4039 """
David Zeuthenc68f0822017-03-31 17:22:35 -04004040 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04004041
David Zeuthenb1b994d2017-03-06 18:01:31 -05004042 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
4043 partition_size):
4044 """Implementation of the append_vbmeta_image command.
4045
4046 Arguments:
4047 image_filename: File to add the footer to.
4048 vbmeta_image_filename: File to get vbmeta struct from.
4049 partition_size: Size of partition.
4050
4051 Raises:
4052 AvbError: If an argument is incorrect.
4053 """
4054 image = ImageHandler(image_filename)
4055
4056 if partition_size % image.block_size != 0:
4057 raise AvbError('Partition size of {} is not a multiple of the image '
4058 'block size {}.'.format(partition_size,
4059 image.block_size))
4060
4061 # If there's already a footer, truncate the image to its original
4062 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07004063 if image.image_size >= AvbFooter.SIZE:
4064 image.seek(image.image_size - AvbFooter.SIZE)
4065 try:
4066 footer = AvbFooter(image.read(AvbFooter.SIZE))
4067 # Existing footer found. Just truncate.
4068 original_image_size = footer.original_image_size
4069 image.truncate(footer.original_image_size)
4070 except (LookupError, struct.error):
4071 original_image_size = image.image_size
4072 else:
4073 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05004074 original_image_size = image.image_size
4075
4076 # If anything goes wrong from here-on, restore the image back to
4077 # its original size.
4078 try:
4079 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
4080 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
4081
4082 # If the image isn't sparse, its size might not be a multiple of
4083 # the block size. This will screw up padding later so just grow it.
4084 if image.image_size % image.block_size != 0:
4085 assert not image.is_sparse
4086 padding_needed = image.block_size - (image.image_size%image.block_size)
4087 image.truncate(image.image_size + padding_needed)
4088
4089 # The append_raw() method requires content with size being a
4090 # multiple of |block_size| so add padding as needed. Also record
4091 # where this is written to since we'll need to put that in the
4092 # footer.
4093 vbmeta_offset = image.image_size
4094 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
4095 len(vbmeta_blob))
4096 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
4097
4098 # Append vbmeta blob and footer
4099 image.append_raw(vbmeta_blob_with_padding)
4100 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
4101
4102 # Now insert a DONT_CARE chunk with enough bytes such that the
4103 # final Footer block is at the end of partition_size..
4104 image.append_dont_care(partition_size - vbmeta_end_offset -
4105 1*image.block_size)
4106
4107 # Generate the Footer that tells where the VBMeta footer
4108 # is. Also put enough padding in the front of the footer since
4109 # we'll write out an entire block.
4110 footer = AvbFooter()
4111 footer.original_image_size = original_image_size
4112 footer.vbmeta_offset = vbmeta_offset
4113 footer.vbmeta_size = len(vbmeta_blob)
4114 footer_blob = footer.encode()
4115 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4116 footer_blob)
4117 image.append_raw(footer_blob_with_padding)
4118
4119 except:
4120 # Truncate back to original size, then re-raise
4121 image.truncate(original_image_size)
4122 raise
4123
David Zeuthena4fee8b2016-08-22 15:20:43 -04004124 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004125 hash_algorithm, salt, chain_partitions, algorithm_name,
4126 key_path,
4127 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05004128 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004129 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04004130 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04004131 signing_helper, signing_helper_with_files,
4132 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004133 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004134 print_required_libavb_version, use_persistent_digest,
4135 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04004136 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04004137
4138 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004139 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04004140 partition_size: Size of partition.
4141 partition_name: Name of partition (without A/B suffix).
4142 hash_algorithm: Hash algorithm to use.
4143 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05004144 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04004145 algorithm_name: Name of algorithm to use.
4146 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05004147 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04004148 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05004149 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04004150 props: Properties to insert (List of strings of the form 'key:value').
4151 props_from_file: Properties to insert (List of strings 'key:<path>').
4152 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004153 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04004154 dm-verity kernel cmdline from.
4155 include_descriptors_from_image: List of file objects for which
4156 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04004157 calc_max_image_size: Don't store the footer - instead calculate the
4158 maximum image size leaving enough room for metadata with the
4159 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004160 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004161 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05004162 release_string: None or avbtool release string.
4163 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05004164 output_vbmeta_image: If not None, also write vbmeta struct to this file.
4165 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04004166 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004167 use_persistent_digest: Use a persistent digest on device.
4168 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04004169
4170 Raises:
4171 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04004172 """
David Zeuthen1097a782017-05-31 15:53:17 -04004173
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004174 required_libavb_version_minor = 0
4175 if use_persistent_digest or do_not_use_ab:
4176 required_libavb_version_minor = 1
4177
David Zeuthen1097a782017-05-31 15:53:17 -04004178 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04004179 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01004180 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04004181 return
4182
David Zeuthenbf562452017-05-17 18:04:43 -04004183 # First, calculate the maximum image size such that an image
4184 # this size + metadata (footer + vbmeta struct) fits in
4185 # |partition_size|.
4186 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07004187 if partition_size < max_metadata_size:
4188 raise AvbError('Parition size of {} is too small. '
4189 'Needs to be at least {}'.format(
4190 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04004191 max_image_size = partition_size - max_metadata_size
4192
4193 # If we're asked to only calculate the maximum image size, we're done.
4194 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004195 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04004196 return
4197
David Zeuthena4fee8b2016-08-22 15:20:43 -04004198 image = ImageHandler(image_filename)
4199
4200 if partition_size % image.block_size != 0:
4201 raise AvbError('Partition size of {} is not a multiple of the image '
4202 'block size {}.'.format(partition_size,
4203 image.block_size))
4204
David Zeuthen21e95262016-07-27 17:58:40 -04004205 # If there's already a footer, truncate the image to its original
4206 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
4207 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07004208 if image.image_size >= AvbFooter.SIZE:
4209 image.seek(image.image_size - AvbFooter.SIZE)
4210 try:
4211 footer = AvbFooter(image.read(AvbFooter.SIZE))
4212 # Existing footer found. Just truncate.
4213 original_image_size = footer.original_image_size
4214 image.truncate(footer.original_image_size)
4215 except (LookupError, struct.error):
4216 original_image_size = image.image_size
4217 else:
4218 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04004219 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04004220
4221 # If anything goes wrong from here-on, restore the image back to
4222 # its original size.
4223 try:
David Zeuthen09692692016-09-30 16:16:40 -04004224 # If image size exceeds the maximum image size, fail.
4225 if image.image_size > max_image_size:
4226 raise AvbError('Image size of {} exceeds maximum image '
4227 'size of {} in order to fit in a partition '
4228 'size of {}.'.format(image.image_size, max_image_size,
4229 partition_size))
4230
David Zeuthen21e95262016-07-27 17:58:40 -04004231 digest_size = len(hashlib.new(name=hash_algorithm).digest())
4232 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01004233 salt = binascii.unhexlify(salt)
4234 elif salt is None and not use_persistent_digest:
4235 # If salt is not explicitly specified, choose a hash that's the same
4236 # size as the hash size. Don't populate a random salt if this
4237 # descriptor is being created to use a persistent digest on device.
4238 hash_size = digest_size
4239 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004240 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01004241 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004242
4243 hasher = hashlib.new(name=hash_algorithm, string=salt)
4244 # TODO(zeuthen): might want to read this in chunks to avoid
4245 # memory pressure, then again, this is only supposed to be used
4246 # on kernel/initramfs partitions. Possible optimization.
4247 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04004248 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04004249 digest = hasher.digest()
4250
4251 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04004252 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04004253 h_desc.hash_algorithm = hash_algorithm
4254 h_desc.partition_name = partition_name
4255 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004256 h_desc.flags = 0
4257 if do_not_use_ab:
4258 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
4259 if not use_persistent_digest:
4260 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04004261
4262 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04004263 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04004264 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05004265 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05004266 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004267 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04004268 include_descriptors_from_image, signing_helper,
4269 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004270 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04004271
David Zeuthend247fcb2017-02-16 12:09:27 -05004272 # Write vbmeta blob, if requested.
4273 if output_vbmeta_image:
4274 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004275
David Zeuthend247fcb2017-02-16 12:09:27 -05004276 # Append vbmeta blob and footer, unless requested not to.
4277 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07004278 # If the image isn't sparse, its size might not be a multiple of
4279 # the block size. This will screw up padding later so just grow it.
4280 if image.image_size % image.block_size != 0:
4281 assert not image.is_sparse
4282 padding_needed = image.block_size - (
4283 image.image_size % image.block_size)
4284 image.truncate(image.image_size + padding_needed)
4285
4286 # The append_raw() method requires content with size being a
4287 # multiple of |block_size| so add padding as needed. Also record
4288 # where this is written to since we'll need to put that in the
4289 # footer.
4290 vbmeta_offset = image.image_size
4291 padding_needed = (
4292 round_to_multiple(len(vbmeta_blob), image.block_size) -
4293 len(vbmeta_blob))
4294 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
4295
David Zeuthend247fcb2017-02-16 12:09:27 -05004296 image.append_raw(vbmeta_blob_with_padding)
4297 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
4298
4299 # Now insert a DONT_CARE chunk with enough bytes such that the
4300 # final Footer block is at the end of partition_size..
4301 image.append_dont_care(partition_size - vbmeta_end_offset -
4302 1*image.block_size)
4303
4304 # Generate the Footer that tells where the VBMeta footer
4305 # is. Also put enough padding in the front of the footer since
4306 # we'll write out an entire block.
4307 footer = AvbFooter()
4308 footer.original_image_size = original_image_size
4309 footer.vbmeta_offset = vbmeta_offset
4310 footer.vbmeta_size = len(vbmeta_blob)
4311 footer_blob = footer.encode()
4312 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4313 footer_blob)
4314 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004315
David Zeuthen21e95262016-07-27 17:58:40 -04004316 except:
4317 # Truncate back to original size, then re-raise
4318 image.truncate(original_image_size)
4319 raise
4320
David Zeuthena4fee8b2016-08-22 15:20:43 -04004321 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004322 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004323 block_size, salt, chain_partitions, algorithm_name,
4324 key_path,
4325 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004326 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004327 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004328 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004329 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05004330 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004331 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004332 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004333 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004334 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01004335 use_persistent_root_digest, do_not_use_ab,
4336 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04004337 """Implements the 'add_hashtree_footer' command.
4338
4339 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
4340 more information about dm-verity and these hashes.
4341
4342 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004343 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004344 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04004345 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004346 generate_fec: If True, generate FEC codes.
4347 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04004348 hash_algorithm: Hash algorithm to use.
4349 block_size: Block size to use.
4350 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05004351 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04004352 algorithm_name: Name of algorithm to use.
4353 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05004354 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04004355 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05004356 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04004357 props: Properties to insert (List of strings of the form 'key:value').
4358 props_from_file: Properties to insert (List of strings 'key:<path>').
4359 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004360 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04004361 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04004362 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
4363 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04004364 include_descriptors_from_image: List of file objects for which
4365 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04004366 calc_max_image_size: Don't store the hashtree or footer - instead
4367 calculate the maximum image size leaving enough room for hashtree
4368 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004369 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004370 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05004371 release_string: None or avbtool release string.
4372 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05004373 output_vbmeta_image: If not None, also write vbmeta struct to this file.
4374 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04004375 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004376 use_persistent_root_digest: Use a persistent root digest on device.
4377 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09004378 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04004379
4380 Raises:
4381 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04004382 """
David Zeuthen1097a782017-05-31 15:53:17 -04004383
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004384 required_libavb_version_minor = 0
4385 if use_persistent_root_digest or do_not_use_ab:
4386 required_libavb_version_minor = 1
4387
David Zeuthen1097a782017-05-31 15:53:17 -04004388 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04004389 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01004390 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04004391 return
4392
David Zeuthen09692692016-09-30 16:16:40 -04004393 digest_size = len(hashlib.new(name=hash_algorithm).digest())
4394 digest_padding = round_to_pow2(digest_size) - digest_size
4395
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004396 # If |partition_size| is given (e.g. not 0), calculate the maximum image
4397 # size such that an image this size + the hashtree + metadata (footer +
4398 # vbmeta struct) fits in |partition_size|. We use very conservative figures
4399 # for metadata.
4400 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09004401 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004402 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09004403 if not no_hashtree:
4404 (_, max_tree_size) = calc_hash_level_offsets(
4405 partition_size, block_size, digest_size + digest_padding)
4406 if generate_fec:
4407 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004408 max_metadata_size = (max_fec_size + max_tree_size +
4409 self.MAX_VBMETA_SIZE +
4410 self.MAX_FOOTER_SIZE)
4411 max_image_size = partition_size - max_metadata_size
4412 else:
4413 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04004414
4415 # If we're asked to only calculate the maximum image size, we're done.
4416 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004417 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04004418 return
4419
David Zeuthena4fee8b2016-08-22 15:20:43 -04004420 image = ImageHandler(image_filename)
4421
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004422 if partition_size > 0:
4423 if partition_size % image.block_size != 0:
4424 raise AvbError('Partition size of {} is not a multiple of the image '
4425 'block size {}.'.format(partition_size,
4426 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01004427 elif image.image_size % image.block_size != 0:
4428 raise AvbError('File size of {} is not a multiple of the image '
4429 'block size {}.'.format(image.image_size,
4430 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04004431
David Zeuthen21e95262016-07-27 17:58:40 -04004432 # If there's already a footer, truncate the image to its original
4433 # size. This way 'avbtool add_hashtree_footer' is idempotent
4434 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07004435 if image.image_size >= AvbFooter.SIZE:
4436 image.seek(image.image_size - AvbFooter.SIZE)
4437 try:
4438 footer = AvbFooter(image.read(AvbFooter.SIZE))
4439 # Existing footer found. Just truncate.
4440 original_image_size = footer.original_image_size
4441 image.truncate(footer.original_image_size)
4442 except (LookupError, struct.error):
4443 original_image_size = image.image_size
4444 else:
4445 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04004446 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04004447
4448 # If anything goes wrong from here-on, restore the image back to
4449 # its original size.
4450 try:
4451 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04004452 rounded_image_size = round_to_multiple(image.image_size, block_size)
4453 if rounded_image_size > image.image_size:
4454 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04004455
David Zeuthen09692692016-09-30 16:16:40 -04004456 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004457 if partition_size > 0:
4458 if image.image_size > max_image_size:
4459 raise AvbError('Image size of {} exceeds maximum image '
4460 'size of {} in order to fit in a partition '
4461 'size of {}.'.format(image.image_size, max_image_size,
4462 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04004463
4464 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01004465 salt = binascii.unhexlify(salt)
4466 elif salt is None and not use_persistent_root_digest:
4467 # If salt is not explicitly specified, choose a hash that's the same
4468 # size as the hash size. Don't populate a random salt if this
4469 # descriptor is being created to use a persistent digest on device.
4470 hash_size = digest_size
4471 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004472 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01004473 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004474
David Zeuthena4fee8b2016-08-22 15:20:43 -04004475 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04004476 # offsets in advance.
4477 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04004478 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04004479
David Zeuthena4fee8b2016-08-22 15:20:43 -04004480 # If the image isn't sparse, its size might not be a multiple of
4481 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04004482 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004483 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04004484 padding_needed = image.block_size - (image.image_size%image.block_size)
4485 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04004486
David Zeuthena4fee8b2016-08-22 15:20:43 -04004487 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04004488 tree_offset = image.image_size
4489 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004490 block_size,
4491 hash_algorithm, salt,
4492 digest_padding,
4493 hash_level_offsets,
4494 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004495
4496 # Generate HashtreeDescriptor with details about the tree we
4497 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09004498 if no_hashtree:
4499 tree_size = 0
4500 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04004501 ht_desc = AvbHashtreeDescriptor()
4502 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04004503 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04004504 ht_desc.tree_offset = tree_offset
4505 ht_desc.tree_size = tree_size
4506 ht_desc.data_block_size = block_size
4507 ht_desc.hash_block_size = block_size
4508 ht_desc.hash_algorithm = hash_algorithm
4509 ht_desc.partition_name = partition_name
4510 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004511 if do_not_use_ab:
4512 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
4513 if not use_persistent_root_digest:
4514 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04004515
David Zeuthen09692692016-09-30 16:16:40 -04004516 # Write the hash tree
4517 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
4518 len(hash_tree))
4519 hash_tree_with_padding = hash_tree + '\0'*padding_needed
4520 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004521 len_hashtree_and_fec = len(hash_tree_with_padding)
4522
4523 # Generate FEC codes, if requested.
4524 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09004525 if no_hashtree:
4526 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07004527 else:
4528 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004529 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
4530 len(fec_data))
4531 fec_data_with_padding = fec_data + '\0'*padding_needed
4532 fec_offset = image.image_size
4533 image.append_raw(fec_data_with_padding)
4534 len_hashtree_and_fec += len(fec_data_with_padding)
4535 # Update the hashtree descriptor.
4536 ht_desc.fec_num_roots = fec_num_roots
4537 ht_desc.fec_offset = fec_offset
4538 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04004539
David Zeuthen73f2afa2017-05-17 16:54:11 -04004540 ht_desc_to_setup = None
4541 if setup_as_rootfs_from_kernel:
4542 ht_desc_to_setup = ht_desc
4543
David Zeuthena4fee8b2016-08-22 15:20:43 -04004544 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004545 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04004546 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05004547 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05004548 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004549 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04004550 include_descriptors_from_image, signing_helper,
4551 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004552 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004553 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
4554 len(vbmeta_blob))
4555 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004556
David Zeuthend247fcb2017-02-16 12:09:27 -05004557 # Write vbmeta blob, if requested.
4558 if output_vbmeta_image:
4559 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04004560
David Zeuthend247fcb2017-02-16 12:09:27 -05004561 # Append vbmeta blob and footer, unless requested not to.
4562 if not do_not_append_vbmeta_image:
4563 image.append_raw(vbmeta_blob_with_padding)
4564
4565 # Now insert a DONT_CARE chunk with enough bytes such that the
4566 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004567 if partition_size > 0:
4568 image.append_dont_care(partition_size - image.image_size -
4569 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05004570
4571 # Generate the Footer that tells where the VBMeta footer
4572 # is. Also put enough padding in the front of the footer since
4573 # we'll write out an entire block.
4574 footer = AvbFooter()
4575 footer.original_image_size = original_image_size
4576 footer.vbmeta_offset = vbmeta_offset
4577 footer.vbmeta_size = len(vbmeta_blob)
4578 footer_blob = footer.encode()
4579 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4580 footer_blob)
4581 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004582
David Zeuthen21e95262016-07-27 17:58:40 -04004583 except:
David Zeuthen09692692016-09-30 16:16:40 -04004584 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04004585 image.truncate(original_image_size)
4586 raise
4587
David Zeuthenc68f0822017-03-31 17:22:35 -04004588 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004589 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004590 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004591 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08004592 """Implements the 'make_atx_certificate' command.
4593
4594 Android Things certificates are required for Android Things public key
4595 metadata. They chain the vbmeta signing key for a particular product back to
4596 a fused, permanent root key. These certificates are fixed-length and fixed-
4597 format with the explicit goal of not parsing ASN.1 in bootloader code.
4598
4599 Arguments:
4600 output: Certificate will be written to this file on success.
4601 authority_key_path: A PEM file path with the authority private key.
4602 If None, then a certificate will be created without a
4603 signature. The signature can be created out-of-band
4604 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04004605 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08004606 subject_key_version: A 64-bit version value. If this is None, the number
4607 of seconds since the epoch is used.
4608 subject: A subject identifier. For Product Signing Key certificates this
4609 should be the same Product ID found in the permanent attributes.
4610 is_intermediate_authority: True if the certificate is for an intermediate
4611 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08004612 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08004613 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004614 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08004615 """
4616 signed_data = bytearray()
4617 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004618 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004619 hasher = hashlib.sha256()
4620 hasher.update(subject)
4621 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08004622 if not usage:
4623 usage = 'com.google.android.things.vboot'
4624 if is_intermediate_authority:
4625 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08004626 hasher = hashlib.sha256()
4627 hasher.update(usage)
4628 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07004629 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08004630 subject_key_version = int(time.time())
4631 signed_data.extend(struct.pack('<Q', subject_key_version))
4632 signature = bytearray()
4633 if authority_key_path:
4634 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08004635 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09004636 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01004637 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09004638 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08004639 hasher.update(signed_data)
4640 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04004641 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4642 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004643 alg.signature_num_bytes, authority_key_path,
4644 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08004645 output.write(signed_data)
4646 output.write(signature)
4647
David Zeuthenc68f0822017-03-31 17:22:35 -04004648 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004649 product_id):
4650 """Implements the 'make_atx_permanent_attributes' command.
4651
4652 Android Things permanent attributes are designed to be permanent for a
4653 particular product and a hash of these attributes should be fused into
4654 hardware to enforce this.
4655
4656 Arguments:
4657 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04004658 root_authority_key_path: Path to a PEM or DER public key for
4659 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08004660 product_id: A 16-byte Product ID.
4661
4662 Raises:
4663 AvbError: If an argument is incorrect.
4664 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004665 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004666 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004667 raise AvbError('Invalid Product ID length.')
4668 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004669 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004670 output.write(product_id)
4671
4672 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08004673 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08004674 """Implements the 'make_atx_metadata' command.
4675
4676 Android Things metadata are included in vbmeta images to facilitate
4677 verification. The output of this command can be used as the
4678 public_key_metadata argument to other commands.
4679
4680 Arguments:
4681 output: Metadata will be written to this file on success.
4682 intermediate_key_certificate: A certificate file as output by
4683 make_atx_certificate with
4684 is_intermediate_authority set to true.
4685 product_key_certificate: A certificate file as output by
4686 make_atx_certificate with
4687 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08004688
4689 Raises:
4690 AvbError: If an argument is incorrect.
4691 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004692 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004693 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004694 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08004695 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004696 raise AvbError('Invalid product key certificate length.')
4697 output.write(struct.pack('<I', 1)) # Format Version
4698 output.write(intermediate_key_certificate)
4699 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08004700
Darren Krahnfccd64e2018-01-16 17:39:35 -08004701 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
4702 unlock_key_certificate, challenge_path,
4703 unlock_key_path, signing_helper,
4704 signing_helper_with_files):
4705 """Implements the 'make_atx_unlock_credential' command.
4706
4707 Android Things unlock credentials can be used to authorize the unlock of AVB
4708 on a device. These credentials are presented to an Android Things bootloader
4709 via the fastboot interface in response to a 16-byte challenge. This method
4710 creates all fields of the credential except the challenge signature field
4711 (which is the last field) and can optionally create the challenge signature
4712 field as well if a challenge and the unlock_key_path is provided.
4713
4714 Arguments:
4715 output: The credential will be written to this file on success.
4716 intermediate_key_certificate: A certificate file as output by
4717 make_atx_certificate with
4718 is_intermediate_authority set to true.
4719 unlock_key_certificate: A certificate file as output by
4720 make_atx_certificate with
4721 is_intermediate_authority set to false and the
4722 usage set to
4723 'com.google.android.things.vboot.unlock'.
4724 challenge_path: [optional] A path to the challenge to sign.
4725 unlock_key_path: [optional] A PEM file path with the unlock private key.
4726 signing_helper: Program which signs a hash and returns the signature.
4727 signing_helper_with_files: Same as signing_helper but uses files instead.
4728
4729 Raises:
4730 AvbError: If an argument is incorrect.
4731 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004732 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
4733 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08004734 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4735 raise AvbError('Invalid intermediate key certificate length.')
4736 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4737 raise AvbError('Invalid product key certificate length.')
4738 challenge = bytearray()
4739 if challenge_path:
4740 with open(challenge_path, 'r') as f:
4741 challenge = f.read()
4742 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
4743 raise AvbError('Invalid unlock challenge length.')
4744 output.write(struct.pack('<I', 1)) # Format Version
4745 output.write(intermediate_key_certificate)
4746 output.write(unlock_key_certificate)
4747 if challenge_path and unlock_key_path:
4748 signature = bytearray()
4749 padding_and_hash = bytearray()
4750 algorithm_name = 'SHA512_RSA4096'
4751 alg = ALGORITHMS[algorithm_name]
4752 hasher = hashlib.sha512()
4753 padding_and_hash.extend(alg.padding)
4754 hasher.update(challenge)
4755 padding_and_hash.extend(hasher.digest())
4756 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4757 algorithm_name,
4758 alg.signature_num_bytes, unlock_key_path,
4759 padding_and_hash))
4760 output.write(signature)
4761
David Zeuthen21e95262016-07-27 17:58:40 -04004762
4763def calc_hash_level_offsets(image_size, block_size, digest_size):
4764 """Calculate the offsets of all the hash-levels in a Merkle-tree.
4765
4766 Arguments:
4767 image_size: The size of the image to calculate a Merkle-tree for.
4768 block_size: The block size, e.g. 4096.
4769 digest_size: The size of each hash, e.g. 32 for SHA-256.
4770
4771 Returns:
4772 A tuple where the first argument is an array of offsets and the
4773 second is size of the tree, in bytes.
4774 """
4775 level_offsets = []
4776 level_sizes = []
4777 tree_size = 0
4778
4779 num_levels = 0
4780 size = image_size
4781 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01004782 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04004783 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4784
4785 level_sizes.append(level_size)
4786 tree_size += level_size
4787 num_levels += 1
4788
4789 size = level_size
4790
4791 for n in range(0, num_levels):
4792 offset = 0
4793 for m in range(n + 1, num_levels):
4794 offset += level_sizes[m]
4795 level_offsets.append(offset)
4796
David Zeuthena4fee8b2016-08-22 15:20:43 -04004797 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004798
4799
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004800# See system/extras/libfec/include/fec/io.h for these definitions.
4801FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4802FEC_MAGIC = 0xfecfecfe
4803
4804
4805def calc_fec_data_size(image_size, num_roots):
4806 """Calculates how much space FEC data will take.
4807
Jan Monschfe00c0a2019-12-11 11:19:40 +01004808 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004809 image_size: The size of the image.
4810 num_roots: Number of roots.
4811
4812 Returns:
4813 The number of bytes needed for FEC for an image of the given size
4814 and with the requested number of FEC roots.
4815
4816 Raises:
4817 ValueError: If output from the 'fec' tool is invalid.
4818
4819 """
4820 p = subprocess.Popen(
4821 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4822 stdout=subprocess.PIPE,
4823 stderr=subprocess.PIPE)
4824 (pout, perr) = p.communicate()
4825 retcode = p.wait()
4826 if retcode != 0:
4827 raise ValueError('Error invoking fec: {}'.format(perr))
4828 return int(pout)
4829
4830
4831def generate_fec_data(image_filename, num_roots):
4832 """Generate FEC codes for an image.
4833
Jan Monschfe00c0a2019-12-11 11:19:40 +01004834 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004835 image_filename: The filename of the image.
4836 num_roots: Number of roots.
4837
4838 Returns:
4839 The FEC data blob.
4840
4841 Raises:
4842 ValueError: If output from the 'fec' tool is invalid.
4843 """
4844 fec_tmpfile = tempfile.NamedTemporaryFile()
4845 subprocess.check_call(
4846 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4847 fec_tmpfile.name],
4848 stderr=open(os.devnull))
4849 fec_data = fec_tmpfile.read()
4850 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4851 footer_data = fec_data[-footer_size:]
4852 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4853 footer_data)
4854 if magic != FEC_MAGIC:
4855 raise ValueError('Unexpected magic in FEC footer')
4856 return fec_data[0:fec_size]
4857
4858
David Zeuthen21e95262016-07-27 17:58:40 -04004859def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004860 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004861 """Generates a Merkle-tree for a file.
4862
Jan Monschfe00c0a2019-12-11 11:19:40 +01004863 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004864 image: The image, as a file.
4865 image_size: The size of the image.
4866 block_size: The block size, e.g. 4096.
4867 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4868 salt: The salt to use.
4869 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004870 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004871 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004872
4873 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004874 A tuple where the first element is the top-level hash and the
4875 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04004876 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004877 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004878 hash_src_offset = 0
4879 hash_src_size = image_size
4880 level_num = 0
4881 while hash_src_size > block_size:
4882 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004883 remaining = hash_src_size
4884 while remaining > 0:
4885 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004886 # Only read from the file for the first level - for subsequent
4887 # levels, access the array we're building.
4888 if level_num == 0:
4889 image.seek(hash_src_offset + hash_src_size - remaining)
4890 data = image.read(min(remaining, block_size))
4891 else:
4892 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4893 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004894 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004895
4896 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004897 if len(data) < block_size:
4898 hasher.update('\0' * (block_size - len(data)))
4899 level_output += hasher.digest()
4900 if digest_padding > 0:
4901 level_output += '\0' * digest_padding
4902
4903 padding_needed = (round_to_multiple(
4904 len(level_output), block_size) - len(level_output))
4905 level_output += '\0' * padding_needed
4906
David Zeuthena4fee8b2016-08-22 15:20:43 -04004907 # Copy level-output into resulting tree.
4908 offset = hash_level_offsets[level_num]
4909 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004910
David Zeuthena4fee8b2016-08-22 15:20:43 -04004911 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004912 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004913 level_num += 1
4914
4915 hasher = hashlib.new(name=hash_alg_name, string=salt)
4916 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004917 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04004918
4919
4920class AvbTool(object):
4921 """Object for avbtool command-line tool."""
4922
4923 def __init__(self):
4924 """Initializer method."""
4925 self.avb = Avb()
4926
4927 def _add_common_args(self, sub_parser):
4928 """Adds arguments used by several sub-commands.
4929
4930 Arguments:
4931 sub_parser: The parser to add arguments to.
4932 """
4933 sub_parser.add_argument('--algorithm',
4934 help='Algorithm to use (default: NONE)',
4935 metavar='ALGORITHM',
4936 default='NONE')
4937 sub_parser.add_argument('--key',
4938 help='Path to RSA private key file',
4939 metavar='KEY',
4940 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004941 sub_parser.add_argument('--signing_helper',
4942 help='Path to helper used for signing',
4943 metavar='APP',
4944 default=None,
4945 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004946 sub_parser.add_argument('--signing_helper_with_files',
4947 help='Path to helper used for signing using files',
4948 metavar='APP',
4949 default=None,
4950 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004951 sub_parser.add_argument('--public_key_metadata',
4952 help='Path to public key metadata file',
4953 metavar='KEY_METADATA',
4954 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004955 sub_parser.add_argument('--rollback_index',
4956 help='Rollback Index',
4957 type=parse_number,
4958 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004959 # This is used internally for unit tests. Do not include in --help output.
4960 sub_parser.add_argument('--internal_release_string',
4961 help=argparse.SUPPRESS)
4962 sub_parser.add_argument('--append_to_release_string',
4963 help='Text to append to release string',
4964 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004965 sub_parser.add_argument('--prop',
4966 help='Add property',
4967 metavar='KEY:VALUE',
4968 action='append')
4969 sub_parser.add_argument('--prop_from_file',
4970 help='Add property from file',
4971 metavar='KEY:PATH',
4972 action='append')
4973 sub_parser.add_argument('--kernel_cmdline',
4974 help='Add kernel cmdline',
4975 metavar='CMDLINE',
4976 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004977 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4978 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4979 # at some future point.
4980 sub_parser.add_argument('--setup_rootfs_from_kernel',
4981 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004982 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004983 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004984 type=argparse.FileType('rb'))
4985 sub_parser.add_argument('--include_descriptors_from_image',
4986 help='Include descriptors from image',
4987 metavar='IMAGE',
4988 action='append',
4989 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004990 sub_parser.add_argument('--print_required_libavb_version',
4991 help=('Don\'t store the footer - '
4992 'instead calculate the required libavb '
4993 'version for the given options.'),
4994 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004995 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4996 sub_parser.add_argument('--chain_partition',
4997 help='Allow signed integrity-data for partition',
4998 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4999 action='append')
5000 sub_parser.add_argument('--flags',
5001 help='VBMeta flags',
5002 type=parse_number,
5003 default=0)
5004 sub_parser.add_argument('--set_hashtree_disabled_flag',
5005 help='Set the HASHTREE_DISABLED flag',
5006 action='store_true')
5007
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08005008 def _add_common_footer_args(self, sub_parser):
5009 """Adds arguments used by add_*_footer sub-commands.
5010
5011 Arguments:
5012 sub_parser: The parser to add arguments to.
5013 """
5014 sub_parser.add_argument('--use_persistent_digest',
5015 help='Use a persistent digest on device instead of '
5016 'storing the digest in the descriptor. This '
5017 'cannot be used with A/B so must be combined '
5018 'with --do_not_use_ab when an A/B suffix is '
5019 'expected at runtime.',
5020 action='store_true')
5021 sub_parser.add_argument('--do_not_use_ab',
5022 help='The partition does not use A/B even when an '
5023 'A/B suffix is present. This must not be used '
5024 'for vbmeta or chained partitions.',
5025 action='store_true')
5026
David Zeuthena5fd3a42017-02-27 16:38:54 -05005027 def _fixup_common_args(self, args):
5028 """Common fixups needed by subcommands.
5029
5030 Arguments:
5031 args: Arguments to modify.
5032
5033 Returns:
5034 The modified arguments.
5035 """
5036 if args.set_hashtree_disabled_flag:
5037 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
5038 return args
David Zeuthen21e95262016-07-27 17:58:40 -04005039
5040 def run(self, argv):
5041 """Command-line processor.
5042
5043 Arguments:
5044 argv: Pass sys.argv from main.
5045 """
5046 parser = argparse.ArgumentParser()
5047 subparsers = parser.add_subparsers(title='subcommands')
5048
5049 sub_parser = subparsers.add_parser('version',
5050 help='Prints version of avbtool.')
5051 sub_parser.set_defaults(func=self.version)
5052
5053 sub_parser = subparsers.add_parser('extract_public_key',
5054 help='Extract public key.')
5055 sub_parser.add_argument('--key',
5056 help='Path to RSA private key file',
5057 required=True)
5058 sub_parser.add_argument('--output',
5059 help='Output file name',
5060 type=argparse.FileType('wb'),
5061 required=True)
5062 sub_parser.set_defaults(func=self.extract_public_key)
5063
5064 sub_parser = subparsers.add_parser('make_vbmeta_image',
5065 help='Makes a vbmeta image.')
5066 sub_parser.add_argument('--output',
5067 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04005068 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04005069 sub_parser.add_argument('--padding_size',
5070 metavar='NUMBER',
5071 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01005072 'its size is a multiple of NUMBER '
5073 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04005074 type=parse_number,
5075 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04005076 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04005077 sub_parser.set_defaults(func=self.make_vbmeta_image)
5078
Dan Austinb12b2c12019-12-15 20:28:02 -08005079 sub_parser = subparsers.add_parser('make_icp_from_vbmeta',
5080 help='Makes an ICP enhanced vbmeta image'
5081 ' from an existing vbmeta image.')
5082 sub_parser.add_argument('--output',
5083 help='Output file name.',
5084 type=argparse.FileType('wb'),
5085 default=sys.stdout)
5086 sub_parser.add_argument('--vbmeta_image_path',
5087 help='Path to a generate vbmeta image file.')
5088 sub_parser.add_argument('--version_incremental', help='Current build ID.')
5089 sub_parser.add_argument('--manufacturer_key',
5090 help='Path to the PEM file containing the '
5091 'manufacturer key for use with the log.')
5092 sub_parser.add_argument('--transparency_log_servers',
5093 help='List of transparency log servers in '
5094 'host:port format. This must not be None and must '
5095 'be the same size as transparency_log_pub_keys. '
5096 'Also, transparency_log_servers[n] must correspond '
5097 'to transparency_log_pub_keys[n] for all values n.',
5098 nargs='*')
5099 sub_parser.add_argument('--transparency_log_pub_keys',
5100 help='Paths to PEM files containing transparency '
5101 'log server key(s). This must not be None and must '
5102 'be the same size as transparency_log_servers. '
5103 'Also, transparency_log_pub_keys[n] must '
5104 'correspond to transparency_log_servers[n] for all '
5105 'values n.', nargs='*')
5106 sub_parser.add_argument('--padding_size',
5107 metavar='NUMBER',
5108 help='If non-zero, pads output with NUL bytes so '
5109 'its size is a multiple of NUMBER '
5110 '(default: 0)',
5111 type=parse_number,
5112 default=0)
5113 self._add_common_args(sub_parser)
5114 sub_parser.set_defaults(func=self.make_icp_from_vbmeta)
5115
David Zeuthen21e95262016-07-27 17:58:40 -04005116 sub_parser = subparsers.add_parser('add_hash_footer',
5117 help='Add hashes and footer to image.')
5118 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04005119 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04005120 type=argparse.FileType('rab+'))
5121 sub_parser.add_argument('--partition_size',
5122 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04005123 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04005124 sub_parser.add_argument('--partition_name',
5125 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04005126 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04005127 sub_parser.add_argument('--hash_algorithm',
5128 help='Hash algorithm to use (default: sha256)',
5129 default='sha256')
5130 sub_parser.add_argument('--salt',
5131 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04005132 sub_parser.add_argument('--calc_max_image_size',
5133 help=('Don\'t store the footer - '
5134 'instead calculate the maximum image size '
5135 'leaving enough room for metadata with '
5136 'the given partition size.'),
5137 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05005138 sub_parser.add_argument('--output_vbmeta_image',
5139 help='Also write vbmeta struct to file',
5140 type=argparse.FileType('wb'))
5141 sub_parser.add_argument('--do_not_append_vbmeta_image',
5142 help=('Do not append vbmeta struct or footer '
5143 'to the image'),
5144 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04005145 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08005146 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04005147 sub_parser.set_defaults(func=self.add_hash_footer)
5148
David Zeuthenb1b994d2017-03-06 18:01:31 -05005149 sub_parser = subparsers.add_parser('append_vbmeta_image',
5150 help='Append vbmeta image to image.')
5151 sub_parser.add_argument('--image',
5152 help='Image to append vbmeta blob to',
5153 type=argparse.FileType('rab+'))
5154 sub_parser.add_argument('--partition_size',
5155 help='Partition size',
5156 type=parse_number,
5157 required=True)
5158 sub_parser.add_argument('--vbmeta_image',
5159 help='Image with vbmeta blob to append',
5160 type=argparse.FileType('rb'))
5161 sub_parser.set_defaults(func=self.append_vbmeta_image)
5162
Jan Monscheeb28b62019-12-05 16:17:09 +01005163 sub_parser = subparsers.add_parser(
5164 'add_hashtree_footer',
5165 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04005166 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04005167 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04005168 type=argparse.FileType('rab+'))
5169 sub_parser.add_argument('--partition_size',
5170 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04005171 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04005172 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04005173 sub_parser.add_argument('--partition_name',
5174 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04005175 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04005176 sub_parser.add_argument('--hash_algorithm',
5177 help='Hash algorithm to use (default: sha1)',
5178 default='sha1')
5179 sub_parser.add_argument('--salt',
5180 help='Salt in hex (default: /dev/urandom)')
5181 sub_parser.add_argument('--block_size',
5182 help='Block size (default: 4096)',
5183 type=parse_number,
5184 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04005185 # TODO(zeuthen): The --generate_fec option was removed when we
5186 # moved to generating FEC by default. To avoid breaking existing
5187 # users needing to transition we simply just print a warning below
5188 # in add_hashtree_footer(). Remove this option and the warning at
5189 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04005190 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04005191 help=argparse.SUPPRESS,
5192 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01005193 sub_parser.add_argument(
5194 '--do_not_generate_fec',
5195 help='Do not generate forward-error-correction codes',
5196 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04005197 sub_parser.add_argument('--fec_num_roots',
5198 help='Number of roots for FEC (default: 2)',
5199 type=parse_number,
5200 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04005201 sub_parser.add_argument('--calc_max_image_size',
5202 help=('Don\'t store the hashtree or footer - '
5203 'instead calculate the maximum image size '
5204 'leaving enough room for hashtree '
5205 'and metadata with the given partition '
5206 'size.'),
5207 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05005208 sub_parser.add_argument('--output_vbmeta_image',
5209 help='Also write vbmeta struct to file',
5210 type=argparse.FileType('wb'))
5211 sub_parser.add_argument('--do_not_append_vbmeta_image',
5212 help=('Do not append vbmeta struct or footer '
5213 'to the image'),
5214 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04005215 # This is different from --setup_rootfs_from_kernel insofar that
5216 # it doesn't take an IMAGE, the generated cmdline will be for the
5217 # hashtree we're adding.
5218 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
5219 action='store_true',
5220 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09005221 sub_parser.add_argument('--no_hashtree',
5222 action='store_true',
5223 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04005224 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08005225 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04005226 sub_parser.set_defaults(func=self.add_hashtree_footer)
5227
5228 sub_parser = subparsers.add_parser('erase_footer',
5229 help='Erase footer from an image.')
5230 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04005231 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04005232 type=argparse.FileType('rwb+'),
5233 required=True)
5234 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05005235 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04005236 action='store_true')
5237 sub_parser.set_defaults(func=self.erase_footer)
5238
David Zeuthen1394f762019-04-30 10:20:11 -04005239 sub_parser = subparsers.add_parser('zero_hashtree',
5240 help='Zero out hashtree and FEC data.')
5241 sub_parser.add_argument('--image',
5242 help='Image with a footer',
5243 type=argparse.FileType('rwb+'),
5244 required=True)
5245 sub_parser.set_defaults(func=self.zero_hashtree)
5246
Jan Monscheeb28b62019-12-05 16:17:09 +01005247 sub_parser = subparsers.add_parser(
5248 'extract_vbmeta_image',
5249 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04005250 sub_parser.add_argument('--image',
5251 help='Image with footer',
5252 type=argparse.FileType('rb'),
5253 required=True)
5254 sub_parser.add_argument('--output',
5255 help='Output file name',
5256 type=argparse.FileType('wb'))
5257 sub_parser.add_argument('--padding_size',
5258 metavar='NUMBER',
5259 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01005260 'its size is a multiple of NUMBER '
5261 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04005262 type=parse_number,
5263 default=0)
5264 sub_parser.set_defaults(func=self.extract_vbmeta_image)
5265
David Zeuthen2bc232b2017-04-19 14:25:19 -04005266 sub_parser = subparsers.add_parser('resize_image',
5267 help='Resize image with a footer.')
5268 sub_parser.add_argument('--image',
5269 help='Image with a footer',
5270 type=argparse.FileType('rwb+'),
5271 required=True)
5272 sub_parser.add_argument('--partition_size',
5273 help='New partition size',
5274 type=parse_number)
5275 sub_parser.set_defaults(func=self.resize_image)
5276
David Zeuthen21e95262016-07-27 17:58:40 -04005277 sub_parser = subparsers.add_parser(
5278 'info_image',
5279 help='Show information about vbmeta or footer.')
5280 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04005281 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04005282 type=argparse.FileType('rb'),
5283 required=True)
5284 sub_parser.add_argument('--output',
5285 help='Write info to file',
5286 type=argparse.FileType('wt'),
5287 default=sys.stdout)
5288 sub_parser.set_defaults(func=self.info_image)
5289
David Zeuthenb623d8b2017-04-04 16:05:53 -04005290 sub_parser = subparsers.add_parser(
5291 'verify_image',
5292 help='Verify an image.')
5293 sub_parser.add_argument('--image',
5294 help='Image to verify',
5295 type=argparse.FileType('rb'),
5296 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04005297 sub_parser.add_argument('--key',
5298 help='Check embedded public key matches KEY',
5299 metavar='KEY',
5300 required=False)
5301 sub_parser.add_argument('--expected_chain_partition',
5302 help='Expected chain partition',
5303 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
5304 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01005305 sub_parser.add_argument(
5306 '--follow_chain_partitions',
5307 help=('Follows chain partitions even when not '
5308 'specified with the --expected_chain_partition option'),
5309 action='store_true')
5310 sub_parser.add_argument(
5311 '--accept_zeroed_hashtree',
5312 help=('Accept images where the hashtree or FEC data is zeroed out'),
5313 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04005314 sub_parser.set_defaults(func=self.verify_image)
5315
David Zeuthenb8643c02018-05-17 17:21:18 -04005316 sub_parser = subparsers.add_parser(
5317 'calculate_vbmeta_digest',
5318 help='Calculate vbmeta digest.')
5319 sub_parser.add_argument('--image',
5320 help='Image to calculate digest for',
5321 type=argparse.FileType('rb'),
5322 required=True)
5323 sub_parser.add_argument('--hash_algorithm',
5324 help='Hash algorithm to use (default: sha256)',
5325 default='sha256')
5326 sub_parser.add_argument('--output',
5327 help='Write hex digest to file (default: stdout)',
5328 type=argparse.FileType('wt'),
5329 default=sys.stdout)
5330 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
5331
David Zeuthenf7d2e752018-09-20 13:30:41 -04005332 sub_parser = subparsers.add_parser(
5333 'calculate_kernel_cmdline',
5334 help='Calculate kernel cmdline.')
5335 sub_parser.add_argument('--image',
5336 help='Image to calculate kernel cmdline for',
5337 type=argparse.FileType('rb'),
5338 required=True)
5339 sub_parser.add_argument('--hashtree_disabled',
5340 help='Return the cmdline for hashtree disabled',
5341 action='store_true')
5342 sub_parser.add_argument('--output',
5343 help='Write cmdline to file (default: stdout)',
5344 type=argparse.FileType('wt'),
5345 default=sys.stdout)
5346 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
5347
David Zeuthen8b6973b2016-09-20 12:39:49 -04005348 sub_parser = subparsers.add_parser('set_ab_metadata',
5349 help='Set A/B metadata.')
5350 sub_parser.add_argument('--misc_image',
5351 help=('The misc image to modify. If the image does '
5352 'not exist, it will be created.'),
5353 type=argparse.FileType('r+b'),
5354 required=True)
5355 sub_parser.add_argument('--slot_data',
5356 help=('Slot data of the form "priority", '
5357 '"tries_remaining", "sucessful_boot" for '
5358 'slot A followed by the same for slot B, '
5359 'separated by colons. The default value '
5360 'is 15:7:0:14:7:0.'),
5361 default='15:7:0:14:7:0')
5362 sub_parser.set_defaults(func=self.set_ab_metadata)
5363
Darren Krahn147b08d2016-12-20 16:38:29 -08005364 sub_parser = subparsers.add_parser(
5365 'make_atx_certificate',
5366 help='Create an Android Things eXtension (ATX) certificate.')
5367 sub_parser.add_argument('--output',
5368 help='Write certificate to file',
5369 type=argparse.FileType('wb'),
5370 default=sys.stdout)
5371 sub_parser.add_argument('--subject',
5372 help=('Path to subject file'),
5373 type=argparse.FileType('rb'),
5374 required=True)
5375 sub_parser.add_argument('--subject_key',
5376 help=('Path to subject RSA public key file'),
5377 type=argparse.FileType('rb'),
5378 required=True)
5379 sub_parser.add_argument('--subject_key_version',
5380 help=('Version of the subject key'),
5381 type=parse_number,
5382 required=False)
5383 sub_parser.add_argument('--subject_is_intermediate_authority',
5384 help=('Generate an intermediate authority '
5385 'certificate'),
5386 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08005387 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07005388 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08005389 'string'),
5390 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08005391 sub_parser.add_argument('--authority_key',
5392 help='Path to authority RSA private key file',
5393 required=False)
5394 sub_parser.add_argument('--signing_helper',
5395 help='Path to helper used for signing',
5396 metavar='APP',
5397 default=None,
5398 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04005399 sub_parser.add_argument('--signing_helper_with_files',
5400 help='Path to helper used for signing using files',
5401 metavar='APP',
5402 default=None,
5403 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08005404 sub_parser.set_defaults(func=self.make_atx_certificate)
5405
5406 sub_parser = subparsers.add_parser(
5407 'make_atx_permanent_attributes',
5408 help='Create Android Things eXtension (ATX) permanent attributes.')
5409 sub_parser.add_argument('--output',
5410 help='Write attributes to file',
5411 type=argparse.FileType('wb'),
5412 default=sys.stdout)
5413 sub_parser.add_argument('--root_authority_key',
5414 help='Path to authority RSA public key file',
5415 type=argparse.FileType('rb'),
5416 required=True)
5417 sub_parser.add_argument('--product_id',
5418 help=('Path to Product ID file'),
5419 type=argparse.FileType('rb'),
5420 required=True)
5421 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
5422
5423 sub_parser = subparsers.add_parser(
5424 'make_atx_metadata',
5425 help='Create Android Things eXtension (ATX) metadata.')
5426 sub_parser.add_argument('--output',
5427 help='Write metadata to file',
5428 type=argparse.FileType('wb'),
5429 default=sys.stdout)
5430 sub_parser.add_argument('--intermediate_key_certificate',
5431 help='Path to intermediate key certificate file',
5432 type=argparse.FileType('rb'),
5433 required=True)
5434 sub_parser.add_argument('--product_key_certificate',
5435 help='Path to product key certificate file',
5436 type=argparse.FileType('rb'),
5437 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08005438 sub_parser.set_defaults(func=self.make_atx_metadata)
5439
Darren Krahnfccd64e2018-01-16 17:39:35 -08005440 sub_parser = subparsers.add_parser(
5441 'make_atx_unlock_credential',
5442 help='Create an Android Things eXtension (ATX) unlock credential.')
5443 sub_parser.add_argument('--output',
5444 help='Write credential to file',
5445 type=argparse.FileType('wb'),
5446 default=sys.stdout)
5447 sub_parser.add_argument('--intermediate_key_certificate',
5448 help='Path to intermediate key certificate file',
5449 type=argparse.FileType('rb'),
5450 required=True)
5451 sub_parser.add_argument('--unlock_key_certificate',
5452 help='Path to unlock key certificate file',
5453 type=argparse.FileType('rb'),
5454 required=True)
5455 sub_parser.add_argument('--challenge',
5456 help='Path to the challenge to sign (optional). If '
5457 'this is not provided the challenge signature '
5458 'field is omitted and can be concatenated '
5459 'later.',
5460 required=False)
5461 sub_parser.add_argument('--unlock_key',
5462 help='Path to unlock key (optional). Must be '
5463 'provided if using --challenge.',
5464 required=False)
5465 sub_parser.add_argument('--signing_helper',
5466 help='Path to helper used for signing',
5467 metavar='APP',
5468 default=None,
5469 required=False)
5470 sub_parser.add_argument('--signing_helper_with_files',
5471 help='Path to helper used for signing using files',
5472 metavar='APP',
5473 default=None,
5474 required=False)
5475 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
5476
David Zeuthen21e95262016-07-27 17:58:40 -04005477 args = parser.parse_args(argv[1:])
5478 try:
5479 args.func(args)
5480 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01005481 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04005482 sys.exit(1)
5483
5484 def version(self, _):
5485 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01005486 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04005487
5488 def extract_public_key(self, args):
5489 """Implements the 'extract_public_key' sub-command."""
5490 self.avb.extract_public_key(args.key, args.output)
5491
5492 def make_vbmeta_image(self, args):
5493 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05005494 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04005495 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05005496 args.algorithm, args.key,
5497 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05005498 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04005499 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05005500 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05005501 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05005502 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04005503 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05005504 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04005505 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04005506 args.print_required_libavb_version,
5507 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04005508
Dan Austinb12b2c12019-12-15 20:28:02 -08005509 def make_icp_from_vbmeta(self, args):
5510 """Implements the 'make_icp_from_vbmeta' sub-command."""
5511 args = self._fixup_common_args(args)
5512 self.avb.make_icp_from_vbmeta(args.vbmeta_image_path,
5513 args.output, args.algorithm,
5514 args.signing_helper,
5515 args.signing_helper_with_files,
5516 args.version_incremental,
5517 args.transparency_log_servers,
5518 args.transparency_log_pub_keys,
5519 args.manufacturer_key,
5520 args.padding_size)
5521
David Zeuthenb1b994d2017-03-06 18:01:31 -05005522 def append_vbmeta_image(self, args):
5523 """Implements the 'append_vbmeta_image' sub-command."""
5524 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
5525 args.partition_size)
5526
David Zeuthen21e95262016-07-27 17:58:40 -04005527 def add_hash_footer(self, args):
5528 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05005529 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04005530 self.avb.add_hash_footer(args.image.name if args.image else None,
5531 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04005532 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05005533 args.salt, args.chain_partition, args.algorithm,
5534 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05005535 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05005536 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05005537 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05005538 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05005539 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04005540 args.calc_max_image_size,
5541 args.signing_helper,
5542 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05005543 args.internal_release_string,
5544 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05005545 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04005546 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08005547 args.print_required_libavb_version,
5548 args.use_persistent_digest,
5549 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04005550
5551 def add_hashtree_footer(self, args):
5552 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05005553 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04005554 # TODO(zeuthen): Remove when removing support for the
5555 # '--generate_fec' option above.
5556 if args.generate_fec:
5557 sys.stderr.write('The --generate_fec option is deprecated since FEC '
5558 'is now generated by default. Use the option '
5559 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01005560 self.avb.add_hashtree_footer(
5561 args.image.name if args.image else None,
5562 args.partition_size,
5563 args.partition_name,
5564 not args.do_not_generate_fec, args.fec_num_roots,
5565 args.hash_algorithm, args.block_size,
5566 args.salt, args.chain_partition, args.algorithm,
5567 args.key, args.public_key_metadata,
5568 args.rollback_index, args.flags, args.prop,
5569 args.prop_from_file,
5570 args.kernel_cmdline,
5571 args.setup_rootfs_from_kernel,
5572 args.setup_as_rootfs_from_kernel,
5573 args.include_descriptors_from_image,
5574 args.calc_max_image_size,
5575 args.signing_helper,
5576 args.signing_helper_with_files,
5577 args.internal_release_string,
5578 args.append_to_release_string,
5579 args.output_vbmeta_image,
5580 args.do_not_append_vbmeta_image,
5581 args.print_required_libavb_version,
5582 args.use_persistent_digest,
5583 args.do_not_use_ab,
5584 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05005585
David Zeuthen21e95262016-07-27 17:58:40 -04005586 def erase_footer(self, args):
5587 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005588 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04005589
David Zeuthen1394f762019-04-30 10:20:11 -04005590 def zero_hashtree(self, args):
5591 """Implements the 'zero_hashtree' sub-command."""
5592 self.avb.zero_hashtree(args.image.name)
5593
David Zeuthen49936b42018-08-07 17:38:58 -04005594 def extract_vbmeta_image(self, args):
5595 """Implements the 'extract_vbmeta_image' sub-command."""
5596 self.avb.extract_vbmeta_image(args.output, args.image.name,
5597 args.padding_size)
5598
David Zeuthen2bc232b2017-04-19 14:25:19 -04005599 def resize_image(self, args):
5600 """Implements the 'resize_image' sub-command."""
5601 self.avb.resize_image(args.image.name, args.partition_size)
5602
David Zeuthen8b6973b2016-09-20 12:39:49 -04005603 def set_ab_metadata(self, args):
5604 """Implements the 'set_ab_metadata' sub-command."""
5605 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
5606
David Zeuthen21e95262016-07-27 17:58:40 -04005607 def info_image(self, args):
5608 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005609 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04005610
David Zeuthenb623d8b2017-04-04 16:05:53 -04005611 def verify_image(self, args):
5612 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04005613 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05005614 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04005615 args.follow_chain_partitions,
5616 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04005617
David Zeuthenb8643c02018-05-17 17:21:18 -04005618 def calculate_vbmeta_digest(self, args):
5619 """Implements the 'calculate_vbmeta_digest' sub-command."""
5620 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
5621 args.output)
5622
David Zeuthenf7d2e752018-09-20 13:30:41 -04005623 def calculate_kernel_cmdline(self, args):
5624 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01005625 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
5626 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04005627
Darren Krahn147b08d2016-12-20 16:38:29 -08005628 def make_atx_certificate(self, args):
5629 """Implements the 'make_atx_certificate' sub-command."""
5630 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04005631 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005632 args.subject_key_version,
5633 args.subject.read(),
5634 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08005635 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04005636 args.signing_helper,
5637 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08005638
5639 def make_atx_permanent_attributes(self, args):
5640 """Implements the 'make_atx_permanent_attributes' sub-command."""
5641 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04005642 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005643 args.product_id.read())
5644
5645 def make_atx_metadata(self, args):
5646 """Implements the 'make_atx_metadata' sub-command."""
5647 self.avb.make_atx_metadata(args.output,
5648 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08005649 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08005650
Darren Krahnfccd64e2018-01-16 17:39:35 -08005651 def make_atx_unlock_credential(self, args):
5652 """Implements the 'make_atx_unlock_credential' sub-command."""
5653 self.avb.make_atx_unlock_credential(
5654 args.output,
5655 args.intermediate_key_certificate.read(),
5656 args.unlock_key_certificate.read(),
5657 args.challenge,
5658 args.unlock_key,
5659 args.signing_helper,
5660 args.signing_helper_with_files)
5661
David Zeuthen21e95262016-07-27 17:58:40 -04005662
5663if __name__ == '__main__':
5664 tool = AvbTool()
5665 tool.run(sys.argv)