blob: 6960101dcce96372aa546e78af1351b21e26b764 [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
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500547 # We used to have this:
548 #
549 # import Crypto.PublicKey.RSA
550 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
551 # if not key.verify(decode_long(padding_and_digest),
552 # (decode_long(sig_blob), None)):
553 # return False
554 # return True
555 #
556 # but since 'avbtool verify_image' is used on the builders we don't want
557 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
558 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
559 '\n'
560 '[pubkeyinfo]\n'
561 'algorithm=SEQUENCE:rsa_alg\n'
562 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
563 '\n'
564 '[rsa_alg]\n'
565 'algorithm=OID:rsaEncryption\n'
566 'parameter=NULL\n'
567 '\n'
568 '[rsapubkey]\n'
569 'n=INTEGER:%s\n'
570 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'), hex(exponent).rstrip('L')))
571 asn1_tmpfile = tempfile.NamedTemporaryFile()
572 asn1_tmpfile.write(asn1_str)
573 asn1_tmpfile.flush()
574 der_tmpfile = tempfile.NamedTemporaryFile()
575 p = subprocess.Popen(
576 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out', der_tmpfile.name, '-noout'])
577 retcode = p.wait()
578 if retcode != 0:
579 raise AvbError('Error generating DER file')
580
581 p = subprocess.Popen(
582 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name, '-keyform', 'DER', '-raw'],
583 stdin=subprocess.PIPE,
584 stdout=subprocess.PIPE,
585 stderr=subprocess.PIPE)
586 (pout, perr) = p.communicate(str(sig_blob))
587 retcode = p.wait()
588 if retcode != 0:
589 raise AvbError('Error verifying data: {}'.format(perr))
590 recovered_data = bytearray(pout)
591 if recovered_data != padding_and_digest:
592 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400593 return False
594 return True
595
596
David Zeuthena4fee8b2016-08-22 15:20:43 -0400597class ImageChunk(object):
598 """Data structure used for representing chunks in Android sparse files.
599
600 Attributes:
601 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
602 chunk_offset: Offset in the sparse file where this chunk begins.
603 output_offset: Offset in de-sparsified file where output begins.
604 output_size: Number of bytes in output.
605 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
606 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
607 """
608
609 FORMAT = '<2H2I'
610 TYPE_RAW = 0xcac1
611 TYPE_FILL = 0xcac2
612 TYPE_DONT_CARE = 0xcac3
613 TYPE_CRC32 = 0xcac4
614
615 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
616 input_offset, fill_data):
617 """Initializes an ImageChunk object.
618
619 Arguments:
620 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
621 chunk_offset: Offset in the sparse file where this chunk begins.
622 output_offset: Offset in de-sparsified file.
623 output_size: Number of bytes in output.
624 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
625 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
626
627 Raises:
628 ValueError: If data is not well-formed.
629 """
630 self.chunk_type = chunk_type
631 self.chunk_offset = chunk_offset
632 self.output_offset = output_offset
633 self.output_size = output_size
634 self.input_offset = input_offset
635 self.fill_data = fill_data
636 # Check invariants.
637 if self.chunk_type == self.TYPE_RAW:
638 if self.fill_data is not None:
639 raise ValueError('RAW chunk cannot have fill_data set.')
640 if not self.input_offset:
641 raise ValueError('RAW chunk must have input_offset set.')
642 elif self.chunk_type == self.TYPE_FILL:
643 if self.fill_data is None:
644 raise ValueError('FILL chunk must have fill_data set.')
645 if self.input_offset:
646 raise ValueError('FILL chunk cannot have input_offset set.')
647 elif self.chunk_type == self.TYPE_DONT_CARE:
648 if self.fill_data is not None:
649 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
650 if self.input_offset:
651 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
652 else:
653 raise ValueError('Invalid chunk type')
654
655
656class ImageHandler(object):
657 """Abstraction for image I/O with support for Android sparse images.
658
659 This class provides an interface for working with image files that
660 may be using the Android Sparse Image format. When an instance is
661 constructed, we test whether it's an Android sparse file. If so,
662 operations will be on the sparse file by interpreting the sparse
663 format, otherwise they will be directly on the file. Either way the
664 operations do the same.
665
666 For reading, this interface mimics a file object - it has seek(),
667 tell(), and read() methods. For writing, only truncation
668 (truncate()) and appending is supported (append_raw() and
669 append_dont_care()). Additionally, data can only be written in units
670 of the block size.
671
672 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400673 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400674 is_sparse: Whether the file being operated on is sparse.
675 block_size: The block size, typically 4096.
676 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400677 """
678 # See system/core/libsparse/sparse_format.h for details.
679 MAGIC = 0xed26ff3a
680 HEADER_FORMAT = '<I4H4I'
681
682 # These are formats and offset of just the |total_chunks| and
683 # |total_blocks| fields.
684 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
685 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
686
687 def __init__(self, image_filename):
688 """Initializes an image handler.
689
690 Arguments:
691 image_filename: The name of the file to operate on.
692
693 Raises:
694 ValueError: If data in the file is invalid.
695 """
David Zeuthen49936b42018-08-07 17:38:58 -0400696 self.filename = image_filename
David Zeuthena4fee8b2016-08-22 15:20:43 -0400697 self._read_header()
698
699 def _read_header(self):
700 """Initializes internal data structures used for reading file.
701
702 This may be called multiple times and is typically called after
703 modifying the file (e.g. appending, truncation).
704
705 Raises:
706 ValueError: If data in the file is invalid.
707 """
708 self.is_sparse = False
709 self.block_size = 4096
710 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400711 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400712 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 self.image_size = self._image.tell()
714
715 self._image.seek(0, os.SEEK_SET)
716 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
717 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
718 block_size, self._num_total_blocks, self._num_total_chunks,
719 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
720 if magic != self.MAGIC:
721 # Not a sparse image, our job here is done.
722 return
723 if not (major_version == 1 and minor_version == 0):
724 raise ValueError('Encountered sparse image format version {}.{} but '
725 'only 1.0 is supported'.format(major_version,
726 minor_version))
727 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
728 raise ValueError('Unexpected file_hdr_sz value {}.'.
729 format(file_hdr_sz))
730 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
731 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
732 format(chunk_hdr_sz))
733
734 self.block_size = block_size
735
736 # Build an list of chunks by parsing the file.
737 self._chunks = []
738
739 # Find the smallest offset where only "Don't care" chunks
740 # follow. This will be the size of the content in the sparse
741 # image.
742 offset = 0
743 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400744 for _ in xrange(1, self._num_total_chunks + 1):
745 chunk_offset = self._image.tell()
746
747 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
748 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
749 header_bin)
750 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
751
David Zeuthena4fee8b2016-08-22 15:20:43 -0400752 if chunk_type == ImageChunk.TYPE_RAW:
753 if data_sz != (chunk_sz * self.block_size):
754 raise ValueError('Raw chunk input size ({}) does not match output '
755 'size ({})'.
756 format(data_sz, chunk_sz*self.block_size))
757 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
758 chunk_offset,
759 output_offset,
760 chunk_sz*self.block_size,
761 self._image.tell(),
762 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700763 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400764
765 elif chunk_type == ImageChunk.TYPE_FILL:
766 if data_sz != 4:
767 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
768 'has {}'.format(data_sz))
769 fill_data = self._image.read(4)
770 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
771 chunk_offset,
772 output_offset,
773 chunk_sz*self.block_size,
774 None,
775 fill_data))
776 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
777 if data_sz != 0:
778 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
779 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400780 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
781 chunk_offset,
782 output_offset,
783 chunk_sz*self.block_size,
784 None,
785 None))
786 elif chunk_type == ImageChunk.TYPE_CRC32:
787 if data_sz != 4:
788 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
789 'this has {}'.format(data_sz))
790 self._image.read(4)
791 else:
792 raise ValueError('Unknown chunk type {}'.format(chunk_type))
793
794 offset += chunk_sz
795 output_offset += chunk_sz*self.block_size
796
797 # Record where sparse data end.
798 self._sparse_end = self._image.tell()
799
800 # Now that we've traversed all chunks, sanity check.
801 if self._num_total_blocks != offset:
802 raise ValueError('The header said we should have {} output blocks, '
803 'but we saw {}'.format(self._num_total_blocks, offset))
804 junk_len = len(self._image.read())
805 if junk_len > 0:
806 raise ValueError('There were {} bytes of extra data at the end of the '
807 'file.'.format(junk_len))
808
David Zeuthen09692692016-09-30 16:16:40 -0400809 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400810 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400811
812 # This is used when bisecting in read() to find the initial slice.
813 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
814
815 self.is_sparse = True
816
817 def _update_chunks_and_blocks(self):
818 """Helper function to update the image header.
819
820 The the |total_chunks| and |total_blocks| fields in the header
821 will be set to value of the |_num_total_blocks| and
822 |_num_total_chunks| attributes.
823
824 """
825 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
826 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
827 self._num_total_blocks,
828 self._num_total_chunks))
829
830 def append_dont_care(self, num_bytes):
831 """Appends a DONT_CARE chunk to the sparse file.
832
833 The given number of bytes must be a multiple of the block size.
834
835 Arguments:
836 num_bytes: Size in number of bytes of the DONT_CARE chunk.
837 """
838 assert num_bytes % self.block_size == 0
839
840 if not self.is_sparse:
841 self._image.seek(0, os.SEEK_END)
842 # This is more efficient that writing NUL bytes since it'll add
843 # a hole on file systems that support sparse files (native
844 # sparse, not Android sparse).
845 self._image.truncate(self._image.tell() + num_bytes)
846 self._read_header()
847 return
848
849 self._num_total_chunks += 1
850 self._num_total_blocks += num_bytes / self.block_size
851 self._update_chunks_and_blocks()
852
853 self._image.seek(self._sparse_end, os.SEEK_SET)
854 self._image.write(struct.pack(ImageChunk.FORMAT,
855 ImageChunk.TYPE_DONT_CARE,
856 0, # Reserved
857 num_bytes / self.block_size,
858 struct.calcsize(ImageChunk.FORMAT)))
859 self._read_header()
860
861 def append_raw(self, data):
862 """Appends a RAW chunk to the sparse file.
863
864 The length of the given data must be a multiple of the block size.
865
866 Arguments:
867 data: Data to append.
868 """
869 assert len(data) % self.block_size == 0
870
871 if not self.is_sparse:
872 self._image.seek(0, os.SEEK_END)
873 self._image.write(data)
874 self._read_header()
875 return
876
877 self._num_total_chunks += 1
878 self._num_total_blocks += len(data) / self.block_size
879 self._update_chunks_and_blocks()
880
881 self._image.seek(self._sparse_end, os.SEEK_SET)
882 self._image.write(struct.pack(ImageChunk.FORMAT,
883 ImageChunk.TYPE_RAW,
884 0, # Reserved
885 len(data) / self.block_size,
886 len(data) +
887 struct.calcsize(ImageChunk.FORMAT)))
888 self._image.write(data)
889 self._read_header()
890
891 def append_fill(self, fill_data, size):
892 """Appends a fill chunk to the sparse file.
893
894 The total length of the fill data must be a multiple of the block size.
895
896 Arguments:
897 fill_data: Fill data to append - must be four bytes.
898 size: Number of chunk - must be a multiple of four and the block size.
899 """
900 assert len(fill_data) == 4
901 assert size % 4 == 0
902 assert size % self.block_size == 0
903
904 if not self.is_sparse:
905 self._image.seek(0, os.SEEK_END)
906 self._image.write(fill_data * (size/4))
907 self._read_header()
908 return
909
910 self._num_total_chunks += 1
911 self._num_total_blocks += size / self.block_size
912 self._update_chunks_and_blocks()
913
914 self._image.seek(self._sparse_end, os.SEEK_SET)
915 self._image.write(struct.pack(ImageChunk.FORMAT,
916 ImageChunk.TYPE_FILL,
917 0, # Reserved
918 size / self.block_size,
919 4 + struct.calcsize(ImageChunk.FORMAT)))
920 self._image.write(fill_data)
921 self._read_header()
922
923 def seek(self, offset):
924 """Sets the cursor position for reading from unsparsified file.
925
926 Arguments:
927 offset: Offset to seek to from the beginning of the file.
928 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700929 if offset < 0:
930 raise RuntimeError("Seeking with negative offset: %d" % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400931 self._file_pos = offset
932
933 def read(self, size):
934 """Reads data from the unsparsified file.
935
936 This method may return fewer than |size| bytes of data if the end
937 of the file was encountered.
938
939 The file cursor for reading is advanced by the number of bytes
940 read.
941
942 Arguments:
943 size: Number of bytes to read.
944
945 Returns:
946 The data.
947
948 """
949 if not self.is_sparse:
950 self._image.seek(self._file_pos)
951 data = self._image.read(size)
952 self._file_pos += len(data)
953 return data
954
955 # Iterate over all chunks.
956 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
957 self._file_pos) - 1
958 data = bytearray()
959 to_go = size
960 while to_go > 0:
961 chunk = self._chunks[chunk_idx]
962 chunk_pos_offset = self._file_pos - chunk.output_offset
963 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
964
965 if chunk.chunk_type == ImageChunk.TYPE_RAW:
966 self._image.seek(chunk.input_offset + chunk_pos_offset)
967 data.extend(self._image.read(chunk_pos_to_go))
968 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
969 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
970 offset_mod = chunk_pos_offset % len(chunk.fill_data)
971 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
972 else:
973 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
974 data.extend('\0' * chunk_pos_to_go)
975
976 to_go -= chunk_pos_to_go
977 self._file_pos += chunk_pos_to_go
978 chunk_idx += 1
979 # Generate partial read in case of EOF.
980 if chunk_idx >= len(self._chunks):
981 break
982
983 return data
984
985 def tell(self):
986 """Returns the file cursor position for reading from unsparsified file.
987
988 Returns:
989 The file cursor position for reading.
990 """
991 return self._file_pos
992
993 def truncate(self, size):
994 """Truncates the unsparsified file.
995
996 Arguments:
997 size: Desired size of unsparsified file.
998
999 Raises:
1000 ValueError: If desired size isn't a multiple of the block size.
1001 """
1002 if not self.is_sparse:
1003 self._image.truncate(size)
1004 self._read_header()
1005 return
1006
1007 if size % self.block_size != 0:
1008 raise ValueError('Cannot truncate to a size which is not a multiple '
1009 'of the block size')
1010
1011 if size == self.image_size:
1012 # Trivial where there's nothing to do.
1013 return
1014 elif size < self.image_size:
1015 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1016 chunk = self._chunks[chunk_idx]
1017 if chunk.output_offset != size:
1018 # Truncation in the middle of a trunk - need to keep the chunk
1019 # and modify it.
1020 chunk_idx_for_update = chunk_idx + 1
1021 num_to_keep = size - chunk.output_offset
1022 assert num_to_keep % self.block_size == 0
1023 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1024 truncate_at = (chunk.chunk_offset +
1025 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1026 data_sz = num_to_keep
1027 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1028 truncate_at = (chunk.chunk_offset +
1029 struct.calcsize(ImageChunk.FORMAT) + 4)
1030 data_sz = 4
1031 else:
1032 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1033 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1034 data_sz = 0
1035 chunk_sz = num_to_keep/self.block_size
1036 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1037 self._image.seek(chunk.chunk_offset)
1038 self._image.write(struct.pack(ImageChunk.FORMAT,
1039 chunk.chunk_type,
1040 0, # Reserved
1041 chunk_sz,
1042 total_sz))
1043 chunk.output_size = num_to_keep
1044 else:
1045 # Truncation at trunk boundary.
1046 truncate_at = chunk.chunk_offset
1047 chunk_idx_for_update = chunk_idx
1048
1049 self._num_total_chunks = chunk_idx_for_update
1050 self._num_total_blocks = 0
1051 for i in range(0, chunk_idx_for_update):
1052 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1053 self._update_chunks_and_blocks()
1054 self._image.truncate(truncate_at)
1055
1056 # We've modified the file so re-read all data.
1057 self._read_header()
1058 else:
1059 # Truncating to grow - just add a DONT_CARE section.
1060 self.append_dont_care(size - self.image_size)
1061
1062
David Zeuthen21e95262016-07-27 17:58:40 -04001063class AvbDescriptor(object):
1064 """Class for AVB descriptor.
1065
1066 See the |AvbDescriptor| C struct for more information.
1067
1068 Attributes:
1069 tag: The tag identifying what kind of descriptor this is.
1070 data: The data in the descriptor.
1071 """
1072
1073 SIZE = 16
1074 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1075
1076 def __init__(self, data):
1077 """Initializes a new property descriptor.
1078
1079 Arguments:
1080 data: If not None, must be a bytearray().
1081
1082 Raises:
1083 LookupError: If the given descriptor is malformed.
1084 """
1085 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1086
1087 if data:
1088 (self.tag, num_bytes_following) = (
1089 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1090 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1091 else:
1092 self.tag = None
1093 self.data = None
1094
1095 def print_desc(self, o):
1096 """Print the descriptor.
1097
1098 Arguments:
1099 o: The object to write the output to.
1100 """
1101 o.write(' Unknown descriptor:\n')
1102 o.write(' Tag: {}\n'.format(self.tag))
1103 if len(self.data) < 256:
1104 o.write(' Data: {} ({} bytes)\n'.format(
1105 repr(str(self.data)), len(self.data)))
1106 else:
1107 o.write(' Data: {} bytes\n'.format(len(self.data)))
1108
1109 def encode(self):
1110 """Serializes the descriptor.
1111
1112 Returns:
1113 A bytearray() with the descriptor data.
1114 """
1115 num_bytes_following = len(self.data)
1116 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1117 padding_size = nbf_with_padding - num_bytes_following
1118 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1119 padding = struct.pack(str(padding_size) + 'x')
1120 ret = desc + self.data + padding
1121 return bytearray(ret)
1122
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001123 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001124 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001125 """Verifies contents of the descriptor - used in verify_image sub-command.
1126
1127 Arguments:
1128 image_dir: The directory of the file being verified.
1129 image_ext: The extension of the file being verified (e.g. '.img').
1130 expected_chain_partitions_map: A map from partition name to the
1131 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001132 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001133 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001134
1135 Returns:
1136 True if the descriptor verifies, False otherwise.
1137 """
1138 # Nothing to do.
1139 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001140
1141class AvbPropertyDescriptor(AvbDescriptor):
1142 """A class for property descriptors.
1143
1144 See the |AvbPropertyDescriptor| C struct for more information.
1145
1146 Attributes:
1147 key: The key.
1148 value: The key.
1149 """
1150
1151 TAG = 0
1152 SIZE = 32
1153 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1154 'Q' # key size (bytes)
1155 'Q') # value size (bytes)
1156
1157 def __init__(self, data=None):
1158 """Initializes a new property descriptor.
1159
1160 Arguments:
1161 data: If not None, must be a bytearray of size |SIZE|.
1162
1163 Raises:
1164 LookupError: If the given descriptor is malformed.
1165 """
1166 AvbDescriptor.__init__(self, None)
1167 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1168
1169 if data:
1170 (tag, num_bytes_following, key_size,
1171 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1172 expected_size = round_to_multiple(
1173 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1174 if tag != self.TAG or num_bytes_following != expected_size:
1175 raise LookupError('Given data does not look like a property '
1176 'descriptor.')
1177 self.key = data[self.SIZE:(self.SIZE + key_size)]
1178 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1179 value_size)]
1180 else:
1181 self.key = ''
1182 self.value = ''
1183
1184 def print_desc(self, o):
1185 """Print the descriptor.
1186
1187 Arguments:
1188 o: The object to write the output to.
1189 """
1190 if len(self.value) < 256:
1191 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1192 else:
1193 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1194
1195 def encode(self):
1196 """Serializes the descriptor.
1197
1198 Returns:
1199 A bytearray() with the descriptor data.
1200 """
1201 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1202 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1203 padding_size = nbf_with_padding - num_bytes_following
1204 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1205 len(self.key), len(self.value))
1206 padding = struct.pack(str(padding_size) + 'x')
1207 ret = desc + self.key + '\0' + self.value + '\0' + padding
1208 return bytearray(ret)
1209
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001210 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001211 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001212 """Verifies contents of the descriptor - used in verify_image sub-command.
1213
1214 Arguments:
1215 image_dir: The directory of the file being verified.
1216 image_ext: The extension of the file being verified (e.g. '.img').
1217 expected_chain_partitions_map: A map from partition name to the
1218 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001219 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001220 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001221
1222 Returns:
1223 True if the descriptor verifies, False otherwise.
1224 """
1225 # Nothing to do.
1226 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001227
1228class AvbHashtreeDescriptor(AvbDescriptor):
1229 """A class for hashtree descriptors.
1230
1231 See the |AvbHashtreeDescriptor| C struct for more information.
1232
1233 Attributes:
1234 dm_verity_version: dm-verity version used.
1235 image_size: Size of the image, after rounding up to |block_size|.
1236 tree_offset: Offset of the hash tree in the file.
1237 tree_size: Size of the tree.
1238 data_block_size: Data block size
1239 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001240 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1241 fec_offset: Offset of FEC data (0 if FEC is not used).
1242 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001243 hash_algorithm: Hash algorithm used.
1244 partition_name: Partition name.
1245 salt: Salt used.
1246 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001247 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001248 """
1249
1250 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001251 RESERVED = 60
1252 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001253 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1254 'L' # dm-verity version used
1255 'Q' # image size (bytes)
1256 'Q' # tree offset (bytes)
1257 'Q' # tree size (bytes)
1258 'L' # data block size (bytes)
1259 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001260 'L' # FEC number of roots
1261 'Q' # FEC offset (bytes)
1262 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001263 '32s' # hash algorithm used
1264 'L' # partition name (bytes)
1265 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001266 'L' # root digest length (bytes)
1267 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001268 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001269
1270 def __init__(self, data=None):
1271 """Initializes a new hashtree descriptor.
1272
1273 Arguments:
1274 data: If not None, must be a bytearray of size |SIZE|.
1275
1276 Raises:
1277 LookupError: If the given descriptor is malformed.
1278 """
1279 AvbDescriptor.__init__(self, None)
1280 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1281
1282 if data:
1283 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1284 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001285 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1286 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001287 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1288 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001289 expected_size = round_to_multiple(
1290 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1291 if tag != self.TAG or num_bytes_following != expected_size:
1292 raise LookupError('Given data does not look like a hashtree '
1293 'descriptor.')
1294 # Nuke NUL-bytes at the end.
1295 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1296 o = 0
1297 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1298 partition_name_len)])
1299 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1300 self.partition_name.decode('utf-8')
1301 o += partition_name_len
1302 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1303 o += salt_len
1304 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1305 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001306 if root_digest_len != 0:
1307 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001308
1309 else:
1310 self.dm_verity_version = 0
1311 self.image_size = 0
1312 self.tree_offset = 0
1313 self.tree_size = 0
1314 self.data_block_size = 0
1315 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001316 self.fec_num_roots = 0
1317 self.fec_offset = 0
1318 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001319 self.hash_algorithm = ''
1320 self.partition_name = ''
1321 self.salt = bytearray()
1322 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001323 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001324
1325 def print_desc(self, o):
1326 """Print the descriptor.
1327
1328 Arguments:
1329 o: The object to write the output to.
1330 """
1331 o.write(' Hashtree descriptor:\n')
1332 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1333 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1334 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1335 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1336 o.write(' Data Block Size: {} bytes\n'.format(
1337 self.data_block_size))
1338 o.write(' Hash Block Size: {} bytes\n'.format(
1339 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001340 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1341 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1342 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001343 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1344 o.write(' Partition Name: {}\n'.format(self.partition_name))
1345 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1346 'hex')))
1347 o.write(' Root Digest: {}\n'.format(str(
1348 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001349 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001350
1351 def encode(self):
1352 """Serializes the descriptor.
1353
1354 Returns:
1355 A bytearray() with the descriptor data.
1356 """
1357 encoded_name = self.partition_name.encode('utf-8')
1358 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1359 len(self.root_digest) - 16)
1360 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1361 padding_size = nbf_with_padding - num_bytes_following
1362 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1363 self.dm_verity_version, self.image_size,
1364 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001365 self.hash_block_size, self.fec_num_roots,
1366 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001367 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001368 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001369 padding = struct.pack(str(padding_size) + 'x')
1370 ret = desc + encoded_name + self.salt + self.root_digest + padding
1371 return bytearray(ret)
1372
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001373 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001374 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001375 """Verifies contents of the descriptor - used in verify_image sub-command.
1376
1377 Arguments:
1378 image_dir: The directory of the file being verified.
1379 image_ext: The extension of the file being verified (e.g. '.img').
1380 expected_chain_partitions_map: A map from partition name to the
1381 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001382 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001383 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001384
1385 Returns:
1386 True if the descriptor verifies, False otherwise.
1387 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001388 if self.partition_name == '':
1389 image = image_containing_descriptor
1390 else:
1391 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1392 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001393 # Generate the hashtree and checks that it matches what's in the file.
1394 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1395 digest_padding = round_to_pow2(digest_size) - digest_size
1396 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1397 self.image_size, self.data_block_size, digest_size + digest_padding)
1398 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1399 self.data_block_size,
1400 self.hash_algorithm, self.salt,
1401 digest_padding,
1402 hash_level_offsets,
1403 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001404 # The root digest must match unless it is not embedded in the descriptor.
1405 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001406 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1407 format(image_filename))
1408 return False
1409 # ... also check that the on-disk hashtree matches
1410 image.seek(self.tree_offset)
1411 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001412 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001413 if is_zeroed and accept_zeroed_hashtree:
1414 print ('{}: skipping verification since hashtree is zeroed and --accept_zeroed_hashtree was given'
1415 .format(self.partition_name))
1416 else:
1417 if hash_tree != hash_tree_ondisk:
1418 sys.stderr.write('hashtree of {} contains invalid data\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001419 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001420 return False
1421 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1422 .format(self.partition_name, self.hash_algorithm, image.filename,
1423 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001424 # TODO: we could also verify that the FEC stored in the image is
1425 # correct but this a) currently requires the 'fec' binary; and b)
1426 # takes a long time; and c) is not strictly needed for
1427 # verification purposes as we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001428 return True
1429
David Zeuthen21e95262016-07-27 17:58:40 -04001430
1431class AvbHashDescriptor(AvbDescriptor):
1432 """A class for hash descriptors.
1433
1434 See the |AvbHashDescriptor| C struct for more information.
1435
1436 Attributes:
1437 image_size: Image size, in bytes.
1438 hash_algorithm: Hash algorithm used.
1439 partition_name: Partition name.
1440 salt: Salt used.
1441 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001442 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001443 """
1444
1445 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001446 RESERVED = 60
1447 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001448 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1449 'Q' # image size (bytes)
1450 '32s' # hash algorithm used
1451 'L' # partition name (bytes)
1452 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001453 'L' # digest length (bytes)
1454 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001455 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001456
1457 def __init__(self, data=None):
1458 """Initializes a new hash descriptor.
1459
1460 Arguments:
1461 data: If not None, must be a bytearray of size |SIZE|.
1462
1463 Raises:
1464 LookupError: If the given descriptor is malformed.
1465 """
1466 AvbDescriptor.__init__(self, None)
1467 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1468
1469 if data:
1470 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1471 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001472 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1473 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001474 expected_size = round_to_multiple(
1475 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1476 if tag != self.TAG or num_bytes_following != expected_size:
1477 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1478 # Nuke NUL-bytes at the end.
1479 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1480 o = 0
1481 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1482 partition_name_len)])
1483 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1484 self.partition_name.decode('utf-8')
1485 o += partition_name_len
1486 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1487 o += salt_len
1488 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1489 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001490 if digest_len != 0:
1491 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001492
1493 else:
1494 self.image_size = 0
1495 self.hash_algorithm = ''
1496 self.partition_name = ''
1497 self.salt = bytearray()
1498 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001499 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001500
1501 def print_desc(self, o):
1502 """Print the descriptor.
1503
1504 Arguments:
1505 o: The object to write the output to.
1506 """
1507 o.write(' Hash descriptor:\n')
1508 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1509 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1510 o.write(' Partition Name: {}\n'.format(self.partition_name))
1511 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1512 'hex')))
1513 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1514 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001515 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001516
1517 def encode(self):
1518 """Serializes the descriptor.
1519
1520 Returns:
1521 A bytearray() with the descriptor data.
1522 """
1523 encoded_name = self.partition_name.encode('utf-8')
1524 num_bytes_following = (
1525 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1526 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1527 padding_size = nbf_with_padding - num_bytes_following
1528 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1529 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001530 len(self.salt), len(self.digest), self.flags,
1531 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001532 padding = struct.pack(str(padding_size) + 'x')
1533 ret = desc + encoded_name + self.salt + self.digest + padding
1534 return bytearray(ret)
1535
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001536 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001537 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001538 """Verifies contents of the descriptor - used in verify_image sub-command.
1539
1540 Arguments:
1541 image_dir: The directory of the file being verified.
1542 image_ext: The extension of the file being verified (e.g. '.img').
1543 expected_chain_partitions_map: A map from partition name to the
1544 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001545 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001546 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001547
1548 Returns:
1549 True if the descriptor verifies, False otherwise.
1550 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001551 if self.partition_name == '':
1552 image = image_containing_descriptor
1553 else:
1554 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1555 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001556 data = image.read(self.image_size)
1557 ha = hashlib.new(self.hash_algorithm)
1558 ha.update(self.salt)
1559 ha.update(data)
1560 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001561 # The digest must match unless there is no digest in the descriptor.
1562 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001563 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1564 format(self.hash_algorithm, image_filename))
1565 return False
1566 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001567 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001568 self.image_size))
1569 return True
1570
David Zeuthen21e95262016-07-27 17:58:40 -04001571
1572class AvbKernelCmdlineDescriptor(AvbDescriptor):
1573 """A class for kernel command-line descriptors.
1574
1575 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1576
1577 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001578 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001579 kernel_cmdline: The kernel command-line.
1580 """
1581
1582 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001583 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001584 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001585 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001586 'L') # cmdline length (bytes)
1587
David Zeuthenfd41eb92016-11-17 12:24:47 -05001588 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1589 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1590
David Zeuthen21e95262016-07-27 17:58:40 -04001591 def __init__(self, data=None):
1592 """Initializes a new kernel cmdline descriptor.
1593
1594 Arguments:
1595 data: If not None, must be a bytearray of size |SIZE|.
1596
1597 Raises:
1598 LookupError: If the given descriptor is malformed.
1599 """
1600 AvbDescriptor.__init__(self, None)
1601 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1602
1603 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001604 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001605 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1606 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1607 8)
1608 if tag != self.TAG or num_bytes_following != expected_size:
1609 raise LookupError('Given data does not look like a kernel cmdline '
1610 'descriptor.')
1611 # Nuke NUL-bytes at the end.
1612 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1613 kernel_cmdline_length)])
1614 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1615 self.kernel_cmdline.decode('utf-8')
1616 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001617 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001618 self.kernel_cmdline = ''
1619
1620 def print_desc(self, o):
1621 """Print the descriptor.
1622
1623 Arguments:
1624 o: The object to write the output to.
1625 """
1626 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001627 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001628 o.write(' Kernel Cmdline: {}\n'.format(repr(
1629 self.kernel_cmdline)))
1630
1631 def encode(self):
1632 """Serializes the descriptor.
1633
1634 Returns:
1635 A bytearray() with the descriptor data.
1636 """
1637 encoded_str = self.kernel_cmdline.encode('utf-8')
1638 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1639 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1640 padding_size = nbf_with_padding - num_bytes_following
1641 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001642 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001643 padding = struct.pack(str(padding_size) + 'x')
1644 ret = desc + encoded_str + padding
1645 return bytearray(ret)
1646
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001647 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001648 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001649 """Verifies contents of the descriptor - used in verify_image sub-command.
1650
1651 Arguments:
1652 image_dir: The directory of the file being verified.
1653 image_ext: The extension of the file being verified (e.g. '.img').
1654 expected_chain_partitions_map: A map from partition name to the
1655 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001656 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001657 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001658
1659 Returns:
1660 True if the descriptor verifies, False otherwise.
1661 """
1662 # Nothing to verify.
1663 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001664
1665class AvbChainPartitionDescriptor(AvbDescriptor):
1666 """A class for chained partition descriptors.
1667
1668 See the |AvbChainPartitionDescriptor| C struct for more information.
1669
1670 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001671 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001672 partition_name: Partition name.
1673 public_key: Bytes for the public key.
1674 """
1675
1676 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001677 RESERVED = 64
1678 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001679 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001680 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001681 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001682 'L' + # public_key_size (bytes)
1683 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001684
1685 def __init__(self, data=None):
1686 """Initializes a new chain partition descriptor.
1687
1688 Arguments:
1689 data: If not None, must be a bytearray of size |SIZE|.
1690
1691 Raises:
1692 LookupError: If the given descriptor is malformed.
1693 """
1694 AvbDescriptor.__init__(self, None)
1695 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1696
1697 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001698 (tag, num_bytes_following, self.rollback_index_location,
1699 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001700 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001701 expected_size = round_to_multiple(
1702 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1703 if tag != self.TAG or num_bytes_following != expected_size:
1704 raise LookupError('Given data does not look like a chain partition '
1705 'descriptor.')
1706 o = 0
1707 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1708 partition_name_len)])
1709 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1710 self.partition_name.decode('utf-8')
1711 o += partition_name_len
1712 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1713
1714 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001715 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001716 self.partition_name = ''
1717 self.public_key = bytearray()
1718
1719 def print_desc(self, o):
1720 """Print the descriptor.
1721
1722 Arguments:
1723 o: The object to write the output to.
1724 """
1725 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001726 o.write(' Partition Name: {}\n'.format(self.partition_name))
1727 o.write(' Rollback Index Location: {}\n'.format(
1728 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001729 # Just show the SHA1 of the key, for size reasons.
1730 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001731 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001732
1733 def encode(self):
1734 """Serializes the descriptor.
1735
1736 Returns:
1737 A bytearray() with the descriptor data.
1738 """
1739 encoded_name = self.partition_name.encode('utf-8')
1740 num_bytes_following = (
1741 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1742 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1743 padding_size = nbf_with_padding - num_bytes_following
1744 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001745 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001746 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001747 padding = struct.pack(str(padding_size) + 'x')
1748 ret = desc + encoded_name + self.public_key + padding
1749 return bytearray(ret)
1750
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001751 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001752 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001753 """Verifies contents of the descriptor - used in verify_image sub-command.
1754
1755 Arguments:
1756 image_dir: The directory of the file being verified.
1757 image_ext: The extension of the file being verified (e.g. '.img').
1758 expected_chain_partitions_map: A map from partition name to the
1759 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001760 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001761 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001762
1763 Returns:
1764 True if the descriptor verifies, False otherwise.
1765 """
1766 value = expected_chain_partitions_map.get(self.partition_name)
1767 if not value:
1768 sys.stderr.write('No expected chain partition for partition {}. Use '
1769 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001770 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001771 format(self.partition_name))
1772 return False
1773 rollback_index_location, pk_blob = value
1774
1775 if self.rollback_index_location != rollback_index_location:
1776 sys.stderr.write('Expected rollback_index_location {} does not '
1777 'match {} in descriptor for partition {}\n'.
1778 format(rollback_index_location,
1779 self.rollback_index_location,
1780 self.partition_name))
1781 return False
1782
1783 if self.public_key != pk_blob:
1784 sys.stderr.write('Expected public key blob does not match public '
1785 'key blob in descriptor for partition {}\n'.
1786 format(self.partition_name))
1787 return False
1788
1789 print ('{}: Successfully verified chain partition descriptor matches '
1790 'expected data'.format(self.partition_name))
1791
1792 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001793
1794DESCRIPTOR_CLASSES = [
1795 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1796 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1797]
1798
1799
1800def parse_descriptors(data):
1801 """Parses a blob of data into descriptors.
1802
1803 Arguments:
1804 data: A bytearray() with encoded descriptors.
1805
1806 Returns:
1807 A list of instances of objects derived from AvbDescriptor. For
1808 unknown descriptors, the class AvbDescriptor is used.
1809 """
1810 o = 0
1811 ret = []
1812 while o < len(data):
1813 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1814 if tag < len(DESCRIPTOR_CLASSES):
1815 c = DESCRIPTOR_CLASSES[tag]
1816 else:
1817 c = AvbDescriptor
1818 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1819 o += 16 + nb_following
1820 return ret
1821
1822
1823class AvbFooter(object):
1824 """A class for parsing and writing footers.
1825
1826 Footers are stored at the end of partitions and point to where the
1827 AvbVBMeta blob is located. They also contain the original size of
1828 the image before AVB information was added.
1829
1830 Attributes:
1831 magic: Magic for identifying the footer, see |MAGIC|.
1832 version_major: The major version of avbtool that wrote the footer.
1833 version_minor: The minor version of avbtool that wrote the footer.
1834 original_image_size: Original image size.
1835 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1836 vbmeta_size: Size of the AvbVBMeta blob.
1837 """
1838
1839 MAGIC = 'AVBf'
1840 SIZE = 64
1841 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001842 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1843 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001844 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1845 'Q' # Original image size.
1846 'Q' # Offset of VBMeta blob.
1847 'Q' + # Size of VBMeta blob.
1848 str(RESERVED) + 'x') # padding for reserved bytes
1849
1850 def __init__(self, data=None):
1851 """Initializes a new footer object.
1852
1853 Arguments:
1854 data: If not None, must be a bytearray of size 4096.
1855
1856 Raises:
1857 LookupError: If the given footer is malformed.
1858 struct.error: If the given data has no footer.
1859 """
1860 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1861
1862 if data:
1863 (self.magic, self.version_major, self.version_minor,
1864 self.original_image_size, self.vbmeta_offset,
1865 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1866 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001867 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001868 else:
1869 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001870 self.version_major = self.FOOTER_VERSION_MAJOR
1871 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001872 self.original_image_size = 0
1873 self.vbmeta_offset = 0
1874 self.vbmeta_size = 0
1875
David Zeuthena4fee8b2016-08-22 15:20:43 -04001876 def encode(self):
1877 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001878
David Zeuthena4fee8b2016-08-22 15:20:43 -04001879 Returns:
1880 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001881 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001882 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1883 self.version_minor, self.original_image_size,
1884 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001885
1886
1887class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001888 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001889
1890 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07001891 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1892 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04001893 """
1894
1895 SIZE = 256
1896
David Zeuthene3cadca2017-02-22 21:25:46 -05001897 # Keep in sync with |reserved0| and |reserved| field of
1898 # |AvbVBMetaImageHeader|.
1899 RESERVED0 = 4
1900 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001901
1902 # Keep in sync with |AvbVBMetaImageHeader|.
1903 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1904 '2Q' # 2 x block size
1905 'L' # algorithm type
1906 '2Q' # offset, size (hash)
1907 '2Q' # offset, size (signature)
1908 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001909 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001910 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001911 'Q' # rollback_index
1912 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001913 str(RESERVED0) + 'x' + # padding for reserved bytes
1914 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001915 str(RESERVED) + 'x') # padding for reserved bytes
1916
1917 def __init__(self, data=None):
1918 """Initializes a new header object.
1919
1920 Arguments:
1921 data: If not None, must be a bytearray of size 8192.
1922
1923 Raises:
1924 Exception: If the given data is malformed.
1925 """
1926 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1927
1928 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001929 (self.magic, self.required_libavb_version_major,
1930 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001931 self.authentication_data_block_size, self.auxiliary_data_block_size,
1932 self.algorithm_type, self.hash_offset, self.hash_size,
1933 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001934 self.public_key_size, self.public_key_metadata_offset,
1935 self.public_key_metadata_size, self.descriptors_offset,
1936 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001937 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001938 self.flags,
1939 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001940 # Nuke NUL-bytes at the end of the string.
1941 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001942 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001943 else:
1944 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001945 # Start by just requiring version 1.0. Code that adds features
1946 # in a future version can use bump_required_libavb_version_minor() to
1947 # bump the minor.
1948 self.required_libavb_version_major = AVB_VERSION_MAJOR
1949 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001950 self.authentication_data_block_size = 0
1951 self.auxiliary_data_block_size = 0
1952 self.algorithm_type = 0
1953 self.hash_offset = 0
1954 self.hash_size = 0
1955 self.signature_offset = 0
1956 self.signature_size = 0
1957 self.public_key_offset = 0
1958 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001959 self.public_key_metadata_offset = 0
1960 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001961 self.descriptors_offset = 0
1962 self.descriptors_size = 0
1963 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001964 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001965 self.release_string = get_release_string()
1966
1967 def bump_required_libavb_version_minor(self, minor):
1968 """Function to bump required_libavb_version_minor.
1969
1970 Call this when writing data that requires a specific libavb
1971 version to parse it.
1972
1973 Arguments:
1974 minor: The minor version of libavb that has support for the feature.
1975 """
1976 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001977 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001978
1979 def save(self, output):
1980 """Serializes the header (256 bytes) to disk.
1981
1982 Arguments:
1983 output: The object to write the output to.
1984 """
1985 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001986 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1987 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001988 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1989 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001990 self.public_key_offset, self.public_key_size,
1991 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001992 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001993 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001994
1995 def encode(self):
1996 """Serializes the header (256) to a bytearray().
1997
1998 Returns:
1999 A bytearray() with the encoded header.
2000 """
2001 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002002 self.required_libavb_version_major,
2003 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002004 self.authentication_data_block_size,
2005 self.auxiliary_data_block_size, self.algorithm_type,
2006 self.hash_offset, self.hash_size, self.signature_offset,
2007 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002008 self.public_key_size, self.public_key_metadata_offset,
2009 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002010 self.descriptors_size, self.rollback_index, self.flags,
2011 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002012
2013
2014class Avb(object):
2015 """Business logic for avbtool command-line tool."""
2016
David Zeuthen8b6973b2016-09-20 12:39:49 -04002017 # Keep in sync with avb_ab_flow.h.
2018 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2019 AB_MAGIC = '\0AB0'
2020 AB_MAJOR_VERSION = 1
2021 AB_MINOR_VERSION = 0
2022 AB_MISC_METADATA_OFFSET = 2048
2023
David Zeuthen09692692016-09-30 16:16:40 -04002024 # Constants for maximum metadata size. These are used to give
2025 # meaningful errors if the value passed in via --partition_size is
2026 # too small and when --calc_max_image_size is used. We use
2027 # conservative figures.
2028 MAX_VBMETA_SIZE = 64 * 1024
2029 MAX_FOOTER_SIZE = 4096
2030
David Zeuthen49936b42018-08-07 17:38:58 -04002031 def extract_vbmeta_image(self, output, image_filename, padding_size):
2032 """Implements the 'extract_vbmeta_image' command.
2033
2034 Arguments:
2035 output: Write vbmeta struct to this file.
2036 image_filename: File to extract vbmeta data from (with a footer).
2037 padding_size: If not 0, pads output so size is a multiple of the number.
2038
2039 Raises:
2040 AvbError: If there's no footer in the image.
2041 """
2042 image = ImageHandler(image_filename)
2043
2044 (footer, _, _, _) = self._parse_image(image)
2045
2046 if not footer:
2047 raise AvbError('Given image does not have a footer.')
2048
2049 image.seek(footer.vbmeta_offset)
2050 vbmeta_blob = image.read(footer.vbmeta_size)
2051 output.write(vbmeta_blob)
2052
2053 if padding_size > 0:
2054 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2055 padding_needed = padded_size - len(vbmeta_blob)
2056 output.write('\0' * padding_needed)
2057
David Zeuthena4fee8b2016-08-22 15:20:43 -04002058 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002059 """Implements the 'erase_footer' command.
2060
2061 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002062 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002063 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002064
2065 Raises:
2066 AvbError: If there's no footer in the image.
2067 """
2068
David Zeuthena4fee8b2016-08-22 15:20:43 -04002069 image = ImageHandler(image_filename)
2070
David Zeuthen21e95262016-07-27 17:58:40 -04002071 (footer, _, descriptors, _) = self._parse_image(image)
2072
2073 if not footer:
2074 raise AvbError('Given image does not have a footer.')
2075
2076 new_image_size = None
2077 if not keep_hashtree:
2078 new_image_size = footer.original_image_size
2079 else:
2080 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002081 # descriptor to figure out the location and size of the hashtree
2082 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002083 for desc in descriptors:
2084 if isinstance(desc, AvbHashtreeDescriptor):
2085 # The hashtree is always just following the main data so the
2086 # new size is easily derived.
2087 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002088 # If the image has FEC codes, also keep those.
2089 if desc.fec_offset > 0:
2090 fec_end = desc.fec_offset + desc.fec_size
2091 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002092 break
2093 if not new_image_size:
2094 raise AvbError('Requested to keep hashtree but no hashtree '
2095 'descriptor was found.')
2096
2097 # And cut...
2098 image.truncate(new_image_size)
2099
David Zeuthen1394f762019-04-30 10:20:11 -04002100 def zero_hashtree(self, image_filename):
2101 """Implements the 'zero_hashtree' command.
2102
2103 Arguments:
2104 image_filename: File to zero hashtree and FEC data from.
2105
2106 Raises:
2107 AvbError: If there's no footer in the image.
2108 """
2109
2110 image = ImageHandler(image_filename)
2111
2112 (footer, _, descriptors, _) = self._parse_image(image)
2113
2114 if not footer:
2115 raise AvbError('Given image does not have a footer.')
2116
2117 # Search for a hashtree descriptor to figure out the location and
2118 # size of the hashtree and FEC.
2119 ht_desc = None
2120 for desc in descriptors:
2121 if isinstance(desc, AvbHashtreeDescriptor):
2122 ht_desc = desc
2123 break
2124
2125 if not ht_desc:
2126 raise AvbError('No hashtree descriptor was found.')
2127
2128 zero_ht_start_offset = ht_desc.tree_offset
2129 zero_ht_num_bytes = ht_desc.tree_size
2130 zero_fec_start_offset = None
2131 zero_fec_num_bytes = 0
2132 if ht_desc.fec_offset > 0:
2133 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2134 raise AvbError('Hash-tree and FEC data must be adjacent.')
2135 zero_fec_start_offset = ht_desc.fec_offset
2136 zero_fec_num_bytes = ht_desc.fec_size
2137 zero_end_offset = zero_ht_start_offset + zero_ht_num_bytes + zero_fec_num_bytes
2138 image.seek(zero_end_offset)
2139 data = image.read(image.image_size - zero_end_offset)
2140
2141 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2142 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2143 # beginning of both hashtree and FEC. (That way, in the future we can add
2144 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2145 #
2146 # Applications can use these markers to detect that the hashtree and/or
2147 # FEC needs to be recomputed.
2148 image.truncate(zero_ht_start_offset)
2149 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2150 image.append_raw(data_zeroed_firstblock)
2151 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2152 if zero_fec_start_offset:
2153 image.append_raw(data_zeroed_firstblock)
2154 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2155 image.append_raw(data)
2156
David Zeuthen2bc232b2017-04-19 14:25:19 -04002157 def resize_image(self, image_filename, partition_size):
2158 """Implements the 'resize_image' command.
2159
2160 Arguments:
2161 image_filename: File with footer to resize.
2162 partition_size: The new size of the image.
2163
2164 Raises:
2165 AvbError: If there's no footer in the image.
2166 """
2167
2168 image = ImageHandler(image_filename)
2169
2170 if partition_size % image.block_size != 0:
2171 raise AvbError('Partition size of {} is not a multiple of the image '
2172 'block size {}.'.format(partition_size,
2173 image.block_size))
2174
2175 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2176
2177 if not footer:
2178 raise AvbError('Given image does not have a footer.')
2179
2180 # The vbmeta blob is always at the end of the data so resizing an
2181 # image amounts to just moving the footer around.
2182
2183 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2184 if vbmeta_end_offset % image.block_size != 0:
2185 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2186
2187 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002188 raise AvbError('Requested size of {} is too small for an image '
2189 'of size {}.'
2190 .format(partition_size,
2191 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002192
2193 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2194 # with enough bytes such that the final Footer block is at the end
2195 # of partition_size.
2196 image.truncate(vbmeta_end_offset)
2197 image.append_dont_care(partition_size - vbmeta_end_offset -
2198 1*image.block_size)
2199
2200 # Just reuse the same footer - only difference is that we're
2201 # writing it in a different place.
2202 footer_blob = footer.encode()
2203 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2204 footer_blob)
2205 image.append_raw(footer_blob_with_padding)
2206
David Zeuthen8b6973b2016-09-20 12:39:49 -04002207 def set_ab_metadata(self, misc_image, slot_data):
2208 """Implements the 'set_ab_metadata' command.
2209
2210 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2211 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2212
2213 Arguments:
2214 misc_image: The misc image to write to.
2215 slot_data: Slot data as a string
2216
2217 Raises:
2218 AvbError: If slot data is malformed.
2219 """
2220 tokens = slot_data.split(':')
2221 if len(tokens) != 6:
2222 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2223 a_priority = int(tokens[0])
2224 a_tries_remaining = int(tokens[1])
2225 a_success = True if int(tokens[2]) != 0 else False
2226 b_priority = int(tokens[3])
2227 b_tries_remaining = int(tokens[4])
2228 b_success = True if int(tokens[5]) != 0 else False
2229
2230 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2231 self.AB_MAGIC,
2232 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2233 a_priority, a_tries_remaining, a_success,
2234 b_priority, b_tries_remaining, b_success)
2235 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2236 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2237 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2238 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2239 misc_image.write(ab_data)
2240
David Zeuthena4fee8b2016-08-22 15:20:43 -04002241 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002242 """Implements the 'info_image' command.
2243
2244 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002245 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002246 output: Output file to write human-readable information to (file object).
2247 """
2248
David Zeuthena4fee8b2016-08-22 15:20:43 -04002249 image = ImageHandler(image_filename)
2250
David Zeuthen21e95262016-07-27 17:58:40 -04002251 o = output
2252
2253 (footer, header, descriptors, image_size) = self._parse_image(image)
2254
2255 if footer:
2256 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2257 footer.version_minor))
2258 o.write('Image size: {} bytes\n'.format(image_size))
2259 o.write('Original image size: {} bytes\n'.format(
2260 footer.original_image_size))
2261 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2262 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2263 o.write('--\n')
2264
2265 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2266
David Zeuthene3cadca2017-02-22 21:25:46 -05002267 o.write('Minimum libavb version: {}.{}{}\n'.format(
2268 header.required_libavb_version_major,
2269 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002270 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002271 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2272 o.write('Authentication Block: {} bytes\n'.format(
2273 header.authentication_data_block_size))
2274 o.write('Auxiliary Block: {} bytes\n'.format(
2275 header.auxiliary_data_block_size))
2276 o.write('Algorithm: {}\n'.format(alg_name))
2277 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002278 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002279 o.write('Release String: \'{}\'\n'.format(
2280 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002281
2282 # Print descriptors.
2283 num_printed = 0
2284 o.write('Descriptors:\n')
2285 for desc in descriptors:
2286 desc.print_desc(o)
2287 num_printed += 1
2288 if num_printed == 0:
2289 o.write(' (none)\n')
2290
David Zeuthen1394f762019-04-30 10:20:11 -04002291 def verify_image(self, image_filename, key_path, expected_chain_partitions, follow_chain_partitions,
2292 accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002293 """Implements the 'verify_image' command.
2294
2295 Arguments:
2296 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002297 key_path: None or check that embedded public key matches key at given path.
2298 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthene947cb62019-01-25 15:27:08 -05002299 follow_chain_partitions: If True, will follows chain partitions even when not
2300 specified with the --expected_chain_partition option
David Zeuthen1394f762019-04-30 10:20:11 -04002301 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002302 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002303 expected_chain_partitions_map = {}
2304 if expected_chain_partitions:
2305 used_locations = {}
2306 for cp in expected_chain_partitions:
2307 cp_tokens = cp.split(':')
2308 if len(cp_tokens) != 3:
2309 raise AvbError('Malformed chained partition "{}".'.format(cp))
2310 partition_name = cp_tokens[0]
2311 rollback_index_location = int(cp_tokens[1])
2312 file_path = cp_tokens[2]
2313 pk_blob = open(file_path).read()
2314 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2315
2316 image_dir = os.path.dirname(image_filename)
2317 image_ext = os.path.splitext(image_filename)[1]
2318
2319 key_blob = None
2320 if key_path:
2321 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2322 key_blob = encode_rsa_key(key_path)
2323 else:
2324 print 'Verifying image {} using embedded public key'.format(image_filename)
2325
David Zeuthenb623d8b2017-04-04 16:05:53 -04002326 image = ImageHandler(image_filename)
2327 (footer, header, descriptors, image_size) = self._parse_image(image)
2328 offset = 0
2329 if footer:
2330 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002331
David Zeuthenb623d8b2017-04-04 16:05:53 -04002332 image.seek(offset)
David Zeuthen49936b42018-08-07 17:38:58 -04002333 vbmeta_blob = image.read(header.SIZE + header.authentication_data_block_size +
2334 header.auxiliary_data_block_size)
2335
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002336 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002337 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002338 raise AvbError('Signature check failed for {} vbmeta struct {}'
2339 .format(alg_name, image_filename))
2340
2341 if key_blob:
2342 # The embedded public key is in the auxiliary block at an offset.
2343 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002344 key_offset += header.authentication_data_block_size
2345 key_offset += header.public_key_offset
2346 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002347 if key_blob != key_blob_in_vbmeta:
2348 raise AvbError('Embedded public key does not match given key.')
2349
2350 if footer:
2351 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002352 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002353 else:
2354 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002355 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002356
2357 for desc in descriptors:
David Zeuthene947cb62019-01-25 15:27:08 -05002358 if (isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions and
2359 expected_chain_partitions_map.get(desc.partition_name) == None):
2360 # In this case we're processing a chain descriptor but don't have a
2361 # --expect_chain_partition ... however --follow_chain_partitions was
2362 # specified so we shouldn't error out in desc.verify().
2363 print ('{}: Chained but ROLLBACK_SLOT (which is {}) and KEY (which has sha1 {}) not specified'
2364 .format(desc.partition_name, desc.rollback_index_location,
2365 hashlib.sha1(desc.public_key).hexdigest()))
2366 else:
David Zeuthen1394f762019-04-30 10:20:11 -04002367 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image,
2368 accept_zeroed_hashtree):
David Zeuthene947cb62019-01-25 15:27:08 -05002369 raise AvbError('Error verifying descriptor.')
2370 # Honor --follow_chain_partitions - add '--' to make the output more readable.
2371 if isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions:
2372 print '--'
2373 chained_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
David Zeuthen9d364652019-05-14 15:30:37 -04002374 self.verify_image(chained_image_filename, key_path, None, False, accept_zeroed_hashtree)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002375
David Zeuthenb623d8b2017-04-04 16:05:53 -04002376
David Zeuthenb8643c02018-05-17 17:21:18 -04002377 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2378 """Implements the 'calculate_vbmeta_digest' command.
2379
2380 Arguments:
2381 image_filename: Image file to get information from (file object).
2382 hash_algorithm: Hash algorithm used.
2383 output: Output file to write human-readable information to (file object).
2384 """
2385
2386 image_dir = os.path.dirname(image_filename)
2387 image_ext = os.path.splitext(image_filename)[1]
2388
2389 image = ImageHandler(image_filename)
2390 (footer, header, descriptors, image_size) = self._parse_image(image)
2391 offset = 0
2392 if footer:
2393 offset = footer.vbmeta_offset
2394 size = (header.SIZE + header.authentication_data_block_size +
2395 header.auxiliary_data_block_size)
2396 image.seek(offset)
2397 vbmeta_blob = image.read(size)
2398
2399 hasher = hashlib.new(name=hash_algorithm)
2400 hasher.update(vbmeta_blob)
2401
2402 for desc in descriptors:
2403 if isinstance(desc, AvbChainPartitionDescriptor):
2404 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2405 ch_image = ImageHandler(ch_image_filename)
2406 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2407 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002408 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2409 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002410 if ch_footer:
2411 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002412 ch_image.seek(ch_offset)
2413 ch_vbmeta_blob = ch_image.read(ch_size)
2414 hasher.update(ch_vbmeta_blob)
2415
2416 digest = hasher.digest()
2417 output.write('{}\n'.format(digest.encode('hex')))
2418
2419
David Zeuthenf7d2e752018-09-20 13:30:41 -04002420 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2421 """Implements the 'calculate_kernel_cmdline' command.
2422
2423 Arguments:
2424 image_filename: Image file to get information from (file object).
2425 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2426 output: Output file to write human-readable information to (file object).
2427 """
2428
2429 image = ImageHandler(image_filename)
2430 _, _, descriptors, _ = self._parse_image(image)
2431
2432 image_dir = os.path.dirname(image_filename)
2433 image_ext = os.path.splitext(image_filename)[1]
2434
2435 cmdline_descriptors = []
2436 for desc in descriptors:
2437 if isinstance(desc, AvbChainPartitionDescriptor):
2438 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2439 ch_image = ImageHandler(ch_image_filename)
2440 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2441 for ch_desc in ch_descriptors:
2442 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2443 cmdline_descriptors.append(ch_desc)
2444 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2445 cmdline_descriptors.append(desc)
2446
2447 kernel_cmdline_snippets = []
2448 for desc in cmdline_descriptors:
2449 use_cmdline = True
2450 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) != 0:
2451 if hashtree_disabled:
2452 use_cmdline = False
2453 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2454 if not hashtree_disabled:
2455 use_cmdline = False
2456 if use_cmdline:
2457 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2458 output.write(' '.join(kernel_cmdline_snippets))
2459
2460
David Zeuthen21e95262016-07-27 17:58:40 -04002461 def _parse_image(self, image):
2462 """Gets information about an image.
2463
2464 The image can either be a vbmeta or an image with a footer.
2465
2466 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002467 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002468
2469 Returns:
2470 A tuple where the first argument is a AvbFooter (None if there
2471 is no footer on the image), the second argument is a
2472 AvbVBMetaHeader, the third argument is a list of
2473 AvbDescriptor-derived instances, and the fourth argument is the
2474 size of |image|.
2475 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002476 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002477 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002478 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002479 try:
2480 footer = AvbFooter(image.read(AvbFooter.SIZE))
2481 except (LookupError, struct.error):
2482 # Nope, just seek back to the start.
2483 image.seek(0)
2484
2485 vbmeta_offset = 0
2486 if footer:
2487 vbmeta_offset = footer.vbmeta_offset
2488
2489 image.seek(vbmeta_offset)
2490 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2491
2492 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2493 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2494 desc_start_offset = aux_block_offset + h.descriptors_offset
2495 image.seek(desc_start_offset)
2496 descriptors = parse_descriptors(image.read(h.descriptors_size))
2497
David Zeuthen09692692016-09-30 16:16:40 -04002498 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002499
David Zeuthenb1b994d2017-03-06 18:01:31 -05002500 def _load_vbmeta_blob(self, image):
2501 """Gets the vbmeta struct and associated sections.
2502
2503 The image can either be a vbmeta.img or an image with a footer.
2504
2505 Arguments:
2506 image: An ImageHandler (vbmeta or footer).
2507
2508 Returns:
2509 A blob with the vbmeta struct and other sections.
2510 """
2511 assert isinstance(image, ImageHandler)
2512 footer = None
2513 image.seek(image.image_size - AvbFooter.SIZE)
2514 try:
2515 footer = AvbFooter(image.read(AvbFooter.SIZE))
2516 except (LookupError, struct.error):
2517 # Nope, just seek back to the start.
2518 image.seek(0)
2519
2520 vbmeta_offset = 0
2521 if footer:
2522 vbmeta_offset = footer.vbmeta_offset
2523
2524 image.seek(vbmeta_offset)
2525 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2526
2527 image.seek(vbmeta_offset)
2528 data_size = AvbVBMetaHeader.SIZE
2529 data_size += h.authentication_data_block_size
2530 data_size += h.auxiliary_data_block_size
2531 return image.read(data_size)
2532
David Zeuthen73f2afa2017-05-17 16:54:11 -04002533 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002534 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002535
2536 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002537 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002538
2539 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002540 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2541 instructions. There is one for when hashtree is not disabled and one for
2542 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002543
David Zeuthen21e95262016-07-27 17:58:40 -04002544 """
2545
David Zeuthen21e95262016-07-27 17:58:40 -04002546 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002547 c += '0' # start
2548 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2549 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2550 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2551 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2552 c += ' {}'.format(ht.data_block_size) # data_block
2553 c += ' {}'.format(ht.hash_block_size) # hash_block
2554 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2555 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2556 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2557 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2558 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2559 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002560 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002561 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002562 c += ' ignore_zero_blocks'
2563 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2564 c += ' fec_roots {}'.format(ht.fec_num_roots)
2565 # Note that fec_blocks is the size that FEC covers, *not* the
2566 # size of the FEC data. Since we use FEC for everything up until
2567 # the FEC data, it's the same as the offset.
2568 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2569 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2570 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002571 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002572 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002573 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002574 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002575
David Zeuthenfd41eb92016-11-17 12:24:47 -05002576 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002577 desc = AvbKernelCmdlineDescriptor()
2578 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002579 desc.flags = (
2580 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2581
2582 # The descriptor for when hashtree verification is disabled is a lot
2583 # simpler - we just set the root to the partition.
2584 desc_no_ht = AvbKernelCmdlineDescriptor()
2585 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2586 desc_no_ht.flags = (
2587 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2588
2589 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002590
David Zeuthen73f2afa2017-05-17 16:54:11 -04002591 def _get_cmdline_descriptors_for_dm_verity(self, image):
2592 """Generate kernel cmdline descriptors for dm-verity.
2593
2594 Arguments:
2595 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2596
2597 Returns:
2598 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2599 instructions. There is one for when hashtree is not disabled and one for
2600 when it is.
2601
2602 Raises:
2603 AvbError: If |image| doesn't have a hashtree descriptor.
2604
2605 """
2606
2607 (_, _, descriptors, _) = self._parse_image(image)
2608
2609 ht = None
2610 for desc in descriptors:
2611 if isinstance(desc, AvbHashtreeDescriptor):
2612 ht = desc
2613 break
2614
2615 if not ht:
2616 raise AvbError('No hashtree descriptor in given image')
2617
2618 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2619
David Zeuthen21e95262016-07-27 17:58:40 -04002620 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002621 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002622 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002623 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002624 include_descriptors_from_image,
2625 signing_helper,
2626 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002627 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002628 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002629 print_required_libavb_version,
2630 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002631 """Implements the 'make_vbmeta_image' command.
2632
2633 Arguments:
2634 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002635 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002636 algorithm_name: Name of algorithm to use.
2637 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002638 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002639 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002640 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002641 props: Properties to insert (list of strings of the form 'key:value').
2642 props_from_file: Properties to insert (list of strings 'key:<path>').
2643 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002644 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002645 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002646 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002647 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002648 release_string: None or avbtool release string to use instead of default.
2649 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002650 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002651 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002652
2653 Raises:
2654 AvbError: If a chained partition is malformed.
2655 """
2656
David Zeuthen1097a782017-05-31 15:53:17 -04002657 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002658 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002659 if include_descriptors_from_image:
2660 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2661 # version of all included descriptors.
2662 tmp_header = AvbVBMetaHeader()
2663 for image in include_descriptors_from_image:
2664 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2665 tmp_header.bump_required_libavb_version_minor(
2666 image_header.required_libavb_version_minor)
2667 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2668 else:
2669 # Descriptors aside, all vbmeta features are supported in 1.0.
2670 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002671 return
2672
2673 if not output:
2674 raise AvbError('No output file given')
2675
David Zeuthen21e95262016-07-27 17:58:40 -04002676 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002677 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002678 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002679 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002680 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002681 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002682 include_descriptors_from_image, signing_helper,
2683 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002684 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002685
2686 # Write entire vbmeta blob (header, authentication, auxiliary).
2687 output.seek(0)
2688 output.write(vbmeta_blob)
2689
David Zeuthen97cb5802017-06-01 16:14:05 -04002690 if padding_size > 0:
2691 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2692 padding_needed = padded_size - len(vbmeta_blob)
2693 output.write('\0' * padding_needed)
2694
David Zeuthen18666ab2016-11-15 11:18:05 -05002695 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2696 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002697 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002698 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002699 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002700 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002701 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002702 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002703 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002704 release_string, append_to_release_string,
2705 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002706 """Generates a VBMeta blob.
2707
2708 This blob contains the header (struct AvbVBMetaHeader), the
2709 authentication data block (which contains the hash and signature
2710 for the header and auxiliary block), and the auxiliary block
2711 (which contains descriptors, the public key used, and other data).
2712
2713 The |key| parameter can |None| only if the |algorithm_name| is
2714 'NONE'.
2715
2716 Arguments:
2717 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2718 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002719 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002720 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002721 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002722 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002723 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002724 props: Properties to insert (List of strings of the form 'key:value').
2725 props_from_file: Properties to insert (List of strings 'key:<path>').
2726 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002727 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002728 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002729 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2730 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002731 include_descriptors_from_image: List of file objects for which
2732 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002733 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002734 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002735 release_string: None or avbtool release string.
2736 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002737 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002738
2739 Returns:
2740 A bytearray() with the VBMeta blob.
2741
2742 Raises:
2743 Exception: If the |algorithm_name| is not found, if no key has
2744 been given and the given algorithm requires one, or the key is
2745 of the wrong size.
2746
2747 """
2748 try:
2749 alg = ALGORITHMS[algorithm_name]
2750 except KeyError:
2751 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2752
David Zeuthena5fd3a42017-02-27 16:38:54 -05002753 if not descriptors:
2754 descriptors = []
2755
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002756 h = AvbVBMetaHeader()
2757 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2758
David Zeuthena5fd3a42017-02-27 16:38:54 -05002759 # Insert chained partition descriptors, if any
2760 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002761 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002762 for cp in chain_partitions:
2763 cp_tokens = cp.split(':')
2764 if len(cp_tokens) != 3:
2765 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002766 partition_name = cp_tokens[0]
2767 rollback_index_location = int(cp_tokens[1])
2768 file_path = cp_tokens[2]
2769 # Check that the same rollback location isn't being used by
2770 # multiple chained partitions.
2771 if used_locations.get(rollback_index_location):
2772 raise AvbError('Rollback Index Location {} is already in use.'.format(
2773 rollback_index_location))
2774 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002775 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002776 desc.partition_name = partition_name
2777 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002778 if desc.rollback_index_location < 1:
2779 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002780 desc.public_key = open(file_path, 'rb').read()
2781 descriptors.append(desc)
2782
David Zeuthen21e95262016-07-27 17:58:40 -04002783 # Descriptors.
2784 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002785 for desc in descriptors:
2786 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002787
2788 # Add properties.
2789 if props:
2790 for prop in props:
2791 idx = prop.find(':')
2792 if idx == -1:
2793 raise AvbError('Malformed property "{}".'.format(prop))
2794 desc = AvbPropertyDescriptor()
2795 desc.key = prop[0:idx]
2796 desc.value = prop[(idx + 1):]
2797 encoded_descriptors.extend(desc.encode())
2798 if props_from_file:
2799 for prop in props_from_file:
2800 idx = prop.find(':')
2801 if idx == -1:
2802 raise AvbError('Malformed property "{}".'.format(prop))
2803 desc = AvbPropertyDescriptor()
2804 desc.key = prop[0:idx]
2805 desc.value = prop[(idx + 1):]
2806 file_path = prop[(idx + 1):]
2807 desc.value = open(file_path, 'rb').read()
2808 encoded_descriptors.extend(desc.encode())
2809
David Zeuthen73f2afa2017-05-17 16:54:11 -04002810 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002811 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002812 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002813 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002814 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2815 encoded_descriptors.extend(cmdline_desc[0].encode())
2816 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002817
David Zeuthen73f2afa2017-05-17 16:54:11 -04002818 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2819 if ht_desc_to_setup:
2820 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2821 ht_desc_to_setup)
2822 encoded_descriptors.extend(cmdline_desc[0].encode())
2823 encoded_descriptors.extend(cmdline_desc[1].encode())
2824
David Zeuthen21e95262016-07-27 17:58:40 -04002825 # Add kernel command-lines.
2826 if kernel_cmdlines:
2827 for i in kernel_cmdlines:
2828 desc = AvbKernelCmdlineDescriptor()
2829 desc.kernel_cmdline = i
2830 encoded_descriptors.extend(desc.encode())
2831
2832 # Add descriptors from other images.
2833 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002834 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002835 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002836 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002837 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2838 image_handler)
2839 # Bump the required libavb version to support all included descriptors.
2840 h.bump_required_libavb_version_minor(
2841 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002842 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002843 # The --include_descriptors_from_image option is used in some setups
2844 # with images A and B where both A and B contain a descriptor
2845 # for a partition with the same name. Since it's not meaningful
2846 # to include both descriptors, only include the last seen descriptor.
2847 # See bug 76386656 for details.
2848 if hasattr(desc, 'partition_name'):
2849 key = type(desc).__name__ + '_' + desc.partition_name
2850 descriptors_dict[key] = desc.encode()
2851 else:
2852 encoded_descriptors.extend(desc.encode())
2853 for key in sorted(descriptors_dict.keys()):
2854 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002855
David Zeuthen18666ab2016-11-15 11:18:05 -05002856 # Load public key metadata blob, if requested.
2857 pkmd_blob = []
2858 if public_key_metadata_path:
2859 with open(public_key_metadata_path) as f:
2860 pkmd_blob = f.read()
2861
David Zeuthen21e95262016-07-27 17:58:40 -04002862 key = None
2863 encoded_key = bytearray()
2864 if alg.public_key_num_bytes > 0:
2865 if not key_path:
2866 raise AvbError('Key is required for algorithm {}'.format(
2867 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002868 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002869 if len(encoded_key) != alg.public_key_num_bytes:
2870 raise AvbError('Key is wrong size for algorithm {}'.format(
2871 algorithm_name))
2872
David Zeuthene3cadca2017-02-22 21:25:46 -05002873 # Override release string, if requested.
2874 if isinstance(release_string, (str, unicode)):
2875 h.release_string = release_string
2876
2877 # Append to release string, if requested. Also insert a space before.
2878 if isinstance(append_to_release_string, (str, unicode)):
2879 h.release_string += ' ' + append_to_release_string
2880
David Zeuthen18666ab2016-11-15 11:18:05 -05002881 # For the Auxiliary data block, descriptors are stored at offset 0,
2882 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002883 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002884 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002885 h.descriptors_offset = 0
2886 h.descriptors_size = len(encoded_descriptors)
2887 h.public_key_offset = h.descriptors_size
2888 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002889 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2890 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002891
2892 # For the Authentication data block, the hash is first and then
2893 # the signature.
2894 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002895 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002896 h.algorithm_type = alg.algorithm_type
2897 h.hash_offset = 0
2898 h.hash_size = alg.hash_num_bytes
2899 # Signature offset and size - it's stored right after the hash
2900 # (in Authentication data block).
2901 h.signature_offset = alg.hash_num_bytes
2902 h.signature_size = alg.signature_num_bytes
2903
2904 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002905 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002906
2907 # Generate Header data block.
2908 header_data_blob = h.encode()
2909
2910 # Generate Auxiliary data block.
2911 aux_data_blob = bytearray()
2912 aux_data_blob.extend(encoded_descriptors)
2913 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002914 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002915 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2916 aux_data_blob.extend('\0' * padding_bytes)
2917
2918 # Calculate the hash.
2919 binary_hash = bytearray()
2920 binary_signature = bytearray()
2921 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002922 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002923 ha.update(header_data_blob)
2924 ha.update(aux_data_blob)
2925 binary_hash.extend(ha.digest())
2926
2927 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002928 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002929 binary_signature.extend(raw_sign(signing_helper,
2930 signing_helper_with_files,
2931 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002932 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002933 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002934
2935 # Generate Authentication data block.
2936 auth_data_blob = bytearray()
2937 auth_data_blob.extend(binary_hash)
2938 auth_data_blob.extend(binary_signature)
2939 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2940 auth_data_blob.extend('\0' * padding_bytes)
2941
2942 return header_data_blob + auth_data_blob + aux_data_blob
2943
2944 def extract_public_key(self, key_path, output):
2945 """Implements the 'extract_public_key' command.
2946
2947 Arguments:
2948 key_path: The path to a RSA private key file.
2949 output: The file to write to.
2950 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002951 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002952
David Zeuthenb1b994d2017-03-06 18:01:31 -05002953 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2954 partition_size):
2955 """Implementation of the append_vbmeta_image command.
2956
2957 Arguments:
2958 image_filename: File to add the footer to.
2959 vbmeta_image_filename: File to get vbmeta struct from.
2960 partition_size: Size of partition.
2961
2962 Raises:
2963 AvbError: If an argument is incorrect.
2964 """
2965 image = ImageHandler(image_filename)
2966
2967 if partition_size % image.block_size != 0:
2968 raise AvbError('Partition size of {} is not a multiple of the image '
2969 'block size {}.'.format(partition_size,
2970 image.block_size))
2971
2972 # If there's already a footer, truncate the image to its original
2973 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002974 if image.image_size >= AvbFooter.SIZE:
2975 image.seek(image.image_size - AvbFooter.SIZE)
2976 try:
2977 footer = AvbFooter(image.read(AvbFooter.SIZE))
2978 # Existing footer found. Just truncate.
2979 original_image_size = footer.original_image_size
2980 image.truncate(footer.original_image_size)
2981 except (LookupError, struct.error):
2982 original_image_size = image.image_size
2983 else:
2984 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002985 original_image_size = image.image_size
2986
2987 # If anything goes wrong from here-on, restore the image back to
2988 # its original size.
2989 try:
2990 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2991 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2992
2993 # If the image isn't sparse, its size might not be a multiple of
2994 # the block size. This will screw up padding later so just grow it.
2995 if image.image_size % image.block_size != 0:
2996 assert not image.is_sparse
2997 padding_needed = image.block_size - (image.image_size%image.block_size)
2998 image.truncate(image.image_size + padding_needed)
2999
3000 # The append_raw() method requires content with size being a
3001 # multiple of |block_size| so add padding as needed. Also record
3002 # where this is written to since we'll need to put that in the
3003 # footer.
3004 vbmeta_offset = image.image_size
3005 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3006 len(vbmeta_blob))
3007 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3008
3009 # Append vbmeta blob and footer
3010 image.append_raw(vbmeta_blob_with_padding)
3011 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3012
3013 # Now insert a DONT_CARE chunk with enough bytes such that the
3014 # final Footer block is at the end of partition_size..
3015 image.append_dont_care(partition_size - vbmeta_end_offset -
3016 1*image.block_size)
3017
3018 # Generate the Footer that tells where the VBMeta footer
3019 # is. Also put enough padding in the front of the footer since
3020 # we'll write out an entire block.
3021 footer = AvbFooter()
3022 footer.original_image_size = original_image_size
3023 footer.vbmeta_offset = vbmeta_offset
3024 footer.vbmeta_size = len(vbmeta_blob)
3025 footer_blob = footer.encode()
3026 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3027 footer_blob)
3028 image.append_raw(footer_blob_with_padding)
3029
3030 except:
3031 # Truncate back to original size, then re-raise
3032 image.truncate(original_image_size)
3033 raise
3034
David Zeuthena4fee8b2016-08-22 15:20:43 -04003035 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003036 hash_algorithm, salt, chain_partitions, algorithm_name,
3037 key_path,
3038 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003039 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003040 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003041 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003042 signing_helper, signing_helper_with_files,
3043 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003044 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003045 print_required_libavb_version, use_persistent_digest,
3046 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003047 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003048
3049 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003050 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003051 partition_size: Size of partition.
3052 partition_name: Name of partition (without A/B suffix).
3053 hash_algorithm: Hash algorithm to use.
3054 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003055 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003056 algorithm_name: Name of algorithm to use.
3057 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003058 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003059 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003060 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003061 props: Properties to insert (List of strings of the form 'key:value').
3062 props_from_file: Properties to insert (List of strings 'key:<path>').
3063 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003064 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003065 dm-verity kernel cmdline from.
3066 include_descriptors_from_image: List of file objects for which
3067 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003068 calc_max_image_size: Don't store the footer - instead calculate the
3069 maximum image size leaving enough room for metadata with the
3070 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003071 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003072 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003073 release_string: None or avbtool release string.
3074 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003075 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3076 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003077 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003078 use_persistent_digest: Use a persistent digest on device.
3079 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003080
3081 Raises:
3082 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003083 """
David Zeuthen1097a782017-05-31 15:53:17 -04003084
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003085 required_libavb_version_minor = 0
3086 if use_persistent_digest or do_not_use_ab:
3087 required_libavb_version_minor = 1
3088
David Zeuthen1097a782017-05-31 15:53:17 -04003089 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003090 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003091 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003092 return
3093
David Zeuthenbf562452017-05-17 18:04:43 -04003094 # First, calculate the maximum image size such that an image
3095 # this size + metadata (footer + vbmeta struct) fits in
3096 # |partition_size|.
3097 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003098 if partition_size < max_metadata_size:
3099 raise AvbError('Parition size of {} is too small. '
3100 'Needs to be at least {}'.format(
3101 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003102 max_image_size = partition_size - max_metadata_size
3103
3104 # If we're asked to only calculate the maximum image size, we're done.
3105 if calc_max_image_size:
3106 print '{}'.format(max_image_size)
3107 return
3108
David Zeuthena4fee8b2016-08-22 15:20:43 -04003109 image = ImageHandler(image_filename)
3110
3111 if partition_size % image.block_size != 0:
3112 raise AvbError('Partition size of {} is not a multiple of the image '
3113 'block size {}.'.format(partition_size,
3114 image.block_size))
3115
David Zeuthen21e95262016-07-27 17:58:40 -04003116 # If there's already a footer, truncate the image to its original
3117 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3118 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003119 if image.image_size >= AvbFooter.SIZE:
3120 image.seek(image.image_size - AvbFooter.SIZE)
3121 try:
3122 footer = AvbFooter(image.read(AvbFooter.SIZE))
3123 # Existing footer found. Just truncate.
3124 original_image_size = footer.original_image_size
3125 image.truncate(footer.original_image_size)
3126 except (LookupError, struct.error):
3127 original_image_size = image.image_size
3128 else:
3129 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003130 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003131
3132 # If anything goes wrong from here-on, restore the image back to
3133 # its original size.
3134 try:
David Zeuthen09692692016-09-30 16:16:40 -04003135 # If image size exceeds the maximum image size, fail.
3136 if image.image_size > max_image_size:
3137 raise AvbError('Image size of {} exceeds maximum image '
3138 'size of {} in order to fit in a partition '
3139 'size of {}.'.format(image.image_size, max_image_size,
3140 partition_size))
3141
David Zeuthen21e95262016-07-27 17:58:40 -04003142 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3143 if salt:
3144 salt = salt.decode('hex')
3145 else:
Bryan Henry45354282018-10-25 18:37:27 -07003146 if salt is None and not use_persistent_digest:
3147 # If salt is not explicitly specified, choose a hash that's the same
3148 # size as the hash size. Don't populate a random salt if this
3149 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003150 hash_size = digest_size
3151 salt = open('/dev/urandom').read(hash_size)
3152 else:
3153 salt = ''
3154
3155 hasher = hashlib.new(name=hash_algorithm, string=salt)
3156 # TODO(zeuthen): might want to read this in chunks to avoid
3157 # memory pressure, then again, this is only supposed to be used
3158 # on kernel/initramfs partitions. Possible optimization.
3159 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003160 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003161 digest = hasher.digest()
3162
3163 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003164 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003165 h_desc.hash_algorithm = hash_algorithm
3166 h_desc.partition_name = partition_name
3167 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003168 h_desc.flags = 0
3169 if do_not_use_ab:
3170 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3171 if not use_persistent_digest:
3172 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003173
3174 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003175 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003176 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003177 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003178 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003179 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003180 include_descriptors_from_image, signing_helper,
3181 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003182 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003183
David Zeuthend247fcb2017-02-16 12:09:27 -05003184 # Write vbmeta blob, if requested.
3185 if output_vbmeta_image:
3186 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003187
David Zeuthend247fcb2017-02-16 12:09:27 -05003188 # Append vbmeta blob and footer, unless requested not to.
3189 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003190 # If the image isn't sparse, its size might not be a multiple of
3191 # the block size. This will screw up padding later so just grow it.
3192 if image.image_size % image.block_size != 0:
3193 assert not image.is_sparse
3194 padding_needed = image.block_size - (
3195 image.image_size % image.block_size)
3196 image.truncate(image.image_size + padding_needed)
3197
3198 # The append_raw() method requires content with size being a
3199 # multiple of |block_size| so add padding as needed. Also record
3200 # where this is written to since we'll need to put that in the
3201 # footer.
3202 vbmeta_offset = image.image_size
3203 padding_needed = (
3204 round_to_multiple(len(vbmeta_blob), image.block_size) -
3205 len(vbmeta_blob))
3206 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3207
David Zeuthend247fcb2017-02-16 12:09:27 -05003208 image.append_raw(vbmeta_blob_with_padding)
3209 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3210
3211 # Now insert a DONT_CARE chunk with enough bytes such that the
3212 # final Footer block is at the end of partition_size..
3213 image.append_dont_care(partition_size - vbmeta_end_offset -
3214 1*image.block_size)
3215
3216 # Generate the Footer that tells where the VBMeta footer
3217 # is. Also put enough padding in the front of the footer since
3218 # we'll write out an entire block.
3219 footer = AvbFooter()
3220 footer.original_image_size = original_image_size
3221 footer.vbmeta_offset = vbmeta_offset
3222 footer.vbmeta_size = len(vbmeta_blob)
3223 footer_blob = footer.encode()
3224 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3225 footer_blob)
3226 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003227
David Zeuthen21e95262016-07-27 17:58:40 -04003228 except:
3229 # Truncate back to original size, then re-raise
3230 image.truncate(original_image_size)
3231 raise
3232
David Zeuthena4fee8b2016-08-22 15:20:43 -04003233 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003234 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003235 block_size, salt, chain_partitions, algorithm_name,
3236 key_path,
3237 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003238 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003239 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003240 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003241 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003242 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003243 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003244 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003245 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003246 print_required_libavb_version,
Jooyung Hand7221942019-06-17 13:19:57 +09003247 use_persistent_root_digest, do_not_use_ab, no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003248 """Implements the 'add_hashtree_footer' command.
3249
3250 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3251 more information about dm-verity and these hashes.
3252
3253 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003254 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003255 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003256 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003257 generate_fec: If True, generate FEC codes.
3258 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003259 hash_algorithm: Hash algorithm to use.
3260 block_size: Block size to use.
3261 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003262 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003263 algorithm_name: Name of algorithm to use.
3264 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003265 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003266 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003267 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003268 props: Properties to insert (List of strings of the form 'key:value').
3269 props_from_file: Properties to insert (List of strings 'key:<path>').
3270 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003271 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003272 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003273 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3274 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003275 include_descriptors_from_image: List of file objects for which
3276 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003277 calc_max_image_size: Don't store the hashtree or footer - instead
3278 calculate the maximum image size leaving enough room for hashtree
3279 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003280 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003281 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003282 release_string: None or avbtool release string.
3283 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003284 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3285 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003286 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003287 use_persistent_root_digest: Use a persistent root digest on device.
3288 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003289 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003290
3291 Raises:
3292 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003293 """
David Zeuthen1097a782017-05-31 15:53:17 -04003294
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003295 required_libavb_version_minor = 0
3296 if use_persistent_root_digest or do_not_use_ab:
3297 required_libavb_version_minor = 1
3298
David Zeuthen1097a782017-05-31 15:53:17 -04003299 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003300 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003301 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003302 return
3303
David Zeuthen09692692016-09-30 16:16:40 -04003304 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3305 digest_padding = round_to_pow2(digest_size) - digest_size
3306
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003307 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3308 # size such that an image this size + the hashtree + metadata (footer +
3309 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3310 # for metadata.
3311 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003312 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003313 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003314 if not no_hashtree:
3315 (_, max_tree_size) = calc_hash_level_offsets(
3316 partition_size, block_size, digest_size + digest_padding)
3317 if generate_fec:
3318 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003319 max_metadata_size = (max_fec_size + max_tree_size +
3320 self.MAX_VBMETA_SIZE +
3321 self.MAX_FOOTER_SIZE)
3322 max_image_size = partition_size - max_metadata_size
3323 else:
3324 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003325
3326 # If we're asked to only calculate the maximum image size, we're done.
3327 if calc_max_image_size:
3328 print '{}'.format(max_image_size)
3329 return
3330
David Zeuthena4fee8b2016-08-22 15:20:43 -04003331 image = ImageHandler(image_filename)
3332
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003333 if partition_size > 0:
3334 if partition_size % image.block_size != 0:
3335 raise AvbError('Partition size of {} is not a multiple of the image '
3336 'block size {}.'.format(partition_size,
3337 image.block_size))
3338 else:
3339 if image.image_size % image.block_size != 0:
3340 raise AvbError('File size of {} is not a multiple of the image '
3341 'block size {}.'.format(image.image_size,
3342 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003343
David Zeuthen21e95262016-07-27 17:58:40 -04003344 # If there's already a footer, truncate the image to its original
3345 # size. This way 'avbtool add_hashtree_footer' is idempotent
3346 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003347 if image.image_size >= AvbFooter.SIZE:
3348 image.seek(image.image_size - AvbFooter.SIZE)
3349 try:
3350 footer = AvbFooter(image.read(AvbFooter.SIZE))
3351 # Existing footer found. Just truncate.
3352 original_image_size = footer.original_image_size
3353 image.truncate(footer.original_image_size)
3354 except (LookupError, struct.error):
3355 original_image_size = image.image_size
3356 else:
3357 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003358 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003359
3360 # If anything goes wrong from here-on, restore the image back to
3361 # its original size.
3362 try:
3363 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003364 rounded_image_size = round_to_multiple(image.image_size, block_size)
3365 if rounded_image_size > image.image_size:
3366 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003367
David Zeuthen09692692016-09-30 16:16:40 -04003368 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003369 if partition_size > 0:
3370 if image.image_size > max_image_size:
3371 raise AvbError('Image size of {} exceeds maximum image '
3372 'size of {} in order to fit in a partition '
3373 'size of {}.'.format(image.image_size, max_image_size,
3374 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003375
3376 if salt:
3377 salt = salt.decode('hex')
3378 else:
Bryan Henry45354282018-10-25 18:37:27 -07003379 if salt is None and not use_persistent_root_digest:
3380 # If salt is not explicitly specified, choose a hash that's the same
3381 # size as the hash size. Don't populate a random salt if this
3382 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003383 hash_size = digest_size
3384 salt = open('/dev/urandom').read(hash_size)
3385 else:
3386 salt = ''
3387
David Zeuthena4fee8b2016-08-22 15:20:43 -04003388 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003389 # offsets in advance.
3390 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003391 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003392
David Zeuthena4fee8b2016-08-22 15:20:43 -04003393 # If the image isn't sparse, its size might not be a multiple of
3394 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003395 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003396 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003397 padding_needed = image.block_size - (image.image_size%image.block_size)
3398 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003399
David Zeuthena4fee8b2016-08-22 15:20:43 -04003400 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003401 tree_offset = image.image_size
3402 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003403 block_size,
3404 hash_algorithm, salt,
3405 digest_padding,
3406 hash_level_offsets,
3407 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003408
3409 # Generate HashtreeDescriptor with details about the tree we
3410 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003411 if no_hashtree:
3412 tree_size = 0
3413 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003414 ht_desc = AvbHashtreeDescriptor()
3415 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003416 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003417 ht_desc.tree_offset = tree_offset
3418 ht_desc.tree_size = tree_size
3419 ht_desc.data_block_size = block_size
3420 ht_desc.hash_block_size = block_size
3421 ht_desc.hash_algorithm = hash_algorithm
3422 ht_desc.partition_name = partition_name
3423 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003424 if do_not_use_ab:
3425 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3426 if not use_persistent_root_digest:
3427 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003428
David Zeuthen09692692016-09-30 16:16:40 -04003429 # Write the hash tree
3430 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3431 len(hash_tree))
3432 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3433 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003434 len_hashtree_and_fec = len(hash_tree_with_padding)
3435
3436 # Generate FEC codes, if requested.
3437 if generate_fec:
3438 fec_data = generate_fec_data(image_filename, fec_num_roots)
Jooyung Hand7221942019-06-17 13:19:57 +09003439 if no_hashtree:
3440 fec_data = bytearray()
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003441 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3442 len(fec_data))
3443 fec_data_with_padding = fec_data + '\0'*padding_needed
3444 fec_offset = image.image_size
3445 image.append_raw(fec_data_with_padding)
3446 len_hashtree_and_fec += len(fec_data_with_padding)
3447 # Update the hashtree descriptor.
3448 ht_desc.fec_num_roots = fec_num_roots
3449 ht_desc.fec_offset = fec_offset
3450 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003451
David Zeuthen73f2afa2017-05-17 16:54:11 -04003452 ht_desc_to_setup = None
3453 if setup_as_rootfs_from_kernel:
3454 ht_desc_to_setup = ht_desc
3455
David Zeuthena4fee8b2016-08-22 15:20:43 -04003456 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003457 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003458 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003459 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003460 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003461 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003462 include_descriptors_from_image, signing_helper,
3463 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003464 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003465 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3466 len(vbmeta_blob))
3467 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003468
David Zeuthend247fcb2017-02-16 12:09:27 -05003469 # Write vbmeta blob, if requested.
3470 if output_vbmeta_image:
3471 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003472
David Zeuthend247fcb2017-02-16 12:09:27 -05003473 # Append vbmeta blob and footer, unless requested not to.
3474 if not do_not_append_vbmeta_image:
3475 image.append_raw(vbmeta_blob_with_padding)
3476
3477 # Now insert a DONT_CARE chunk with enough bytes such that the
3478 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003479 if partition_size > 0:
3480 image.append_dont_care(partition_size - image.image_size -
3481 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003482
3483 # Generate the Footer that tells where the VBMeta footer
3484 # is. Also put enough padding in the front of the footer since
3485 # we'll write out an entire block.
3486 footer = AvbFooter()
3487 footer.original_image_size = original_image_size
3488 footer.vbmeta_offset = vbmeta_offset
3489 footer.vbmeta_size = len(vbmeta_blob)
3490 footer_blob = footer.encode()
3491 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3492 footer_blob)
3493 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003494
David Zeuthen21e95262016-07-27 17:58:40 -04003495 except:
David Zeuthen09692692016-09-30 16:16:40 -04003496 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003497 image.truncate(original_image_size)
3498 raise
3499
David Zeuthenc68f0822017-03-31 17:22:35 -04003500 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003501 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003502 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003503 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003504 """Implements the 'make_atx_certificate' command.
3505
3506 Android Things certificates are required for Android Things public key
3507 metadata. They chain the vbmeta signing key for a particular product back to
3508 a fused, permanent root key. These certificates are fixed-length and fixed-
3509 format with the explicit goal of not parsing ASN.1 in bootloader code.
3510
3511 Arguments:
3512 output: Certificate will be written to this file on success.
3513 authority_key_path: A PEM file path with the authority private key.
3514 If None, then a certificate will be created without a
3515 signature. The signature can be created out-of-band
3516 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003517 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003518 subject_key_version: A 64-bit version value. If this is None, the number
3519 of seconds since the epoch is used.
3520 subject: A subject identifier. For Product Signing Key certificates this
3521 should be the same Product ID found in the permanent attributes.
3522 is_intermediate_authority: True if the certificate is for an intermediate
3523 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003524 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003525 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003526 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003527 """
3528 signed_data = bytearray()
3529 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003530 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003531 hasher = hashlib.sha256()
3532 hasher.update(subject)
3533 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003534 if not usage:
3535 usage = 'com.google.android.things.vboot'
3536 if is_intermediate_authority:
3537 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003538 hasher = hashlib.sha256()
3539 hasher.update(usage)
3540 signed_data.extend(hasher.digest())
3541 if not subject_key_version:
3542 subject_key_version = int(time.time())
3543 signed_data.extend(struct.pack('<Q', subject_key_version))
3544 signature = bytearray()
3545 if authority_key_path:
3546 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003547 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003548 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003549 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003550 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003551 hasher.update(signed_data)
3552 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003553 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3554 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003555 alg.signature_num_bytes, authority_key_path,
3556 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003557 output.write(signed_data)
3558 output.write(signature)
3559
David Zeuthenc68f0822017-03-31 17:22:35 -04003560 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003561 product_id):
3562 """Implements the 'make_atx_permanent_attributes' command.
3563
3564 Android Things permanent attributes are designed to be permanent for a
3565 particular product and a hash of these attributes should be fused into
3566 hardware to enforce this.
3567
3568 Arguments:
3569 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003570 root_authority_key_path: Path to a PEM or DER public key for
3571 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003572 product_id: A 16-byte Product ID.
3573
3574 Raises:
3575 AvbError: If an argument is incorrect.
3576 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003577 EXPECTED_PRODUCT_ID_SIZE = 16
3578 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003579 raise AvbError('Invalid Product ID length.')
3580 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003581 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003582 output.write(product_id)
3583
3584 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003585 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003586 """Implements the 'make_atx_metadata' command.
3587
3588 Android Things metadata are included in vbmeta images to facilitate
3589 verification. The output of this command can be used as the
3590 public_key_metadata argument to other commands.
3591
3592 Arguments:
3593 output: Metadata will be written to this file on success.
3594 intermediate_key_certificate: A certificate file as output by
3595 make_atx_certificate with
3596 is_intermediate_authority set to true.
3597 product_key_certificate: A certificate file as output by
3598 make_atx_certificate with
3599 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003600
3601 Raises:
3602 AvbError: If an argument is incorrect.
3603 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003604 EXPECTED_CERTIFICATE_SIZE = 1620
3605 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003606 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003607 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003608 raise AvbError('Invalid product key certificate length.')
3609 output.write(struct.pack('<I', 1)) # Format Version
3610 output.write(intermediate_key_certificate)
3611 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003612
Darren Krahnfccd64e2018-01-16 17:39:35 -08003613 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3614 unlock_key_certificate, challenge_path,
3615 unlock_key_path, signing_helper,
3616 signing_helper_with_files):
3617 """Implements the 'make_atx_unlock_credential' command.
3618
3619 Android Things unlock credentials can be used to authorize the unlock of AVB
3620 on a device. These credentials are presented to an Android Things bootloader
3621 via the fastboot interface in response to a 16-byte challenge. This method
3622 creates all fields of the credential except the challenge signature field
3623 (which is the last field) and can optionally create the challenge signature
3624 field as well if a challenge and the unlock_key_path is provided.
3625
3626 Arguments:
3627 output: The credential will be written to this file on success.
3628 intermediate_key_certificate: A certificate file as output by
3629 make_atx_certificate with
3630 is_intermediate_authority set to true.
3631 unlock_key_certificate: A certificate file as output by
3632 make_atx_certificate with
3633 is_intermediate_authority set to false and the
3634 usage set to
3635 'com.google.android.things.vboot.unlock'.
3636 challenge_path: [optional] A path to the challenge to sign.
3637 unlock_key_path: [optional] A PEM file path with the unlock private key.
3638 signing_helper: Program which signs a hash and returns the signature.
3639 signing_helper_with_files: Same as signing_helper but uses files instead.
3640
3641 Raises:
3642 AvbError: If an argument is incorrect.
3643 """
3644 EXPECTED_CERTIFICATE_SIZE = 1620
3645 EXPECTED_CHALLENGE_SIZE = 16
3646 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3647 raise AvbError('Invalid intermediate key certificate length.')
3648 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3649 raise AvbError('Invalid product key certificate length.')
3650 challenge = bytearray()
3651 if challenge_path:
3652 with open(challenge_path, 'r') as f:
3653 challenge = f.read()
3654 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3655 raise AvbError('Invalid unlock challenge length.')
3656 output.write(struct.pack('<I', 1)) # Format Version
3657 output.write(intermediate_key_certificate)
3658 output.write(unlock_key_certificate)
3659 if challenge_path and unlock_key_path:
3660 signature = bytearray()
3661 padding_and_hash = bytearray()
3662 algorithm_name = 'SHA512_RSA4096'
3663 alg = ALGORITHMS[algorithm_name]
3664 hasher = hashlib.sha512()
3665 padding_and_hash.extend(alg.padding)
3666 hasher.update(challenge)
3667 padding_and_hash.extend(hasher.digest())
3668 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3669 algorithm_name,
3670 alg.signature_num_bytes, unlock_key_path,
3671 padding_and_hash))
3672 output.write(signature)
3673
David Zeuthen21e95262016-07-27 17:58:40 -04003674
3675def calc_hash_level_offsets(image_size, block_size, digest_size):
3676 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3677
3678 Arguments:
3679 image_size: The size of the image to calculate a Merkle-tree for.
3680 block_size: The block size, e.g. 4096.
3681 digest_size: The size of each hash, e.g. 32 for SHA-256.
3682
3683 Returns:
3684 A tuple where the first argument is an array of offsets and the
3685 second is size of the tree, in bytes.
3686 """
3687 level_offsets = []
3688 level_sizes = []
3689 tree_size = 0
3690
3691 num_levels = 0
3692 size = image_size
3693 while size > block_size:
3694 num_blocks = (size + block_size - 1) / block_size
3695 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3696
3697 level_sizes.append(level_size)
3698 tree_size += level_size
3699 num_levels += 1
3700
3701 size = level_size
3702
3703 for n in range(0, num_levels):
3704 offset = 0
3705 for m in range(n + 1, num_levels):
3706 offset += level_sizes[m]
3707 level_offsets.append(offset)
3708
David Zeuthena4fee8b2016-08-22 15:20:43 -04003709 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003710
3711
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003712# See system/extras/libfec/include/fec/io.h for these definitions.
3713FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3714FEC_MAGIC = 0xfecfecfe
3715
3716
3717def calc_fec_data_size(image_size, num_roots):
3718 """Calculates how much space FEC data will take.
3719
3720 Args:
3721 image_size: The size of the image.
3722 num_roots: Number of roots.
3723
3724 Returns:
3725 The number of bytes needed for FEC for an image of the given size
3726 and with the requested number of FEC roots.
3727
3728 Raises:
3729 ValueError: If output from the 'fec' tool is invalid.
3730
3731 """
3732 p = subprocess.Popen(
3733 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3734 stdout=subprocess.PIPE,
3735 stderr=subprocess.PIPE)
3736 (pout, perr) = p.communicate()
3737 retcode = p.wait()
3738 if retcode != 0:
3739 raise ValueError('Error invoking fec: {}'.format(perr))
3740 return int(pout)
3741
3742
3743def generate_fec_data(image_filename, num_roots):
3744 """Generate FEC codes for an image.
3745
3746 Args:
3747 image_filename: The filename of the image.
3748 num_roots: Number of roots.
3749
3750 Returns:
3751 The FEC data blob.
3752
3753 Raises:
3754 ValueError: If output from the 'fec' tool is invalid.
3755 """
3756 fec_tmpfile = tempfile.NamedTemporaryFile()
3757 subprocess.check_call(
3758 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3759 fec_tmpfile.name],
3760 stderr=open(os.devnull))
3761 fec_data = fec_tmpfile.read()
3762 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3763 footer_data = fec_data[-footer_size:]
3764 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3765 footer_data)
3766 if magic != FEC_MAGIC:
3767 raise ValueError('Unexpected magic in FEC footer')
3768 return fec_data[0:fec_size]
3769
3770
David Zeuthen21e95262016-07-27 17:58:40 -04003771def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003772 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003773 """Generates a Merkle-tree for a file.
3774
3775 Args:
3776 image: The image, as a file.
3777 image_size: The size of the image.
3778 block_size: The block size, e.g. 4096.
3779 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3780 salt: The salt to use.
3781 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003782 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003783 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003784
3785 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003786 A tuple where the first element is the top-level hash and the
3787 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003788 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003789 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003790 hash_src_offset = 0
3791 hash_src_size = image_size
3792 level_num = 0
3793 while hash_src_size > block_size:
3794 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003795 remaining = hash_src_size
3796 while remaining > 0:
3797 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003798 # Only read from the file for the first level - for subsequent
3799 # levels, access the array we're building.
3800 if level_num == 0:
3801 image.seek(hash_src_offset + hash_src_size - remaining)
3802 data = image.read(min(remaining, block_size))
3803 else:
3804 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3805 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003806 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003807
3808 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003809 if len(data) < block_size:
3810 hasher.update('\0' * (block_size - len(data)))
3811 level_output += hasher.digest()
3812 if digest_padding > 0:
3813 level_output += '\0' * digest_padding
3814
3815 padding_needed = (round_to_multiple(
3816 len(level_output), block_size) - len(level_output))
3817 level_output += '\0' * padding_needed
3818
David Zeuthena4fee8b2016-08-22 15:20:43 -04003819 # Copy level-output into resulting tree.
3820 offset = hash_level_offsets[level_num]
3821 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003822
David Zeuthena4fee8b2016-08-22 15:20:43 -04003823 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003824 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003825 level_num += 1
3826
3827 hasher = hashlib.new(name=hash_alg_name, string=salt)
3828 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003829 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003830
3831
3832class AvbTool(object):
3833 """Object for avbtool command-line tool."""
3834
3835 def __init__(self):
3836 """Initializer method."""
3837 self.avb = Avb()
3838
3839 def _add_common_args(self, sub_parser):
3840 """Adds arguments used by several sub-commands.
3841
3842 Arguments:
3843 sub_parser: The parser to add arguments to.
3844 """
3845 sub_parser.add_argument('--algorithm',
3846 help='Algorithm to use (default: NONE)',
3847 metavar='ALGORITHM',
3848 default='NONE')
3849 sub_parser.add_argument('--key',
3850 help='Path to RSA private key file',
3851 metavar='KEY',
3852 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003853 sub_parser.add_argument('--signing_helper',
3854 help='Path to helper used for signing',
3855 metavar='APP',
3856 default=None,
3857 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003858 sub_parser.add_argument('--signing_helper_with_files',
3859 help='Path to helper used for signing using files',
3860 metavar='APP',
3861 default=None,
3862 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003863 sub_parser.add_argument('--public_key_metadata',
3864 help='Path to public key metadata file',
3865 metavar='KEY_METADATA',
3866 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003867 sub_parser.add_argument('--rollback_index',
3868 help='Rollback Index',
3869 type=parse_number,
3870 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003871 # This is used internally for unit tests. Do not include in --help output.
3872 sub_parser.add_argument('--internal_release_string',
3873 help=argparse.SUPPRESS)
3874 sub_parser.add_argument('--append_to_release_string',
3875 help='Text to append to release string',
3876 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003877 sub_parser.add_argument('--prop',
3878 help='Add property',
3879 metavar='KEY:VALUE',
3880 action='append')
3881 sub_parser.add_argument('--prop_from_file',
3882 help='Add property from file',
3883 metavar='KEY:PATH',
3884 action='append')
3885 sub_parser.add_argument('--kernel_cmdline',
3886 help='Add kernel cmdline',
3887 metavar='CMDLINE',
3888 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003889 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3890 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3891 # at some future point.
3892 sub_parser.add_argument('--setup_rootfs_from_kernel',
3893 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003894 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003895 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003896 type=argparse.FileType('rb'))
3897 sub_parser.add_argument('--include_descriptors_from_image',
3898 help='Include descriptors from image',
3899 metavar='IMAGE',
3900 action='append',
3901 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003902 sub_parser.add_argument('--print_required_libavb_version',
3903 help=('Don\'t store the footer - '
3904 'instead calculate the required libavb '
3905 'version for the given options.'),
3906 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003907 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3908 sub_parser.add_argument('--chain_partition',
3909 help='Allow signed integrity-data for partition',
3910 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3911 action='append')
3912 sub_parser.add_argument('--flags',
3913 help='VBMeta flags',
3914 type=parse_number,
3915 default=0)
3916 sub_parser.add_argument('--set_hashtree_disabled_flag',
3917 help='Set the HASHTREE_DISABLED flag',
3918 action='store_true')
3919
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003920 def _add_common_footer_args(self, sub_parser):
3921 """Adds arguments used by add_*_footer sub-commands.
3922
3923 Arguments:
3924 sub_parser: The parser to add arguments to.
3925 """
3926 sub_parser.add_argument('--use_persistent_digest',
3927 help='Use a persistent digest on device instead of '
3928 'storing the digest in the descriptor. This '
3929 'cannot be used with A/B so must be combined '
3930 'with --do_not_use_ab when an A/B suffix is '
3931 'expected at runtime.',
3932 action='store_true')
3933 sub_parser.add_argument('--do_not_use_ab',
3934 help='The partition does not use A/B even when an '
3935 'A/B suffix is present. This must not be used '
3936 'for vbmeta or chained partitions.',
3937 action='store_true')
3938
David Zeuthena5fd3a42017-02-27 16:38:54 -05003939 def _fixup_common_args(self, args):
3940 """Common fixups needed by subcommands.
3941
3942 Arguments:
3943 args: Arguments to modify.
3944
3945 Returns:
3946 The modified arguments.
3947 """
3948 if args.set_hashtree_disabled_flag:
3949 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3950 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003951
3952 def run(self, argv):
3953 """Command-line processor.
3954
3955 Arguments:
3956 argv: Pass sys.argv from main.
3957 """
3958 parser = argparse.ArgumentParser()
3959 subparsers = parser.add_subparsers(title='subcommands')
3960
3961 sub_parser = subparsers.add_parser('version',
3962 help='Prints version of avbtool.')
3963 sub_parser.set_defaults(func=self.version)
3964
3965 sub_parser = subparsers.add_parser('extract_public_key',
3966 help='Extract public key.')
3967 sub_parser.add_argument('--key',
3968 help='Path to RSA private key file',
3969 required=True)
3970 sub_parser.add_argument('--output',
3971 help='Output file name',
3972 type=argparse.FileType('wb'),
3973 required=True)
3974 sub_parser.set_defaults(func=self.extract_public_key)
3975
3976 sub_parser = subparsers.add_parser('make_vbmeta_image',
3977 help='Makes a vbmeta image.')
3978 sub_parser.add_argument('--output',
3979 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003980 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003981 sub_parser.add_argument('--padding_size',
3982 metavar='NUMBER',
3983 help='If non-zero, pads output with NUL bytes so '
3984 'its size is a multiple of NUMBER (default: 0)',
3985 type=parse_number,
3986 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003987 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003988 sub_parser.set_defaults(func=self.make_vbmeta_image)
3989
3990 sub_parser = subparsers.add_parser('add_hash_footer',
3991 help='Add hashes and footer to image.')
3992 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003993 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003994 type=argparse.FileType('rab+'))
3995 sub_parser.add_argument('--partition_size',
3996 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003997 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003998 sub_parser.add_argument('--partition_name',
3999 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004000 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004001 sub_parser.add_argument('--hash_algorithm',
4002 help='Hash algorithm to use (default: sha256)',
4003 default='sha256')
4004 sub_parser.add_argument('--salt',
4005 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004006 sub_parser.add_argument('--calc_max_image_size',
4007 help=('Don\'t store the footer - '
4008 'instead calculate the maximum image size '
4009 'leaving enough room for metadata with '
4010 'the given partition size.'),
4011 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004012 sub_parser.add_argument('--output_vbmeta_image',
4013 help='Also write vbmeta struct to file',
4014 type=argparse.FileType('wb'))
4015 sub_parser.add_argument('--do_not_append_vbmeta_image',
4016 help=('Do not append vbmeta struct or footer '
4017 'to the image'),
4018 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004019 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004020 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004021 sub_parser.set_defaults(func=self.add_hash_footer)
4022
David Zeuthenb1b994d2017-03-06 18:01:31 -05004023 sub_parser = subparsers.add_parser('append_vbmeta_image',
4024 help='Append vbmeta image to image.')
4025 sub_parser.add_argument('--image',
4026 help='Image to append vbmeta blob to',
4027 type=argparse.FileType('rab+'))
4028 sub_parser.add_argument('--partition_size',
4029 help='Partition size',
4030 type=parse_number,
4031 required=True)
4032 sub_parser.add_argument('--vbmeta_image',
4033 help='Image with vbmeta blob to append',
4034 type=argparse.FileType('rb'))
4035 sub_parser.set_defaults(func=self.append_vbmeta_image)
4036
David Zeuthen21e95262016-07-27 17:58:40 -04004037 sub_parser = subparsers.add_parser('add_hashtree_footer',
4038 help='Add hashtree and footer to image.')
4039 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004040 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004041 type=argparse.FileType('rab+'))
4042 sub_parser.add_argument('--partition_size',
4043 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004044 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004045 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004046 sub_parser.add_argument('--partition_name',
4047 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004048 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004049 sub_parser.add_argument('--hash_algorithm',
4050 help='Hash algorithm to use (default: sha1)',
4051 default='sha1')
4052 sub_parser.add_argument('--salt',
4053 help='Salt in hex (default: /dev/urandom)')
4054 sub_parser.add_argument('--block_size',
4055 help='Block size (default: 4096)',
4056 type=parse_number,
4057 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004058 # TODO(zeuthen): The --generate_fec option was removed when we
4059 # moved to generating FEC by default. To avoid breaking existing
4060 # users needing to transition we simply just print a warning below
4061 # in add_hashtree_footer(). Remove this option and the warning at
4062 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004063 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004064 help=argparse.SUPPRESS,
4065 action='store_true')
4066 sub_parser.add_argument('--do_not_generate_fec',
4067 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004068 action='store_true')
4069 sub_parser.add_argument('--fec_num_roots',
4070 help='Number of roots for FEC (default: 2)',
4071 type=parse_number,
4072 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004073 sub_parser.add_argument('--calc_max_image_size',
4074 help=('Don\'t store the hashtree or footer - '
4075 'instead calculate the maximum image size '
4076 'leaving enough room for hashtree '
4077 'and metadata with the given partition '
4078 'size.'),
4079 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004080 sub_parser.add_argument('--output_vbmeta_image',
4081 help='Also write vbmeta struct to file',
4082 type=argparse.FileType('wb'))
4083 sub_parser.add_argument('--do_not_append_vbmeta_image',
4084 help=('Do not append vbmeta struct or footer '
4085 'to the image'),
4086 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004087 # This is different from --setup_rootfs_from_kernel insofar that
4088 # it doesn't take an IMAGE, the generated cmdline will be for the
4089 # hashtree we're adding.
4090 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4091 action='store_true',
4092 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004093 sub_parser.add_argument('--no_hashtree',
4094 action='store_true',
4095 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004096 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004097 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004098 sub_parser.set_defaults(func=self.add_hashtree_footer)
4099
4100 sub_parser = subparsers.add_parser('erase_footer',
4101 help='Erase footer from an image.')
4102 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004103 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004104 type=argparse.FileType('rwb+'),
4105 required=True)
4106 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004107 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004108 action='store_true')
4109 sub_parser.set_defaults(func=self.erase_footer)
4110
David Zeuthen1394f762019-04-30 10:20:11 -04004111 sub_parser = subparsers.add_parser('zero_hashtree',
4112 help='Zero out hashtree and FEC data.')
4113 sub_parser.add_argument('--image',
4114 help='Image with a footer',
4115 type=argparse.FileType('rwb+'),
4116 required=True)
4117 sub_parser.set_defaults(func=self.zero_hashtree)
4118
David Zeuthen49936b42018-08-07 17:38:58 -04004119 sub_parser = subparsers.add_parser('extract_vbmeta_image',
4120 help='Extracts vbmeta from an image with a footer.')
4121 sub_parser.add_argument('--image',
4122 help='Image with footer',
4123 type=argparse.FileType('rb'),
4124 required=True)
4125 sub_parser.add_argument('--output',
4126 help='Output file name',
4127 type=argparse.FileType('wb'))
4128 sub_parser.add_argument('--padding_size',
4129 metavar='NUMBER',
4130 help='If non-zero, pads output with NUL bytes so '
4131 'its size is a multiple of NUMBER (default: 0)',
4132 type=parse_number,
4133 default=0)
4134 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4135
David Zeuthen2bc232b2017-04-19 14:25:19 -04004136 sub_parser = subparsers.add_parser('resize_image',
4137 help='Resize image with a footer.')
4138 sub_parser.add_argument('--image',
4139 help='Image with a footer',
4140 type=argparse.FileType('rwb+'),
4141 required=True)
4142 sub_parser.add_argument('--partition_size',
4143 help='New partition size',
4144 type=parse_number)
4145 sub_parser.set_defaults(func=self.resize_image)
4146
David Zeuthen21e95262016-07-27 17:58:40 -04004147 sub_parser = subparsers.add_parser(
4148 'info_image',
4149 help='Show information about vbmeta or footer.')
4150 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004151 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004152 type=argparse.FileType('rb'),
4153 required=True)
4154 sub_parser.add_argument('--output',
4155 help='Write info to file',
4156 type=argparse.FileType('wt'),
4157 default=sys.stdout)
4158 sub_parser.set_defaults(func=self.info_image)
4159
David Zeuthenb623d8b2017-04-04 16:05:53 -04004160 sub_parser = subparsers.add_parser(
4161 'verify_image',
4162 help='Verify an image.')
4163 sub_parser.add_argument('--image',
4164 help='Image to verify',
4165 type=argparse.FileType('rb'),
4166 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004167 sub_parser.add_argument('--key',
4168 help='Check embedded public key matches KEY',
4169 metavar='KEY',
4170 required=False)
4171 sub_parser.add_argument('--expected_chain_partition',
4172 help='Expected chain partition',
4173 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4174 action='append')
David Zeuthene947cb62019-01-25 15:27:08 -05004175 sub_parser.add_argument('--follow_chain_partitions',
4176 help=('Follows chain partitions even when not '
4177 'specified with the --expected_chain_partition option'),
4178 action='store_true')
David Zeuthen1394f762019-04-30 10:20:11 -04004179 sub_parser.add_argument('--accept_zeroed_hashtree',
4180 help=('Accept images where the hashtree or FEC data is zeroed out'),
4181 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004182 sub_parser.set_defaults(func=self.verify_image)
4183
David Zeuthenb8643c02018-05-17 17:21:18 -04004184 sub_parser = subparsers.add_parser(
4185 'calculate_vbmeta_digest',
4186 help='Calculate vbmeta digest.')
4187 sub_parser.add_argument('--image',
4188 help='Image to calculate digest for',
4189 type=argparse.FileType('rb'),
4190 required=True)
4191 sub_parser.add_argument('--hash_algorithm',
4192 help='Hash algorithm to use (default: sha256)',
4193 default='sha256')
4194 sub_parser.add_argument('--output',
4195 help='Write hex digest to file (default: stdout)',
4196 type=argparse.FileType('wt'),
4197 default=sys.stdout)
4198 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4199
David Zeuthenf7d2e752018-09-20 13:30:41 -04004200 sub_parser = subparsers.add_parser(
4201 'calculate_kernel_cmdline',
4202 help='Calculate kernel cmdline.')
4203 sub_parser.add_argument('--image',
4204 help='Image to calculate kernel cmdline for',
4205 type=argparse.FileType('rb'),
4206 required=True)
4207 sub_parser.add_argument('--hashtree_disabled',
4208 help='Return the cmdline for hashtree disabled',
4209 action='store_true')
4210 sub_parser.add_argument('--output',
4211 help='Write cmdline to file (default: stdout)',
4212 type=argparse.FileType('wt'),
4213 default=sys.stdout)
4214 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4215
David Zeuthen8b6973b2016-09-20 12:39:49 -04004216 sub_parser = subparsers.add_parser('set_ab_metadata',
4217 help='Set A/B metadata.')
4218 sub_parser.add_argument('--misc_image',
4219 help=('The misc image to modify. If the image does '
4220 'not exist, it will be created.'),
4221 type=argparse.FileType('r+b'),
4222 required=True)
4223 sub_parser.add_argument('--slot_data',
4224 help=('Slot data of the form "priority", '
4225 '"tries_remaining", "sucessful_boot" for '
4226 'slot A followed by the same for slot B, '
4227 'separated by colons. The default value '
4228 'is 15:7:0:14:7:0.'),
4229 default='15:7:0:14:7:0')
4230 sub_parser.set_defaults(func=self.set_ab_metadata)
4231
Darren Krahn147b08d2016-12-20 16:38:29 -08004232 sub_parser = subparsers.add_parser(
4233 'make_atx_certificate',
4234 help='Create an Android Things eXtension (ATX) certificate.')
4235 sub_parser.add_argument('--output',
4236 help='Write certificate to file',
4237 type=argparse.FileType('wb'),
4238 default=sys.stdout)
4239 sub_parser.add_argument('--subject',
4240 help=('Path to subject file'),
4241 type=argparse.FileType('rb'),
4242 required=True)
4243 sub_parser.add_argument('--subject_key',
4244 help=('Path to subject RSA public key file'),
4245 type=argparse.FileType('rb'),
4246 required=True)
4247 sub_parser.add_argument('--subject_key_version',
4248 help=('Version of the subject key'),
4249 type=parse_number,
4250 required=False)
4251 sub_parser.add_argument('--subject_is_intermediate_authority',
4252 help=('Generate an intermediate authority '
4253 'certificate'),
4254 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004255 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004256 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004257 'string'),
4258 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004259 sub_parser.add_argument('--authority_key',
4260 help='Path to authority RSA private key file',
4261 required=False)
4262 sub_parser.add_argument('--signing_helper',
4263 help='Path to helper used for signing',
4264 metavar='APP',
4265 default=None,
4266 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004267 sub_parser.add_argument('--signing_helper_with_files',
4268 help='Path to helper used for signing using files',
4269 metavar='APP',
4270 default=None,
4271 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004272 sub_parser.set_defaults(func=self.make_atx_certificate)
4273
4274 sub_parser = subparsers.add_parser(
4275 'make_atx_permanent_attributes',
4276 help='Create Android Things eXtension (ATX) permanent attributes.')
4277 sub_parser.add_argument('--output',
4278 help='Write attributes to file',
4279 type=argparse.FileType('wb'),
4280 default=sys.stdout)
4281 sub_parser.add_argument('--root_authority_key',
4282 help='Path to authority RSA public key file',
4283 type=argparse.FileType('rb'),
4284 required=True)
4285 sub_parser.add_argument('--product_id',
4286 help=('Path to Product ID file'),
4287 type=argparse.FileType('rb'),
4288 required=True)
4289 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4290
4291 sub_parser = subparsers.add_parser(
4292 'make_atx_metadata',
4293 help='Create Android Things eXtension (ATX) metadata.')
4294 sub_parser.add_argument('--output',
4295 help='Write metadata to file',
4296 type=argparse.FileType('wb'),
4297 default=sys.stdout)
4298 sub_parser.add_argument('--intermediate_key_certificate',
4299 help='Path to intermediate key certificate file',
4300 type=argparse.FileType('rb'),
4301 required=True)
4302 sub_parser.add_argument('--product_key_certificate',
4303 help='Path to product key certificate file',
4304 type=argparse.FileType('rb'),
4305 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004306 sub_parser.set_defaults(func=self.make_atx_metadata)
4307
Darren Krahnfccd64e2018-01-16 17:39:35 -08004308 sub_parser = subparsers.add_parser(
4309 'make_atx_unlock_credential',
4310 help='Create an Android Things eXtension (ATX) unlock credential.')
4311 sub_parser.add_argument('--output',
4312 help='Write credential to file',
4313 type=argparse.FileType('wb'),
4314 default=sys.stdout)
4315 sub_parser.add_argument('--intermediate_key_certificate',
4316 help='Path to intermediate key certificate file',
4317 type=argparse.FileType('rb'),
4318 required=True)
4319 sub_parser.add_argument('--unlock_key_certificate',
4320 help='Path to unlock key certificate file',
4321 type=argparse.FileType('rb'),
4322 required=True)
4323 sub_parser.add_argument('--challenge',
4324 help='Path to the challenge to sign (optional). If '
4325 'this is not provided the challenge signature '
4326 'field is omitted and can be concatenated '
4327 'later.',
4328 required=False)
4329 sub_parser.add_argument('--unlock_key',
4330 help='Path to unlock key (optional). Must be '
4331 'provided if using --challenge.',
4332 required=False)
4333 sub_parser.add_argument('--signing_helper',
4334 help='Path to helper used for signing',
4335 metavar='APP',
4336 default=None,
4337 required=False)
4338 sub_parser.add_argument('--signing_helper_with_files',
4339 help='Path to helper used for signing using files',
4340 metavar='APP',
4341 default=None,
4342 required=False)
4343 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4344
David Zeuthen21e95262016-07-27 17:58:40 -04004345 args = parser.parse_args(argv[1:])
4346 try:
4347 args.func(args)
4348 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004349 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004350 sys.exit(1)
4351
4352 def version(self, _):
4353 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004354 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004355
4356 def extract_public_key(self, args):
4357 """Implements the 'extract_public_key' sub-command."""
4358 self.avb.extract_public_key(args.key, args.output)
4359
4360 def make_vbmeta_image(self, args):
4361 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004362 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004363 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004364 args.algorithm, args.key,
4365 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004366 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004367 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004368 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004369 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004370 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004371 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004372 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004373 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004374 args.print_required_libavb_version,
4375 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004376
David Zeuthenb1b994d2017-03-06 18:01:31 -05004377 def append_vbmeta_image(self, args):
4378 """Implements the 'append_vbmeta_image' sub-command."""
4379 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4380 args.partition_size)
4381
David Zeuthen21e95262016-07-27 17:58:40 -04004382 def add_hash_footer(self, args):
4383 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004384 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004385 self.avb.add_hash_footer(args.image.name if args.image else None,
4386 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004387 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004388 args.salt, args.chain_partition, args.algorithm,
4389 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004390 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004391 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004392 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004393 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004394 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004395 args.calc_max_image_size,
4396 args.signing_helper,
4397 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004398 args.internal_release_string,
4399 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004400 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004401 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004402 args.print_required_libavb_version,
4403 args.use_persistent_digest,
4404 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004405
4406 def add_hashtree_footer(self, args):
4407 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004408 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004409 # TODO(zeuthen): Remove when removing support for the
4410 # '--generate_fec' option above.
4411 if args.generate_fec:
4412 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4413 'is now generated by default. Use the option '
4414 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004415 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4416 args.partition_size,
4417 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004418 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004419 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004420 args.salt, args.chain_partition, args.algorithm,
4421 args.key, args.public_key_metadata,
4422 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004423 args.prop_from_file,
4424 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004425 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004426 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004427 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004428 args.calc_max_image_size,
4429 args.signing_helper,
4430 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004431 args.internal_release_string,
4432 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004433 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004434 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004435 args.print_required_libavb_version,
4436 args.use_persistent_digest,
Jooyung Hand7221942019-06-17 13:19:57 +09004437 args.do_not_use_ab,
4438 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004439
David Zeuthen21e95262016-07-27 17:58:40 -04004440 def erase_footer(self, args):
4441 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004442 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004443
David Zeuthen1394f762019-04-30 10:20:11 -04004444 def zero_hashtree(self, args):
4445 """Implements the 'zero_hashtree' sub-command."""
4446 self.avb.zero_hashtree(args.image.name)
4447
David Zeuthen49936b42018-08-07 17:38:58 -04004448 def extract_vbmeta_image(self, args):
4449 """Implements the 'extract_vbmeta_image' sub-command."""
4450 self.avb.extract_vbmeta_image(args.output, args.image.name,
4451 args.padding_size)
4452
David Zeuthen2bc232b2017-04-19 14:25:19 -04004453 def resize_image(self, args):
4454 """Implements the 'resize_image' sub-command."""
4455 self.avb.resize_image(args.image.name, args.partition_size)
4456
David Zeuthen8b6973b2016-09-20 12:39:49 -04004457 def set_ab_metadata(self, args):
4458 """Implements the 'set_ab_metadata' sub-command."""
4459 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4460
David Zeuthen21e95262016-07-27 17:58:40 -04004461 def info_image(self, args):
4462 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004463 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004464
David Zeuthenb623d8b2017-04-04 16:05:53 -04004465 def verify_image(self, args):
4466 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004467 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004468 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004469 args.follow_chain_partitions,
4470 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004471
David Zeuthenb8643c02018-05-17 17:21:18 -04004472 def calculate_vbmeta_digest(self, args):
4473 """Implements the 'calculate_vbmeta_digest' sub-command."""
4474 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4475 args.output)
4476
David Zeuthenf7d2e752018-09-20 13:30:41 -04004477 def calculate_kernel_cmdline(self, args):
4478 """Implements the 'calculate_kernel_cmdline' sub-command."""
4479 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, args.output)
4480
Darren Krahn147b08d2016-12-20 16:38:29 -08004481 def make_atx_certificate(self, args):
4482 """Implements the 'make_atx_certificate' sub-command."""
4483 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004484 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004485 args.subject_key_version,
4486 args.subject.read(),
4487 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004488 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004489 args.signing_helper,
4490 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004491
4492 def make_atx_permanent_attributes(self, args):
4493 """Implements the 'make_atx_permanent_attributes' sub-command."""
4494 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004495 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004496 args.product_id.read())
4497
4498 def make_atx_metadata(self, args):
4499 """Implements the 'make_atx_metadata' sub-command."""
4500 self.avb.make_atx_metadata(args.output,
4501 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004502 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004503
Darren Krahnfccd64e2018-01-16 17:39:35 -08004504 def make_atx_unlock_credential(self, args):
4505 """Implements the 'make_atx_unlock_credential' sub-command."""
4506 self.avb.make_atx_unlock_credential(
4507 args.output,
4508 args.intermediate_key_certificate.read(),
4509 args.unlock_key_certificate.read(),
4510 args.challenge,
4511 args.unlock_key,
4512 args.signing_helper,
4513 args.signing_helper_with_files)
4514
David Zeuthen21e95262016-07-27 17:58:40 -04004515
4516if __name__ == '__main__':
4517 tool = AvbTool()
4518 tool.run(sys.argv)