blob: 6f327f6733b8172991a8252d587a053b7d7237e8 [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
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040031import math
David Zeuthen21e95262016-07-27 17:58:40 -040032import os
33import struct
34import subprocess
35import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040036import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080037import time
David Zeuthen21e95262016-07-27 17:58:40 -040038
David Zeuthene3cadca2017-02-22 21:25:46 -050039# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040040AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080041AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080044# Keep in sync with libavb/avb_footer.h.
45AVB_FOOTER_VERSION_MAJOR = 1
46AVB_FOOTER_VERSION_MINOR = 0
47
David Zeuthen58305522017-01-11 17:42:47 -050048AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040049
David Zeuthene3cadca2017-02-22 21:25:46 -050050
David Zeuthen21e95262016-07-27 17:58:40 -040051class AvbError(Exception):
52 """Application-specific errors.
53
54 These errors represent issues for which a stack-trace should not be
55 presented.
56
57 Attributes:
58 message: Error message.
59 """
60
61 def __init__(self, message):
62 Exception.__init__(self, message)
63
64
65class Algorithm(object):
66 """Contains details about an algorithm.
67
Tao Bao80418a52018-07-20 11:41:22 -070068 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040069
70 The constant |ALGORITHMS| is a dictionary from human-readable
71 names (e.g 'SHA256_RSA2048') to instances of this class.
72
73 Attributes:
74 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040075 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040076 hash_num_bytes: Number of bytes used to store the hash.
77 signature_num_bytes: Number of bytes used to store the signature.
78 public_key_num_bytes: Number of bytes used to store the public key.
79 padding: Padding used for signature, if any.
80 """
81
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
83 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040084 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040085 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.hash_num_bytes = hash_num_bytes
87 self.signature_num_bytes = signature_num_bytes
88 self.public_key_num_bytes = public_key_num_bytes
89 self.padding = padding
90
David Zeuthenb623d8b2017-04-04 16:05:53 -040091
David Zeuthen21e95262016-07-27 17:58:40 -040092# This must be kept in sync with the avb_crypto.h file.
93#
94# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
95# obtained from section 5.2.2 of RFC 4880.
96ALGORITHMS = {
97 'NONE': Algorithm(
98 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040099 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400100 hash_num_bytes=0,
101 signature_num_bytes=0,
102 public_key_num_bytes=0,
103 padding=[]),
104 'SHA256_RSA2048': Algorithm(
105 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400106 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400107 hash_num_bytes=32,
108 signature_num_bytes=256,
109 public_key_num_bytes=8 + 2*2048/8,
110 padding=[
111 # PKCS1-v1_5 padding
112 0x00, 0x01] + [0xff]*202 + [0x00] + [
113 # ASN.1 header
114 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
115 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
116 0x00, 0x04, 0x20,
117 ]),
118 'SHA256_RSA4096': Algorithm(
119 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400120 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400121 hash_num_bytes=32,
122 signature_num_bytes=512,
123 public_key_num_bytes=8 + 2*4096/8,
124 padding=[
125 # PKCS1-v1_5 padding
126 0x00, 0x01] + [0xff]*458 + [0x00] + [
127 # ASN.1 header
128 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
129 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
130 0x00, 0x04, 0x20,
131 ]),
132 'SHA256_RSA8192': Algorithm(
133 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400134 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400135 hash_num_bytes=32,
136 signature_num_bytes=1024,
137 public_key_num_bytes=8 + 2*8192/8,
138 padding=[
139 # PKCS1-v1_5 padding
140 0x00, 0x01] + [0xff]*970 + [0x00] + [
141 # ASN.1 header
142 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
143 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
144 0x00, 0x04, 0x20,
145 ]),
146 'SHA512_RSA2048': Algorithm(
147 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400148 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400149 hash_num_bytes=64,
150 signature_num_bytes=256,
151 public_key_num_bytes=8 + 2*2048/8,
152 padding=[
153 # PKCS1-v1_5 padding
154 0x00, 0x01] + [0xff]*170 + [0x00] + [
155 # ASN.1 header
156 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
157 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
158 0x00, 0x04, 0x40
159 ]),
160 'SHA512_RSA4096': Algorithm(
161 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400162 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400163 hash_num_bytes=64,
164 signature_num_bytes=512,
165 public_key_num_bytes=8 + 2*4096/8,
166 padding=[
167 # PKCS1-v1_5 padding
168 0x00, 0x01] + [0xff]*426 + [0x00] + [
169 # ASN.1 header
170 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
171 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
172 0x00, 0x04, 0x40
173 ]),
174 'SHA512_RSA8192': Algorithm(
175 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400176 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400177 hash_num_bytes=64,
178 signature_num_bytes=1024,
179 public_key_num_bytes=8 + 2*8192/8,
180 padding=[
181 # PKCS1-v1_5 padding
182 0x00, 0x01] + [0xff]*938 + [0x00] + [
183 # ASN.1 header
184 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
185 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
186 0x00, 0x04, 0x40
187 ]),
188}
189
190
David Zeuthene3cadca2017-02-22 21:25:46 -0500191def get_release_string():
192 """Calculates the release string to use in the VBMeta struct."""
193 # Keep in sync with libavb/avb_version.c:avb_version_string().
194 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
195 AVB_VERSION_MINOR,
196 AVB_VERSION_SUB)
197
198
David Zeuthen21e95262016-07-27 17:58:40 -0400199def round_to_multiple(number, size):
200 """Rounds a number up to nearest multiple of another number.
201
202 Args:
203 number: The number to round up.
204 size: The multiple to round up to.
205
206 Returns:
207 If |number| is a multiple of |size|, returns |number|, otherwise
208 returns |number| + |size|.
209 """
210 remainder = number % size
211 if remainder == 0:
212 return number
213 return number + size - remainder
214
215
216def round_to_pow2(number):
217 """Rounds a number up to the next power of 2.
218
219 Args:
220 number: The number to round up.
221
222 Returns:
223 If |number| is already a power of 2 then |number| is
224 returned. Otherwise the smallest power of 2 greater than |number|
225 is returned.
226 """
227 return 2**((number - 1).bit_length())
228
229
David Zeuthen21e95262016-07-27 17:58:40 -0400230def encode_long(num_bits, value):
231 """Encodes a long to a bytearray() using a given amount of bits.
232
233 This number is written big-endian, e.g. with the most significant
234 bit first.
235
David Zeuthenb623d8b2017-04-04 16:05:53 -0400236 This is the reverse of decode_long().
237
David Zeuthen21e95262016-07-27 17:58:40 -0400238 Arguments:
239 num_bits: The number of bits to write, e.g. 2048.
240 value: The value to write.
241
242 Returns:
243 A bytearray() with the encoded long.
244 """
245 ret = bytearray()
246 for bit_pos in range(num_bits, 0, -8):
247 octet = (value >> (bit_pos - 8)) & 0xff
248 ret.extend(struct.pack('!B', octet))
249 return ret
250
251
David Zeuthenb623d8b2017-04-04 16:05:53 -0400252def decode_long(blob):
253 """Decodes a long from a bytearray() using a given amount of bits.
254
255 This number is expected to be in big-endian, e.g. with the most
256 significant bit first.
257
258 This is the reverse of encode_long().
259
260 Arguments:
261 value: A bytearray() with the encoded long.
262
263 Returns:
264 The decoded value.
265 """
266 ret = 0
267 for b in bytearray(blob):
268 ret *= 256
269 ret += b
270 return ret
271
272
David Zeuthen21e95262016-07-27 17:58:40 -0400273def egcd(a, b):
274 """Calculate greatest common divisor of two numbers.
275
276 This implementation uses a recursive version of the extended
277 Euclidian algorithm.
278
279 Arguments:
280 a: First number.
281 b: Second number.
282
283 Returns:
284 A tuple (gcd, x, y) that where |gcd| is the greatest common
285 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
286 """
287 if a == 0:
288 return (b, 0, 1)
289 else:
290 g, y, x = egcd(b % a, a)
291 return (g, x - (b // a) * y, y)
292
293
294def modinv(a, m):
295 """Calculate modular multiplicative inverse of |a| modulo |m|.
296
297 This calculates the number |x| such that |a| * |x| == 1 (modulo
298 |m|). This number only exists if |a| and |m| are co-prime - |None|
299 is returned if this isn't true.
300
301 Arguments:
302 a: The number to calculate a modular inverse of.
303 m: The modulo to use.
304
305 Returns:
306 The modular multiplicative inverse of |a| and |m| or |None| if
307 these numbers are not co-prime.
308 """
309 gcd, x, _ = egcd(a, m)
310 if gcd != 1:
311 return None # modular inverse does not exist
312 else:
313 return x % m
314
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.
352 """
353 # We used to have something as simple as this:
354 #
355 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
356 # self.exponent = key.e
357 # self.modulus = key.n
358 # self.num_bits = key.size() + 1
359 #
360 # but unfortunately PyCrypto is not available in the builder. So
361 # instead just parse openssl(1) output to get this
362 # information. It's ugly but...
363 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
364 p = subprocess.Popen(args,
365 stdin=subprocess.PIPE,
366 stdout=subprocess.PIPE,
367 stderr=subprocess.PIPE)
368 (pout, perr) = p.communicate()
369 if p.wait() != 0:
370 # Could be just a public key is passed, try that.
371 args.append('-pubin')
372 p = subprocess.Popen(args,
373 stdin=subprocess.PIPE,
374 stdout=subprocess.PIPE,
375 stderr=subprocess.PIPE)
376 (pout, perr) = p.communicate()
377 if p.wait() != 0:
378 raise AvbError('Error getting public key: {}'.format(perr))
379
380 if not pout.lower().startswith(self.MODULUS_PREFIX):
381 raise AvbError('Unexpected modulus output')
382
383 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
384
385 # The exponent is assumed to always be 65537 and the number of
386 # bits can be derived from the modulus by rounding up to the
387 # nearest power of 2.
388 self.modulus = int(modulus_hexstr, 16)
389 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
390 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400391
392
David Zeuthenc68f0822017-03-31 17:22:35 -0400393def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400394 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
395
396 This creates a |AvbRSAPublicKeyHeader| as well as the two large
397 numbers (|key_num_bits| bits long) following it.
398
399 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400400 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400401
402 Returns:
403 A bytearray() with the |AvbRSAPublicKeyHeader|.
404 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400405 key = RSAPublicKey(key_path)
406 if key.exponent != 65537:
407 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400408 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400409 # Calculate n0inv = -1/n[0] (mod 2^32)
410 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400411 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400412 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400413 r = 2L**key.modulus.bit_length()
414 rrmodn = r * r % key.modulus
415 ret.extend(struct.pack('!II', key.num_bits, n0inv))
416 ret.extend(encode_long(key.num_bits, key.modulus))
417 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400418 return ret
419
420
421def lookup_algorithm_by_type(alg_type):
422 """Looks up algorithm by type.
423
424 Arguments:
425 alg_type: The integer representing the type.
426
427 Returns:
428 A tuple with the algorithm name and an |Algorithm| instance.
429
430 Raises:
431 Exception: If the algorithm cannot be found
432 """
433 for alg_name in ALGORITHMS:
434 alg_data = ALGORITHMS[alg_name]
435 if alg_data.algorithm_type == alg_type:
436 return (alg_name, alg_data)
437 raise AvbError('Unknown algorithm type {}'.format(alg_type))
438
439
David Zeuthena156d3d2017-06-01 12:08:09 -0400440def raw_sign(signing_helper, signing_helper_with_files,
441 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900442 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800443 """Computes a raw RSA signature using |signing_helper| or openssl.
444
445 Arguments:
446 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400447 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800448 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900449 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800450 key_path: Path to the private key file. Must be PEM format.
451 raw_data_to_sign: Data to sign (bytearray or str expected).
452
453 Returns:
454 A bytearray containing the signature.
455
456 Raises:
457 Exception: If an error occurs.
458 """
459 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400460 if signing_helper_with_files is not None:
461 signing_file = tempfile.NamedTemporaryFile()
462 signing_file.write(str(raw_data_to_sign))
463 signing_file.flush()
David Zeuthene3cadca2017-02-22 21:25:46 -0500464 p = subprocess.Popen(
David Zeuthena156d3d2017-06-01 12:08:09 -0400465 [signing_helper_with_files, algorithm_name, key_path, signing_file.name])
466 retcode = p.wait()
467 if retcode != 0:
468 raise AvbError('Error signing')
469 signing_file.seek(0)
470 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400472 if signing_helper is not None:
473 p = subprocess.Popen(
474 [signing_helper, algorithm_name, key_path],
475 stdin=subprocess.PIPE,
476 stdout=subprocess.PIPE,
477 stderr=subprocess.PIPE)
478 else:
479 p = subprocess.Popen(
480 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
481 stdin=subprocess.PIPE,
482 stdout=subprocess.PIPE,
483 stderr=subprocess.PIPE)
484 (pout, perr) = p.communicate(str(raw_data_to_sign))
485 retcode = p.wait()
486 if retcode != 0:
487 raise AvbError('Error signing: {}'.format(perr))
488 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900489 if len(signature) != signature_num_bytes:
490 raise AvbError('Error signing: Invalid length of signature')
491 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800492
493
David Zeuthenb623d8b2017-04-04 16:05:53 -0400494def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
495 """Checks that the signature in a vbmeta blob was made by
496 the embedded public key.
497
498 Arguments:
499 vbmeta_header: A AvbVBMetaHeader.
500 vbmeta_blob: The whole vbmeta blob, including the header.
501
502 Returns:
503 True if the signature is valid and corresponds to the embedded
504 public key. Also returns True if the vbmeta blob is not signed.
505 """
506 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
507 if alg.hash_name == '':
508 return True
509 header_blob = vbmeta_blob[0:256]
510 auth_offset = 256
511 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
512 aux_size = vbmeta_header.auxiliary_data_block_size
513 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
514 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
515 pubkey_size = vbmeta_header.public_key_size
516 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
517
518 digest_offset = auth_offset + vbmeta_header.hash_offset
519 digest_size = vbmeta_header.hash_size
520 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
521
522 sig_offset = auth_offset + vbmeta_header.signature_offset
523 sig_size = vbmeta_header.signature_size
524 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
525
526 # Now that we've got the stored digest, public key, and signature
527 # all we need to do is to verify. This is the exactly the same
528 # steps as performed in the avb_vbmeta_image_verify() function in
529 # libavb/avb_vbmeta_image.c.
530
531 ha = hashlib.new(alg.hash_name)
532 ha.update(header_blob)
533 ha.update(aux_blob)
534 computed_digest = ha.digest()
535
536 if computed_digest != digest_blob:
537 return False
538
539 padding_and_digest = bytearray(alg.padding)
540 padding_and_digest.extend(computed_digest)
541
542 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
543 modulus_blob = pubkey_blob[8:8 + num_bits/8]
544 modulus = decode_long(modulus_blob)
545 exponent = 65537
546
547 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
548 # is OK since 'avbtool verify_image' is not expected to run on the
549 # Android builders (see bug #36809096).
550 import Crypto.PublicKey.RSA
551 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
552 if not key.verify(decode_long(padding_and_digest),
553 (decode_long(sig_blob), None)):
554 return False
555 return True
556
557
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558class ImageChunk(object):
559 """Data structure used for representing chunks in Android sparse files.
560
561 Attributes:
562 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
563 chunk_offset: Offset in the sparse file where this chunk begins.
564 output_offset: Offset in de-sparsified file where output begins.
565 output_size: Number of bytes in output.
566 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
567 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
568 """
569
570 FORMAT = '<2H2I'
571 TYPE_RAW = 0xcac1
572 TYPE_FILL = 0xcac2
573 TYPE_DONT_CARE = 0xcac3
574 TYPE_CRC32 = 0xcac4
575
576 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
577 input_offset, fill_data):
578 """Initializes an ImageChunk object.
579
580 Arguments:
581 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
582 chunk_offset: Offset in the sparse file where this chunk begins.
583 output_offset: Offset in de-sparsified file.
584 output_size: Number of bytes in output.
585 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
586 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
587
588 Raises:
589 ValueError: If data is not well-formed.
590 """
591 self.chunk_type = chunk_type
592 self.chunk_offset = chunk_offset
593 self.output_offset = output_offset
594 self.output_size = output_size
595 self.input_offset = input_offset
596 self.fill_data = fill_data
597 # Check invariants.
598 if self.chunk_type == self.TYPE_RAW:
599 if self.fill_data is not None:
600 raise ValueError('RAW chunk cannot have fill_data set.')
601 if not self.input_offset:
602 raise ValueError('RAW chunk must have input_offset set.')
603 elif self.chunk_type == self.TYPE_FILL:
604 if self.fill_data is None:
605 raise ValueError('FILL chunk must have fill_data set.')
606 if self.input_offset:
607 raise ValueError('FILL chunk cannot have input_offset set.')
608 elif self.chunk_type == self.TYPE_DONT_CARE:
609 if self.fill_data is not None:
610 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
611 if self.input_offset:
612 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
613 else:
614 raise ValueError('Invalid chunk type')
615
616
617class ImageHandler(object):
618 """Abstraction for image I/O with support for Android sparse images.
619
620 This class provides an interface for working with image files that
621 may be using the Android Sparse Image format. When an instance is
622 constructed, we test whether it's an Android sparse file. If so,
623 operations will be on the sparse file by interpreting the sparse
624 format, otherwise they will be directly on the file. Either way the
625 operations do the same.
626
627 For reading, this interface mimics a file object - it has seek(),
628 tell(), and read() methods. For writing, only truncation
629 (truncate()) and appending is supported (append_raw() and
630 append_dont_care()). Additionally, data can only be written in units
631 of the block size.
632
633 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400634 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400635 is_sparse: Whether the file being operated on is sparse.
636 block_size: The block size, typically 4096.
637 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400638 """
639 # See system/core/libsparse/sparse_format.h for details.
640 MAGIC = 0xed26ff3a
641 HEADER_FORMAT = '<I4H4I'
642
643 # These are formats and offset of just the |total_chunks| and
644 # |total_blocks| fields.
645 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
646 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
647
648 def __init__(self, image_filename):
649 """Initializes an image handler.
650
651 Arguments:
652 image_filename: The name of the file to operate on.
653
654 Raises:
655 ValueError: If data in the file is invalid.
656 """
David Zeuthen49936b42018-08-07 17:38:58 -0400657 self.filename = image_filename
David Zeuthena4fee8b2016-08-22 15:20:43 -0400658 self._read_header()
659
660 def _read_header(self):
661 """Initializes internal data structures used for reading file.
662
663 This may be called multiple times and is typically called after
664 modifying the file (e.g. appending, truncation).
665
666 Raises:
667 ValueError: If data in the file is invalid.
668 """
669 self.is_sparse = False
670 self.block_size = 4096
671 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400672 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400673 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400674 self.image_size = self._image.tell()
675
676 self._image.seek(0, os.SEEK_SET)
677 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
678 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
679 block_size, self._num_total_blocks, self._num_total_chunks,
680 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
681 if magic != self.MAGIC:
682 # Not a sparse image, our job here is done.
683 return
684 if not (major_version == 1 and minor_version == 0):
685 raise ValueError('Encountered sparse image format version {}.{} but '
686 'only 1.0 is supported'.format(major_version,
687 minor_version))
688 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
689 raise ValueError('Unexpected file_hdr_sz value {}.'.
690 format(file_hdr_sz))
691 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
692 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
693 format(chunk_hdr_sz))
694
695 self.block_size = block_size
696
697 # Build an list of chunks by parsing the file.
698 self._chunks = []
699
700 # Find the smallest offset where only "Don't care" chunks
701 # follow. This will be the size of the content in the sparse
702 # image.
703 offset = 0
704 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 for _ in xrange(1, self._num_total_chunks + 1):
706 chunk_offset = self._image.tell()
707
708 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
709 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
710 header_bin)
711 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
712
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 if chunk_type == ImageChunk.TYPE_RAW:
714 if data_sz != (chunk_sz * self.block_size):
715 raise ValueError('Raw chunk input size ({}) does not match output '
716 'size ({})'.
717 format(data_sz, chunk_sz*self.block_size))
718 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
719 chunk_offset,
720 output_offset,
721 chunk_sz*self.block_size,
722 self._image.tell(),
723 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700724 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400725
726 elif chunk_type == ImageChunk.TYPE_FILL:
727 if data_sz != 4:
728 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
729 'has {}'.format(data_sz))
730 fill_data = self._image.read(4)
731 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
732 chunk_offset,
733 output_offset,
734 chunk_sz*self.block_size,
735 None,
736 fill_data))
737 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
738 if data_sz != 0:
739 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
740 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400741 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
742 chunk_offset,
743 output_offset,
744 chunk_sz*self.block_size,
745 None,
746 None))
747 elif chunk_type == ImageChunk.TYPE_CRC32:
748 if data_sz != 4:
749 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
750 'this has {}'.format(data_sz))
751 self._image.read(4)
752 else:
753 raise ValueError('Unknown chunk type {}'.format(chunk_type))
754
755 offset += chunk_sz
756 output_offset += chunk_sz*self.block_size
757
758 # Record where sparse data end.
759 self._sparse_end = self._image.tell()
760
761 # Now that we've traversed all chunks, sanity check.
762 if self._num_total_blocks != offset:
763 raise ValueError('The header said we should have {} output blocks, '
764 'but we saw {}'.format(self._num_total_blocks, offset))
765 junk_len = len(self._image.read())
766 if junk_len > 0:
767 raise ValueError('There were {} bytes of extra data at the end of the '
768 'file.'.format(junk_len))
769
David Zeuthen09692692016-09-30 16:16:40 -0400770 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400771 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400772
773 # This is used when bisecting in read() to find the initial slice.
774 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
775
776 self.is_sparse = True
777
778 def _update_chunks_and_blocks(self):
779 """Helper function to update the image header.
780
781 The the |total_chunks| and |total_blocks| fields in the header
782 will be set to value of the |_num_total_blocks| and
783 |_num_total_chunks| attributes.
784
785 """
786 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
787 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
788 self._num_total_blocks,
789 self._num_total_chunks))
790
791 def append_dont_care(self, num_bytes):
792 """Appends a DONT_CARE chunk to the sparse file.
793
794 The given number of bytes must be a multiple of the block size.
795
796 Arguments:
797 num_bytes: Size in number of bytes of the DONT_CARE chunk.
798 """
799 assert num_bytes % self.block_size == 0
800
801 if not self.is_sparse:
802 self._image.seek(0, os.SEEK_END)
803 # This is more efficient that writing NUL bytes since it'll add
804 # a hole on file systems that support sparse files (native
805 # sparse, not Android sparse).
806 self._image.truncate(self._image.tell() + num_bytes)
807 self._read_header()
808 return
809
810 self._num_total_chunks += 1
811 self._num_total_blocks += num_bytes / self.block_size
812 self._update_chunks_and_blocks()
813
814 self._image.seek(self._sparse_end, os.SEEK_SET)
815 self._image.write(struct.pack(ImageChunk.FORMAT,
816 ImageChunk.TYPE_DONT_CARE,
817 0, # Reserved
818 num_bytes / self.block_size,
819 struct.calcsize(ImageChunk.FORMAT)))
820 self._read_header()
821
822 def append_raw(self, data):
823 """Appends a RAW chunk to the sparse file.
824
825 The length of the given data must be a multiple of the block size.
826
827 Arguments:
828 data: Data to append.
829 """
830 assert len(data) % self.block_size == 0
831
832 if not self.is_sparse:
833 self._image.seek(0, os.SEEK_END)
834 self._image.write(data)
835 self._read_header()
836 return
837
838 self._num_total_chunks += 1
839 self._num_total_blocks += len(data) / self.block_size
840 self._update_chunks_and_blocks()
841
842 self._image.seek(self._sparse_end, os.SEEK_SET)
843 self._image.write(struct.pack(ImageChunk.FORMAT,
844 ImageChunk.TYPE_RAW,
845 0, # Reserved
846 len(data) / self.block_size,
847 len(data) +
848 struct.calcsize(ImageChunk.FORMAT)))
849 self._image.write(data)
850 self._read_header()
851
852 def append_fill(self, fill_data, size):
853 """Appends a fill chunk to the sparse file.
854
855 The total length of the fill data must be a multiple of the block size.
856
857 Arguments:
858 fill_data: Fill data to append - must be four bytes.
859 size: Number of chunk - must be a multiple of four and the block size.
860 """
861 assert len(fill_data) == 4
862 assert size % 4 == 0
863 assert size % self.block_size == 0
864
865 if not self.is_sparse:
866 self._image.seek(0, os.SEEK_END)
867 self._image.write(fill_data * (size/4))
868 self._read_header()
869 return
870
871 self._num_total_chunks += 1
872 self._num_total_blocks += size / self.block_size
873 self._update_chunks_and_blocks()
874
875 self._image.seek(self._sparse_end, os.SEEK_SET)
876 self._image.write(struct.pack(ImageChunk.FORMAT,
877 ImageChunk.TYPE_FILL,
878 0, # Reserved
879 size / self.block_size,
880 4 + struct.calcsize(ImageChunk.FORMAT)))
881 self._image.write(fill_data)
882 self._read_header()
883
884 def seek(self, offset):
885 """Sets the cursor position for reading from unsparsified file.
886
887 Arguments:
888 offset: Offset to seek to from the beginning of the file.
889 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700890 if offset < 0:
891 raise RuntimeError("Seeking with negative offset: %d" % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400892 self._file_pos = offset
893
894 def read(self, size):
895 """Reads data from the unsparsified file.
896
897 This method may return fewer than |size| bytes of data if the end
898 of the file was encountered.
899
900 The file cursor for reading is advanced by the number of bytes
901 read.
902
903 Arguments:
904 size: Number of bytes to read.
905
906 Returns:
907 The data.
908
909 """
910 if not self.is_sparse:
911 self._image.seek(self._file_pos)
912 data = self._image.read(size)
913 self._file_pos += len(data)
914 return data
915
916 # Iterate over all chunks.
917 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
918 self._file_pos) - 1
919 data = bytearray()
920 to_go = size
921 while to_go > 0:
922 chunk = self._chunks[chunk_idx]
923 chunk_pos_offset = self._file_pos - chunk.output_offset
924 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
925
926 if chunk.chunk_type == ImageChunk.TYPE_RAW:
927 self._image.seek(chunk.input_offset + chunk_pos_offset)
928 data.extend(self._image.read(chunk_pos_to_go))
929 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
930 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
931 offset_mod = chunk_pos_offset % len(chunk.fill_data)
932 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
933 else:
934 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
935 data.extend('\0' * chunk_pos_to_go)
936
937 to_go -= chunk_pos_to_go
938 self._file_pos += chunk_pos_to_go
939 chunk_idx += 1
940 # Generate partial read in case of EOF.
941 if chunk_idx >= len(self._chunks):
942 break
943
944 return data
945
946 def tell(self):
947 """Returns the file cursor position for reading from unsparsified file.
948
949 Returns:
950 The file cursor position for reading.
951 """
952 return self._file_pos
953
954 def truncate(self, size):
955 """Truncates the unsparsified file.
956
957 Arguments:
958 size: Desired size of unsparsified file.
959
960 Raises:
961 ValueError: If desired size isn't a multiple of the block size.
962 """
963 if not self.is_sparse:
964 self._image.truncate(size)
965 self._read_header()
966 return
967
968 if size % self.block_size != 0:
969 raise ValueError('Cannot truncate to a size which is not a multiple '
970 'of the block size')
971
972 if size == self.image_size:
973 # Trivial where there's nothing to do.
974 return
975 elif size < self.image_size:
976 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
977 chunk = self._chunks[chunk_idx]
978 if chunk.output_offset != size:
979 # Truncation in the middle of a trunk - need to keep the chunk
980 # and modify it.
981 chunk_idx_for_update = chunk_idx + 1
982 num_to_keep = size - chunk.output_offset
983 assert num_to_keep % self.block_size == 0
984 if chunk.chunk_type == ImageChunk.TYPE_RAW:
985 truncate_at = (chunk.chunk_offset +
986 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
987 data_sz = num_to_keep
988 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
989 truncate_at = (chunk.chunk_offset +
990 struct.calcsize(ImageChunk.FORMAT) + 4)
991 data_sz = 4
992 else:
993 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
994 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
995 data_sz = 0
996 chunk_sz = num_to_keep/self.block_size
997 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
998 self._image.seek(chunk.chunk_offset)
999 self._image.write(struct.pack(ImageChunk.FORMAT,
1000 chunk.chunk_type,
1001 0, # Reserved
1002 chunk_sz,
1003 total_sz))
1004 chunk.output_size = num_to_keep
1005 else:
1006 # Truncation at trunk boundary.
1007 truncate_at = chunk.chunk_offset
1008 chunk_idx_for_update = chunk_idx
1009
1010 self._num_total_chunks = chunk_idx_for_update
1011 self._num_total_blocks = 0
1012 for i in range(0, chunk_idx_for_update):
1013 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1014 self._update_chunks_and_blocks()
1015 self._image.truncate(truncate_at)
1016
1017 # We've modified the file so re-read all data.
1018 self._read_header()
1019 else:
1020 # Truncating to grow - just add a DONT_CARE section.
1021 self.append_dont_care(size - self.image_size)
1022
1023
David Zeuthen21e95262016-07-27 17:58:40 -04001024class AvbDescriptor(object):
1025 """Class for AVB descriptor.
1026
1027 See the |AvbDescriptor| C struct for more information.
1028
1029 Attributes:
1030 tag: The tag identifying what kind of descriptor this is.
1031 data: The data in the descriptor.
1032 """
1033
1034 SIZE = 16
1035 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1036
1037 def __init__(self, data):
1038 """Initializes a new property descriptor.
1039
1040 Arguments:
1041 data: If not None, must be a bytearray().
1042
1043 Raises:
1044 LookupError: If the given descriptor is malformed.
1045 """
1046 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1047
1048 if data:
1049 (self.tag, num_bytes_following) = (
1050 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1051 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1052 else:
1053 self.tag = None
1054 self.data = None
1055
1056 def print_desc(self, o):
1057 """Print the descriptor.
1058
1059 Arguments:
1060 o: The object to write the output to.
1061 """
1062 o.write(' Unknown descriptor:\n')
1063 o.write(' Tag: {}\n'.format(self.tag))
1064 if len(self.data) < 256:
1065 o.write(' Data: {} ({} bytes)\n'.format(
1066 repr(str(self.data)), len(self.data)))
1067 else:
1068 o.write(' Data: {} bytes\n'.format(len(self.data)))
1069
1070 def encode(self):
1071 """Serializes the descriptor.
1072
1073 Returns:
1074 A bytearray() with the descriptor data.
1075 """
1076 num_bytes_following = len(self.data)
1077 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1078 padding_size = nbf_with_padding - num_bytes_following
1079 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1080 padding = struct.pack(str(padding_size) + 'x')
1081 ret = desc + self.data + padding
1082 return bytearray(ret)
1083
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001084 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1085 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001086 """Verifies contents of the descriptor - used in verify_image sub-command.
1087
1088 Arguments:
1089 image_dir: The directory of the file being verified.
1090 image_ext: The extension of the file being verified (e.g. '.img').
1091 expected_chain_partitions_map: A map from partition name to the
1092 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001093 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001094
1095 Returns:
1096 True if the descriptor verifies, False otherwise.
1097 """
1098 # Nothing to do.
1099 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001100
1101class AvbPropertyDescriptor(AvbDescriptor):
1102 """A class for property descriptors.
1103
1104 See the |AvbPropertyDescriptor| C struct for more information.
1105
1106 Attributes:
1107 key: The key.
1108 value: The key.
1109 """
1110
1111 TAG = 0
1112 SIZE = 32
1113 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1114 'Q' # key size (bytes)
1115 'Q') # value size (bytes)
1116
1117 def __init__(self, data=None):
1118 """Initializes a new property descriptor.
1119
1120 Arguments:
1121 data: If not None, must be a bytearray of size |SIZE|.
1122
1123 Raises:
1124 LookupError: If the given descriptor is malformed.
1125 """
1126 AvbDescriptor.__init__(self, None)
1127 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1128
1129 if data:
1130 (tag, num_bytes_following, key_size,
1131 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1132 expected_size = round_to_multiple(
1133 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1134 if tag != self.TAG or num_bytes_following != expected_size:
1135 raise LookupError('Given data does not look like a property '
1136 'descriptor.')
1137 self.key = data[self.SIZE:(self.SIZE + key_size)]
1138 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1139 value_size)]
1140 else:
1141 self.key = ''
1142 self.value = ''
1143
1144 def print_desc(self, o):
1145 """Print the descriptor.
1146
1147 Arguments:
1148 o: The object to write the output to.
1149 """
1150 if len(self.value) < 256:
1151 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1152 else:
1153 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1154
1155 def encode(self):
1156 """Serializes the descriptor.
1157
1158 Returns:
1159 A bytearray() with the descriptor data.
1160 """
1161 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1162 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1163 padding_size = nbf_with_padding - num_bytes_following
1164 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1165 len(self.key), len(self.value))
1166 padding = struct.pack(str(padding_size) + 'x')
1167 ret = desc + self.key + '\0' + self.value + '\0' + padding
1168 return bytearray(ret)
1169
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001170 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1171 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001172 """Verifies contents of the descriptor - used in verify_image sub-command.
1173
1174 Arguments:
1175 image_dir: The directory of the file being verified.
1176 image_ext: The extension of the file being verified (e.g. '.img').
1177 expected_chain_partitions_map: A map from partition name to the
1178 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001179 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001180
1181 Returns:
1182 True if the descriptor verifies, False otherwise.
1183 """
1184 # Nothing to do.
1185 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001186
1187class AvbHashtreeDescriptor(AvbDescriptor):
1188 """A class for hashtree descriptors.
1189
1190 See the |AvbHashtreeDescriptor| C struct for more information.
1191
1192 Attributes:
1193 dm_verity_version: dm-verity version used.
1194 image_size: Size of the image, after rounding up to |block_size|.
1195 tree_offset: Offset of the hash tree in the file.
1196 tree_size: Size of the tree.
1197 data_block_size: Data block size
1198 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001199 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1200 fec_offset: Offset of FEC data (0 if FEC is not used).
1201 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001202 hash_algorithm: Hash algorithm used.
1203 partition_name: Partition name.
1204 salt: Salt used.
1205 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001206 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001207 """
1208
1209 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001210 RESERVED = 60
1211 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001212 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1213 'L' # dm-verity version used
1214 'Q' # image size (bytes)
1215 'Q' # tree offset (bytes)
1216 'Q' # tree size (bytes)
1217 'L' # data block size (bytes)
1218 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001219 'L' # FEC number of roots
1220 'Q' # FEC offset (bytes)
1221 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001222 '32s' # hash algorithm used
1223 'L' # partition name (bytes)
1224 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001225 'L' # root digest length (bytes)
1226 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001227 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001228
1229 def __init__(self, data=None):
1230 """Initializes a new hashtree descriptor.
1231
1232 Arguments:
1233 data: If not None, must be a bytearray of size |SIZE|.
1234
1235 Raises:
1236 LookupError: If the given descriptor is malformed.
1237 """
1238 AvbDescriptor.__init__(self, None)
1239 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1240
1241 if data:
1242 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1243 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001244 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1245 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001246 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1247 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001248 expected_size = round_to_multiple(
1249 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1250 if tag != self.TAG or num_bytes_following != expected_size:
1251 raise LookupError('Given data does not look like a hashtree '
1252 'descriptor.')
1253 # Nuke NUL-bytes at the end.
1254 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1255 o = 0
1256 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1257 partition_name_len)])
1258 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1259 self.partition_name.decode('utf-8')
1260 o += partition_name_len
1261 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1262 o += salt_len
1263 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1264 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001265 if root_digest_len != 0:
1266 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001267
1268 else:
1269 self.dm_verity_version = 0
1270 self.image_size = 0
1271 self.tree_offset = 0
1272 self.tree_size = 0
1273 self.data_block_size = 0
1274 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001275 self.fec_num_roots = 0
1276 self.fec_offset = 0
1277 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001278 self.hash_algorithm = ''
1279 self.partition_name = ''
1280 self.salt = bytearray()
1281 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001282 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001283
1284 def print_desc(self, o):
1285 """Print the descriptor.
1286
1287 Arguments:
1288 o: The object to write the output to.
1289 """
1290 o.write(' Hashtree descriptor:\n')
1291 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1292 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1293 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1294 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1295 o.write(' Data Block Size: {} bytes\n'.format(
1296 self.data_block_size))
1297 o.write(' Hash Block Size: {} bytes\n'.format(
1298 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001299 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1300 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1301 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001302 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1303 o.write(' Partition Name: {}\n'.format(self.partition_name))
1304 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1305 'hex')))
1306 o.write(' Root Digest: {}\n'.format(str(
1307 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001308 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001309
1310 def encode(self):
1311 """Serializes the descriptor.
1312
1313 Returns:
1314 A bytearray() with the descriptor data.
1315 """
1316 encoded_name = self.partition_name.encode('utf-8')
1317 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1318 len(self.root_digest) - 16)
1319 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1320 padding_size = nbf_with_padding - num_bytes_following
1321 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1322 self.dm_verity_version, self.image_size,
1323 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001324 self.hash_block_size, self.fec_num_roots,
1325 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001326 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001327 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001328 padding = struct.pack(str(padding_size) + 'x')
1329 ret = desc + encoded_name + self.salt + self.root_digest + padding
1330 return bytearray(ret)
1331
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001332 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1333 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001334 """Verifies contents of the descriptor - used in verify_image sub-command.
1335
1336 Arguments:
1337 image_dir: The directory of the file being verified.
1338 image_ext: The extension of the file being verified (e.g. '.img').
1339 expected_chain_partitions_map: A map from partition name to the
1340 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001341 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001342
1343 Returns:
1344 True if the descriptor verifies, False otherwise.
1345 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001346 if self.partition_name == '':
1347 image = image_containing_descriptor
1348 else:
1349 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1350 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001351 # Generate the hashtree and checks that it matches what's in the file.
1352 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1353 digest_padding = round_to_pow2(digest_size) - digest_size
1354 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1355 self.image_size, self.data_block_size, digest_size + digest_padding)
1356 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1357 self.data_block_size,
1358 self.hash_algorithm, self.salt,
1359 digest_padding,
1360 hash_level_offsets,
1361 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001362 # The root digest must match unless it is not embedded in the descriptor.
1363 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001364 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1365 format(image_filename))
1366 return False
1367 # ... also check that the on-disk hashtree matches
1368 image.seek(self.tree_offset)
1369 hash_tree_ondisk = image.read(self.tree_size)
1370 if hash_tree != hash_tree_ondisk:
1371 sys.stderr.write('hashtree of {} contains invalid data\n'.
1372 format(image_filename))
1373 return False
1374 # TODO: we could also verify that the FEC stored in the image is
1375 # correct but this a) currently requires the 'fec' binary; and b)
1376 # takes a long time; and c) is not strictly needed for
1377 # verification purposes as we've already verified the root hash.
1378 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001379 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001380 self.image_size))
1381 return True
1382
David Zeuthen21e95262016-07-27 17:58:40 -04001383
1384class AvbHashDescriptor(AvbDescriptor):
1385 """A class for hash descriptors.
1386
1387 See the |AvbHashDescriptor| C struct for more information.
1388
1389 Attributes:
1390 image_size: Image size, in bytes.
1391 hash_algorithm: Hash algorithm used.
1392 partition_name: Partition name.
1393 salt: Salt used.
1394 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001395 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001396 """
1397
1398 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001399 RESERVED = 60
1400 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001401 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1402 'Q' # image size (bytes)
1403 '32s' # hash algorithm used
1404 'L' # partition name (bytes)
1405 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001406 'L' # digest length (bytes)
1407 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001408 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001409
1410 def __init__(self, data=None):
1411 """Initializes a new hash descriptor.
1412
1413 Arguments:
1414 data: If not None, must be a bytearray of size |SIZE|.
1415
1416 Raises:
1417 LookupError: If the given descriptor is malformed.
1418 """
1419 AvbDescriptor.__init__(self, None)
1420 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1421
1422 if data:
1423 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1424 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001425 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1426 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001427 expected_size = round_to_multiple(
1428 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1429 if tag != self.TAG or num_bytes_following != expected_size:
1430 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1431 # Nuke NUL-bytes at the end.
1432 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1433 o = 0
1434 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1435 partition_name_len)])
1436 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1437 self.partition_name.decode('utf-8')
1438 o += partition_name_len
1439 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1440 o += salt_len
1441 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1442 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001443 if digest_len != 0:
1444 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001445
1446 else:
1447 self.image_size = 0
1448 self.hash_algorithm = ''
1449 self.partition_name = ''
1450 self.salt = bytearray()
1451 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001452 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001453
1454 def print_desc(self, o):
1455 """Print the descriptor.
1456
1457 Arguments:
1458 o: The object to write the output to.
1459 """
1460 o.write(' Hash descriptor:\n')
1461 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1462 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1463 o.write(' Partition Name: {}\n'.format(self.partition_name))
1464 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1465 'hex')))
1466 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1467 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001468 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001469
1470 def encode(self):
1471 """Serializes the descriptor.
1472
1473 Returns:
1474 A bytearray() with the descriptor data.
1475 """
1476 encoded_name = self.partition_name.encode('utf-8')
1477 num_bytes_following = (
1478 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1479 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1480 padding_size = nbf_with_padding - num_bytes_following
1481 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1482 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001483 len(self.salt), len(self.digest), self.flags,
1484 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001485 padding = struct.pack(str(padding_size) + 'x')
1486 ret = desc + encoded_name + self.salt + self.digest + padding
1487 return bytearray(ret)
1488
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001489 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1490 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001491 """Verifies contents of the descriptor - used in verify_image sub-command.
1492
1493 Arguments:
1494 image_dir: The directory of the file being verified.
1495 image_ext: The extension of the file being verified (e.g. '.img').
1496 expected_chain_partitions_map: A map from partition name to the
1497 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001498 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001499
1500 Returns:
1501 True if the descriptor verifies, False otherwise.
1502 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001503 if self.partition_name == '':
1504 image = image_containing_descriptor
1505 else:
1506 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1507 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001508 data = image.read(self.image_size)
1509 ha = hashlib.new(self.hash_algorithm)
1510 ha.update(self.salt)
1511 ha.update(data)
1512 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001513 # The digest must match unless there is no digest in the descriptor.
1514 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001515 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1516 format(self.hash_algorithm, image_filename))
1517 return False
1518 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001519 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001520 self.image_size))
1521 return True
1522
David Zeuthen21e95262016-07-27 17:58:40 -04001523
1524class AvbKernelCmdlineDescriptor(AvbDescriptor):
1525 """A class for kernel command-line descriptors.
1526
1527 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1528
1529 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001530 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001531 kernel_cmdline: The kernel command-line.
1532 """
1533
1534 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001535 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001536 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001537 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001538 'L') # cmdline length (bytes)
1539
David Zeuthenfd41eb92016-11-17 12:24:47 -05001540 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1541 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1542
David Zeuthen21e95262016-07-27 17:58:40 -04001543 def __init__(self, data=None):
1544 """Initializes a new kernel cmdline descriptor.
1545
1546 Arguments:
1547 data: If not None, must be a bytearray of size |SIZE|.
1548
1549 Raises:
1550 LookupError: If the given descriptor is malformed.
1551 """
1552 AvbDescriptor.__init__(self, None)
1553 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1554
1555 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001556 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001557 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1558 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1559 8)
1560 if tag != self.TAG or num_bytes_following != expected_size:
1561 raise LookupError('Given data does not look like a kernel cmdline '
1562 'descriptor.')
1563 # Nuke NUL-bytes at the end.
1564 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1565 kernel_cmdline_length)])
1566 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1567 self.kernel_cmdline.decode('utf-8')
1568 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001569 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001570 self.kernel_cmdline = ''
1571
1572 def print_desc(self, o):
1573 """Print the descriptor.
1574
1575 Arguments:
1576 o: The object to write the output to.
1577 """
1578 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001579 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001580 o.write(' Kernel Cmdline: {}\n'.format(repr(
1581 self.kernel_cmdline)))
1582
1583 def encode(self):
1584 """Serializes the descriptor.
1585
1586 Returns:
1587 A bytearray() with the descriptor data.
1588 """
1589 encoded_str = self.kernel_cmdline.encode('utf-8')
1590 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1591 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1592 padding_size = nbf_with_padding - num_bytes_following
1593 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001594 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001595 padding = struct.pack(str(padding_size) + 'x')
1596 ret = desc + encoded_str + padding
1597 return bytearray(ret)
1598
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001599 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1600 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001601 """Verifies contents of the descriptor - used in verify_image sub-command.
1602
1603 Arguments:
1604 image_dir: The directory of the file being verified.
1605 image_ext: The extension of the file being verified (e.g. '.img').
1606 expected_chain_partitions_map: A map from partition name to the
1607 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001608 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001609
1610 Returns:
1611 True if the descriptor verifies, False otherwise.
1612 """
1613 # Nothing to verify.
1614 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001615
1616class AvbChainPartitionDescriptor(AvbDescriptor):
1617 """A class for chained partition descriptors.
1618
1619 See the |AvbChainPartitionDescriptor| C struct for more information.
1620
1621 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001622 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001623 partition_name: Partition name.
1624 public_key: Bytes for the public key.
1625 """
1626
1627 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001628 RESERVED = 64
1629 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001630 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001631 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001632 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001633 'L' + # public_key_size (bytes)
1634 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001635
1636 def __init__(self, data=None):
1637 """Initializes a new chain partition descriptor.
1638
1639 Arguments:
1640 data: If not None, must be a bytearray of size |SIZE|.
1641
1642 Raises:
1643 LookupError: If the given descriptor is malformed.
1644 """
1645 AvbDescriptor.__init__(self, None)
1646 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1647
1648 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001649 (tag, num_bytes_following, self.rollback_index_location,
1650 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001651 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001652 expected_size = round_to_multiple(
1653 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1654 if tag != self.TAG or num_bytes_following != expected_size:
1655 raise LookupError('Given data does not look like a chain partition '
1656 'descriptor.')
1657 o = 0
1658 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1659 partition_name_len)])
1660 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1661 self.partition_name.decode('utf-8')
1662 o += partition_name_len
1663 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1664
1665 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001666 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001667 self.partition_name = ''
1668 self.public_key = bytearray()
1669
1670 def print_desc(self, o):
1671 """Print the descriptor.
1672
1673 Arguments:
1674 o: The object to write the output to.
1675 """
1676 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001677 o.write(' Partition Name: {}\n'.format(self.partition_name))
1678 o.write(' Rollback Index Location: {}\n'.format(
1679 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001680 # Just show the SHA1 of the key, for size reasons.
1681 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001682 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001683
1684 def encode(self):
1685 """Serializes the descriptor.
1686
1687 Returns:
1688 A bytearray() with the descriptor data.
1689 """
1690 encoded_name = self.partition_name.encode('utf-8')
1691 num_bytes_following = (
1692 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1693 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1694 padding_size = nbf_with_padding - num_bytes_following
1695 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001696 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001697 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001698 padding = struct.pack(str(padding_size) + 'x')
1699 ret = desc + encoded_name + self.public_key + padding
1700 return bytearray(ret)
1701
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001702 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1703 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001704 """Verifies contents of the descriptor - used in verify_image sub-command.
1705
1706 Arguments:
1707 image_dir: The directory of the file being verified.
1708 image_ext: The extension of the file being verified (e.g. '.img').
1709 expected_chain_partitions_map: A map from partition name to the
1710 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001711 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001712
1713 Returns:
1714 True if the descriptor verifies, False otherwise.
1715 """
1716 value = expected_chain_partitions_map.get(self.partition_name)
1717 if not value:
1718 sys.stderr.write('No expected chain partition for partition {}. Use '
1719 '--expected_chain_partition to specify expected '
1720 'contents.\n'.
1721 format(self.partition_name))
1722 return False
1723 rollback_index_location, pk_blob = value
1724
1725 if self.rollback_index_location != rollback_index_location:
1726 sys.stderr.write('Expected rollback_index_location {} does not '
1727 'match {} in descriptor for partition {}\n'.
1728 format(rollback_index_location,
1729 self.rollback_index_location,
1730 self.partition_name))
1731 return False
1732
1733 if self.public_key != pk_blob:
1734 sys.stderr.write('Expected public key blob does not match public '
1735 'key blob in descriptor for partition {}\n'.
1736 format(self.partition_name))
1737 return False
1738
1739 print ('{}: Successfully verified chain partition descriptor matches '
1740 'expected data'.format(self.partition_name))
1741
1742 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001743
1744DESCRIPTOR_CLASSES = [
1745 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1746 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1747]
1748
1749
1750def parse_descriptors(data):
1751 """Parses a blob of data into descriptors.
1752
1753 Arguments:
1754 data: A bytearray() with encoded descriptors.
1755
1756 Returns:
1757 A list of instances of objects derived from AvbDescriptor. For
1758 unknown descriptors, the class AvbDescriptor is used.
1759 """
1760 o = 0
1761 ret = []
1762 while o < len(data):
1763 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1764 if tag < len(DESCRIPTOR_CLASSES):
1765 c = DESCRIPTOR_CLASSES[tag]
1766 else:
1767 c = AvbDescriptor
1768 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1769 o += 16 + nb_following
1770 return ret
1771
1772
1773class AvbFooter(object):
1774 """A class for parsing and writing footers.
1775
1776 Footers are stored at the end of partitions and point to where the
1777 AvbVBMeta blob is located. They also contain the original size of
1778 the image before AVB information was added.
1779
1780 Attributes:
1781 magic: Magic for identifying the footer, see |MAGIC|.
1782 version_major: The major version of avbtool that wrote the footer.
1783 version_minor: The minor version of avbtool that wrote the footer.
1784 original_image_size: Original image size.
1785 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1786 vbmeta_size: Size of the AvbVBMeta blob.
1787 """
1788
1789 MAGIC = 'AVBf'
1790 SIZE = 64
1791 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001792 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1793 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001794 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1795 'Q' # Original image size.
1796 'Q' # Offset of VBMeta blob.
1797 'Q' + # Size of VBMeta blob.
1798 str(RESERVED) + 'x') # padding for reserved bytes
1799
1800 def __init__(self, data=None):
1801 """Initializes a new footer object.
1802
1803 Arguments:
1804 data: If not None, must be a bytearray of size 4096.
1805
1806 Raises:
1807 LookupError: If the given footer is malformed.
1808 struct.error: If the given data has no footer.
1809 """
1810 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1811
1812 if data:
1813 (self.magic, self.version_major, self.version_minor,
1814 self.original_image_size, self.vbmeta_offset,
1815 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1816 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001817 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001818 else:
1819 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001820 self.version_major = self.FOOTER_VERSION_MAJOR
1821 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001822 self.original_image_size = 0
1823 self.vbmeta_offset = 0
1824 self.vbmeta_size = 0
1825
David Zeuthena4fee8b2016-08-22 15:20:43 -04001826 def encode(self):
1827 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001828
David Zeuthena4fee8b2016-08-22 15:20:43 -04001829 Returns:
1830 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001831 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001832 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1833 self.version_minor, self.original_image_size,
1834 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001835
1836
1837class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001838 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001839
1840 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07001841 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1842 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04001843 """
1844
1845 SIZE = 256
1846
David Zeuthene3cadca2017-02-22 21:25:46 -05001847 # Keep in sync with |reserved0| and |reserved| field of
1848 # |AvbVBMetaImageHeader|.
1849 RESERVED0 = 4
1850 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001851
1852 # Keep in sync with |AvbVBMetaImageHeader|.
1853 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1854 '2Q' # 2 x block size
1855 'L' # algorithm type
1856 '2Q' # offset, size (hash)
1857 '2Q' # offset, size (signature)
1858 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001859 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001860 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001861 'Q' # rollback_index
1862 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001863 str(RESERVED0) + 'x' + # padding for reserved bytes
1864 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001865 str(RESERVED) + 'x') # padding for reserved bytes
1866
1867 def __init__(self, data=None):
1868 """Initializes a new header object.
1869
1870 Arguments:
1871 data: If not None, must be a bytearray of size 8192.
1872
1873 Raises:
1874 Exception: If the given data is malformed.
1875 """
1876 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1877
1878 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001879 (self.magic, self.required_libavb_version_major,
1880 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001881 self.authentication_data_block_size, self.auxiliary_data_block_size,
1882 self.algorithm_type, self.hash_offset, self.hash_size,
1883 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001884 self.public_key_size, self.public_key_metadata_offset,
1885 self.public_key_metadata_size, self.descriptors_offset,
1886 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001887 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001888 self.flags,
1889 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001890 # Nuke NUL-bytes at the end of the string.
1891 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001892 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001893 else:
1894 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001895 # Start by just requiring version 1.0. Code that adds features
1896 # in a future version can use bump_required_libavb_version_minor() to
1897 # bump the minor.
1898 self.required_libavb_version_major = AVB_VERSION_MAJOR
1899 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001900 self.authentication_data_block_size = 0
1901 self.auxiliary_data_block_size = 0
1902 self.algorithm_type = 0
1903 self.hash_offset = 0
1904 self.hash_size = 0
1905 self.signature_offset = 0
1906 self.signature_size = 0
1907 self.public_key_offset = 0
1908 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001909 self.public_key_metadata_offset = 0
1910 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001911 self.descriptors_offset = 0
1912 self.descriptors_size = 0
1913 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001914 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001915 self.release_string = get_release_string()
1916
1917 def bump_required_libavb_version_minor(self, minor):
1918 """Function to bump required_libavb_version_minor.
1919
1920 Call this when writing data that requires a specific libavb
1921 version to parse it.
1922
1923 Arguments:
1924 minor: The minor version of libavb that has support for the feature.
1925 """
1926 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001927 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001928
1929 def save(self, output):
1930 """Serializes the header (256 bytes) to disk.
1931
1932 Arguments:
1933 output: The object to write the output to.
1934 """
1935 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001936 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1937 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001938 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1939 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001940 self.public_key_offset, self.public_key_size,
1941 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001942 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001943 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001944
1945 def encode(self):
1946 """Serializes the header (256) to a bytearray().
1947
1948 Returns:
1949 A bytearray() with the encoded header.
1950 """
1951 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001952 self.required_libavb_version_major,
1953 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001954 self.authentication_data_block_size,
1955 self.auxiliary_data_block_size, self.algorithm_type,
1956 self.hash_offset, self.hash_size, self.signature_offset,
1957 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001958 self.public_key_size, self.public_key_metadata_offset,
1959 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001960 self.descriptors_size, self.rollback_index, self.flags,
1961 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001962
1963
1964class Avb(object):
1965 """Business logic for avbtool command-line tool."""
1966
David Zeuthen8b6973b2016-09-20 12:39:49 -04001967 # Keep in sync with avb_ab_flow.h.
1968 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1969 AB_MAGIC = '\0AB0'
1970 AB_MAJOR_VERSION = 1
1971 AB_MINOR_VERSION = 0
1972 AB_MISC_METADATA_OFFSET = 2048
1973
David Zeuthen09692692016-09-30 16:16:40 -04001974 # Constants for maximum metadata size. These are used to give
1975 # meaningful errors if the value passed in via --partition_size is
1976 # too small and when --calc_max_image_size is used. We use
1977 # conservative figures.
1978 MAX_VBMETA_SIZE = 64 * 1024
1979 MAX_FOOTER_SIZE = 4096
1980
David Zeuthen49936b42018-08-07 17:38:58 -04001981 def extract_vbmeta_image(self, output, image_filename, padding_size):
1982 """Implements the 'extract_vbmeta_image' command.
1983
1984 Arguments:
1985 output: Write vbmeta struct to this file.
1986 image_filename: File to extract vbmeta data from (with a footer).
1987 padding_size: If not 0, pads output so size is a multiple of the number.
1988
1989 Raises:
1990 AvbError: If there's no footer in the image.
1991 """
1992 image = ImageHandler(image_filename)
1993
1994 (footer, _, _, _) = self._parse_image(image)
1995
1996 if not footer:
1997 raise AvbError('Given image does not have a footer.')
1998
1999 image.seek(footer.vbmeta_offset)
2000 vbmeta_blob = image.read(footer.vbmeta_size)
2001 output.write(vbmeta_blob)
2002
2003 if padding_size > 0:
2004 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2005 padding_needed = padded_size - len(vbmeta_blob)
2006 output.write('\0' * padding_needed)
2007
David Zeuthena4fee8b2016-08-22 15:20:43 -04002008 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002009 """Implements the 'erase_footer' command.
2010
2011 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002012 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002013 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002014
2015 Raises:
2016 AvbError: If there's no footer in the image.
2017 """
2018
David Zeuthena4fee8b2016-08-22 15:20:43 -04002019 image = ImageHandler(image_filename)
2020
David Zeuthen21e95262016-07-27 17:58:40 -04002021 (footer, _, descriptors, _) = self._parse_image(image)
2022
2023 if not footer:
2024 raise AvbError('Given image does not have a footer.')
2025
2026 new_image_size = None
2027 if not keep_hashtree:
2028 new_image_size = footer.original_image_size
2029 else:
2030 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002031 # descriptor to figure out the location and size of the hashtree
2032 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002033 for desc in descriptors:
2034 if isinstance(desc, AvbHashtreeDescriptor):
2035 # The hashtree is always just following the main data so the
2036 # new size is easily derived.
2037 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002038 # If the image has FEC codes, also keep those.
2039 if desc.fec_offset > 0:
2040 fec_end = desc.fec_offset + desc.fec_size
2041 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002042 break
2043 if not new_image_size:
2044 raise AvbError('Requested to keep hashtree but no hashtree '
2045 'descriptor was found.')
2046
2047 # And cut...
2048 image.truncate(new_image_size)
2049
David Zeuthen2bc232b2017-04-19 14:25:19 -04002050 def resize_image(self, image_filename, partition_size):
2051 """Implements the 'resize_image' command.
2052
2053 Arguments:
2054 image_filename: File with footer to resize.
2055 partition_size: The new size of the image.
2056
2057 Raises:
2058 AvbError: If there's no footer in the image.
2059 """
2060
2061 image = ImageHandler(image_filename)
2062
2063 if partition_size % image.block_size != 0:
2064 raise AvbError('Partition size of {} is not a multiple of the image '
2065 'block size {}.'.format(partition_size,
2066 image.block_size))
2067
2068 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2069
2070 if not footer:
2071 raise AvbError('Given image does not have a footer.')
2072
2073 # The vbmeta blob is always at the end of the data so resizing an
2074 # image amounts to just moving the footer around.
2075
2076 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2077 if vbmeta_end_offset % image.block_size != 0:
2078 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2079
2080 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002081 raise AvbError('Requested size of {} is too small for an image '
2082 'of size {}.'
2083 .format(partition_size,
2084 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002085
2086 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2087 # with enough bytes such that the final Footer block is at the end
2088 # of partition_size.
2089 image.truncate(vbmeta_end_offset)
2090 image.append_dont_care(partition_size - vbmeta_end_offset -
2091 1*image.block_size)
2092
2093 # Just reuse the same footer - only difference is that we're
2094 # writing it in a different place.
2095 footer_blob = footer.encode()
2096 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2097 footer_blob)
2098 image.append_raw(footer_blob_with_padding)
2099
David Zeuthen8b6973b2016-09-20 12:39:49 -04002100 def set_ab_metadata(self, misc_image, slot_data):
2101 """Implements the 'set_ab_metadata' command.
2102
2103 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2104 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2105
2106 Arguments:
2107 misc_image: The misc image to write to.
2108 slot_data: Slot data as a string
2109
2110 Raises:
2111 AvbError: If slot data is malformed.
2112 """
2113 tokens = slot_data.split(':')
2114 if len(tokens) != 6:
2115 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2116 a_priority = int(tokens[0])
2117 a_tries_remaining = int(tokens[1])
2118 a_success = True if int(tokens[2]) != 0 else False
2119 b_priority = int(tokens[3])
2120 b_tries_remaining = int(tokens[4])
2121 b_success = True if int(tokens[5]) != 0 else False
2122
2123 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2124 self.AB_MAGIC,
2125 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2126 a_priority, a_tries_remaining, a_success,
2127 b_priority, b_tries_remaining, b_success)
2128 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2129 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2130 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2131 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2132 misc_image.write(ab_data)
2133
David Zeuthena4fee8b2016-08-22 15:20:43 -04002134 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002135 """Implements the 'info_image' command.
2136
2137 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002138 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002139 output: Output file to write human-readable information to (file object).
2140 """
2141
David Zeuthena4fee8b2016-08-22 15:20:43 -04002142 image = ImageHandler(image_filename)
2143
David Zeuthen21e95262016-07-27 17:58:40 -04002144 o = output
2145
2146 (footer, header, descriptors, image_size) = self._parse_image(image)
2147
2148 if footer:
2149 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2150 footer.version_minor))
2151 o.write('Image size: {} bytes\n'.format(image_size))
2152 o.write('Original image size: {} bytes\n'.format(
2153 footer.original_image_size))
2154 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2155 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2156 o.write('--\n')
2157
2158 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2159
David Zeuthene3cadca2017-02-22 21:25:46 -05002160 o.write('Minimum libavb version: {}.{}{}\n'.format(
2161 header.required_libavb_version_major,
2162 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002163 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002164 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2165 o.write('Authentication Block: {} bytes\n'.format(
2166 header.authentication_data_block_size))
2167 o.write('Auxiliary Block: {} bytes\n'.format(
2168 header.auxiliary_data_block_size))
2169 o.write('Algorithm: {}\n'.format(alg_name))
2170 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002171 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002172 o.write('Release String: \'{}\'\n'.format(
2173 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002174
2175 # Print descriptors.
2176 num_printed = 0
2177 o.write('Descriptors:\n')
2178 for desc in descriptors:
2179 desc.print_desc(o)
2180 num_printed += 1
2181 if num_printed == 0:
2182 o.write(' (none)\n')
2183
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002184 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002185 """Implements the 'verify_image' command.
2186
2187 Arguments:
2188 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002189 key_path: None or check that embedded public key matches key at given path.
2190 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002191 """
2192
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002193 expected_chain_partitions_map = {}
2194 if expected_chain_partitions:
2195 used_locations = {}
2196 for cp in expected_chain_partitions:
2197 cp_tokens = cp.split(':')
2198 if len(cp_tokens) != 3:
2199 raise AvbError('Malformed chained partition "{}".'.format(cp))
2200 partition_name = cp_tokens[0]
2201 rollback_index_location = int(cp_tokens[1])
2202 file_path = cp_tokens[2]
2203 pk_blob = open(file_path).read()
2204 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2205
2206 image_dir = os.path.dirname(image_filename)
2207 image_ext = os.path.splitext(image_filename)[1]
2208
2209 key_blob = None
2210 if key_path:
2211 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2212 key_blob = encode_rsa_key(key_path)
2213 else:
2214 print 'Verifying image {} using embedded public key'.format(image_filename)
2215
David Zeuthenb623d8b2017-04-04 16:05:53 -04002216 image = ImageHandler(image_filename)
2217 (footer, header, descriptors, image_size) = self._parse_image(image)
2218 offset = 0
2219 if footer:
2220 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002221
David Zeuthenb623d8b2017-04-04 16:05:53 -04002222 image.seek(offset)
David Zeuthen49936b42018-08-07 17:38:58 -04002223 vbmeta_blob = image.read(header.SIZE + header.authentication_data_block_size +
2224 header.auxiliary_data_block_size)
2225
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002226 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002227 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002228 raise AvbError('Signature check failed for {} vbmeta struct {}'
2229 .format(alg_name, image_filename))
2230
2231 if key_blob:
2232 # The embedded public key is in the auxiliary block at an offset.
2233 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002234 key_offset += header.authentication_data_block_size
2235 key_offset += header.public_key_offset
2236 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002237 if key_blob != key_blob_in_vbmeta:
2238 raise AvbError('Embedded public key does not match given key.')
2239
2240 if footer:
2241 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002242 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002243 else:
2244 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002245 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002246
2247 for desc in descriptors:
David Zeuthenf4f51eb2018-09-20 14:56:46 -04002248 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002249 raise AvbError('Error verifying descriptor.')
David Zeuthen49936b42018-08-07 17:38:58 -04002250 # Note how AvbDescriptor.verify() method verifies only the descriptor
2251 # contents which in the case of chain descriptors means checking only its
2252 # contents matches what is in |expected_chain_partitions_map|.
2253 #
2254 # Specifically AvbHashtreeDescriptor.verify(), doesn't follow chain
2255 # descriptors e.g. if it's a chain descriptor for 'system' it will not try
2256 # to verify system.img. Why? Because the whole idea of chain descriptors
2257 # is separate organizations. That is, when they are used it's assumed that
2258 # all you have is the public key, not an actual image (because if you had
2259 # the image you wouldn't need to use a chain partition in the first
2260 # place).
2261 #
2262 # However in certain situations you do have the image so it would be nice
2263 # to add something like a --follow_chain_descriptors option for 'avbtool
2264 # verify_image' which will look for and follow images specified by chain
2265 # descriptors. Maybe it should be on a per-partition basis and specificied
2266 # as part of the --expected_chain_partition paramter, maybe if the
2267 # partition name ends with a '+' or something. Something to think about.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002268
David Zeuthenb623d8b2017-04-04 16:05:53 -04002269
David Zeuthenb8643c02018-05-17 17:21:18 -04002270 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2271 """Implements the 'calculate_vbmeta_digest' command.
2272
2273 Arguments:
2274 image_filename: Image file to get information from (file object).
2275 hash_algorithm: Hash algorithm used.
2276 output: Output file to write human-readable information to (file object).
2277 """
2278
2279 image_dir = os.path.dirname(image_filename)
2280 image_ext = os.path.splitext(image_filename)[1]
2281
2282 image = ImageHandler(image_filename)
2283 (footer, header, descriptors, image_size) = self._parse_image(image)
2284 offset = 0
2285 if footer:
2286 offset = footer.vbmeta_offset
2287 size = (header.SIZE + header.authentication_data_block_size +
2288 header.auxiliary_data_block_size)
2289 image.seek(offset)
2290 vbmeta_blob = image.read(size)
2291
2292 hasher = hashlib.new(name=hash_algorithm)
2293 hasher.update(vbmeta_blob)
2294
2295 for desc in descriptors:
2296 if isinstance(desc, AvbChainPartitionDescriptor):
2297 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2298 ch_image = ImageHandler(ch_image_filename)
2299 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2300 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002301 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2302 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002303 if ch_footer:
2304 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002305 ch_image.seek(ch_offset)
2306 ch_vbmeta_blob = ch_image.read(ch_size)
2307 hasher.update(ch_vbmeta_blob)
2308
2309 digest = hasher.digest()
2310 output.write('{}\n'.format(digest.encode('hex')))
2311
2312
David Zeuthenf7d2e752018-09-20 13:30:41 -04002313 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2314 """Implements the 'calculate_kernel_cmdline' command.
2315
2316 Arguments:
2317 image_filename: Image file to get information from (file object).
2318 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2319 output: Output file to write human-readable information to (file object).
2320 """
2321
2322 image = ImageHandler(image_filename)
2323 _, _, descriptors, _ = self._parse_image(image)
2324
2325 image_dir = os.path.dirname(image_filename)
2326 image_ext = os.path.splitext(image_filename)[1]
2327
2328 cmdline_descriptors = []
2329 for desc in descriptors:
2330 if isinstance(desc, AvbChainPartitionDescriptor):
2331 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2332 ch_image = ImageHandler(ch_image_filename)
2333 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2334 for ch_desc in ch_descriptors:
2335 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2336 cmdline_descriptors.append(ch_desc)
2337 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2338 cmdline_descriptors.append(desc)
2339
2340 kernel_cmdline_snippets = []
2341 for desc in cmdline_descriptors:
2342 use_cmdline = True
2343 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) != 0:
2344 if hashtree_disabled:
2345 use_cmdline = False
2346 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2347 if not hashtree_disabled:
2348 use_cmdline = False
2349 if use_cmdline:
2350 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2351 output.write(' '.join(kernel_cmdline_snippets))
2352
2353
David Zeuthen21e95262016-07-27 17:58:40 -04002354 def _parse_image(self, image):
2355 """Gets information about an image.
2356
2357 The image can either be a vbmeta or an image with a footer.
2358
2359 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002360 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002361
2362 Returns:
2363 A tuple where the first argument is a AvbFooter (None if there
2364 is no footer on the image), the second argument is a
2365 AvbVBMetaHeader, the third argument is a list of
2366 AvbDescriptor-derived instances, and the fourth argument is the
2367 size of |image|.
2368 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002369 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002370 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002371 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002372 try:
2373 footer = AvbFooter(image.read(AvbFooter.SIZE))
2374 except (LookupError, struct.error):
2375 # Nope, just seek back to the start.
2376 image.seek(0)
2377
2378 vbmeta_offset = 0
2379 if footer:
2380 vbmeta_offset = footer.vbmeta_offset
2381
2382 image.seek(vbmeta_offset)
2383 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2384
2385 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2386 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2387 desc_start_offset = aux_block_offset + h.descriptors_offset
2388 image.seek(desc_start_offset)
2389 descriptors = parse_descriptors(image.read(h.descriptors_size))
2390
David Zeuthen09692692016-09-30 16:16:40 -04002391 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002392
David Zeuthenb1b994d2017-03-06 18:01:31 -05002393 def _load_vbmeta_blob(self, image):
2394 """Gets the vbmeta struct and associated sections.
2395
2396 The image can either be a vbmeta.img or an image with a footer.
2397
2398 Arguments:
2399 image: An ImageHandler (vbmeta or footer).
2400
2401 Returns:
2402 A blob with the vbmeta struct and other sections.
2403 """
2404 assert isinstance(image, ImageHandler)
2405 footer = None
2406 image.seek(image.image_size - AvbFooter.SIZE)
2407 try:
2408 footer = AvbFooter(image.read(AvbFooter.SIZE))
2409 except (LookupError, struct.error):
2410 # Nope, just seek back to the start.
2411 image.seek(0)
2412
2413 vbmeta_offset = 0
2414 if footer:
2415 vbmeta_offset = footer.vbmeta_offset
2416
2417 image.seek(vbmeta_offset)
2418 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2419
2420 image.seek(vbmeta_offset)
2421 data_size = AvbVBMetaHeader.SIZE
2422 data_size += h.authentication_data_block_size
2423 data_size += h.auxiliary_data_block_size
2424 return image.read(data_size)
2425
David Zeuthen73f2afa2017-05-17 16:54:11 -04002426 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002427 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002428
2429 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002430 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002431
2432 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002433 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2434 instructions. There is one for when hashtree is not disabled and one for
2435 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002436
David Zeuthen21e95262016-07-27 17:58:40 -04002437 """
2438
David Zeuthen21e95262016-07-27 17:58:40 -04002439 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002440 c += '0' # start
2441 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2442 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2443 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2444 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2445 c += ' {}'.format(ht.data_block_size) # data_block
2446 c += ' {}'.format(ht.hash_block_size) # hash_block
2447 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2448 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2449 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2450 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2451 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2452 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002453 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002454 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002455 c += ' ignore_zero_blocks'
2456 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2457 c += ' fec_roots {}'.format(ht.fec_num_roots)
2458 # Note that fec_blocks is the size that FEC covers, *not* the
2459 # size of the FEC data. Since we use FEC for everything up until
2460 # the FEC data, it's the same as the offset.
2461 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2462 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2463 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002464 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002465 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002466 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002467 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002468
David Zeuthenfd41eb92016-11-17 12:24:47 -05002469 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002470 desc = AvbKernelCmdlineDescriptor()
2471 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002472 desc.flags = (
2473 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2474
2475 # The descriptor for when hashtree verification is disabled is a lot
2476 # simpler - we just set the root to the partition.
2477 desc_no_ht = AvbKernelCmdlineDescriptor()
2478 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2479 desc_no_ht.flags = (
2480 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2481
2482 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002483
David Zeuthen73f2afa2017-05-17 16:54:11 -04002484 def _get_cmdline_descriptors_for_dm_verity(self, image):
2485 """Generate kernel cmdline descriptors for dm-verity.
2486
2487 Arguments:
2488 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2489
2490 Returns:
2491 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2492 instructions. There is one for when hashtree is not disabled and one for
2493 when it is.
2494
2495 Raises:
2496 AvbError: If |image| doesn't have a hashtree descriptor.
2497
2498 """
2499
2500 (_, _, descriptors, _) = self._parse_image(image)
2501
2502 ht = None
2503 for desc in descriptors:
2504 if isinstance(desc, AvbHashtreeDescriptor):
2505 ht = desc
2506 break
2507
2508 if not ht:
2509 raise AvbError('No hashtree descriptor in given image')
2510
2511 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2512
David Zeuthen21e95262016-07-27 17:58:40 -04002513 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002514 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002515 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002516 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002517 include_descriptors_from_image,
2518 signing_helper,
2519 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002520 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002521 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002522 print_required_libavb_version,
2523 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002524 """Implements the 'make_vbmeta_image' command.
2525
2526 Arguments:
2527 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002528 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002529 algorithm_name: Name of algorithm to use.
2530 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002531 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002532 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002533 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002534 props: Properties to insert (list of strings of the form 'key:value').
2535 props_from_file: Properties to insert (list of strings 'key:<path>').
2536 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002537 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002538 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002539 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002540 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002541 release_string: None or avbtool release string to use instead of default.
2542 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002543 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002544 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002545
2546 Raises:
2547 AvbError: If a chained partition is malformed.
2548 """
2549
David Zeuthen1097a782017-05-31 15:53:17 -04002550 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002551 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002552 if include_descriptors_from_image:
2553 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2554 # version of all included descriptors.
2555 tmp_header = AvbVBMetaHeader()
2556 for image in include_descriptors_from_image:
2557 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2558 tmp_header.bump_required_libavb_version_minor(
2559 image_header.required_libavb_version_minor)
2560 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2561 else:
2562 # Descriptors aside, all vbmeta features are supported in 1.0.
2563 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002564 return
2565
2566 if not output:
2567 raise AvbError('No output file given')
2568
David Zeuthen21e95262016-07-27 17:58:40 -04002569 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002570 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002571 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002572 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002573 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002574 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002575 include_descriptors_from_image, signing_helper,
2576 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002577 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002578
2579 # Write entire vbmeta blob (header, authentication, auxiliary).
2580 output.seek(0)
2581 output.write(vbmeta_blob)
2582
David Zeuthen97cb5802017-06-01 16:14:05 -04002583 if padding_size > 0:
2584 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2585 padding_needed = padded_size - len(vbmeta_blob)
2586 output.write('\0' * padding_needed)
2587
David Zeuthen18666ab2016-11-15 11:18:05 -05002588 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2589 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002590 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002591 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002592 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002593 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002594 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002595 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002596 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002597 release_string, append_to_release_string,
2598 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002599 """Generates a VBMeta blob.
2600
2601 This blob contains the header (struct AvbVBMetaHeader), the
2602 authentication data block (which contains the hash and signature
2603 for the header and auxiliary block), and the auxiliary block
2604 (which contains descriptors, the public key used, and other data).
2605
2606 The |key| parameter can |None| only if the |algorithm_name| is
2607 'NONE'.
2608
2609 Arguments:
2610 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2611 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002612 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002613 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002614 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002615 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002616 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002617 props: Properties to insert (List of strings of the form 'key:value').
2618 props_from_file: Properties to insert (List of strings 'key:<path>').
2619 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002620 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002621 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002622 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2623 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002624 include_descriptors_from_image: List of file objects for which
2625 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002626 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002627 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002628 release_string: None or avbtool release string.
2629 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002630 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002631
2632 Returns:
2633 A bytearray() with the VBMeta blob.
2634
2635 Raises:
2636 Exception: If the |algorithm_name| is not found, if no key has
2637 been given and the given algorithm requires one, or the key is
2638 of the wrong size.
2639
2640 """
2641 try:
2642 alg = ALGORITHMS[algorithm_name]
2643 except KeyError:
2644 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2645
David Zeuthena5fd3a42017-02-27 16:38:54 -05002646 if not descriptors:
2647 descriptors = []
2648
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002649 h = AvbVBMetaHeader()
2650 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2651
David Zeuthena5fd3a42017-02-27 16:38:54 -05002652 # Insert chained partition descriptors, if any
2653 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002654 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002655 for cp in chain_partitions:
2656 cp_tokens = cp.split(':')
2657 if len(cp_tokens) != 3:
2658 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002659 partition_name = cp_tokens[0]
2660 rollback_index_location = int(cp_tokens[1])
2661 file_path = cp_tokens[2]
2662 # Check that the same rollback location isn't being used by
2663 # multiple chained partitions.
2664 if used_locations.get(rollback_index_location):
2665 raise AvbError('Rollback Index Location {} is already in use.'.format(
2666 rollback_index_location))
2667 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002668 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002669 desc.partition_name = partition_name
2670 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002671 if desc.rollback_index_location < 1:
2672 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002673 desc.public_key = open(file_path, 'rb').read()
2674 descriptors.append(desc)
2675
David Zeuthen21e95262016-07-27 17:58:40 -04002676 # Descriptors.
2677 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002678 for desc in descriptors:
2679 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002680
2681 # Add properties.
2682 if props:
2683 for prop in props:
2684 idx = prop.find(':')
2685 if idx == -1:
2686 raise AvbError('Malformed property "{}".'.format(prop))
2687 desc = AvbPropertyDescriptor()
2688 desc.key = prop[0:idx]
2689 desc.value = prop[(idx + 1):]
2690 encoded_descriptors.extend(desc.encode())
2691 if props_from_file:
2692 for prop in props_from_file:
2693 idx = prop.find(':')
2694 if idx == -1:
2695 raise AvbError('Malformed property "{}".'.format(prop))
2696 desc = AvbPropertyDescriptor()
2697 desc.key = prop[0:idx]
2698 desc.value = prop[(idx + 1):]
2699 file_path = prop[(idx + 1):]
2700 desc.value = open(file_path, 'rb').read()
2701 encoded_descriptors.extend(desc.encode())
2702
David Zeuthen73f2afa2017-05-17 16:54:11 -04002703 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002704 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002705 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002706 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002707 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2708 encoded_descriptors.extend(cmdline_desc[0].encode())
2709 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002710
David Zeuthen73f2afa2017-05-17 16:54:11 -04002711 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2712 if ht_desc_to_setup:
2713 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2714 ht_desc_to_setup)
2715 encoded_descriptors.extend(cmdline_desc[0].encode())
2716 encoded_descriptors.extend(cmdline_desc[1].encode())
2717
David Zeuthen21e95262016-07-27 17:58:40 -04002718 # Add kernel command-lines.
2719 if kernel_cmdlines:
2720 for i in kernel_cmdlines:
2721 desc = AvbKernelCmdlineDescriptor()
2722 desc.kernel_cmdline = i
2723 encoded_descriptors.extend(desc.encode())
2724
2725 # Add descriptors from other images.
2726 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002727 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002728 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002729 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002730 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2731 image_handler)
2732 # Bump the required libavb version to support all included descriptors.
2733 h.bump_required_libavb_version_minor(
2734 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002735 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002736 # The --include_descriptors_from_image option is used in some setups
2737 # with images A and B where both A and B contain a descriptor
2738 # for a partition with the same name. Since it's not meaningful
2739 # to include both descriptors, only include the last seen descriptor.
2740 # See bug 76386656 for details.
2741 if hasattr(desc, 'partition_name'):
2742 key = type(desc).__name__ + '_' + desc.partition_name
2743 descriptors_dict[key] = desc.encode()
2744 else:
2745 encoded_descriptors.extend(desc.encode())
2746 for key in sorted(descriptors_dict.keys()):
2747 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002748
David Zeuthen18666ab2016-11-15 11:18:05 -05002749 # Load public key metadata blob, if requested.
2750 pkmd_blob = []
2751 if public_key_metadata_path:
2752 with open(public_key_metadata_path) as f:
2753 pkmd_blob = f.read()
2754
David Zeuthen21e95262016-07-27 17:58:40 -04002755 key = None
2756 encoded_key = bytearray()
2757 if alg.public_key_num_bytes > 0:
2758 if not key_path:
2759 raise AvbError('Key is required for algorithm {}'.format(
2760 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002761 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002762 if len(encoded_key) != alg.public_key_num_bytes:
2763 raise AvbError('Key is wrong size for algorithm {}'.format(
2764 algorithm_name))
2765
David Zeuthene3cadca2017-02-22 21:25:46 -05002766 # Override release string, if requested.
2767 if isinstance(release_string, (str, unicode)):
2768 h.release_string = release_string
2769
2770 # Append to release string, if requested. Also insert a space before.
2771 if isinstance(append_to_release_string, (str, unicode)):
2772 h.release_string += ' ' + append_to_release_string
2773
David Zeuthen18666ab2016-11-15 11:18:05 -05002774 # For the Auxiliary data block, descriptors are stored at offset 0,
2775 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002776 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002777 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002778 h.descriptors_offset = 0
2779 h.descriptors_size = len(encoded_descriptors)
2780 h.public_key_offset = h.descriptors_size
2781 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002782 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2783 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002784
2785 # For the Authentication data block, the hash is first and then
2786 # the signature.
2787 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002788 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002789 h.algorithm_type = alg.algorithm_type
2790 h.hash_offset = 0
2791 h.hash_size = alg.hash_num_bytes
2792 # Signature offset and size - it's stored right after the hash
2793 # (in Authentication data block).
2794 h.signature_offset = alg.hash_num_bytes
2795 h.signature_size = alg.signature_num_bytes
2796
2797 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002798 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002799
2800 # Generate Header data block.
2801 header_data_blob = h.encode()
2802
2803 # Generate Auxiliary data block.
2804 aux_data_blob = bytearray()
2805 aux_data_blob.extend(encoded_descriptors)
2806 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002807 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002808 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2809 aux_data_blob.extend('\0' * padding_bytes)
2810
2811 # Calculate the hash.
2812 binary_hash = bytearray()
2813 binary_signature = bytearray()
2814 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002815 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002816 ha.update(header_data_blob)
2817 ha.update(aux_data_blob)
2818 binary_hash.extend(ha.digest())
2819
2820 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002821 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002822 binary_signature.extend(raw_sign(signing_helper,
2823 signing_helper_with_files,
2824 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002825 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002826 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002827
2828 # Generate Authentication data block.
2829 auth_data_blob = bytearray()
2830 auth_data_blob.extend(binary_hash)
2831 auth_data_blob.extend(binary_signature)
2832 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2833 auth_data_blob.extend('\0' * padding_bytes)
2834
2835 return header_data_blob + auth_data_blob + aux_data_blob
2836
2837 def extract_public_key(self, key_path, output):
2838 """Implements the 'extract_public_key' command.
2839
2840 Arguments:
2841 key_path: The path to a RSA private key file.
2842 output: The file to write to.
2843 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002844 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002845
David Zeuthenb1b994d2017-03-06 18:01:31 -05002846 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2847 partition_size):
2848 """Implementation of the append_vbmeta_image command.
2849
2850 Arguments:
2851 image_filename: File to add the footer to.
2852 vbmeta_image_filename: File to get vbmeta struct from.
2853 partition_size: Size of partition.
2854
2855 Raises:
2856 AvbError: If an argument is incorrect.
2857 """
2858 image = ImageHandler(image_filename)
2859
2860 if partition_size % image.block_size != 0:
2861 raise AvbError('Partition size of {} is not a multiple of the image '
2862 'block size {}.'.format(partition_size,
2863 image.block_size))
2864
2865 # If there's already a footer, truncate the image to its original
2866 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002867 if image.image_size >= AvbFooter.SIZE:
2868 image.seek(image.image_size - AvbFooter.SIZE)
2869 try:
2870 footer = AvbFooter(image.read(AvbFooter.SIZE))
2871 # Existing footer found. Just truncate.
2872 original_image_size = footer.original_image_size
2873 image.truncate(footer.original_image_size)
2874 except (LookupError, struct.error):
2875 original_image_size = image.image_size
2876 else:
2877 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002878 original_image_size = image.image_size
2879
2880 # If anything goes wrong from here-on, restore the image back to
2881 # its original size.
2882 try:
2883 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2884 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2885
2886 # If the image isn't sparse, its size might not be a multiple of
2887 # the block size. This will screw up padding later so just grow it.
2888 if image.image_size % image.block_size != 0:
2889 assert not image.is_sparse
2890 padding_needed = image.block_size - (image.image_size%image.block_size)
2891 image.truncate(image.image_size + padding_needed)
2892
2893 # The append_raw() method requires content with size being a
2894 # multiple of |block_size| so add padding as needed. Also record
2895 # where this is written to since we'll need to put that in the
2896 # footer.
2897 vbmeta_offset = image.image_size
2898 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2899 len(vbmeta_blob))
2900 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2901
2902 # Append vbmeta blob and footer
2903 image.append_raw(vbmeta_blob_with_padding)
2904 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2905
2906 # Now insert a DONT_CARE chunk with enough bytes such that the
2907 # final Footer block is at the end of partition_size..
2908 image.append_dont_care(partition_size - vbmeta_end_offset -
2909 1*image.block_size)
2910
2911 # Generate the Footer that tells where the VBMeta footer
2912 # is. Also put enough padding in the front of the footer since
2913 # we'll write out an entire block.
2914 footer = AvbFooter()
2915 footer.original_image_size = original_image_size
2916 footer.vbmeta_offset = vbmeta_offset
2917 footer.vbmeta_size = len(vbmeta_blob)
2918 footer_blob = footer.encode()
2919 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2920 footer_blob)
2921 image.append_raw(footer_blob_with_padding)
2922
2923 except:
2924 # Truncate back to original size, then re-raise
2925 image.truncate(original_image_size)
2926 raise
2927
David Zeuthena4fee8b2016-08-22 15:20:43 -04002928 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002929 hash_algorithm, salt, chain_partitions, algorithm_name,
2930 key_path,
2931 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002932 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002933 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002934 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002935 signing_helper, signing_helper_with_files,
2936 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002937 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002938 print_required_libavb_version, use_persistent_digest,
2939 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002940 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002941
2942 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002943 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002944 partition_size: Size of partition.
2945 partition_name: Name of partition (without A/B suffix).
2946 hash_algorithm: Hash algorithm to use.
2947 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002948 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002949 algorithm_name: Name of algorithm to use.
2950 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002951 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002952 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002953 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002954 props: Properties to insert (List of strings of the form 'key:value').
2955 props_from_file: Properties to insert (List of strings 'key:<path>').
2956 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002957 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002958 dm-verity kernel cmdline from.
2959 include_descriptors_from_image: List of file objects for which
2960 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002961 calc_max_image_size: Don't store the footer - instead calculate the
2962 maximum image size leaving enough room for metadata with the
2963 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002964 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002965 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002966 release_string: None or avbtool release string.
2967 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002968 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2969 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002970 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002971 use_persistent_digest: Use a persistent digest on device.
2972 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002973
2974 Raises:
2975 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002976 """
David Zeuthen1097a782017-05-31 15:53:17 -04002977
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002978 required_libavb_version_minor = 0
2979 if use_persistent_digest or do_not_use_ab:
2980 required_libavb_version_minor = 1
2981
David Zeuthen1097a782017-05-31 15:53:17 -04002982 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002983 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002984 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04002985 return
2986
David Zeuthenbf562452017-05-17 18:04:43 -04002987 # First, calculate the maximum image size such that an image
2988 # this size + metadata (footer + vbmeta struct) fits in
2989 # |partition_size|.
2990 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002991 if partition_size < max_metadata_size:
2992 raise AvbError('Parition size of {} is too small. '
2993 'Needs to be at least {}'.format(
2994 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04002995 max_image_size = partition_size - max_metadata_size
2996
2997 # If we're asked to only calculate the maximum image size, we're done.
2998 if calc_max_image_size:
2999 print '{}'.format(max_image_size)
3000 return
3001
David Zeuthena4fee8b2016-08-22 15:20:43 -04003002 image = ImageHandler(image_filename)
3003
3004 if partition_size % image.block_size != 0:
3005 raise AvbError('Partition size of {} is not a multiple of the image '
3006 'block size {}.'.format(partition_size,
3007 image.block_size))
3008
David Zeuthen21e95262016-07-27 17:58:40 -04003009 # If there's already a footer, truncate the image to its original
3010 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3011 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003012 if image.image_size >= AvbFooter.SIZE:
3013 image.seek(image.image_size - AvbFooter.SIZE)
3014 try:
3015 footer = AvbFooter(image.read(AvbFooter.SIZE))
3016 # Existing footer found. Just truncate.
3017 original_image_size = footer.original_image_size
3018 image.truncate(footer.original_image_size)
3019 except (LookupError, struct.error):
3020 original_image_size = image.image_size
3021 else:
3022 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003023 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003024
3025 # If anything goes wrong from here-on, restore the image back to
3026 # its original size.
3027 try:
David Zeuthen09692692016-09-30 16:16:40 -04003028 # If image size exceeds the maximum image size, fail.
3029 if image.image_size > max_image_size:
3030 raise AvbError('Image size of {} exceeds maximum image '
3031 'size of {} in order to fit in a partition '
3032 'size of {}.'.format(image.image_size, max_image_size,
3033 partition_size))
3034
David Zeuthen21e95262016-07-27 17:58:40 -04003035 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3036 if salt:
3037 salt = salt.decode('hex')
3038 else:
3039 if salt is None:
3040 # If salt is not explicitly specified, choose a hash
3041 # that's the same size as the hash size.
3042 hash_size = digest_size
3043 salt = open('/dev/urandom').read(hash_size)
3044 else:
3045 salt = ''
3046
3047 hasher = hashlib.new(name=hash_algorithm, string=salt)
3048 # TODO(zeuthen): might want to read this in chunks to avoid
3049 # memory pressure, then again, this is only supposed to be used
3050 # on kernel/initramfs partitions. Possible optimization.
3051 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003052 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003053 digest = hasher.digest()
3054
3055 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003056 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003057 h_desc.hash_algorithm = hash_algorithm
3058 h_desc.partition_name = partition_name
3059 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003060 h_desc.flags = 0
3061 if do_not_use_ab:
3062 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3063 if not use_persistent_digest:
3064 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003065
3066 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003067 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003068 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003069 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003070 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003071 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003072 include_descriptors_from_image, signing_helper,
3073 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003074 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003075
David Zeuthend247fcb2017-02-16 12:09:27 -05003076 # Write vbmeta blob, if requested.
3077 if output_vbmeta_image:
3078 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003079
David Zeuthend247fcb2017-02-16 12:09:27 -05003080 # Append vbmeta blob and footer, unless requested not to.
3081 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003082 # If the image isn't sparse, its size might not be a multiple of
3083 # the block size. This will screw up padding later so just grow it.
3084 if image.image_size % image.block_size != 0:
3085 assert not image.is_sparse
3086 padding_needed = image.block_size - (
3087 image.image_size % image.block_size)
3088 image.truncate(image.image_size + padding_needed)
3089
3090 # The append_raw() method requires content with size being a
3091 # multiple of |block_size| so add padding as needed. Also record
3092 # where this is written to since we'll need to put that in the
3093 # footer.
3094 vbmeta_offset = image.image_size
3095 padding_needed = (
3096 round_to_multiple(len(vbmeta_blob), image.block_size) -
3097 len(vbmeta_blob))
3098 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3099
David Zeuthend247fcb2017-02-16 12:09:27 -05003100 image.append_raw(vbmeta_blob_with_padding)
3101 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3102
3103 # Now insert a DONT_CARE chunk with enough bytes such that the
3104 # final Footer block is at the end of partition_size..
3105 image.append_dont_care(partition_size - vbmeta_end_offset -
3106 1*image.block_size)
3107
3108 # Generate the Footer that tells where the VBMeta footer
3109 # is. Also put enough padding in the front of the footer since
3110 # we'll write out an entire block.
3111 footer = AvbFooter()
3112 footer.original_image_size = original_image_size
3113 footer.vbmeta_offset = vbmeta_offset
3114 footer.vbmeta_size = len(vbmeta_blob)
3115 footer_blob = footer.encode()
3116 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3117 footer_blob)
3118 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003119
David Zeuthen21e95262016-07-27 17:58:40 -04003120 except:
3121 # Truncate back to original size, then re-raise
3122 image.truncate(original_image_size)
3123 raise
3124
David Zeuthena4fee8b2016-08-22 15:20:43 -04003125 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003126 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003127 block_size, salt, chain_partitions, algorithm_name,
3128 key_path,
3129 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003130 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003131 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003132 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003133 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003134 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003135 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003136 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003137 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003138 print_required_libavb_version,
3139 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04003140 """Implements the 'add_hashtree_footer' command.
3141
3142 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3143 more information about dm-verity and these hashes.
3144
3145 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003146 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003147 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003148 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003149 generate_fec: If True, generate FEC codes.
3150 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003151 hash_algorithm: Hash algorithm to use.
3152 block_size: Block size to use.
3153 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003154 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003155 algorithm_name: Name of algorithm to use.
3156 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003157 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003158 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003159 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003160 props: Properties to insert (List of strings of the form 'key:value').
3161 props_from_file: Properties to insert (List of strings 'key:<path>').
3162 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003163 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003164 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003165 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3166 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003167 include_descriptors_from_image: List of file objects for which
3168 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003169 calc_max_image_size: Don't store the hashtree or footer - instead
3170 calculate the maximum image size leaving enough room for hashtree
3171 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003172 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003173 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003174 release_string: None or avbtool release string.
3175 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003176 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3177 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003178 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003179 use_persistent_root_digest: Use a persistent root digest on device.
3180 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003181
3182 Raises:
3183 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003184 """
David Zeuthen1097a782017-05-31 15:53:17 -04003185
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003186 required_libavb_version_minor = 0
3187 if use_persistent_root_digest or do_not_use_ab:
3188 required_libavb_version_minor = 1
3189
David Zeuthen1097a782017-05-31 15:53:17 -04003190 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003191 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003192 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003193 return
3194
David Zeuthen09692692016-09-30 16:16:40 -04003195 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3196 digest_padding = round_to_pow2(digest_size) - digest_size
3197
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003198 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3199 # size such that an image this size + the hashtree + metadata (footer +
3200 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3201 # for metadata.
3202 if partition_size > 0:
3203 (_, max_tree_size) = calc_hash_level_offsets(
3204 partition_size, block_size, digest_size + digest_padding)
3205 max_fec_size = 0
3206 if generate_fec:
3207 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3208 max_metadata_size = (max_fec_size + max_tree_size +
3209 self.MAX_VBMETA_SIZE +
3210 self.MAX_FOOTER_SIZE)
3211 max_image_size = partition_size - max_metadata_size
3212 else:
3213 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003214
3215 # If we're asked to only calculate the maximum image size, we're done.
3216 if calc_max_image_size:
3217 print '{}'.format(max_image_size)
3218 return
3219
David Zeuthena4fee8b2016-08-22 15:20:43 -04003220 image = ImageHandler(image_filename)
3221
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003222 if partition_size > 0:
3223 if partition_size % image.block_size != 0:
3224 raise AvbError('Partition size of {} is not a multiple of the image '
3225 'block size {}.'.format(partition_size,
3226 image.block_size))
3227 else:
3228 if image.image_size % image.block_size != 0:
3229 raise AvbError('File size of {} is not a multiple of the image '
3230 'block size {}.'.format(image.image_size,
3231 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003232
David Zeuthen21e95262016-07-27 17:58:40 -04003233 # If there's already a footer, truncate the image to its original
3234 # size. This way 'avbtool add_hashtree_footer' is idempotent
3235 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003236 if image.image_size >= AvbFooter.SIZE:
3237 image.seek(image.image_size - AvbFooter.SIZE)
3238 try:
3239 footer = AvbFooter(image.read(AvbFooter.SIZE))
3240 # Existing footer found. Just truncate.
3241 original_image_size = footer.original_image_size
3242 image.truncate(footer.original_image_size)
3243 except (LookupError, struct.error):
3244 original_image_size = image.image_size
3245 else:
3246 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003247 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003248
3249 # If anything goes wrong from here-on, restore the image back to
3250 # its original size.
3251 try:
3252 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003253 rounded_image_size = round_to_multiple(image.image_size, block_size)
3254 if rounded_image_size > image.image_size:
3255 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003256
David Zeuthen09692692016-09-30 16:16:40 -04003257 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003258 if partition_size > 0:
3259 if image.image_size > max_image_size:
3260 raise AvbError('Image size of {} exceeds maximum image '
3261 'size of {} in order to fit in a partition '
3262 'size of {}.'.format(image.image_size, max_image_size,
3263 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003264
3265 if salt:
3266 salt = salt.decode('hex')
3267 else:
3268 if salt is None:
3269 # If salt is not explicitly specified, choose a hash
3270 # that's the same size as the hash size.
3271 hash_size = digest_size
3272 salt = open('/dev/urandom').read(hash_size)
3273 else:
3274 salt = ''
3275
David Zeuthena4fee8b2016-08-22 15:20:43 -04003276 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003277 # offsets in advance.
3278 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003279 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003280
David Zeuthena4fee8b2016-08-22 15:20:43 -04003281 # If the image isn't sparse, its size might not be a multiple of
3282 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003283 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003284 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003285 padding_needed = image.block_size - (image.image_size%image.block_size)
3286 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003287
David Zeuthena4fee8b2016-08-22 15:20:43 -04003288 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003289 tree_offset = image.image_size
3290 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003291 block_size,
3292 hash_algorithm, salt,
3293 digest_padding,
3294 hash_level_offsets,
3295 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003296
3297 # Generate HashtreeDescriptor with details about the tree we
3298 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003299 ht_desc = AvbHashtreeDescriptor()
3300 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003301 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003302 ht_desc.tree_offset = tree_offset
3303 ht_desc.tree_size = tree_size
3304 ht_desc.data_block_size = block_size
3305 ht_desc.hash_block_size = block_size
3306 ht_desc.hash_algorithm = hash_algorithm
3307 ht_desc.partition_name = partition_name
3308 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003309 if do_not_use_ab:
3310 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3311 if not use_persistent_root_digest:
3312 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003313
David Zeuthen09692692016-09-30 16:16:40 -04003314 # Write the hash tree
3315 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3316 len(hash_tree))
3317 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3318 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003319 len_hashtree_and_fec = len(hash_tree_with_padding)
3320
3321 # Generate FEC codes, if requested.
3322 if generate_fec:
3323 fec_data = generate_fec_data(image_filename, fec_num_roots)
3324 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3325 len(fec_data))
3326 fec_data_with_padding = fec_data + '\0'*padding_needed
3327 fec_offset = image.image_size
3328 image.append_raw(fec_data_with_padding)
3329 len_hashtree_and_fec += len(fec_data_with_padding)
3330 # Update the hashtree descriptor.
3331 ht_desc.fec_num_roots = fec_num_roots
3332 ht_desc.fec_offset = fec_offset
3333 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003334
David Zeuthen73f2afa2017-05-17 16:54:11 -04003335 ht_desc_to_setup = None
3336 if setup_as_rootfs_from_kernel:
3337 ht_desc_to_setup = ht_desc
3338
David Zeuthena4fee8b2016-08-22 15:20:43 -04003339 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003340 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003341 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003342 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003343 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003344 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003345 include_descriptors_from_image, signing_helper,
3346 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003347 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003348 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3349 len(vbmeta_blob))
3350 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003351
David Zeuthend247fcb2017-02-16 12:09:27 -05003352 # Write vbmeta blob, if requested.
3353 if output_vbmeta_image:
3354 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003355
David Zeuthend247fcb2017-02-16 12:09:27 -05003356 # Append vbmeta blob and footer, unless requested not to.
3357 if not do_not_append_vbmeta_image:
3358 image.append_raw(vbmeta_blob_with_padding)
3359
3360 # Now insert a DONT_CARE chunk with enough bytes such that the
3361 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003362 if partition_size > 0:
3363 image.append_dont_care(partition_size - image.image_size -
3364 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003365
3366 # Generate the Footer that tells where the VBMeta footer
3367 # is. Also put enough padding in the front of the footer since
3368 # we'll write out an entire block.
3369 footer = AvbFooter()
3370 footer.original_image_size = original_image_size
3371 footer.vbmeta_offset = vbmeta_offset
3372 footer.vbmeta_size = len(vbmeta_blob)
3373 footer_blob = footer.encode()
3374 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3375 footer_blob)
3376 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003377
David Zeuthen21e95262016-07-27 17:58:40 -04003378 except:
David Zeuthen09692692016-09-30 16:16:40 -04003379 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003380 image.truncate(original_image_size)
3381 raise
3382
David Zeuthenc68f0822017-03-31 17:22:35 -04003383 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003384 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003385 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003386 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003387 """Implements the 'make_atx_certificate' command.
3388
3389 Android Things certificates are required for Android Things public key
3390 metadata. They chain the vbmeta signing key for a particular product back to
3391 a fused, permanent root key. These certificates are fixed-length and fixed-
3392 format with the explicit goal of not parsing ASN.1 in bootloader code.
3393
3394 Arguments:
3395 output: Certificate will be written to this file on success.
3396 authority_key_path: A PEM file path with the authority private key.
3397 If None, then a certificate will be created without a
3398 signature. The signature can be created out-of-band
3399 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003400 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003401 subject_key_version: A 64-bit version value. If this is None, the number
3402 of seconds since the epoch is used.
3403 subject: A subject identifier. For Product Signing Key certificates this
3404 should be the same Product ID found in the permanent attributes.
3405 is_intermediate_authority: True if the certificate is for an intermediate
3406 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003407 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003408 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003409 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003410 """
3411 signed_data = bytearray()
3412 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003413 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003414 hasher = hashlib.sha256()
3415 hasher.update(subject)
3416 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003417 if not usage:
3418 usage = 'com.google.android.things.vboot'
3419 if is_intermediate_authority:
3420 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003421 hasher = hashlib.sha256()
3422 hasher.update(usage)
3423 signed_data.extend(hasher.digest())
3424 if not subject_key_version:
3425 subject_key_version = int(time.time())
3426 signed_data.extend(struct.pack('<Q', subject_key_version))
3427 signature = bytearray()
3428 if authority_key_path:
3429 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003430 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003431 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003432 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003433 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003434 hasher.update(signed_data)
3435 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003436 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3437 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003438 alg.signature_num_bytes, authority_key_path,
3439 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003440 output.write(signed_data)
3441 output.write(signature)
3442
David Zeuthenc68f0822017-03-31 17:22:35 -04003443 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003444 product_id):
3445 """Implements the 'make_atx_permanent_attributes' command.
3446
3447 Android Things permanent attributes are designed to be permanent for a
3448 particular product and a hash of these attributes should be fused into
3449 hardware to enforce this.
3450
3451 Arguments:
3452 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003453 root_authority_key_path: Path to a PEM or DER public key for
3454 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003455 product_id: A 16-byte Product ID.
3456
3457 Raises:
3458 AvbError: If an argument is incorrect.
3459 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003460 EXPECTED_PRODUCT_ID_SIZE = 16
3461 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003462 raise AvbError('Invalid Product ID length.')
3463 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003464 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003465 output.write(product_id)
3466
3467 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003468 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003469 """Implements the 'make_atx_metadata' command.
3470
3471 Android Things metadata are included in vbmeta images to facilitate
3472 verification. The output of this command can be used as the
3473 public_key_metadata argument to other commands.
3474
3475 Arguments:
3476 output: Metadata will be written to this file on success.
3477 intermediate_key_certificate: A certificate file as output by
3478 make_atx_certificate with
3479 is_intermediate_authority set to true.
3480 product_key_certificate: A certificate file as output by
3481 make_atx_certificate with
3482 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003483
3484 Raises:
3485 AvbError: If an argument is incorrect.
3486 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003487 EXPECTED_CERTIFICATE_SIZE = 1620
3488 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003489 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003490 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003491 raise AvbError('Invalid product key certificate length.')
3492 output.write(struct.pack('<I', 1)) # Format Version
3493 output.write(intermediate_key_certificate)
3494 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003495
Darren Krahnfccd64e2018-01-16 17:39:35 -08003496 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3497 unlock_key_certificate, challenge_path,
3498 unlock_key_path, signing_helper,
3499 signing_helper_with_files):
3500 """Implements the 'make_atx_unlock_credential' command.
3501
3502 Android Things unlock credentials can be used to authorize the unlock of AVB
3503 on a device. These credentials are presented to an Android Things bootloader
3504 via the fastboot interface in response to a 16-byte challenge. This method
3505 creates all fields of the credential except the challenge signature field
3506 (which is the last field) and can optionally create the challenge signature
3507 field as well if a challenge and the unlock_key_path is provided.
3508
3509 Arguments:
3510 output: The credential will be written to this file on success.
3511 intermediate_key_certificate: A certificate file as output by
3512 make_atx_certificate with
3513 is_intermediate_authority set to true.
3514 unlock_key_certificate: A certificate file as output by
3515 make_atx_certificate with
3516 is_intermediate_authority set to false and the
3517 usage set to
3518 'com.google.android.things.vboot.unlock'.
3519 challenge_path: [optional] A path to the challenge to sign.
3520 unlock_key_path: [optional] A PEM file path with the unlock private key.
3521 signing_helper: Program which signs a hash and returns the signature.
3522 signing_helper_with_files: Same as signing_helper but uses files instead.
3523
3524 Raises:
3525 AvbError: If an argument is incorrect.
3526 """
3527 EXPECTED_CERTIFICATE_SIZE = 1620
3528 EXPECTED_CHALLENGE_SIZE = 16
3529 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3530 raise AvbError('Invalid intermediate key certificate length.')
3531 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3532 raise AvbError('Invalid product key certificate length.')
3533 challenge = bytearray()
3534 if challenge_path:
3535 with open(challenge_path, 'r') as f:
3536 challenge = f.read()
3537 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3538 raise AvbError('Invalid unlock challenge length.')
3539 output.write(struct.pack('<I', 1)) # Format Version
3540 output.write(intermediate_key_certificate)
3541 output.write(unlock_key_certificate)
3542 if challenge_path and unlock_key_path:
3543 signature = bytearray()
3544 padding_and_hash = bytearray()
3545 algorithm_name = 'SHA512_RSA4096'
3546 alg = ALGORITHMS[algorithm_name]
3547 hasher = hashlib.sha512()
3548 padding_and_hash.extend(alg.padding)
3549 hasher.update(challenge)
3550 padding_and_hash.extend(hasher.digest())
3551 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3552 algorithm_name,
3553 alg.signature_num_bytes, unlock_key_path,
3554 padding_and_hash))
3555 output.write(signature)
3556
David Zeuthen21e95262016-07-27 17:58:40 -04003557
3558def calc_hash_level_offsets(image_size, block_size, digest_size):
3559 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3560
3561 Arguments:
3562 image_size: The size of the image to calculate a Merkle-tree for.
3563 block_size: The block size, e.g. 4096.
3564 digest_size: The size of each hash, e.g. 32 for SHA-256.
3565
3566 Returns:
3567 A tuple where the first argument is an array of offsets and the
3568 second is size of the tree, in bytes.
3569 """
3570 level_offsets = []
3571 level_sizes = []
3572 tree_size = 0
3573
3574 num_levels = 0
3575 size = image_size
3576 while size > block_size:
3577 num_blocks = (size + block_size - 1) / block_size
3578 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3579
3580 level_sizes.append(level_size)
3581 tree_size += level_size
3582 num_levels += 1
3583
3584 size = level_size
3585
3586 for n in range(0, num_levels):
3587 offset = 0
3588 for m in range(n + 1, num_levels):
3589 offset += level_sizes[m]
3590 level_offsets.append(offset)
3591
David Zeuthena4fee8b2016-08-22 15:20:43 -04003592 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003593
3594
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003595# See system/extras/libfec/include/fec/io.h for these definitions.
3596FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3597FEC_MAGIC = 0xfecfecfe
3598
3599
3600def calc_fec_data_size(image_size, num_roots):
3601 """Calculates how much space FEC data will take.
3602
3603 Args:
3604 image_size: The size of the image.
3605 num_roots: Number of roots.
3606
3607 Returns:
3608 The number of bytes needed for FEC for an image of the given size
3609 and with the requested number of FEC roots.
3610
3611 Raises:
3612 ValueError: If output from the 'fec' tool is invalid.
3613
3614 """
3615 p = subprocess.Popen(
3616 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3617 stdout=subprocess.PIPE,
3618 stderr=subprocess.PIPE)
3619 (pout, perr) = p.communicate()
3620 retcode = p.wait()
3621 if retcode != 0:
3622 raise ValueError('Error invoking fec: {}'.format(perr))
3623 return int(pout)
3624
3625
3626def generate_fec_data(image_filename, num_roots):
3627 """Generate FEC codes for an image.
3628
3629 Args:
3630 image_filename: The filename of the image.
3631 num_roots: Number of roots.
3632
3633 Returns:
3634 The FEC data blob.
3635
3636 Raises:
3637 ValueError: If output from the 'fec' tool is invalid.
3638 """
3639 fec_tmpfile = tempfile.NamedTemporaryFile()
3640 subprocess.check_call(
3641 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3642 fec_tmpfile.name],
3643 stderr=open(os.devnull))
3644 fec_data = fec_tmpfile.read()
3645 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3646 footer_data = fec_data[-footer_size:]
3647 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3648 footer_data)
3649 if magic != FEC_MAGIC:
3650 raise ValueError('Unexpected magic in FEC footer')
3651 return fec_data[0:fec_size]
3652
3653
David Zeuthen21e95262016-07-27 17:58:40 -04003654def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003655 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003656 """Generates a Merkle-tree for a file.
3657
3658 Args:
3659 image: The image, as a file.
3660 image_size: The size of the image.
3661 block_size: The block size, e.g. 4096.
3662 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3663 salt: The salt to use.
3664 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003665 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003666 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003667
3668 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003669 A tuple where the first element is the top-level hash and the
3670 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003671 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003672 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003673 hash_src_offset = 0
3674 hash_src_size = image_size
3675 level_num = 0
3676 while hash_src_size > block_size:
3677 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003678 remaining = hash_src_size
3679 while remaining > 0:
3680 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003681 # Only read from the file for the first level - for subsequent
3682 # levels, access the array we're building.
3683 if level_num == 0:
3684 image.seek(hash_src_offset + hash_src_size - remaining)
3685 data = image.read(min(remaining, block_size))
3686 else:
3687 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3688 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003689 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003690
3691 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003692 if len(data) < block_size:
3693 hasher.update('\0' * (block_size - len(data)))
3694 level_output += hasher.digest()
3695 if digest_padding > 0:
3696 level_output += '\0' * digest_padding
3697
3698 padding_needed = (round_to_multiple(
3699 len(level_output), block_size) - len(level_output))
3700 level_output += '\0' * padding_needed
3701
David Zeuthena4fee8b2016-08-22 15:20:43 -04003702 # Copy level-output into resulting tree.
3703 offset = hash_level_offsets[level_num]
3704 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003705
David Zeuthena4fee8b2016-08-22 15:20:43 -04003706 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003707 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003708 level_num += 1
3709
3710 hasher = hashlib.new(name=hash_alg_name, string=salt)
3711 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003712 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003713
3714
3715class AvbTool(object):
3716 """Object for avbtool command-line tool."""
3717
3718 def __init__(self):
3719 """Initializer method."""
3720 self.avb = Avb()
3721
3722 def _add_common_args(self, sub_parser):
3723 """Adds arguments used by several sub-commands.
3724
3725 Arguments:
3726 sub_parser: The parser to add arguments to.
3727 """
3728 sub_parser.add_argument('--algorithm',
3729 help='Algorithm to use (default: NONE)',
3730 metavar='ALGORITHM',
3731 default='NONE')
3732 sub_parser.add_argument('--key',
3733 help='Path to RSA private key file',
3734 metavar='KEY',
3735 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003736 sub_parser.add_argument('--signing_helper',
3737 help='Path to helper used for signing',
3738 metavar='APP',
3739 default=None,
3740 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003741 sub_parser.add_argument('--signing_helper_with_files',
3742 help='Path to helper used for signing using files',
3743 metavar='APP',
3744 default=None,
3745 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003746 sub_parser.add_argument('--public_key_metadata',
3747 help='Path to public key metadata file',
3748 metavar='KEY_METADATA',
3749 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003750 sub_parser.add_argument('--rollback_index',
3751 help='Rollback Index',
3752 type=parse_number,
3753 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003754 # This is used internally for unit tests. Do not include in --help output.
3755 sub_parser.add_argument('--internal_release_string',
3756 help=argparse.SUPPRESS)
3757 sub_parser.add_argument('--append_to_release_string',
3758 help='Text to append to release string',
3759 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003760 sub_parser.add_argument('--prop',
3761 help='Add property',
3762 metavar='KEY:VALUE',
3763 action='append')
3764 sub_parser.add_argument('--prop_from_file',
3765 help='Add property from file',
3766 metavar='KEY:PATH',
3767 action='append')
3768 sub_parser.add_argument('--kernel_cmdline',
3769 help='Add kernel cmdline',
3770 metavar='CMDLINE',
3771 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003772 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3773 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3774 # at some future point.
3775 sub_parser.add_argument('--setup_rootfs_from_kernel',
3776 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003777 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003778 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003779 type=argparse.FileType('rb'))
3780 sub_parser.add_argument('--include_descriptors_from_image',
3781 help='Include descriptors from image',
3782 metavar='IMAGE',
3783 action='append',
3784 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003785 sub_parser.add_argument('--print_required_libavb_version',
3786 help=('Don\'t store the footer - '
3787 'instead calculate the required libavb '
3788 'version for the given options.'),
3789 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003790 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3791 sub_parser.add_argument('--chain_partition',
3792 help='Allow signed integrity-data for partition',
3793 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3794 action='append')
3795 sub_parser.add_argument('--flags',
3796 help='VBMeta flags',
3797 type=parse_number,
3798 default=0)
3799 sub_parser.add_argument('--set_hashtree_disabled_flag',
3800 help='Set the HASHTREE_DISABLED flag',
3801 action='store_true')
3802
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003803 def _add_common_footer_args(self, sub_parser):
3804 """Adds arguments used by add_*_footer sub-commands.
3805
3806 Arguments:
3807 sub_parser: The parser to add arguments to.
3808 """
3809 sub_parser.add_argument('--use_persistent_digest',
3810 help='Use a persistent digest on device instead of '
3811 'storing the digest in the descriptor. This '
3812 'cannot be used with A/B so must be combined '
3813 'with --do_not_use_ab when an A/B suffix is '
3814 'expected at runtime.',
3815 action='store_true')
3816 sub_parser.add_argument('--do_not_use_ab',
3817 help='The partition does not use A/B even when an '
3818 'A/B suffix is present. This must not be used '
3819 'for vbmeta or chained partitions.',
3820 action='store_true')
3821
David Zeuthena5fd3a42017-02-27 16:38:54 -05003822 def _fixup_common_args(self, args):
3823 """Common fixups needed by subcommands.
3824
3825 Arguments:
3826 args: Arguments to modify.
3827
3828 Returns:
3829 The modified arguments.
3830 """
3831 if args.set_hashtree_disabled_flag:
3832 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3833 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003834
3835 def run(self, argv):
3836 """Command-line processor.
3837
3838 Arguments:
3839 argv: Pass sys.argv from main.
3840 """
3841 parser = argparse.ArgumentParser()
3842 subparsers = parser.add_subparsers(title='subcommands')
3843
3844 sub_parser = subparsers.add_parser('version',
3845 help='Prints version of avbtool.')
3846 sub_parser.set_defaults(func=self.version)
3847
3848 sub_parser = subparsers.add_parser('extract_public_key',
3849 help='Extract public key.')
3850 sub_parser.add_argument('--key',
3851 help='Path to RSA private key file',
3852 required=True)
3853 sub_parser.add_argument('--output',
3854 help='Output file name',
3855 type=argparse.FileType('wb'),
3856 required=True)
3857 sub_parser.set_defaults(func=self.extract_public_key)
3858
3859 sub_parser = subparsers.add_parser('make_vbmeta_image',
3860 help='Makes a vbmeta image.')
3861 sub_parser.add_argument('--output',
3862 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003863 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003864 sub_parser.add_argument('--padding_size',
3865 metavar='NUMBER',
3866 help='If non-zero, pads output with NUL bytes so '
3867 'its size is a multiple of NUMBER (default: 0)',
3868 type=parse_number,
3869 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003870 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003871 sub_parser.set_defaults(func=self.make_vbmeta_image)
3872
3873 sub_parser = subparsers.add_parser('add_hash_footer',
3874 help='Add hashes and footer to image.')
3875 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003876 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003877 type=argparse.FileType('rab+'))
3878 sub_parser.add_argument('--partition_size',
3879 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003880 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003881 sub_parser.add_argument('--partition_name',
3882 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003883 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003884 sub_parser.add_argument('--hash_algorithm',
3885 help='Hash algorithm to use (default: sha256)',
3886 default='sha256')
3887 sub_parser.add_argument('--salt',
3888 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003889 sub_parser.add_argument('--calc_max_image_size',
3890 help=('Don\'t store the footer - '
3891 'instead calculate the maximum image size '
3892 'leaving enough room for metadata with '
3893 'the given partition size.'),
3894 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003895 sub_parser.add_argument('--output_vbmeta_image',
3896 help='Also write vbmeta struct to file',
3897 type=argparse.FileType('wb'))
3898 sub_parser.add_argument('--do_not_append_vbmeta_image',
3899 help=('Do not append vbmeta struct or footer '
3900 'to the image'),
3901 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003902 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003903 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003904 sub_parser.set_defaults(func=self.add_hash_footer)
3905
David Zeuthenb1b994d2017-03-06 18:01:31 -05003906 sub_parser = subparsers.add_parser('append_vbmeta_image',
3907 help='Append vbmeta image to image.')
3908 sub_parser.add_argument('--image',
3909 help='Image to append vbmeta blob to',
3910 type=argparse.FileType('rab+'))
3911 sub_parser.add_argument('--partition_size',
3912 help='Partition size',
3913 type=parse_number,
3914 required=True)
3915 sub_parser.add_argument('--vbmeta_image',
3916 help='Image with vbmeta blob to append',
3917 type=argparse.FileType('rb'))
3918 sub_parser.set_defaults(func=self.append_vbmeta_image)
3919
David Zeuthen21e95262016-07-27 17:58:40 -04003920 sub_parser = subparsers.add_parser('add_hashtree_footer',
3921 help='Add hashtree and footer to image.')
3922 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003923 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003924 type=argparse.FileType('rab+'))
3925 sub_parser.add_argument('--partition_size',
3926 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003927 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04003928 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003929 sub_parser.add_argument('--partition_name',
3930 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003931 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04003932 sub_parser.add_argument('--hash_algorithm',
3933 help='Hash algorithm to use (default: sha1)',
3934 default='sha1')
3935 sub_parser.add_argument('--salt',
3936 help='Salt in hex (default: /dev/urandom)')
3937 sub_parser.add_argument('--block_size',
3938 help='Block size (default: 4096)',
3939 type=parse_number,
3940 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003941 # TODO(zeuthen): The --generate_fec option was removed when we
3942 # moved to generating FEC by default. To avoid breaking existing
3943 # users needing to transition we simply just print a warning below
3944 # in add_hashtree_footer(). Remove this option and the warning at
3945 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003946 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003947 help=argparse.SUPPRESS,
3948 action='store_true')
3949 sub_parser.add_argument('--do_not_generate_fec',
3950 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003951 action='store_true')
3952 sub_parser.add_argument('--fec_num_roots',
3953 help='Number of roots for FEC (default: 2)',
3954 type=parse_number,
3955 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003956 sub_parser.add_argument('--calc_max_image_size',
3957 help=('Don\'t store the hashtree or footer - '
3958 'instead calculate the maximum image size '
3959 'leaving enough room for hashtree '
3960 'and metadata with the given partition '
3961 'size.'),
3962 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003963 sub_parser.add_argument('--output_vbmeta_image',
3964 help='Also write vbmeta struct to file',
3965 type=argparse.FileType('wb'))
3966 sub_parser.add_argument('--do_not_append_vbmeta_image',
3967 help=('Do not append vbmeta struct or footer '
3968 'to the image'),
3969 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003970 # This is different from --setup_rootfs_from_kernel insofar that
3971 # it doesn't take an IMAGE, the generated cmdline will be for the
3972 # hashtree we're adding.
3973 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3974 action='store_true',
3975 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003976 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003977 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003978 sub_parser.set_defaults(func=self.add_hashtree_footer)
3979
3980 sub_parser = subparsers.add_parser('erase_footer',
3981 help='Erase footer from an image.')
3982 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003983 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003984 type=argparse.FileType('rwb+'),
3985 required=True)
3986 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003987 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003988 action='store_true')
3989 sub_parser.set_defaults(func=self.erase_footer)
3990
David Zeuthen49936b42018-08-07 17:38:58 -04003991 sub_parser = subparsers.add_parser('extract_vbmeta_image',
3992 help='Extracts vbmeta from an image with a footer.')
3993 sub_parser.add_argument('--image',
3994 help='Image with footer',
3995 type=argparse.FileType('rb'),
3996 required=True)
3997 sub_parser.add_argument('--output',
3998 help='Output file name',
3999 type=argparse.FileType('wb'))
4000 sub_parser.add_argument('--padding_size',
4001 metavar='NUMBER',
4002 help='If non-zero, pads output with NUL bytes so '
4003 'its size is a multiple of NUMBER (default: 0)',
4004 type=parse_number,
4005 default=0)
4006 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4007
David Zeuthen2bc232b2017-04-19 14:25:19 -04004008 sub_parser = subparsers.add_parser('resize_image',
4009 help='Resize image with a footer.')
4010 sub_parser.add_argument('--image',
4011 help='Image with a footer',
4012 type=argparse.FileType('rwb+'),
4013 required=True)
4014 sub_parser.add_argument('--partition_size',
4015 help='New partition size',
4016 type=parse_number)
4017 sub_parser.set_defaults(func=self.resize_image)
4018
David Zeuthen21e95262016-07-27 17:58:40 -04004019 sub_parser = subparsers.add_parser(
4020 'info_image',
4021 help='Show information about vbmeta or footer.')
4022 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004023 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004024 type=argparse.FileType('rb'),
4025 required=True)
4026 sub_parser.add_argument('--output',
4027 help='Write info to file',
4028 type=argparse.FileType('wt'),
4029 default=sys.stdout)
4030 sub_parser.set_defaults(func=self.info_image)
4031
David Zeuthenb623d8b2017-04-04 16:05:53 -04004032 sub_parser = subparsers.add_parser(
4033 'verify_image',
4034 help='Verify an image.')
4035 sub_parser.add_argument('--image',
4036 help='Image to verify',
4037 type=argparse.FileType('rb'),
4038 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004039 sub_parser.add_argument('--key',
4040 help='Check embedded public key matches KEY',
4041 metavar='KEY',
4042 required=False)
4043 sub_parser.add_argument('--expected_chain_partition',
4044 help='Expected chain partition',
4045 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4046 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004047 sub_parser.set_defaults(func=self.verify_image)
4048
David Zeuthenb8643c02018-05-17 17:21:18 -04004049 sub_parser = subparsers.add_parser(
4050 'calculate_vbmeta_digest',
4051 help='Calculate vbmeta digest.')
4052 sub_parser.add_argument('--image',
4053 help='Image to calculate digest for',
4054 type=argparse.FileType('rb'),
4055 required=True)
4056 sub_parser.add_argument('--hash_algorithm',
4057 help='Hash algorithm to use (default: sha256)',
4058 default='sha256')
4059 sub_parser.add_argument('--output',
4060 help='Write hex digest to file (default: stdout)',
4061 type=argparse.FileType('wt'),
4062 default=sys.stdout)
4063 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4064
David Zeuthenf7d2e752018-09-20 13:30:41 -04004065 sub_parser = subparsers.add_parser(
4066 'calculate_kernel_cmdline',
4067 help='Calculate kernel cmdline.')
4068 sub_parser.add_argument('--image',
4069 help='Image to calculate kernel cmdline for',
4070 type=argparse.FileType('rb'),
4071 required=True)
4072 sub_parser.add_argument('--hashtree_disabled',
4073 help='Return the cmdline for hashtree disabled',
4074 action='store_true')
4075 sub_parser.add_argument('--output',
4076 help='Write cmdline to file (default: stdout)',
4077 type=argparse.FileType('wt'),
4078 default=sys.stdout)
4079 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4080
David Zeuthen8b6973b2016-09-20 12:39:49 -04004081 sub_parser = subparsers.add_parser('set_ab_metadata',
4082 help='Set A/B metadata.')
4083 sub_parser.add_argument('--misc_image',
4084 help=('The misc image to modify. If the image does '
4085 'not exist, it will be created.'),
4086 type=argparse.FileType('r+b'),
4087 required=True)
4088 sub_parser.add_argument('--slot_data',
4089 help=('Slot data of the form "priority", '
4090 '"tries_remaining", "sucessful_boot" for '
4091 'slot A followed by the same for slot B, '
4092 'separated by colons. The default value '
4093 'is 15:7:0:14:7:0.'),
4094 default='15:7:0:14:7:0')
4095 sub_parser.set_defaults(func=self.set_ab_metadata)
4096
Darren Krahn147b08d2016-12-20 16:38:29 -08004097 sub_parser = subparsers.add_parser(
4098 'make_atx_certificate',
4099 help='Create an Android Things eXtension (ATX) certificate.')
4100 sub_parser.add_argument('--output',
4101 help='Write certificate to file',
4102 type=argparse.FileType('wb'),
4103 default=sys.stdout)
4104 sub_parser.add_argument('--subject',
4105 help=('Path to subject file'),
4106 type=argparse.FileType('rb'),
4107 required=True)
4108 sub_parser.add_argument('--subject_key',
4109 help=('Path to subject RSA public key file'),
4110 type=argparse.FileType('rb'),
4111 required=True)
4112 sub_parser.add_argument('--subject_key_version',
4113 help=('Version of the subject key'),
4114 type=parse_number,
4115 required=False)
4116 sub_parser.add_argument('--subject_is_intermediate_authority',
4117 help=('Generate an intermediate authority '
4118 'certificate'),
4119 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004120 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004121 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004122 'string'),
4123 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004124 sub_parser.add_argument('--authority_key',
4125 help='Path to authority RSA private key file',
4126 required=False)
4127 sub_parser.add_argument('--signing_helper',
4128 help='Path to helper used for signing',
4129 metavar='APP',
4130 default=None,
4131 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004132 sub_parser.add_argument('--signing_helper_with_files',
4133 help='Path to helper used for signing using files',
4134 metavar='APP',
4135 default=None,
4136 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004137 sub_parser.set_defaults(func=self.make_atx_certificate)
4138
4139 sub_parser = subparsers.add_parser(
4140 'make_atx_permanent_attributes',
4141 help='Create Android Things eXtension (ATX) permanent attributes.')
4142 sub_parser.add_argument('--output',
4143 help='Write attributes to file',
4144 type=argparse.FileType('wb'),
4145 default=sys.stdout)
4146 sub_parser.add_argument('--root_authority_key',
4147 help='Path to authority RSA public key file',
4148 type=argparse.FileType('rb'),
4149 required=True)
4150 sub_parser.add_argument('--product_id',
4151 help=('Path to Product ID file'),
4152 type=argparse.FileType('rb'),
4153 required=True)
4154 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4155
4156 sub_parser = subparsers.add_parser(
4157 'make_atx_metadata',
4158 help='Create Android Things eXtension (ATX) metadata.')
4159 sub_parser.add_argument('--output',
4160 help='Write metadata to file',
4161 type=argparse.FileType('wb'),
4162 default=sys.stdout)
4163 sub_parser.add_argument('--intermediate_key_certificate',
4164 help='Path to intermediate key certificate file',
4165 type=argparse.FileType('rb'),
4166 required=True)
4167 sub_parser.add_argument('--product_key_certificate',
4168 help='Path to product key certificate file',
4169 type=argparse.FileType('rb'),
4170 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004171 sub_parser.set_defaults(func=self.make_atx_metadata)
4172
Darren Krahnfccd64e2018-01-16 17:39:35 -08004173 sub_parser = subparsers.add_parser(
4174 'make_atx_unlock_credential',
4175 help='Create an Android Things eXtension (ATX) unlock credential.')
4176 sub_parser.add_argument('--output',
4177 help='Write credential to file',
4178 type=argparse.FileType('wb'),
4179 default=sys.stdout)
4180 sub_parser.add_argument('--intermediate_key_certificate',
4181 help='Path to intermediate key certificate file',
4182 type=argparse.FileType('rb'),
4183 required=True)
4184 sub_parser.add_argument('--unlock_key_certificate',
4185 help='Path to unlock key certificate file',
4186 type=argparse.FileType('rb'),
4187 required=True)
4188 sub_parser.add_argument('--challenge',
4189 help='Path to the challenge to sign (optional). If '
4190 'this is not provided the challenge signature '
4191 'field is omitted and can be concatenated '
4192 'later.',
4193 required=False)
4194 sub_parser.add_argument('--unlock_key',
4195 help='Path to unlock key (optional). Must be '
4196 'provided if using --challenge.',
4197 required=False)
4198 sub_parser.add_argument('--signing_helper',
4199 help='Path to helper used for signing',
4200 metavar='APP',
4201 default=None,
4202 required=False)
4203 sub_parser.add_argument('--signing_helper_with_files',
4204 help='Path to helper used for signing using files',
4205 metavar='APP',
4206 default=None,
4207 required=False)
4208 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4209
David Zeuthen21e95262016-07-27 17:58:40 -04004210 args = parser.parse_args(argv[1:])
4211 try:
4212 args.func(args)
4213 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004214 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004215 sys.exit(1)
4216
4217 def version(self, _):
4218 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004219 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004220
4221 def extract_public_key(self, args):
4222 """Implements the 'extract_public_key' sub-command."""
4223 self.avb.extract_public_key(args.key, args.output)
4224
4225 def make_vbmeta_image(self, args):
4226 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004227 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004228 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004229 args.algorithm, args.key,
4230 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004231 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004232 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004233 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004234 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004235 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004236 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004237 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004238 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004239 args.print_required_libavb_version,
4240 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004241
David Zeuthenb1b994d2017-03-06 18:01:31 -05004242 def append_vbmeta_image(self, args):
4243 """Implements the 'append_vbmeta_image' sub-command."""
4244 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4245 args.partition_size)
4246
David Zeuthen21e95262016-07-27 17:58:40 -04004247 def add_hash_footer(self, args):
4248 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004249 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004250 self.avb.add_hash_footer(args.image.name if args.image else None,
4251 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004252 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004253 args.salt, args.chain_partition, args.algorithm,
4254 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004255 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004256 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004257 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004258 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004259 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004260 args.calc_max_image_size,
4261 args.signing_helper,
4262 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004263 args.internal_release_string,
4264 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004265 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004266 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004267 args.print_required_libavb_version,
4268 args.use_persistent_digest,
4269 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004270
4271 def add_hashtree_footer(self, args):
4272 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004273 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004274 # TODO(zeuthen): Remove when removing support for the
4275 # '--generate_fec' option above.
4276 if args.generate_fec:
4277 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4278 'is now generated by default. Use the option '
4279 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004280 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4281 args.partition_size,
4282 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004283 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004284 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004285 args.salt, args.chain_partition, args.algorithm,
4286 args.key, args.public_key_metadata,
4287 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004288 args.prop_from_file,
4289 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004290 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004291 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004292 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004293 args.calc_max_image_size,
4294 args.signing_helper,
4295 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004296 args.internal_release_string,
4297 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004298 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004299 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004300 args.print_required_libavb_version,
4301 args.use_persistent_digest,
4302 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05004303
David Zeuthen21e95262016-07-27 17:58:40 -04004304 def erase_footer(self, args):
4305 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004306 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004307
David Zeuthen49936b42018-08-07 17:38:58 -04004308 def extract_vbmeta_image(self, args):
4309 """Implements the 'extract_vbmeta_image' sub-command."""
4310 self.avb.extract_vbmeta_image(args.output, args.image.name,
4311 args.padding_size)
4312
David Zeuthen2bc232b2017-04-19 14:25:19 -04004313 def resize_image(self, args):
4314 """Implements the 'resize_image' sub-command."""
4315 self.avb.resize_image(args.image.name, args.partition_size)
4316
David Zeuthen8b6973b2016-09-20 12:39:49 -04004317 def set_ab_metadata(self, args):
4318 """Implements the 'set_ab_metadata' sub-command."""
4319 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4320
David Zeuthen21e95262016-07-27 17:58:40 -04004321 def info_image(self, args):
4322 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004323 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004324
David Zeuthenb623d8b2017-04-04 16:05:53 -04004325 def verify_image(self, args):
4326 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004327 self.avb.verify_image(args.image.name, args.key,
4328 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004329
David Zeuthenb8643c02018-05-17 17:21:18 -04004330 def calculate_vbmeta_digest(self, args):
4331 """Implements the 'calculate_vbmeta_digest' sub-command."""
4332 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4333 args.output)
4334
David Zeuthenf7d2e752018-09-20 13:30:41 -04004335 def calculate_kernel_cmdline(self, args):
4336 """Implements the 'calculate_kernel_cmdline' sub-command."""
4337 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, args.output)
4338
Darren Krahn147b08d2016-12-20 16:38:29 -08004339 def make_atx_certificate(self, args):
4340 """Implements the 'make_atx_certificate' sub-command."""
4341 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004342 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004343 args.subject_key_version,
4344 args.subject.read(),
4345 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004346 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004347 args.signing_helper,
4348 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004349
4350 def make_atx_permanent_attributes(self, args):
4351 """Implements the 'make_atx_permanent_attributes' sub-command."""
4352 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004353 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004354 args.product_id.read())
4355
4356 def make_atx_metadata(self, args):
4357 """Implements the 'make_atx_metadata' sub-command."""
4358 self.avb.make_atx_metadata(args.output,
4359 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004360 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004361
Darren Krahnfccd64e2018-01-16 17:39:35 -08004362 def make_atx_unlock_credential(self, args):
4363 """Implements the 'make_atx_unlock_credential' sub-command."""
4364 self.avb.make_atx_unlock_credential(
4365 args.output,
4366 args.intermediate_key_certificate.read(),
4367 args.unlock_key_certificate.read(),
4368 args.challenge,
4369 args.unlock_key,
4370 args.signing_helper,
4371 args.signing_helper_with_files)
4372
David Zeuthen21e95262016-07-27 17:58:40 -04004373
4374if __name__ == '__main__':
4375 tool = AvbTool()
4376 tool.run(sys.argv)