blob: 665c24e5ed7b15f8fd76eac0deefac3946d22f95 [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 == '':
Tao Bao558bd752019-09-18 18:18:34 -07001389 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001390 image = image_containing_descriptor
1391 else:
1392 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1393 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001394 # Generate the hashtree and checks that it matches what's in the file.
1395 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1396 digest_padding = round_to_pow2(digest_size) - digest_size
1397 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1398 self.image_size, self.data_block_size, digest_size + digest_padding)
1399 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1400 self.data_block_size,
1401 self.hash_algorithm, self.salt,
1402 digest_padding,
1403 hash_level_offsets,
1404 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001405 # The root digest must match unless it is not embedded in the descriptor.
1406 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001407 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1408 format(image_filename))
1409 return False
1410 # ... also check that the on-disk hashtree matches
1411 image.seek(self.tree_offset)
1412 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001413 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001414 if is_zeroed and accept_zeroed_hashtree:
1415 print ('{}: skipping verification since hashtree is zeroed and --accept_zeroed_hashtree was given'
1416 .format(self.partition_name))
1417 else:
1418 if hash_tree != hash_tree_ondisk:
1419 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001420 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001421 return False
1422 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1423 .format(self.partition_name, self.hash_algorithm, image.filename,
1424 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001425 # TODO: we could also verify that the FEC stored in the image is
1426 # correct but this a) currently requires the 'fec' binary; and b)
1427 # takes a long time; and c) is not strictly needed for
1428 # verification purposes as we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001429 return True
1430
David Zeuthen21e95262016-07-27 17:58:40 -04001431
1432class AvbHashDescriptor(AvbDescriptor):
1433 """A class for hash descriptors.
1434
1435 See the |AvbHashDescriptor| C struct for more information.
1436
1437 Attributes:
1438 image_size: Image size, in bytes.
1439 hash_algorithm: Hash algorithm used.
1440 partition_name: Partition name.
1441 salt: Salt used.
1442 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001443 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001444 """
1445
1446 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001447 RESERVED = 60
1448 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001449 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1450 'Q' # image size (bytes)
1451 '32s' # hash algorithm used
1452 'L' # partition name (bytes)
1453 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001454 'L' # digest length (bytes)
1455 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001456 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001457
1458 def __init__(self, data=None):
1459 """Initializes a new hash descriptor.
1460
1461 Arguments:
1462 data: If not None, must be a bytearray of size |SIZE|.
1463
1464 Raises:
1465 LookupError: If the given descriptor is malformed.
1466 """
1467 AvbDescriptor.__init__(self, None)
1468 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1469
1470 if data:
1471 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1472 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001473 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1474 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001475 expected_size = round_to_multiple(
1476 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1477 if tag != self.TAG or num_bytes_following != expected_size:
1478 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1479 # Nuke NUL-bytes at the end.
1480 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1481 o = 0
1482 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1483 partition_name_len)])
1484 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1485 self.partition_name.decode('utf-8')
1486 o += partition_name_len
1487 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1488 o += salt_len
1489 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1490 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001491 if digest_len != 0:
1492 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001493
1494 else:
1495 self.image_size = 0
1496 self.hash_algorithm = ''
1497 self.partition_name = ''
1498 self.salt = bytearray()
1499 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001500 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001501
1502 def print_desc(self, o):
1503 """Print the descriptor.
1504
1505 Arguments:
1506 o: The object to write the output to.
1507 """
1508 o.write(' Hash descriptor:\n')
1509 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1510 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1511 o.write(' Partition Name: {}\n'.format(self.partition_name))
1512 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1513 'hex')))
1514 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1515 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001516 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001517
1518 def encode(self):
1519 """Serializes the descriptor.
1520
1521 Returns:
1522 A bytearray() with the descriptor data.
1523 """
1524 encoded_name = self.partition_name.encode('utf-8')
1525 num_bytes_following = (
1526 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1527 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1528 padding_size = nbf_with_padding - num_bytes_following
1529 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1530 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001531 len(self.salt), len(self.digest), self.flags,
1532 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001533 padding = struct.pack(str(padding_size) + 'x')
1534 ret = desc + encoded_name + self.salt + self.digest + padding
1535 return bytearray(ret)
1536
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001537 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001538 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001539 """Verifies contents of the descriptor - used in verify_image sub-command.
1540
1541 Arguments:
1542 image_dir: The directory of the file being verified.
1543 image_ext: The extension of the file being verified (e.g. '.img').
1544 expected_chain_partitions_map: A map from partition name to the
1545 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001546 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001547 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001548
1549 Returns:
1550 True if the descriptor verifies, False otherwise.
1551 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001552 if self.partition_name == '':
Tao Bao558bd752019-09-18 18:18:34 -07001553 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001554 image = image_containing_descriptor
1555 else:
1556 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1557 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001558 data = image.read(self.image_size)
1559 ha = hashlib.new(self.hash_algorithm)
1560 ha.update(self.salt)
1561 ha.update(data)
1562 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001563 # The digest must match unless there is no digest in the descriptor.
1564 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001565 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1566 format(self.hash_algorithm, image_filename))
1567 return False
1568 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001569 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001570 self.image_size))
1571 return True
1572
David Zeuthen21e95262016-07-27 17:58:40 -04001573
1574class AvbKernelCmdlineDescriptor(AvbDescriptor):
1575 """A class for kernel command-line descriptors.
1576
1577 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1578
1579 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001580 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001581 kernel_cmdline: The kernel command-line.
1582 """
1583
1584 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001585 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001586 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001587 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001588 'L') # cmdline length (bytes)
1589
David Zeuthenfd41eb92016-11-17 12:24:47 -05001590 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1591 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1592
David Zeuthen21e95262016-07-27 17:58:40 -04001593 def __init__(self, data=None):
1594 """Initializes a new kernel cmdline descriptor.
1595
1596 Arguments:
1597 data: If not None, must be a bytearray of size |SIZE|.
1598
1599 Raises:
1600 LookupError: If the given descriptor is malformed.
1601 """
1602 AvbDescriptor.__init__(self, None)
1603 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1604
1605 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001606 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001607 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1608 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1609 8)
1610 if tag != self.TAG or num_bytes_following != expected_size:
1611 raise LookupError('Given data does not look like a kernel cmdline '
1612 'descriptor.')
1613 # Nuke NUL-bytes at the end.
1614 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1615 kernel_cmdline_length)])
1616 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1617 self.kernel_cmdline.decode('utf-8')
1618 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001619 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001620 self.kernel_cmdline = ''
1621
1622 def print_desc(self, o):
1623 """Print the descriptor.
1624
1625 Arguments:
1626 o: The object to write the output to.
1627 """
1628 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001629 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001630 o.write(' Kernel Cmdline: {}\n'.format(repr(
1631 self.kernel_cmdline)))
1632
1633 def encode(self):
1634 """Serializes the descriptor.
1635
1636 Returns:
1637 A bytearray() with the descriptor data.
1638 """
1639 encoded_str = self.kernel_cmdline.encode('utf-8')
1640 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1641 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1642 padding_size = nbf_with_padding - num_bytes_following
1643 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001644 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001645 padding = struct.pack(str(padding_size) + 'x')
1646 ret = desc + encoded_str + padding
1647 return bytearray(ret)
1648
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001649 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001650 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001651 """Verifies contents of the descriptor - used in verify_image sub-command.
1652
1653 Arguments:
1654 image_dir: The directory of the file being verified.
1655 image_ext: The extension of the file being verified (e.g. '.img').
1656 expected_chain_partitions_map: A map from partition name to the
1657 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001658 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001659 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001660
1661 Returns:
1662 True if the descriptor verifies, False otherwise.
1663 """
1664 # Nothing to verify.
1665 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001666
1667class AvbChainPartitionDescriptor(AvbDescriptor):
1668 """A class for chained partition descriptors.
1669
1670 See the |AvbChainPartitionDescriptor| C struct for more information.
1671
1672 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001673 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001674 partition_name: Partition name.
1675 public_key: Bytes for the public key.
1676 """
1677
1678 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001679 RESERVED = 64
1680 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001681 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001682 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001683 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001684 'L' + # public_key_size (bytes)
1685 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001686
1687 def __init__(self, data=None):
1688 """Initializes a new chain partition descriptor.
1689
1690 Arguments:
1691 data: If not None, must be a bytearray of size |SIZE|.
1692
1693 Raises:
1694 LookupError: If the given descriptor is malformed.
1695 """
1696 AvbDescriptor.__init__(self, None)
1697 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1698
1699 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001700 (tag, num_bytes_following, self.rollback_index_location,
1701 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001702 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001703 expected_size = round_to_multiple(
1704 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1705 if tag != self.TAG or num_bytes_following != expected_size:
1706 raise LookupError('Given data does not look like a chain partition '
1707 'descriptor.')
1708 o = 0
1709 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1710 partition_name_len)])
1711 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1712 self.partition_name.decode('utf-8')
1713 o += partition_name_len
1714 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1715
1716 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001717 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001718 self.partition_name = ''
1719 self.public_key = bytearray()
1720
1721 def print_desc(self, o):
1722 """Print the descriptor.
1723
1724 Arguments:
1725 o: The object to write the output to.
1726 """
1727 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001728 o.write(' Partition Name: {}\n'.format(self.partition_name))
1729 o.write(' Rollback Index Location: {}\n'.format(
1730 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001731 # Just show the SHA1 of the key, for size reasons.
1732 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001733 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001734
1735 def encode(self):
1736 """Serializes the descriptor.
1737
1738 Returns:
1739 A bytearray() with the descriptor data.
1740 """
1741 encoded_name = self.partition_name.encode('utf-8')
1742 num_bytes_following = (
1743 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1744 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1745 padding_size = nbf_with_padding - num_bytes_following
1746 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001747 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001748 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001749 padding = struct.pack(str(padding_size) + 'x')
1750 ret = desc + encoded_name + self.public_key + padding
1751 return bytearray(ret)
1752
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001753 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001754 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001755 """Verifies contents of the descriptor - used in verify_image sub-command.
1756
1757 Arguments:
1758 image_dir: The directory of the file being verified.
1759 image_ext: The extension of the file being verified (e.g. '.img').
1760 expected_chain_partitions_map: A map from partition name to the
1761 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001762 image_containing_descriptor: The image the descriptor is in.
David Zeuthen1394f762019-04-30 10:20:11 -04001763 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001764
1765 Returns:
1766 True if the descriptor verifies, False otherwise.
1767 """
1768 value = expected_chain_partitions_map.get(self.partition_name)
1769 if not value:
1770 sys.stderr.write('No expected chain partition for partition {}. Use '
1771 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001772 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001773 format(self.partition_name))
1774 return False
1775 rollback_index_location, pk_blob = value
1776
1777 if self.rollback_index_location != rollback_index_location:
1778 sys.stderr.write('Expected rollback_index_location {} does not '
1779 'match {} in descriptor for partition {}\n'.
1780 format(rollback_index_location,
1781 self.rollback_index_location,
1782 self.partition_name))
1783 return False
1784
1785 if self.public_key != pk_blob:
1786 sys.stderr.write('Expected public key blob does not match public '
1787 'key blob in descriptor for partition {}\n'.
1788 format(self.partition_name))
1789 return False
1790
1791 print ('{}: Successfully verified chain partition descriptor matches '
1792 'expected data'.format(self.partition_name))
1793
1794 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001795
1796DESCRIPTOR_CLASSES = [
1797 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1798 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1799]
1800
1801
1802def parse_descriptors(data):
1803 """Parses a blob of data into descriptors.
1804
1805 Arguments:
1806 data: A bytearray() with encoded descriptors.
1807
1808 Returns:
1809 A list of instances of objects derived from AvbDescriptor. For
1810 unknown descriptors, the class AvbDescriptor is used.
1811 """
1812 o = 0
1813 ret = []
1814 while o < len(data):
1815 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1816 if tag < len(DESCRIPTOR_CLASSES):
1817 c = DESCRIPTOR_CLASSES[tag]
1818 else:
1819 c = AvbDescriptor
1820 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1821 o += 16 + nb_following
1822 return ret
1823
1824
1825class AvbFooter(object):
1826 """A class for parsing and writing footers.
1827
1828 Footers are stored at the end of partitions and point to where the
1829 AvbVBMeta blob is located. They also contain the original size of
1830 the image before AVB information was added.
1831
1832 Attributes:
1833 magic: Magic for identifying the footer, see |MAGIC|.
1834 version_major: The major version of avbtool that wrote the footer.
1835 version_minor: The minor version of avbtool that wrote the footer.
1836 original_image_size: Original image size.
1837 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1838 vbmeta_size: Size of the AvbVBMeta blob.
1839 """
1840
1841 MAGIC = 'AVBf'
1842 SIZE = 64
1843 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001844 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1845 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001846 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1847 'Q' # Original image size.
1848 'Q' # Offset of VBMeta blob.
1849 'Q' + # Size of VBMeta blob.
1850 str(RESERVED) + 'x') # padding for reserved bytes
1851
1852 def __init__(self, data=None):
1853 """Initializes a new footer object.
1854
1855 Arguments:
1856 data: If not None, must be a bytearray of size 4096.
1857
1858 Raises:
1859 LookupError: If the given footer is malformed.
1860 struct.error: If the given data has no footer.
1861 """
1862 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1863
1864 if data:
1865 (self.magic, self.version_major, self.version_minor,
1866 self.original_image_size, self.vbmeta_offset,
1867 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1868 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001869 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001870 else:
1871 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001872 self.version_major = self.FOOTER_VERSION_MAJOR
1873 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001874 self.original_image_size = 0
1875 self.vbmeta_offset = 0
1876 self.vbmeta_size = 0
1877
David Zeuthena4fee8b2016-08-22 15:20:43 -04001878 def encode(self):
1879 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001880
David Zeuthena4fee8b2016-08-22 15:20:43 -04001881 Returns:
1882 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001883 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001884 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1885 self.version_minor, self.original_image_size,
1886 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001887
1888
1889class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001890 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001891
1892 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07001893 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1894 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04001895 """
1896
1897 SIZE = 256
1898
David Zeuthene3cadca2017-02-22 21:25:46 -05001899 # Keep in sync with |reserved0| and |reserved| field of
1900 # |AvbVBMetaImageHeader|.
1901 RESERVED0 = 4
1902 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001903
1904 # Keep in sync with |AvbVBMetaImageHeader|.
1905 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1906 '2Q' # 2 x block size
1907 'L' # algorithm type
1908 '2Q' # offset, size (hash)
1909 '2Q' # offset, size (signature)
1910 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001911 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001912 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001913 'Q' # rollback_index
1914 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001915 str(RESERVED0) + 'x' + # padding for reserved bytes
1916 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001917 str(RESERVED) + 'x') # padding for reserved bytes
1918
1919 def __init__(self, data=None):
1920 """Initializes a new header object.
1921
1922 Arguments:
1923 data: If not None, must be a bytearray of size 8192.
1924
1925 Raises:
1926 Exception: If the given data is malformed.
1927 """
1928 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1929
1930 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001931 (self.magic, self.required_libavb_version_major,
1932 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001933 self.authentication_data_block_size, self.auxiliary_data_block_size,
1934 self.algorithm_type, self.hash_offset, self.hash_size,
1935 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001936 self.public_key_size, self.public_key_metadata_offset,
1937 self.public_key_metadata_size, self.descriptors_offset,
1938 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001939 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001940 self.flags,
1941 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001942 # Nuke NUL-bytes at the end of the string.
1943 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001944 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001945 else:
1946 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001947 # Start by just requiring version 1.0. Code that adds features
1948 # in a future version can use bump_required_libavb_version_minor() to
1949 # bump the minor.
1950 self.required_libavb_version_major = AVB_VERSION_MAJOR
1951 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001952 self.authentication_data_block_size = 0
1953 self.auxiliary_data_block_size = 0
1954 self.algorithm_type = 0
1955 self.hash_offset = 0
1956 self.hash_size = 0
1957 self.signature_offset = 0
1958 self.signature_size = 0
1959 self.public_key_offset = 0
1960 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001961 self.public_key_metadata_offset = 0
1962 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001963 self.descriptors_offset = 0
1964 self.descriptors_size = 0
1965 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001966 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001967 self.release_string = get_release_string()
1968
1969 def bump_required_libavb_version_minor(self, minor):
1970 """Function to bump required_libavb_version_minor.
1971
1972 Call this when writing data that requires a specific libavb
1973 version to parse it.
1974
1975 Arguments:
1976 minor: The minor version of libavb that has support for the feature.
1977 """
1978 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001979 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001980
1981 def save(self, output):
1982 """Serializes the header (256 bytes) to disk.
1983
1984 Arguments:
1985 output: The object to write the output to.
1986 """
1987 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001988 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1989 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001990 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1991 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001992 self.public_key_offset, self.public_key_size,
1993 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001994 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001995 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001996
1997 def encode(self):
1998 """Serializes the header (256) to a bytearray().
1999
2000 Returns:
2001 A bytearray() with the encoded header.
2002 """
2003 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002004 self.required_libavb_version_major,
2005 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002006 self.authentication_data_block_size,
2007 self.auxiliary_data_block_size, self.algorithm_type,
2008 self.hash_offset, self.hash_size, self.signature_offset,
2009 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002010 self.public_key_size, self.public_key_metadata_offset,
2011 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002012 self.descriptors_size, self.rollback_index, self.flags,
2013 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002014
2015
2016class Avb(object):
2017 """Business logic for avbtool command-line tool."""
2018
David Zeuthen8b6973b2016-09-20 12:39:49 -04002019 # Keep in sync with avb_ab_flow.h.
2020 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2021 AB_MAGIC = '\0AB0'
2022 AB_MAJOR_VERSION = 1
2023 AB_MINOR_VERSION = 0
2024 AB_MISC_METADATA_OFFSET = 2048
2025
David Zeuthen09692692016-09-30 16:16:40 -04002026 # Constants for maximum metadata size. These are used to give
2027 # meaningful errors if the value passed in via --partition_size is
2028 # too small and when --calc_max_image_size is used. We use
2029 # conservative figures.
2030 MAX_VBMETA_SIZE = 64 * 1024
2031 MAX_FOOTER_SIZE = 4096
2032
David Zeuthen49936b42018-08-07 17:38:58 -04002033 def extract_vbmeta_image(self, output, image_filename, padding_size):
2034 """Implements the 'extract_vbmeta_image' command.
2035
2036 Arguments:
2037 output: Write vbmeta struct to this file.
2038 image_filename: File to extract vbmeta data from (with a footer).
2039 padding_size: If not 0, pads output so size is a multiple of the number.
2040
2041 Raises:
2042 AvbError: If there's no footer in the image.
2043 """
2044 image = ImageHandler(image_filename)
2045
2046 (footer, _, _, _) = self._parse_image(image)
2047
2048 if not footer:
2049 raise AvbError('Given image does not have a footer.')
2050
2051 image.seek(footer.vbmeta_offset)
2052 vbmeta_blob = image.read(footer.vbmeta_size)
2053 output.write(vbmeta_blob)
2054
2055 if padding_size > 0:
2056 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2057 padding_needed = padded_size - len(vbmeta_blob)
2058 output.write('\0' * padding_needed)
2059
David Zeuthena4fee8b2016-08-22 15:20:43 -04002060 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002061 """Implements the 'erase_footer' command.
2062
2063 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002064 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002065 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002066
2067 Raises:
2068 AvbError: If there's no footer in the image.
2069 """
2070
David Zeuthena4fee8b2016-08-22 15:20:43 -04002071 image = ImageHandler(image_filename)
2072
David Zeuthen21e95262016-07-27 17:58:40 -04002073 (footer, _, descriptors, _) = self._parse_image(image)
2074
2075 if not footer:
2076 raise AvbError('Given image does not have a footer.')
2077
2078 new_image_size = None
2079 if not keep_hashtree:
2080 new_image_size = footer.original_image_size
2081 else:
2082 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002083 # descriptor to figure out the location and size of the hashtree
2084 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002085 for desc in descriptors:
2086 if isinstance(desc, AvbHashtreeDescriptor):
2087 # The hashtree is always just following the main data so the
2088 # new size is easily derived.
2089 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002090 # If the image has FEC codes, also keep those.
2091 if desc.fec_offset > 0:
2092 fec_end = desc.fec_offset + desc.fec_size
2093 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002094 break
2095 if not new_image_size:
2096 raise AvbError('Requested to keep hashtree but no hashtree '
2097 'descriptor was found.')
2098
2099 # And cut...
2100 image.truncate(new_image_size)
2101
David Zeuthen1394f762019-04-30 10:20:11 -04002102 def zero_hashtree(self, image_filename):
2103 """Implements the 'zero_hashtree' command.
2104
2105 Arguments:
2106 image_filename: File to zero hashtree and FEC data from.
2107
2108 Raises:
2109 AvbError: If there's no footer in the image.
2110 """
2111
2112 image = ImageHandler(image_filename)
2113
2114 (footer, _, descriptors, _) = self._parse_image(image)
2115
2116 if not footer:
2117 raise AvbError('Given image does not have a footer.')
2118
2119 # Search for a hashtree descriptor to figure out the location and
2120 # size of the hashtree and FEC.
2121 ht_desc = None
2122 for desc in descriptors:
2123 if isinstance(desc, AvbHashtreeDescriptor):
2124 ht_desc = desc
2125 break
2126
2127 if not ht_desc:
2128 raise AvbError('No hashtree descriptor was found.')
2129
2130 zero_ht_start_offset = ht_desc.tree_offset
2131 zero_ht_num_bytes = ht_desc.tree_size
2132 zero_fec_start_offset = None
2133 zero_fec_num_bytes = 0
2134 if ht_desc.fec_offset > 0:
2135 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2136 raise AvbError('Hash-tree and FEC data must be adjacent.')
2137 zero_fec_start_offset = ht_desc.fec_offset
2138 zero_fec_num_bytes = ht_desc.fec_size
2139 zero_end_offset = zero_ht_start_offset + zero_ht_num_bytes + zero_fec_num_bytes
2140 image.seek(zero_end_offset)
2141 data = image.read(image.image_size - zero_end_offset)
2142
2143 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2144 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2145 # beginning of both hashtree and FEC. (That way, in the future we can add
2146 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2147 #
2148 # Applications can use these markers to detect that the hashtree and/or
2149 # FEC needs to be recomputed.
2150 image.truncate(zero_ht_start_offset)
2151 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2152 image.append_raw(data_zeroed_firstblock)
2153 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2154 if zero_fec_start_offset:
2155 image.append_raw(data_zeroed_firstblock)
2156 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2157 image.append_raw(data)
2158
David Zeuthen2bc232b2017-04-19 14:25:19 -04002159 def resize_image(self, image_filename, partition_size):
2160 """Implements the 'resize_image' command.
2161
2162 Arguments:
2163 image_filename: File with footer to resize.
2164 partition_size: The new size of the image.
2165
2166 Raises:
2167 AvbError: If there's no footer in the image.
2168 """
2169
2170 image = ImageHandler(image_filename)
2171
2172 if partition_size % image.block_size != 0:
2173 raise AvbError('Partition size of {} is not a multiple of the image '
2174 'block size {}.'.format(partition_size,
2175 image.block_size))
2176
2177 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2178
2179 if not footer:
2180 raise AvbError('Given image does not have a footer.')
2181
2182 # The vbmeta blob is always at the end of the data so resizing an
2183 # image amounts to just moving the footer around.
2184
2185 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2186 if vbmeta_end_offset % image.block_size != 0:
2187 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2188
2189 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002190 raise AvbError('Requested size of {} is too small for an image '
2191 'of size {}.'
2192 .format(partition_size,
2193 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002194
2195 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2196 # with enough bytes such that the final Footer block is at the end
2197 # of partition_size.
2198 image.truncate(vbmeta_end_offset)
2199 image.append_dont_care(partition_size - vbmeta_end_offset -
2200 1*image.block_size)
2201
2202 # Just reuse the same footer - only difference is that we're
2203 # writing it in a different place.
2204 footer_blob = footer.encode()
2205 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2206 footer_blob)
2207 image.append_raw(footer_blob_with_padding)
2208
David Zeuthen8b6973b2016-09-20 12:39:49 -04002209 def set_ab_metadata(self, misc_image, slot_data):
2210 """Implements the 'set_ab_metadata' command.
2211
2212 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2213 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2214
2215 Arguments:
2216 misc_image: The misc image to write to.
2217 slot_data: Slot data as a string
2218
2219 Raises:
2220 AvbError: If slot data is malformed.
2221 """
2222 tokens = slot_data.split(':')
2223 if len(tokens) != 6:
2224 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2225 a_priority = int(tokens[0])
2226 a_tries_remaining = int(tokens[1])
2227 a_success = True if int(tokens[2]) != 0 else False
2228 b_priority = int(tokens[3])
2229 b_tries_remaining = int(tokens[4])
2230 b_success = True if int(tokens[5]) != 0 else False
2231
2232 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2233 self.AB_MAGIC,
2234 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2235 a_priority, a_tries_remaining, a_success,
2236 b_priority, b_tries_remaining, b_success)
2237 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2238 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2239 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2240 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2241 misc_image.write(ab_data)
2242
David Zeuthena4fee8b2016-08-22 15:20:43 -04002243 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002244 """Implements the 'info_image' command.
2245
2246 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002247 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002248 output: Output file to write human-readable information to (file object).
2249 """
2250
David Zeuthena4fee8b2016-08-22 15:20:43 -04002251 image = ImageHandler(image_filename)
2252
David Zeuthen21e95262016-07-27 17:58:40 -04002253 o = output
2254
2255 (footer, header, descriptors, image_size) = self._parse_image(image)
2256
2257 if footer:
2258 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2259 footer.version_minor))
2260 o.write('Image size: {} bytes\n'.format(image_size))
2261 o.write('Original image size: {} bytes\n'.format(
2262 footer.original_image_size))
2263 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2264 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2265 o.write('--\n')
2266
2267 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2268
David Zeuthene3cadca2017-02-22 21:25:46 -05002269 o.write('Minimum libavb version: {}.{}{}\n'.format(
2270 header.required_libavb_version_major,
2271 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002272 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002273 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2274 o.write('Authentication Block: {} bytes\n'.format(
2275 header.authentication_data_block_size))
2276 o.write('Auxiliary Block: {} bytes\n'.format(
2277 header.auxiliary_data_block_size))
2278 o.write('Algorithm: {}\n'.format(alg_name))
2279 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002280 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002281 o.write('Release String: \'{}\'\n'.format(
2282 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002283
2284 # Print descriptors.
2285 num_printed = 0
2286 o.write('Descriptors:\n')
2287 for desc in descriptors:
2288 desc.print_desc(o)
2289 num_printed += 1
2290 if num_printed == 0:
2291 o.write(' (none)\n')
2292
David Zeuthen1394f762019-04-30 10:20:11 -04002293 def verify_image(self, image_filename, key_path, expected_chain_partitions, follow_chain_partitions,
2294 accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002295 """Implements the 'verify_image' command.
2296
2297 Arguments:
2298 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002299 key_path: None or check that embedded public key matches key at given path.
2300 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthene947cb62019-01-25 15:27:08 -05002301 follow_chain_partitions: If True, will follows chain partitions even when not
2302 specified with the --expected_chain_partition option
David Zeuthen1394f762019-04-30 10:20:11 -04002303 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is zeroed out.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002304 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002305 expected_chain_partitions_map = {}
2306 if expected_chain_partitions:
2307 used_locations = {}
2308 for cp in expected_chain_partitions:
2309 cp_tokens = cp.split(':')
2310 if len(cp_tokens) != 3:
2311 raise AvbError('Malformed chained partition "{}".'.format(cp))
2312 partition_name = cp_tokens[0]
2313 rollback_index_location = int(cp_tokens[1])
2314 file_path = cp_tokens[2]
2315 pk_blob = open(file_path).read()
2316 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2317
2318 image_dir = os.path.dirname(image_filename)
2319 image_ext = os.path.splitext(image_filename)[1]
2320
2321 key_blob = None
2322 if key_path:
2323 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2324 key_blob = encode_rsa_key(key_path)
2325 else:
2326 print 'Verifying image {} using embedded public key'.format(image_filename)
2327
David Zeuthenb623d8b2017-04-04 16:05:53 -04002328 image = ImageHandler(image_filename)
2329 (footer, header, descriptors, image_size) = self._parse_image(image)
2330 offset = 0
2331 if footer:
2332 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002333
David Zeuthenb623d8b2017-04-04 16:05:53 -04002334 image.seek(offset)
David Zeuthen49936b42018-08-07 17:38:58 -04002335 vbmeta_blob = image.read(header.SIZE + header.authentication_data_block_size +
2336 header.auxiliary_data_block_size)
2337
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002338 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002339 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002340 raise AvbError('Signature check failed for {} vbmeta struct {}'
2341 .format(alg_name, image_filename))
2342
2343 if key_blob:
2344 # The embedded public key is in the auxiliary block at an offset.
2345 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002346 key_offset += header.authentication_data_block_size
2347 key_offset += header.public_key_offset
2348 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002349 if key_blob != key_blob_in_vbmeta:
2350 raise AvbError('Embedded public key does not match given key.')
2351
2352 if footer:
2353 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002354 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002355 else:
2356 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002357 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002358
2359 for desc in descriptors:
David Zeuthene947cb62019-01-25 15:27:08 -05002360 if (isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions and
2361 expected_chain_partitions_map.get(desc.partition_name) == None):
2362 # In this case we're processing a chain descriptor but don't have a
2363 # --expect_chain_partition ... however --follow_chain_partitions was
2364 # specified so we shouldn't error out in desc.verify().
2365 print ('{}: Chained but ROLLBACK_SLOT (which is {}) and KEY (which has sha1 {}) not specified'
2366 .format(desc.partition_name, desc.rollback_index_location,
2367 hashlib.sha1(desc.public_key).hexdigest()))
2368 else:
David Zeuthen1394f762019-04-30 10:20:11 -04002369 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image,
2370 accept_zeroed_hashtree):
David Zeuthene947cb62019-01-25 15:27:08 -05002371 raise AvbError('Error verifying descriptor.')
2372 # Honor --follow_chain_partitions - add '--' to make the output more readable.
2373 if isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions:
2374 print '--'
2375 chained_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
David Zeuthen9d364652019-05-14 15:30:37 -04002376 self.verify_image(chained_image_filename, key_path, None, False, accept_zeroed_hashtree)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002377
David Zeuthenb623d8b2017-04-04 16:05:53 -04002378
David Zeuthenb8643c02018-05-17 17:21:18 -04002379 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2380 """Implements the 'calculate_vbmeta_digest' command.
2381
2382 Arguments:
2383 image_filename: Image file to get information from (file object).
2384 hash_algorithm: Hash algorithm used.
2385 output: Output file to write human-readable information to (file object).
2386 """
2387
2388 image_dir = os.path.dirname(image_filename)
2389 image_ext = os.path.splitext(image_filename)[1]
2390
2391 image = ImageHandler(image_filename)
2392 (footer, header, descriptors, image_size) = self._parse_image(image)
2393 offset = 0
2394 if footer:
2395 offset = footer.vbmeta_offset
2396 size = (header.SIZE + header.authentication_data_block_size +
2397 header.auxiliary_data_block_size)
2398 image.seek(offset)
2399 vbmeta_blob = image.read(size)
2400
2401 hasher = hashlib.new(name=hash_algorithm)
2402 hasher.update(vbmeta_blob)
2403
2404 for desc in descriptors:
2405 if isinstance(desc, AvbChainPartitionDescriptor):
2406 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2407 ch_image = ImageHandler(ch_image_filename)
2408 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2409 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002410 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2411 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002412 if ch_footer:
2413 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002414 ch_image.seek(ch_offset)
2415 ch_vbmeta_blob = ch_image.read(ch_size)
2416 hasher.update(ch_vbmeta_blob)
2417
2418 digest = hasher.digest()
2419 output.write('{}\n'.format(digest.encode('hex')))
2420
2421
David Zeuthenf7d2e752018-09-20 13:30:41 -04002422 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2423 """Implements the 'calculate_kernel_cmdline' command.
2424
2425 Arguments:
2426 image_filename: Image file to get information from (file object).
2427 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2428 output: Output file to write human-readable information to (file object).
2429 """
2430
2431 image = ImageHandler(image_filename)
2432 _, _, descriptors, _ = self._parse_image(image)
2433
2434 image_dir = os.path.dirname(image_filename)
2435 image_ext = os.path.splitext(image_filename)[1]
2436
2437 cmdline_descriptors = []
2438 for desc in descriptors:
2439 if isinstance(desc, AvbChainPartitionDescriptor):
2440 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2441 ch_image = ImageHandler(ch_image_filename)
2442 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2443 for ch_desc in ch_descriptors:
2444 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2445 cmdline_descriptors.append(ch_desc)
2446 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2447 cmdline_descriptors.append(desc)
2448
2449 kernel_cmdline_snippets = []
2450 for desc in cmdline_descriptors:
2451 use_cmdline = True
2452 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) != 0:
2453 if hashtree_disabled:
2454 use_cmdline = False
2455 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2456 if not hashtree_disabled:
2457 use_cmdline = False
2458 if use_cmdline:
2459 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2460 output.write(' '.join(kernel_cmdline_snippets))
2461
2462
David Zeuthen21e95262016-07-27 17:58:40 -04002463 def _parse_image(self, image):
2464 """Gets information about an image.
2465
2466 The image can either be a vbmeta or an image with a footer.
2467
2468 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002469 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002470
2471 Returns:
2472 A tuple where the first argument is a AvbFooter (None if there
2473 is no footer on the image), the second argument is a
2474 AvbVBMetaHeader, the third argument is a list of
2475 AvbDescriptor-derived instances, and the fourth argument is the
2476 size of |image|.
2477 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002478 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002479 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002480 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002481 try:
2482 footer = AvbFooter(image.read(AvbFooter.SIZE))
2483 except (LookupError, struct.error):
2484 # Nope, just seek back to the start.
2485 image.seek(0)
2486
2487 vbmeta_offset = 0
2488 if footer:
2489 vbmeta_offset = footer.vbmeta_offset
2490
2491 image.seek(vbmeta_offset)
2492 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2493
2494 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2495 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2496 desc_start_offset = aux_block_offset + h.descriptors_offset
2497 image.seek(desc_start_offset)
2498 descriptors = parse_descriptors(image.read(h.descriptors_size))
2499
David Zeuthen09692692016-09-30 16:16:40 -04002500 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002501
David Zeuthenb1b994d2017-03-06 18:01:31 -05002502 def _load_vbmeta_blob(self, image):
2503 """Gets the vbmeta struct and associated sections.
2504
2505 The image can either be a vbmeta.img or an image with a footer.
2506
2507 Arguments:
2508 image: An ImageHandler (vbmeta or footer).
2509
2510 Returns:
2511 A blob with the vbmeta struct and other sections.
2512 """
2513 assert isinstance(image, ImageHandler)
2514 footer = None
2515 image.seek(image.image_size - AvbFooter.SIZE)
2516 try:
2517 footer = AvbFooter(image.read(AvbFooter.SIZE))
2518 except (LookupError, struct.error):
2519 # Nope, just seek back to the start.
2520 image.seek(0)
2521
2522 vbmeta_offset = 0
2523 if footer:
2524 vbmeta_offset = footer.vbmeta_offset
2525
2526 image.seek(vbmeta_offset)
2527 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2528
2529 image.seek(vbmeta_offset)
2530 data_size = AvbVBMetaHeader.SIZE
2531 data_size += h.authentication_data_block_size
2532 data_size += h.auxiliary_data_block_size
2533 return image.read(data_size)
2534
David Zeuthen73f2afa2017-05-17 16:54:11 -04002535 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002536 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002537
2538 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002539 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002540
2541 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002542 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2543 instructions. There is one for when hashtree is not disabled and one for
2544 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002545
David Zeuthen21e95262016-07-27 17:58:40 -04002546 """
2547
David Zeuthen21e95262016-07-27 17:58:40 -04002548 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002549 c += '0' # start
2550 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2551 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2552 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2553 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2554 c += ' {}'.format(ht.data_block_size) # data_block
2555 c += ' {}'.format(ht.hash_block_size) # hash_block
2556 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2557 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2558 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2559 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2560 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2561 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002562 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002563 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002564 c += ' ignore_zero_blocks'
2565 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2566 c += ' fec_roots {}'.format(ht.fec_num_roots)
2567 # Note that fec_blocks is the size that FEC covers, *not* the
2568 # size of the FEC data. Since we use FEC for everything up until
2569 # the FEC data, it's the same as the offset.
2570 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2571 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2572 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002573 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002574 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002575 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002576 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002577
David Zeuthenfd41eb92016-11-17 12:24:47 -05002578 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002579 desc = AvbKernelCmdlineDescriptor()
2580 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002581 desc.flags = (
2582 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2583
2584 # The descriptor for when hashtree verification is disabled is a lot
2585 # simpler - we just set the root to the partition.
2586 desc_no_ht = AvbKernelCmdlineDescriptor()
2587 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2588 desc_no_ht.flags = (
2589 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2590
2591 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002592
David Zeuthen73f2afa2017-05-17 16:54:11 -04002593 def _get_cmdline_descriptors_for_dm_verity(self, image):
2594 """Generate kernel cmdline descriptors for dm-verity.
2595
2596 Arguments:
2597 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2598
2599 Returns:
2600 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2601 instructions. There is one for when hashtree is not disabled and one for
2602 when it is.
2603
2604 Raises:
2605 AvbError: If |image| doesn't have a hashtree descriptor.
2606
2607 """
2608
2609 (_, _, descriptors, _) = self._parse_image(image)
2610
2611 ht = None
2612 for desc in descriptors:
2613 if isinstance(desc, AvbHashtreeDescriptor):
2614 ht = desc
2615 break
2616
2617 if not ht:
2618 raise AvbError('No hashtree descriptor in given image')
2619
2620 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2621
David Zeuthen21e95262016-07-27 17:58:40 -04002622 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002623 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002624 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002625 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002626 include_descriptors_from_image,
2627 signing_helper,
2628 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002629 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002630 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002631 print_required_libavb_version,
2632 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002633 """Implements the 'make_vbmeta_image' command.
2634
2635 Arguments:
2636 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002637 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002638 algorithm_name: Name of algorithm to use.
2639 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002640 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002641 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002642 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002643 props: Properties to insert (list of strings of the form 'key:value').
2644 props_from_file: Properties to insert (list of strings 'key:<path>').
2645 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002646 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002647 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002648 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002649 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002650 release_string: None or avbtool release string to use instead of default.
2651 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002652 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002653 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002654
2655 Raises:
2656 AvbError: If a chained partition is malformed.
2657 """
2658
David Zeuthen1097a782017-05-31 15:53:17 -04002659 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002660 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002661 if include_descriptors_from_image:
2662 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2663 # version of all included descriptors.
2664 tmp_header = AvbVBMetaHeader()
2665 for image in include_descriptors_from_image:
2666 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2667 tmp_header.bump_required_libavb_version_minor(
2668 image_header.required_libavb_version_minor)
2669 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2670 else:
2671 # Descriptors aside, all vbmeta features are supported in 1.0.
2672 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002673 return
2674
2675 if not output:
2676 raise AvbError('No output file given')
2677
David Zeuthen21e95262016-07-27 17:58:40 -04002678 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002679 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002680 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002681 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002682 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002683 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002684 include_descriptors_from_image, signing_helper,
2685 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002686 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002687
2688 # Write entire vbmeta blob (header, authentication, auxiliary).
2689 output.seek(0)
2690 output.write(vbmeta_blob)
2691
David Zeuthen97cb5802017-06-01 16:14:05 -04002692 if padding_size > 0:
2693 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2694 padding_needed = padded_size - len(vbmeta_blob)
2695 output.write('\0' * padding_needed)
2696
David Zeuthen18666ab2016-11-15 11:18:05 -05002697 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2698 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002699 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002700 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002701 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002702 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002703 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002704 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002705 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002706 release_string, append_to_release_string,
2707 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002708 """Generates a VBMeta blob.
2709
2710 This blob contains the header (struct AvbVBMetaHeader), the
2711 authentication data block (which contains the hash and signature
2712 for the header and auxiliary block), and the auxiliary block
2713 (which contains descriptors, the public key used, and other data).
2714
2715 The |key| parameter can |None| only if the |algorithm_name| is
2716 'NONE'.
2717
2718 Arguments:
2719 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2720 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002721 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002722 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002723 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002724 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002725 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002726 props: Properties to insert (List of strings of the form 'key:value').
2727 props_from_file: Properties to insert (List of strings 'key:<path>').
2728 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002729 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002730 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002731 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2732 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002733 include_descriptors_from_image: List of file objects for which
2734 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002735 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002736 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002737 release_string: None or avbtool release string.
2738 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002739 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002740
2741 Returns:
2742 A bytearray() with the VBMeta blob.
2743
2744 Raises:
2745 Exception: If the |algorithm_name| is not found, if no key has
2746 been given and the given algorithm requires one, or the key is
2747 of the wrong size.
2748
2749 """
2750 try:
2751 alg = ALGORITHMS[algorithm_name]
2752 except KeyError:
2753 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2754
David Zeuthena5fd3a42017-02-27 16:38:54 -05002755 if not descriptors:
2756 descriptors = []
2757
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002758 h = AvbVBMetaHeader()
2759 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2760
David Zeuthena5fd3a42017-02-27 16:38:54 -05002761 # Insert chained partition descriptors, if any
2762 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002763 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002764 for cp in chain_partitions:
2765 cp_tokens = cp.split(':')
2766 if len(cp_tokens) != 3:
2767 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002768 partition_name = cp_tokens[0]
2769 rollback_index_location = int(cp_tokens[1])
2770 file_path = cp_tokens[2]
2771 # Check that the same rollback location isn't being used by
2772 # multiple chained partitions.
2773 if used_locations.get(rollback_index_location):
2774 raise AvbError('Rollback Index Location {} is already in use.'.format(
2775 rollback_index_location))
2776 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002777 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002778 desc.partition_name = partition_name
2779 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002780 if desc.rollback_index_location < 1:
2781 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002782 desc.public_key = open(file_path, 'rb').read()
2783 descriptors.append(desc)
2784
David Zeuthen21e95262016-07-27 17:58:40 -04002785 # Descriptors.
2786 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002787 for desc in descriptors:
2788 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002789
2790 # Add properties.
2791 if props:
2792 for prop in props:
2793 idx = prop.find(':')
2794 if idx == -1:
2795 raise AvbError('Malformed property "{}".'.format(prop))
2796 desc = AvbPropertyDescriptor()
2797 desc.key = prop[0:idx]
2798 desc.value = prop[(idx + 1):]
2799 encoded_descriptors.extend(desc.encode())
2800 if props_from_file:
2801 for prop in props_from_file:
2802 idx = prop.find(':')
2803 if idx == -1:
2804 raise AvbError('Malformed property "{}".'.format(prop))
2805 desc = AvbPropertyDescriptor()
2806 desc.key = prop[0:idx]
2807 desc.value = prop[(idx + 1):]
2808 file_path = prop[(idx + 1):]
2809 desc.value = open(file_path, 'rb').read()
2810 encoded_descriptors.extend(desc.encode())
2811
David Zeuthen73f2afa2017-05-17 16:54:11 -04002812 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002813 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002814 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002815 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002816 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2817 encoded_descriptors.extend(cmdline_desc[0].encode())
2818 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002819
David Zeuthen73f2afa2017-05-17 16:54:11 -04002820 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2821 if ht_desc_to_setup:
2822 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2823 ht_desc_to_setup)
2824 encoded_descriptors.extend(cmdline_desc[0].encode())
2825 encoded_descriptors.extend(cmdline_desc[1].encode())
2826
David Zeuthen21e95262016-07-27 17:58:40 -04002827 # Add kernel command-lines.
2828 if kernel_cmdlines:
2829 for i in kernel_cmdlines:
2830 desc = AvbKernelCmdlineDescriptor()
2831 desc.kernel_cmdline = i
2832 encoded_descriptors.extend(desc.encode())
2833
2834 # Add descriptors from other images.
2835 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002836 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002837 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002838 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002839 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2840 image_handler)
2841 # Bump the required libavb version to support all included descriptors.
2842 h.bump_required_libavb_version_minor(
2843 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002844 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002845 # The --include_descriptors_from_image option is used in some setups
2846 # with images A and B where both A and B contain a descriptor
2847 # for a partition with the same name. Since it's not meaningful
2848 # to include both descriptors, only include the last seen descriptor.
2849 # See bug 76386656 for details.
2850 if hasattr(desc, 'partition_name'):
2851 key = type(desc).__name__ + '_' + desc.partition_name
2852 descriptors_dict[key] = desc.encode()
2853 else:
2854 encoded_descriptors.extend(desc.encode())
2855 for key in sorted(descriptors_dict.keys()):
2856 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002857
David Zeuthen18666ab2016-11-15 11:18:05 -05002858 # Load public key metadata blob, if requested.
2859 pkmd_blob = []
2860 if public_key_metadata_path:
2861 with open(public_key_metadata_path) as f:
2862 pkmd_blob = f.read()
2863
David Zeuthen21e95262016-07-27 17:58:40 -04002864 key = None
2865 encoded_key = bytearray()
2866 if alg.public_key_num_bytes > 0:
2867 if not key_path:
2868 raise AvbError('Key is required for algorithm {}'.format(
2869 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002870 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002871 if len(encoded_key) != alg.public_key_num_bytes:
2872 raise AvbError('Key is wrong size for algorithm {}'.format(
2873 algorithm_name))
2874
David Zeuthene3cadca2017-02-22 21:25:46 -05002875 # Override release string, if requested.
2876 if isinstance(release_string, (str, unicode)):
2877 h.release_string = release_string
2878
2879 # Append to release string, if requested. Also insert a space before.
2880 if isinstance(append_to_release_string, (str, unicode)):
2881 h.release_string += ' ' + append_to_release_string
2882
David Zeuthen18666ab2016-11-15 11:18:05 -05002883 # For the Auxiliary data block, descriptors are stored at offset 0,
2884 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002885 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002886 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002887 h.descriptors_offset = 0
2888 h.descriptors_size = len(encoded_descriptors)
2889 h.public_key_offset = h.descriptors_size
2890 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002891 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2892 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002893
2894 # For the Authentication data block, the hash is first and then
2895 # the signature.
2896 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002897 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002898 h.algorithm_type = alg.algorithm_type
2899 h.hash_offset = 0
2900 h.hash_size = alg.hash_num_bytes
2901 # Signature offset and size - it's stored right after the hash
2902 # (in Authentication data block).
2903 h.signature_offset = alg.hash_num_bytes
2904 h.signature_size = alg.signature_num_bytes
2905
2906 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002907 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002908
2909 # Generate Header data block.
2910 header_data_blob = h.encode()
2911
2912 # Generate Auxiliary data block.
2913 aux_data_blob = bytearray()
2914 aux_data_blob.extend(encoded_descriptors)
2915 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002916 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002917 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2918 aux_data_blob.extend('\0' * padding_bytes)
2919
2920 # Calculate the hash.
2921 binary_hash = bytearray()
2922 binary_signature = bytearray()
2923 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002924 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002925 ha.update(header_data_blob)
2926 ha.update(aux_data_blob)
2927 binary_hash.extend(ha.digest())
2928
2929 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002930 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002931 binary_signature.extend(raw_sign(signing_helper,
2932 signing_helper_with_files,
2933 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002934 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002935 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002936
2937 # Generate Authentication data block.
2938 auth_data_blob = bytearray()
2939 auth_data_blob.extend(binary_hash)
2940 auth_data_blob.extend(binary_signature)
2941 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2942 auth_data_blob.extend('\0' * padding_bytes)
2943
2944 return header_data_blob + auth_data_blob + aux_data_blob
2945
2946 def extract_public_key(self, key_path, output):
2947 """Implements the 'extract_public_key' command.
2948
2949 Arguments:
2950 key_path: The path to a RSA private key file.
2951 output: The file to write to.
2952 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002953 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002954
David Zeuthenb1b994d2017-03-06 18:01:31 -05002955 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2956 partition_size):
2957 """Implementation of the append_vbmeta_image command.
2958
2959 Arguments:
2960 image_filename: File to add the footer to.
2961 vbmeta_image_filename: File to get vbmeta struct from.
2962 partition_size: Size of partition.
2963
2964 Raises:
2965 AvbError: If an argument is incorrect.
2966 """
2967 image = ImageHandler(image_filename)
2968
2969 if partition_size % image.block_size != 0:
2970 raise AvbError('Partition size of {} is not a multiple of the image '
2971 'block size {}.'.format(partition_size,
2972 image.block_size))
2973
2974 # If there's already a footer, truncate the image to its original
2975 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002976 if image.image_size >= AvbFooter.SIZE:
2977 image.seek(image.image_size - AvbFooter.SIZE)
2978 try:
2979 footer = AvbFooter(image.read(AvbFooter.SIZE))
2980 # Existing footer found. Just truncate.
2981 original_image_size = footer.original_image_size
2982 image.truncate(footer.original_image_size)
2983 except (LookupError, struct.error):
2984 original_image_size = image.image_size
2985 else:
2986 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002987 original_image_size = image.image_size
2988
2989 # If anything goes wrong from here-on, restore the image back to
2990 # its original size.
2991 try:
2992 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2993 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2994
2995 # If the image isn't sparse, its size might not be a multiple of
2996 # the block size. This will screw up padding later so just grow it.
2997 if image.image_size % image.block_size != 0:
2998 assert not image.is_sparse
2999 padding_needed = image.block_size - (image.image_size%image.block_size)
3000 image.truncate(image.image_size + padding_needed)
3001
3002 # The append_raw() method requires content with size being a
3003 # multiple of |block_size| so add padding as needed. Also record
3004 # where this is written to since we'll need to put that in the
3005 # footer.
3006 vbmeta_offset = image.image_size
3007 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3008 len(vbmeta_blob))
3009 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3010
3011 # Append vbmeta blob and footer
3012 image.append_raw(vbmeta_blob_with_padding)
3013 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3014
3015 # Now insert a DONT_CARE chunk with enough bytes such that the
3016 # final Footer block is at the end of partition_size..
3017 image.append_dont_care(partition_size - vbmeta_end_offset -
3018 1*image.block_size)
3019
3020 # Generate the Footer that tells where the VBMeta footer
3021 # is. Also put enough padding in the front of the footer since
3022 # we'll write out an entire block.
3023 footer = AvbFooter()
3024 footer.original_image_size = original_image_size
3025 footer.vbmeta_offset = vbmeta_offset
3026 footer.vbmeta_size = len(vbmeta_blob)
3027 footer_blob = footer.encode()
3028 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3029 footer_blob)
3030 image.append_raw(footer_blob_with_padding)
3031
3032 except:
3033 # Truncate back to original size, then re-raise
3034 image.truncate(original_image_size)
3035 raise
3036
David Zeuthena4fee8b2016-08-22 15:20:43 -04003037 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003038 hash_algorithm, salt, chain_partitions, algorithm_name,
3039 key_path,
3040 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003041 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003042 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003043 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003044 signing_helper, signing_helper_with_files,
3045 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003046 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003047 print_required_libavb_version, use_persistent_digest,
3048 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003049 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003050
3051 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003052 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003053 partition_size: Size of partition.
3054 partition_name: Name of partition (without A/B suffix).
3055 hash_algorithm: Hash algorithm to use.
3056 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003057 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003058 algorithm_name: Name of algorithm to use.
3059 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003060 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003061 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003062 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003063 props: Properties to insert (List of strings of the form 'key:value').
3064 props_from_file: Properties to insert (List of strings 'key:<path>').
3065 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003066 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003067 dm-verity kernel cmdline from.
3068 include_descriptors_from_image: List of file objects for which
3069 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003070 calc_max_image_size: Don't store the footer - instead calculate the
3071 maximum image size leaving enough room for metadata with the
3072 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003073 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003074 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003075 release_string: None or avbtool release string.
3076 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003077 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3078 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003079 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003080 use_persistent_digest: Use a persistent digest on device.
3081 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003082
3083 Raises:
3084 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003085 """
David Zeuthen1097a782017-05-31 15:53:17 -04003086
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003087 required_libavb_version_minor = 0
3088 if use_persistent_digest or do_not_use_ab:
3089 required_libavb_version_minor = 1
3090
David Zeuthen1097a782017-05-31 15:53:17 -04003091 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003092 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003093 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003094 return
3095
David Zeuthenbf562452017-05-17 18:04:43 -04003096 # First, calculate the maximum image size such that an image
3097 # this size + metadata (footer + vbmeta struct) fits in
3098 # |partition_size|.
3099 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003100 if partition_size < max_metadata_size:
3101 raise AvbError('Parition size of {} is too small. '
3102 'Needs to be at least {}'.format(
3103 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003104 max_image_size = partition_size - max_metadata_size
3105
3106 # If we're asked to only calculate the maximum image size, we're done.
3107 if calc_max_image_size:
3108 print '{}'.format(max_image_size)
3109 return
3110
David Zeuthena4fee8b2016-08-22 15:20:43 -04003111 image = ImageHandler(image_filename)
3112
3113 if partition_size % image.block_size != 0:
3114 raise AvbError('Partition size of {} is not a multiple of the image '
3115 'block size {}.'.format(partition_size,
3116 image.block_size))
3117
David Zeuthen21e95262016-07-27 17:58:40 -04003118 # If there's already a footer, truncate the image to its original
3119 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3120 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003121 if image.image_size >= AvbFooter.SIZE:
3122 image.seek(image.image_size - AvbFooter.SIZE)
3123 try:
3124 footer = AvbFooter(image.read(AvbFooter.SIZE))
3125 # Existing footer found. Just truncate.
3126 original_image_size = footer.original_image_size
3127 image.truncate(footer.original_image_size)
3128 except (LookupError, struct.error):
3129 original_image_size = image.image_size
3130 else:
3131 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003132 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003133
3134 # If anything goes wrong from here-on, restore the image back to
3135 # its original size.
3136 try:
David Zeuthen09692692016-09-30 16:16:40 -04003137 # If image size exceeds the maximum image size, fail.
3138 if image.image_size > max_image_size:
3139 raise AvbError('Image size of {} exceeds maximum image '
3140 'size of {} in order to fit in a partition '
3141 'size of {}.'.format(image.image_size, max_image_size,
3142 partition_size))
3143
David Zeuthen21e95262016-07-27 17:58:40 -04003144 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3145 if salt:
3146 salt = salt.decode('hex')
3147 else:
Bryan Henry45354282018-10-25 18:37:27 -07003148 if salt is None and not use_persistent_digest:
3149 # If salt is not explicitly specified, choose a hash that's the same
3150 # size as the hash size. Don't populate a random salt if this
3151 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003152 hash_size = digest_size
3153 salt = open('/dev/urandom').read(hash_size)
3154 else:
3155 salt = ''
3156
3157 hasher = hashlib.new(name=hash_algorithm, string=salt)
3158 # TODO(zeuthen): might want to read this in chunks to avoid
3159 # memory pressure, then again, this is only supposed to be used
3160 # on kernel/initramfs partitions. Possible optimization.
3161 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003162 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003163 digest = hasher.digest()
3164
3165 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003166 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003167 h_desc.hash_algorithm = hash_algorithm
3168 h_desc.partition_name = partition_name
3169 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003170 h_desc.flags = 0
3171 if do_not_use_ab:
3172 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3173 if not use_persistent_digest:
3174 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003175
3176 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003177 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003178 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003179 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003180 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003181 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003182 include_descriptors_from_image, signing_helper,
3183 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003184 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003185
David Zeuthend247fcb2017-02-16 12:09:27 -05003186 # Write vbmeta blob, if requested.
3187 if output_vbmeta_image:
3188 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003189
David Zeuthend247fcb2017-02-16 12:09:27 -05003190 # Append vbmeta blob and footer, unless requested not to.
3191 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003192 # If the image isn't sparse, its size might not be a multiple of
3193 # the block size. This will screw up padding later so just grow it.
3194 if image.image_size % image.block_size != 0:
3195 assert not image.is_sparse
3196 padding_needed = image.block_size - (
3197 image.image_size % image.block_size)
3198 image.truncate(image.image_size + padding_needed)
3199
3200 # The append_raw() method requires content with size being a
3201 # multiple of |block_size| so add padding as needed. Also record
3202 # where this is written to since we'll need to put that in the
3203 # footer.
3204 vbmeta_offset = image.image_size
3205 padding_needed = (
3206 round_to_multiple(len(vbmeta_blob), image.block_size) -
3207 len(vbmeta_blob))
3208 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3209
David Zeuthend247fcb2017-02-16 12:09:27 -05003210 image.append_raw(vbmeta_blob_with_padding)
3211 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3212
3213 # Now insert a DONT_CARE chunk with enough bytes such that the
3214 # final Footer block is at the end of partition_size..
3215 image.append_dont_care(partition_size - vbmeta_end_offset -
3216 1*image.block_size)
3217
3218 # Generate the Footer that tells where the VBMeta footer
3219 # is. Also put enough padding in the front of the footer since
3220 # we'll write out an entire block.
3221 footer = AvbFooter()
3222 footer.original_image_size = original_image_size
3223 footer.vbmeta_offset = vbmeta_offset
3224 footer.vbmeta_size = len(vbmeta_blob)
3225 footer_blob = footer.encode()
3226 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3227 footer_blob)
3228 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003229
David Zeuthen21e95262016-07-27 17:58:40 -04003230 except:
3231 # Truncate back to original size, then re-raise
3232 image.truncate(original_image_size)
3233 raise
3234
David Zeuthena4fee8b2016-08-22 15:20:43 -04003235 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003236 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003237 block_size, salt, chain_partitions, algorithm_name,
3238 key_path,
3239 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003240 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003241 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003242 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003243 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003244 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003245 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003246 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003247 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003248 print_required_libavb_version,
Jooyung Hand7221942019-06-17 13:19:57 +09003249 use_persistent_root_digest, do_not_use_ab, no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003250 """Implements the 'add_hashtree_footer' command.
3251
3252 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3253 more information about dm-verity and these hashes.
3254
3255 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003256 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003257 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003258 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003259 generate_fec: If True, generate FEC codes.
3260 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003261 hash_algorithm: Hash algorithm to use.
3262 block_size: Block size to use.
3263 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003264 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003265 algorithm_name: Name of algorithm to use.
3266 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003267 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003268 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003269 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003270 props: Properties to insert (List of strings of the form 'key:value').
3271 props_from_file: Properties to insert (List of strings 'key:<path>').
3272 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003273 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003274 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003275 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3276 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003277 include_descriptors_from_image: List of file objects for which
3278 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003279 calc_max_image_size: Don't store the hashtree or footer - instead
3280 calculate the maximum image size leaving enough room for hashtree
3281 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003282 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003283 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003284 release_string: None or avbtool release string.
3285 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003286 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3287 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003288 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003289 use_persistent_root_digest: Use a persistent root digest on device.
3290 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003291 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003292
3293 Raises:
3294 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003295 """
David Zeuthen1097a782017-05-31 15:53:17 -04003296
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003297 required_libavb_version_minor = 0
3298 if use_persistent_root_digest or do_not_use_ab:
3299 required_libavb_version_minor = 1
3300
David Zeuthen1097a782017-05-31 15:53:17 -04003301 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003302 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003303 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003304 return
3305
David Zeuthen09692692016-09-30 16:16:40 -04003306 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3307 digest_padding = round_to_pow2(digest_size) - digest_size
3308
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003309 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3310 # size such that an image this size + the hashtree + metadata (footer +
3311 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3312 # for metadata.
3313 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003314 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003315 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003316 if not no_hashtree:
3317 (_, max_tree_size) = calc_hash_level_offsets(
3318 partition_size, block_size, digest_size + digest_padding)
3319 if generate_fec:
3320 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003321 max_metadata_size = (max_fec_size + max_tree_size +
3322 self.MAX_VBMETA_SIZE +
3323 self.MAX_FOOTER_SIZE)
3324 max_image_size = partition_size - max_metadata_size
3325 else:
3326 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003327
3328 # If we're asked to only calculate the maximum image size, we're done.
3329 if calc_max_image_size:
3330 print '{}'.format(max_image_size)
3331 return
3332
David Zeuthena4fee8b2016-08-22 15:20:43 -04003333 image = ImageHandler(image_filename)
3334
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003335 if partition_size > 0:
3336 if partition_size % image.block_size != 0:
3337 raise AvbError('Partition size of {} is not a multiple of the image '
3338 'block size {}.'.format(partition_size,
3339 image.block_size))
3340 else:
3341 if image.image_size % image.block_size != 0:
3342 raise AvbError('File size of {} is not a multiple of the image '
3343 'block size {}.'.format(image.image_size,
3344 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003345
David Zeuthen21e95262016-07-27 17:58:40 -04003346 # If there's already a footer, truncate the image to its original
3347 # size. This way 'avbtool add_hashtree_footer' is idempotent
3348 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003349 if image.image_size >= AvbFooter.SIZE:
3350 image.seek(image.image_size - AvbFooter.SIZE)
3351 try:
3352 footer = AvbFooter(image.read(AvbFooter.SIZE))
3353 # Existing footer found. Just truncate.
3354 original_image_size = footer.original_image_size
3355 image.truncate(footer.original_image_size)
3356 except (LookupError, struct.error):
3357 original_image_size = image.image_size
3358 else:
3359 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003360 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003361
3362 # If anything goes wrong from here-on, restore the image back to
3363 # its original size.
3364 try:
3365 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003366 rounded_image_size = round_to_multiple(image.image_size, block_size)
3367 if rounded_image_size > image.image_size:
3368 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003369
David Zeuthen09692692016-09-30 16:16:40 -04003370 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003371 if partition_size > 0:
3372 if image.image_size > max_image_size:
3373 raise AvbError('Image size of {} exceeds maximum image '
3374 'size of {} in order to fit in a partition '
3375 'size of {}.'.format(image.image_size, max_image_size,
3376 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003377
3378 if salt:
3379 salt = salt.decode('hex')
3380 else:
Bryan Henry45354282018-10-25 18:37:27 -07003381 if salt is None and not use_persistent_root_digest:
3382 # If salt is not explicitly specified, choose a hash that's the same
3383 # size as the hash size. Don't populate a random salt if this
3384 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003385 hash_size = digest_size
3386 salt = open('/dev/urandom').read(hash_size)
3387 else:
3388 salt = ''
3389
David Zeuthena4fee8b2016-08-22 15:20:43 -04003390 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003391 # offsets in advance.
3392 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003393 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003394
David Zeuthena4fee8b2016-08-22 15:20:43 -04003395 # If the image isn't sparse, its size might not be a multiple of
3396 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003397 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003398 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003399 padding_needed = image.block_size - (image.image_size%image.block_size)
3400 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003401
David Zeuthena4fee8b2016-08-22 15:20:43 -04003402 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003403 tree_offset = image.image_size
3404 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003405 block_size,
3406 hash_algorithm, salt,
3407 digest_padding,
3408 hash_level_offsets,
3409 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003410
3411 # Generate HashtreeDescriptor with details about the tree we
3412 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003413 if no_hashtree:
3414 tree_size = 0
3415 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003416 ht_desc = AvbHashtreeDescriptor()
3417 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003418 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003419 ht_desc.tree_offset = tree_offset
3420 ht_desc.tree_size = tree_size
3421 ht_desc.data_block_size = block_size
3422 ht_desc.hash_block_size = block_size
3423 ht_desc.hash_algorithm = hash_algorithm
3424 ht_desc.partition_name = partition_name
3425 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003426 if do_not_use_ab:
3427 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3428 if not use_persistent_root_digest:
3429 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003430
David Zeuthen09692692016-09-30 16:16:40 -04003431 # Write the hash tree
3432 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3433 len(hash_tree))
3434 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3435 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003436 len_hashtree_and_fec = len(hash_tree_with_padding)
3437
3438 # Generate FEC codes, if requested.
3439 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003440 if no_hashtree:
3441 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003442 else:
3443 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003444 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3445 len(fec_data))
3446 fec_data_with_padding = fec_data + '\0'*padding_needed
3447 fec_offset = image.image_size
3448 image.append_raw(fec_data_with_padding)
3449 len_hashtree_and_fec += len(fec_data_with_padding)
3450 # Update the hashtree descriptor.
3451 ht_desc.fec_num_roots = fec_num_roots
3452 ht_desc.fec_offset = fec_offset
3453 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003454
David Zeuthen73f2afa2017-05-17 16:54:11 -04003455 ht_desc_to_setup = None
3456 if setup_as_rootfs_from_kernel:
3457 ht_desc_to_setup = ht_desc
3458
David Zeuthena4fee8b2016-08-22 15:20:43 -04003459 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003460 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003461 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003462 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003463 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003464 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003465 include_descriptors_from_image, signing_helper,
3466 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003467 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003468 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3469 len(vbmeta_blob))
3470 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003471
David Zeuthend247fcb2017-02-16 12:09:27 -05003472 # Write vbmeta blob, if requested.
3473 if output_vbmeta_image:
3474 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003475
David Zeuthend247fcb2017-02-16 12:09:27 -05003476 # Append vbmeta blob and footer, unless requested not to.
3477 if not do_not_append_vbmeta_image:
3478 image.append_raw(vbmeta_blob_with_padding)
3479
3480 # Now insert a DONT_CARE chunk with enough bytes such that the
3481 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003482 if partition_size > 0:
3483 image.append_dont_care(partition_size - image.image_size -
3484 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003485
3486 # Generate the Footer that tells where the VBMeta footer
3487 # is. Also put enough padding in the front of the footer since
3488 # we'll write out an entire block.
3489 footer = AvbFooter()
3490 footer.original_image_size = original_image_size
3491 footer.vbmeta_offset = vbmeta_offset
3492 footer.vbmeta_size = len(vbmeta_blob)
3493 footer_blob = footer.encode()
3494 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3495 footer_blob)
3496 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003497
David Zeuthen21e95262016-07-27 17:58:40 -04003498 except:
David Zeuthen09692692016-09-30 16:16:40 -04003499 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003500 image.truncate(original_image_size)
3501 raise
3502
David Zeuthenc68f0822017-03-31 17:22:35 -04003503 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003504 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003505 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003506 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003507 """Implements the 'make_atx_certificate' command.
3508
3509 Android Things certificates are required for Android Things public key
3510 metadata. They chain the vbmeta signing key for a particular product back to
3511 a fused, permanent root key. These certificates are fixed-length and fixed-
3512 format with the explicit goal of not parsing ASN.1 in bootloader code.
3513
3514 Arguments:
3515 output: Certificate will be written to this file on success.
3516 authority_key_path: A PEM file path with the authority private key.
3517 If None, then a certificate will be created without a
3518 signature. The signature can be created out-of-band
3519 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003520 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003521 subject_key_version: A 64-bit version value. If this is None, the number
3522 of seconds since the epoch is used.
3523 subject: A subject identifier. For Product Signing Key certificates this
3524 should be the same Product ID found in the permanent attributes.
3525 is_intermediate_authority: True if the certificate is for an intermediate
3526 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003527 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003528 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003529 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003530 """
3531 signed_data = bytearray()
3532 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003533 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003534 hasher = hashlib.sha256()
3535 hasher.update(subject)
3536 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003537 if not usage:
3538 usage = 'com.google.android.things.vboot'
3539 if is_intermediate_authority:
3540 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003541 hasher = hashlib.sha256()
3542 hasher.update(usage)
3543 signed_data.extend(hasher.digest())
3544 if not subject_key_version:
3545 subject_key_version = int(time.time())
3546 signed_data.extend(struct.pack('<Q', subject_key_version))
3547 signature = bytearray()
3548 if authority_key_path:
3549 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003550 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003551 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003552 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003553 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003554 hasher.update(signed_data)
3555 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003556 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3557 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003558 alg.signature_num_bytes, authority_key_path,
3559 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003560 output.write(signed_data)
3561 output.write(signature)
3562
David Zeuthenc68f0822017-03-31 17:22:35 -04003563 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003564 product_id):
3565 """Implements the 'make_atx_permanent_attributes' command.
3566
3567 Android Things permanent attributes are designed to be permanent for a
3568 particular product and a hash of these attributes should be fused into
3569 hardware to enforce this.
3570
3571 Arguments:
3572 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003573 root_authority_key_path: Path to a PEM or DER public key for
3574 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003575 product_id: A 16-byte Product ID.
3576
3577 Raises:
3578 AvbError: If an argument is incorrect.
3579 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003580 EXPECTED_PRODUCT_ID_SIZE = 16
3581 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003582 raise AvbError('Invalid Product ID length.')
3583 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003584 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003585 output.write(product_id)
3586
3587 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003588 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003589 """Implements the 'make_atx_metadata' command.
3590
3591 Android Things metadata are included in vbmeta images to facilitate
3592 verification. The output of this command can be used as the
3593 public_key_metadata argument to other commands.
3594
3595 Arguments:
3596 output: Metadata will be written to this file on success.
3597 intermediate_key_certificate: A certificate file as output by
3598 make_atx_certificate with
3599 is_intermediate_authority set to true.
3600 product_key_certificate: A certificate file as output by
3601 make_atx_certificate with
3602 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003603
3604 Raises:
3605 AvbError: If an argument is incorrect.
3606 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003607 EXPECTED_CERTIFICATE_SIZE = 1620
3608 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003609 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003610 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003611 raise AvbError('Invalid product key certificate length.')
3612 output.write(struct.pack('<I', 1)) # Format Version
3613 output.write(intermediate_key_certificate)
3614 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003615
Darren Krahnfccd64e2018-01-16 17:39:35 -08003616 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3617 unlock_key_certificate, challenge_path,
3618 unlock_key_path, signing_helper,
3619 signing_helper_with_files):
3620 """Implements the 'make_atx_unlock_credential' command.
3621
3622 Android Things unlock credentials can be used to authorize the unlock of AVB
3623 on a device. These credentials are presented to an Android Things bootloader
3624 via the fastboot interface in response to a 16-byte challenge. This method
3625 creates all fields of the credential except the challenge signature field
3626 (which is the last field) and can optionally create the challenge signature
3627 field as well if a challenge and the unlock_key_path is provided.
3628
3629 Arguments:
3630 output: The credential will be written to this file on success.
3631 intermediate_key_certificate: A certificate file as output by
3632 make_atx_certificate with
3633 is_intermediate_authority set to true.
3634 unlock_key_certificate: A certificate file as output by
3635 make_atx_certificate with
3636 is_intermediate_authority set to false and the
3637 usage set to
3638 'com.google.android.things.vboot.unlock'.
3639 challenge_path: [optional] A path to the challenge to sign.
3640 unlock_key_path: [optional] A PEM file path with the unlock private key.
3641 signing_helper: Program which signs a hash and returns the signature.
3642 signing_helper_with_files: Same as signing_helper but uses files instead.
3643
3644 Raises:
3645 AvbError: If an argument is incorrect.
3646 """
3647 EXPECTED_CERTIFICATE_SIZE = 1620
3648 EXPECTED_CHALLENGE_SIZE = 16
3649 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3650 raise AvbError('Invalid intermediate key certificate length.')
3651 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3652 raise AvbError('Invalid product key certificate length.')
3653 challenge = bytearray()
3654 if challenge_path:
3655 with open(challenge_path, 'r') as f:
3656 challenge = f.read()
3657 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3658 raise AvbError('Invalid unlock challenge length.')
3659 output.write(struct.pack('<I', 1)) # Format Version
3660 output.write(intermediate_key_certificate)
3661 output.write(unlock_key_certificate)
3662 if challenge_path and unlock_key_path:
3663 signature = bytearray()
3664 padding_and_hash = bytearray()
3665 algorithm_name = 'SHA512_RSA4096'
3666 alg = ALGORITHMS[algorithm_name]
3667 hasher = hashlib.sha512()
3668 padding_and_hash.extend(alg.padding)
3669 hasher.update(challenge)
3670 padding_and_hash.extend(hasher.digest())
3671 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3672 algorithm_name,
3673 alg.signature_num_bytes, unlock_key_path,
3674 padding_and_hash))
3675 output.write(signature)
3676
David Zeuthen21e95262016-07-27 17:58:40 -04003677
3678def calc_hash_level_offsets(image_size, block_size, digest_size):
3679 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3680
3681 Arguments:
3682 image_size: The size of the image to calculate a Merkle-tree for.
3683 block_size: The block size, e.g. 4096.
3684 digest_size: The size of each hash, e.g. 32 for SHA-256.
3685
3686 Returns:
3687 A tuple where the first argument is an array of offsets and the
3688 second is size of the tree, in bytes.
3689 """
3690 level_offsets = []
3691 level_sizes = []
3692 tree_size = 0
3693
3694 num_levels = 0
3695 size = image_size
3696 while size > block_size:
3697 num_blocks = (size + block_size - 1) / block_size
3698 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3699
3700 level_sizes.append(level_size)
3701 tree_size += level_size
3702 num_levels += 1
3703
3704 size = level_size
3705
3706 for n in range(0, num_levels):
3707 offset = 0
3708 for m in range(n + 1, num_levels):
3709 offset += level_sizes[m]
3710 level_offsets.append(offset)
3711
David Zeuthena4fee8b2016-08-22 15:20:43 -04003712 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003713
3714
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003715# See system/extras/libfec/include/fec/io.h for these definitions.
3716FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3717FEC_MAGIC = 0xfecfecfe
3718
3719
3720def calc_fec_data_size(image_size, num_roots):
3721 """Calculates how much space FEC data will take.
3722
3723 Args:
3724 image_size: The size of the image.
3725 num_roots: Number of roots.
3726
3727 Returns:
3728 The number of bytes needed for FEC for an image of the given size
3729 and with the requested number of FEC roots.
3730
3731 Raises:
3732 ValueError: If output from the 'fec' tool is invalid.
3733
3734 """
3735 p = subprocess.Popen(
3736 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3737 stdout=subprocess.PIPE,
3738 stderr=subprocess.PIPE)
3739 (pout, perr) = p.communicate()
3740 retcode = p.wait()
3741 if retcode != 0:
3742 raise ValueError('Error invoking fec: {}'.format(perr))
3743 return int(pout)
3744
3745
3746def generate_fec_data(image_filename, num_roots):
3747 """Generate FEC codes for an image.
3748
3749 Args:
3750 image_filename: The filename of the image.
3751 num_roots: Number of roots.
3752
3753 Returns:
3754 The FEC data blob.
3755
3756 Raises:
3757 ValueError: If output from the 'fec' tool is invalid.
3758 """
3759 fec_tmpfile = tempfile.NamedTemporaryFile()
3760 subprocess.check_call(
3761 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3762 fec_tmpfile.name],
3763 stderr=open(os.devnull))
3764 fec_data = fec_tmpfile.read()
3765 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3766 footer_data = fec_data[-footer_size:]
3767 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3768 footer_data)
3769 if magic != FEC_MAGIC:
3770 raise ValueError('Unexpected magic in FEC footer')
3771 return fec_data[0:fec_size]
3772
3773
David Zeuthen21e95262016-07-27 17:58:40 -04003774def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003775 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003776 """Generates a Merkle-tree for a file.
3777
3778 Args:
3779 image: The image, as a file.
3780 image_size: The size of the image.
3781 block_size: The block size, e.g. 4096.
3782 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3783 salt: The salt to use.
3784 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003785 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003786 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003787
3788 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003789 A tuple where the first element is the top-level hash and the
3790 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003791 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003792 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003793 hash_src_offset = 0
3794 hash_src_size = image_size
3795 level_num = 0
3796 while hash_src_size > block_size:
3797 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003798 remaining = hash_src_size
3799 while remaining > 0:
3800 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003801 # Only read from the file for the first level - for subsequent
3802 # levels, access the array we're building.
3803 if level_num == 0:
3804 image.seek(hash_src_offset + hash_src_size - remaining)
3805 data = image.read(min(remaining, block_size))
3806 else:
3807 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3808 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003809 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003810
3811 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003812 if len(data) < block_size:
3813 hasher.update('\0' * (block_size - len(data)))
3814 level_output += hasher.digest()
3815 if digest_padding > 0:
3816 level_output += '\0' * digest_padding
3817
3818 padding_needed = (round_to_multiple(
3819 len(level_output), block_size) - len(level_output))
3820 level_output += '\0' * padding_needed
3821
David Zeuthena4fee8b2016-08-22 15:20:43 -04003822 # Copy level-output into resulting tree.
3823 offset = hash_level_offsets[level_num]
3824 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003825
David Zeuthena4fee8b2016-08-22 15:20:43 -04003826 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003827 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003828 level_num += 1
3829
3830 hasher = hashlib.new(name=hash_alg_name, string=salt)
3831 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003832 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003833
3834
3835class AvbTool(object):
3836 """Object for avbtool command-line tool."""
3837
3838 def __init__(self):
3839 """Initializer method."""
3840 self.avb = Avb()
3841
3842 def _add_common_args(self, sub_parser):
3843 """Adds arguments used by several sub-commands.
3844
3845 Arguments:
3846 sub_parser: The parser to add arguments to.
3847 """
3848 sub_parser.add_argument('--algorithm',
3849 help='Algorithm to use (default: NONE)',
3850 metavar='ALGORITHM',
3851 default='NONE')
3852 sub_parser.add_argument('--key',
3853 help='Path to RSA private key file',
3854 metavar='KEY',
3855 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003856 sub_parser.add_argument('--signing_helper',
3857 help='Path to helper used for signing',
3858 metavar='APP',
3859 default=None,
3860 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003861 sub_parser.add_argument('--signing_helper_with_files',
3862 help='Path to helper used for signing using files',
3863 metavar='APP',
3864 default=None,
3865 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003866 sub_parser.add_argument('--public_key_metadata',
3867 help='Path to public key metadata file',
3868 metavar='KEY_METADATA',
3869 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003870 sub_parser.add_argument('--rollback_index',
3871 help='Rollback Index',
3872 type=parse_number,
3873 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003874 # This is used internally for unit tests. Do not include in --help output.
3875 sub_parser.add_argument('--internal_release_string',
3876 help=argparse.SUPPRESS)
3877 sub_parser.add_argument('--append_to_release_string',
3878 help='Text to append to release string',
3879 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003880 sub_parser.add_argument('--prop',
3881 help='Add property',
3882 metavar='KEY:VALUE',
3883 action='append')
3884 sub_parser.add_argument('--prop_from_file',
3885 help='Add property from file',
3886 metavar='KEY:PATH',
3887 action='append')
3888 sub_parser.add_argument('--kernel_cmdline',
3889 help='Add kernel cmdline',
3890 metavar='CMDLINE',
3891 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003892 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3893 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3894 # at some future point.
3895 sub_parser.add_argument('--setup_rootfs_from_kernel',
3896 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003897 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003898 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003899 type=argparse.FileType('rb'))
3900 sub_parser.add_argument('--include_descriptors_from_image',
3901 help='Include descriptors from image',
3902 metavar='IMAGE',
3903 action='append',
3904 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003905 sub_parser.add_argument('--print_required_libavb_version',
3906 help=('Don\'t store the footer - '
3907 'instead calculate the required libavb '
3908 'version for the given options.'),
3909 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003910 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3911 sub_parser.add_argument('--chain_partition',
3912 help='Allow signed integrity-data for partition',
3913 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3914 action='append')
3915 sub_parser.add_argument('--flags',
3916 help='VBMeta flags',
3917 type=parse_number,
3918 default=0)
3919 sub_parser.add_argument('--set_hashtree_disabled_flag',
3920 help='Set the HASHTREE_DISABLED flag',
3921 action='store_true')
3922
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003923 def _add_common_footer_args(self, sub_parser):
3924 """Adds arguments used by add_*_footer sub-commands.
3925
3926 Arguments:
3927 sub_parser: The parser to add arguments to.
3928 """
3929 sub_parser.add_argument('--use_persistent_digest',
3930 help='Use a persistent digest on device instead of '
3931 'storing the digest in the descriptor. This '
3932 'cannot be used with A/B so must be combined '
3933 'with --do_not_use_ab when an A/B suffix is '
3934 'expected at runtime.',
3935 action='store_true')
3936 sub_parser.add_argument('--do_not_use_ab',
3937 help='The partition does not use A/B even when an '
3938 'A/B suffix is present. This must not be used '
3939 'for vbmeta or chained partitions.',
3940 action='store_true')
3941
David Zeuthena5fd3a42017-02-27 16:38:54 -05003942 def _fixup_common_args(self, args):
3943 """Common fixups needed by subcommands.
3944
3945 Arguments:
3946 args: Arguments to modify.
3947
3948 Returns:
3949 The modified arguments.
3950 """
3951 if args.set_hashtree_disabled_flag:
3952 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3953 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003954
3955 def run(self, argv):
3956 """Command-line processor.
3957
3958 Arguments:
3959 argv: Pass sys.argv from main.
3960 """
3961 parser = argparse.ArgumentParser()
3962 subparsers = parser.add_subparsers(title='subcommands')
3963
3964 sub_parser = subparsers.add_parser('version',
3965 help='Prints version of avbtool.')
3966 sub_parser.set_defaults(func=self.version)
3967
3968 sub_parser = subparsers.add_parser('extract_public_key',
3969 help='Extract public key.')
3970 sub_parser.add_argument('--key',
3971 help='Path to RSA private key file',
3972 required=True)
3973 sub_parser.add_argument('--output',
3974 help='Output file name',
3975 type=argparse.FileType('wb'),
3976 required=True)
3977 sub_parser.set_defaults(func=self.extract_public_key)
3978
3979 sub_parser = subparsers.add_parser('make_vbmeta_image',
3980 help='Makes a vbmeta image.')
3981 sub_parser.add_argument('--output',
3982 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003983 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003984 sub_parser.add_argument('--padding_size',
3985 metavar='NUMBER',
3986 help='If non-zero, pads output with NUL bytes so '
3987 'its size is a multiple of NUMBER (default: 0)',
3988 type=parse_number,
3989 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003990 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003991 sub_parser.set_defaults(func=self.make_vbmeta_image)
3992
3993 sub_parser = subparsers.add_parser('add_hash_footer',
3994 help='Add hashes and footer to image.')
3995 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003996 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003997 type=argparse.FileType('rab+'))
3998 sub_parser.add_argument('--partition_size',
3999 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004000 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004001 sub_parser.add_argument('--partition_name',
4002 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004003 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004004 sub_parser.add_argument('--hash_algorithm',
4005 help='Hash algorithm to use (default: sha256)',
4006 default='sha256')
4007 sub_parser.add_argument('--salt',
4008 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004009 sub_parser.add_argument('--calc_max_image_size',
4010 help=('Don\'t store the footer - '
4011 'instead calculate the maximum image size '
4012 'leaving enough room for metadata with '
4013 'the given partition size.'),
4014 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004015 sub_parser.add_argument('--output_vbmeta_image',
4016 help='Also write vbmeta struct to file',
4017 type=argparse.FileType('wb'))
4018 sub_parser.add_argument('--do_not_append_vbmeta_image',
4019 help=('Do not append vbmeta struct or footer '
4020 'to the image'),
4021 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004022 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004023 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004024 sub_parser.set_defaults(func=self.add_hash_footer)
4025
David Zeuthenb1b994d2017-03-06 18:01:31 -05004026 sub_parser = subparsers.add_parser('append_vbmeta_image',
4027 help='Append vbmeta image to image.')
4028 sub_parser.add_argument('--image',
4029 help='Image to append vbmeta blob to',
4030 type=argparse.FileType('rab+'))
4031 sub_parser.add_argument('--partition_size',
4032 help='Partition size',
4033 type=parse_number,
4034 required=True)
4035 sub_parser.add_argument('--vbmeta_image',
4036 help='Image with vbmeta blob to append',
4037 type=argparse.FileType('rb'))
4038 sub_parser.set_defaults(func=self.append_vbmeta_image)
4039
David Zeuthen21e95262016-07-27 17:58:40 -04004040 sub_parser = subparsers.add_parser('add_hashtree_footer',
4041 help='Add hashtree and footer to image.')
4042 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004043 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004044 type=argparse.FileType('rab+'))
4045 sub_parser.add_argument('--partition_size',
4046 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004047 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004048 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004049 sub_parser.add_argument('--partition_name',
4050 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004051 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004052 sub_parser.add_argument('--hash_algorithm',
4053 help='Hash algorithm to use (default: sha1)',
4054 default='sha1')
4055 sub_parser.add_argument('--salt',
4056 help='Salt in hex (default: /dev/urandom)')
4057 sub_parser.add_argument('--block_size',
4058 help='Block size (default: 4096)',
4059 type=parse_number,
4060 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004061 # TODO(zeuthen): The --generate_fec option was removed when we
4062 # moved to generating FEC by default. To avoid breaking existing
4063 # users needing to transition we simply just print a warning below
4064 # in add_hashtree_footer(). Remove this option and the warning at
4065 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004066 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004067 help=argparse.SUPPRESS,
4068 action='store_true')
4069 sub_parser.add_argument('--do_not_generate_fec',
4070 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004071 action='store_true')
4072 sub_parser.add_argument('--fec_num_roots',
4073 help='Number of roots for FEC (default: 2)',
4074 type=parse_number,
4075 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004076 sub_parser.add_argument('--calc_max_image_size',
4077 help=('Don\'t store the hashtree or footer - '
4078 'instead calculate the maximum image size '
4079 'leaving enough room for hashtree '
4080 'and metadata with the given partition '
4081 'size.'),
4082 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004083 sub_parser.add_argument('--output_vbmeta_image',
4084 help='Also write vbmeta struct to file',
4085 type=argparse.FileType('wb'))
4086 sub_parser.add_argument('--do_not_append_vbmeta_image',
4087 help=('Do not append vbmeta struct or footer '
4088 'to the image'),
4089 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004090 # This is different from --setup_rootfs_from_kernel insofar that
4091 # it doesn't take an IMAGE, the generated cmdline will be for the
4092 # hashtree we're adding.
4093 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4094 action='store_true',
4095 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004096 sub_parser.add_argument('--no_hashtree',
4097 action='store_true',
4098 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004099 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004100 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004101 sub_parser.set_defaults(func=self.add_hashtree_footer)
4102
4103 sub_parser = subparsers.add_parser('erase_footer',
4104 help='Erase footer from an image.')
4105 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004106 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004107 type=argparse.FileType('rwb+'),
4108 required=True)
4109 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004110 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004111 action='store_true')
4112 sub_parser.set_defaults(func=self.erase_footer)
4113
David Zeuthen1394f762019-04-30 10:20:11 -04004114 sub_parser = subparsers.add_parser('zero_hashtree',
4115 help='Zero out hashtree and FEC data.')
4116 sub_parser.add_argument('--image',
4117 help='Image with a footer',
4118 type=argparse.FileType('rwb+'),
4119 required=True)
4120 sub_parser.set_defaults(func=self.zero_hashtree)
4121
David Zeuthen49936b42018-08-07 17:38:58 -04004122 sub_parser = subparsers.add_parser('extract_vbmeta_image',
4123 help='Extracts vbmeta from an image with a footer.')
4124 sub_parser.add_argument('--image',
4125 help='Image with footer',
4126 type=argparse.FileType('rb'),
4127 required=True)
4128 sub_parser.add_argument('--output',
4129 help='Output file name',
4130 type=argparse.FileType('wb'))
4131 sub_parser.add_argument('--padding_size',
4132 metavar='NUMBER',
4133 help='If non-zero, pads output with NUL bytes so '
4134 'its size is a multiple of NUMBER (default: 0)',
4135 type=parse_number,
4136 default=0)
4137 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4138
David Zeuthen2bc232b2017-04-19 14:25:19 -04004139 sub_parser = subparsers.add_parser('resize_image',
4140 help='Resize image with a footer.')
4141 sub_parser.add_argument('--image',
4142 help='Image with a footer',
4143 type=argparse.FileType('rwb+'),
4144 required=True)
4145 sub_parser.add_argument('--partition_size',
4146 help='New partition size',
4147 type=parse_number)
4148 sub_parser.set_defaults(func=self.resize_image)
4149
David Zeuthen21e95262016-07-27 17:58:40 -04004150 sub_parser = subparsers.add_parser(
4151 'info_image',
4152 help='Show information about vbmeta or footer.')
4153 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004154 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004155 type=argparse.FileType('rb'),
4156 required=True)
4157 sub_parser.add_argument('--output',
4158 help='Write info to file',
4159 type=argparse.FileType('wt'),
4160 default=sys.stdout)
4161 sub_parser.set_defaults(func=self.info_image)
4162
David Zeuthenb623d8b2017-04-04 16:05:53 -04004163 sub_parser = subparsers.add_parser(
4164 'verify_image',
4165 help='Verify an image.')
4166 sub_parser.add_argument('--image',
4167 help='Image to verify',
4168 type=argparse.FileType('rb'),
4169 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004170 sub_parser.add_argument('--key',
4171 help='Check embedded public key matches KEY',
4172 metavar='KEY',
4173 required=False)
4174 sub_parser.add_argument('--expected_chain_partition',
4175 help='Expected chain partition',
4176 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4177 action='append')
David Zeuthene947cb62019-01-25 15:27:08 -05004178 sub_parser.add_argument('--follow_chain_partitions',
4179 help=('Follows chain partitions even when not '
4180 'specified with the --expected_chain_partition option'),
4181 action='store_true')
David Zeuthen1394f762019-04-30 10:20:11 -04004182 sub_parser.add_argument('--accept_zeroed_hashtree',
4183 help=('Accept images where the hashtree or FEC data is zeroed out'),
4184 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004185 sub_parser.set_defaults(func=self.verify_image)
4186
David Zeuthenb8643c02018-05-17 17:21:18 -04004187 sub_parser = subparsers.add_parser(
4188 'calculate_vbmeta_digest',
4189 help='Calculate vbmeta digest.')
4190 sub_parser.add_argument('--image',
4191 help='Image to calculate digest for',
4192 type=argparse.FileType('rb'),
4193 required=True)
4194 sub_parser.add_argument('--hash_algorithm',
4195 help='Hash algorithm to use (default: sha256)',
4196 default='sha256')
4197 sub_parser.add_argument('--output',
4198 help='Write hex digest to file (default: stdout)',
4199 type=argparse.FileType('wt'),
4200 default=sys.stdout)
4201 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4202
David Zeuthenf7d2e752018-09-20 13:30:41 -04004203 sub_parser = subparsers.add_parser(
4204 'calculate_kernel_cmdline',
4205 help='Calculate kernel cmdline.')
4206 sub_parser.add_argument('--image',
4207 help='Image to calculate kernel cmdline for',
4208 type=argparse.FileType('rb'),
4209 required=True)
4210 sub_parser.add_argument('--hashtree_disabled',
4211 help='Return the cmdline for hashtree disabled',
4212 action='store_true')
4213 sub_parser.add_argument('--output',
4214 help='Write cmdline to file (default: stdout)',
4215 type=argparse.FileType('wt'),
4216 default=sys.stdout)
4217 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4218
David Zeuthen8b6973b2016-09-20 12:39:49 -04004219 sub_parser = subparsers.add_parser('set_ab_metadata',
4220 help='Set A/B metadata.')
4221 sub_parser.add_argument('--misc_image',
4222 help=('The misc image to modify. If the image does '
4223 'not exist, it will be created.'),
4224 type=argparse.FileType('r+b'),
4225 required=True)
4226 sub_parser.add_argument('--slot_data',
4227 help=('Slot data of the form "priority", '
4228 '"tries_remaining", "sucessful_boot" for '
4229 'slot A followed by the same for slot B, '
4230 'separated by colons. The default value '
4231 'is 15:7:0:14:7:0.'),
4232 default='15:7:0:14:7:0')
4233 sub_parser.set_defaults(func=self.set_ab_metadata)
4234
Darren Krahn147b08d2016-12-20 16:38:29 -08004235 sub_parser = subparsers.add_parser(
4236 'make_atx_certificate',
4237 help='Create an Android Things eXtension (ATX) certificate.')
4238 sub_parser.add_argument('--output',
4239 help='Write certificate to file',
4240 type=argparse.FileType('wb'),
4241 default=sys.stdout)
4242 sub_parser.add_argument('--subject',
4243 help=('Path to subject file'),
4244 type=argparse.FileType('rb'),
4245 required=True)
4246 sub_parser.add_argument('--subject_key',
4247 help=('Path to subject RSA public key file'),
4248 type=argparse.FileType('rb'),
4249 required=True)
4250 sub_parser.add_argument('--subject_key_version',
4251 help=('Version of the subject key'),
4252 type=parse_number,
4253 required=False)
4254 sub_parser.add_argument('--subject_is_intermediate_authority',
4255 help=('Generate an intermediate authority '
4256 'certificate'),
4257 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004258 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004259 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004260 'string'),
4261 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004262 sub_parser.add_argument('--authority_key',
4263 help='Path to authority RSA private key file',
4264 required=False)
4265 sub_parser.add_argument('--signing_helper',
4266 help='Path to helper used for signing',
4267 metavar='APP',
4268 default=None,
4269 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004270 sub_parser.add_argument('--signing_helper_with_files',
4271 help='Path to helper used for signing using files',
4272 metavar='APP',
4273 default=None,
4274 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004275 sub_parser.set_defaults(func=self.make_atx_certificate)
4276
4277 sub_parser = subparsers.add_parser(
4278 'make_atx_permanent_attributes',
4279 help='Create Android Things eXtension (ATX) permanent attributes.')
4280 sub_parser.add_argument('--output',
4281 help='Write attributes to file',
4282 type=argparse.FileType('wb'),
4283 default=sys.stdout)
4284 sub_parser.add_argument('--root_authority_key',
4285 help='Path to authority RSA public key file',
4286 type=argparse.FileType('rb'),
4287 required=True)
4288 sub_parser.add_argument('--product_id',
4289 help=('Path to Product ID file'),
4290 type=argparse.FileType('rb'),
4291 required=True)
4292 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4293
4294 sub_parser = subparsers.add_parser(
4295 'make_atx_metadata',
4296 help='Create Android Things eXtension (ATX) metadata.')
4297 sub_parser.add_argument('--output',
4298 help='Write metadata to file',
4299 type=argparse.FileType('wb'),
4300 default=sys.stdout)
4301 sub_parser.add_argument('--intermediate_key_certificate',
4302 help='Path to intermediate key certificate file',
4303 type=argparse.FileType('rb'),
4304 required=True)
4305 sub_parser.add_argument('--product_key_certificate',
4306 help='Path to product key certificate file',
4307 type=argparse.FileType('rb'),
4308 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004309 sub_parser.set_defaults(func=self.make_atx_metadata)
4310
Darren Krahnfccd64e2018-01-16 17:39:35 -08004311 sub_parser = subparsers.add_parser(
4312 'make_atx_unlock_credential',
4313 help='Create an Android Things eXtension (ATX) unlock credential.')
4314 sub_parser.add_argument('--output',
4315 help='Write credential to file',
4316 type=argparse.FileType('wb'),
4317 default=sys.stdout)
4318 sub_parser.add_argument('--intermediate_key_certificate',
4319 help='Path to intermediate key certificate file',
4320 type=argparse.FileType('rb'),
4321 required=True)
4322 sub_parser.add_argument('--unlock_key_certificate',
4323 help='Path to unlock key certificate file',
4324 type=argparse.FileType('rb'),
4325 required=True)
4326 sub_parser.add_argument('--challenge',
4327 help='Path to the challenge to sign (optional). If '
4328 'this is not provided the challenge signature '
4329 'field is omitted and can be concatenated '
4330 'later.',
4331 required=False)
4332 sub_parser.add_argument('--unlock_key',
4333 help='Path to unlock key (optional). Must be '
4334 'provided if using --challenge.',
4335 required=False)
4336 sub_parser.add_argument('--signing_helper',
4337 help='Path to helper used for signing',
4338 metavar='APP',
4339 default=None,
4340 required=False)
4341 sub_parser.add_argument('--signing_helper_with_files',
4342 help='Path to helper used for signing using files',
4343 metavar='APP',
4344 default=None,
4345 required=False)
4346 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4347
David Zeuthen21e95262016-07-27 17:58:40 -04004348 args = parser.parse_args(argv[1:])
4349 try:
4350 args.func(args)
4351 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004352 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004353 sys.exit(1)
4354
4355 def version(self, _):
4356 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004357 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004358
4359 def extract_public_key(self, args):
4360 """Implements the 'extract_public_key' sub-command."""
4361 self.avb.extract_public_key(args.key, args.output)
4362
4363 def make_vbmeta_image(self, args):
4364 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004365 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004366 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004367 args.algorithm, args.key,
4368 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004369 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004370 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004371 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004372 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004373 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004374 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004375 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004376 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004377 args.print_required_libavb_version,
4378 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004379
David Zeuthenb1b994d2017-03-06 18:01:31 -05004380 def append_vbmeta_image(self, args):
4381 """Implements the 'append_vbmeta_image' sub-command."""
4382 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4383 args.partition_size)
4384
David Zeuthen21e95262016-07-27 17:58:40 -04004385 def add_hash_footer(self, args):
4386 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004387 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004388 self.avb.add_hash_footer(args.image.name if args.image else None,
4389 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004390 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004391 args.salt, args.chain_partition, args.algorithm,
4392 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004393 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004394 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004395 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004396 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004397 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004398 args.calc_max_image_size,
4399 args.signing_helper,
4400 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004401 args.internal_release_string,
4402 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004403 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004404 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004405 args.print_required_libavb_version,
4406 args.use_persistent_digest,
4407 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004408
4409 def add_hashtree_footer(self, args):
4410 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004411 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004412 # TODO(zeuthen): Remove when removing support for the
4413 # '--generate_fec' option above.
4414 if args.generate_fec:
4415 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4416 'is now generated by default. Use the option '
4417 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004418 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4419 args.partition_size,
4420 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004421 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004422 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004423 args.salt, args.chain_partition, args.algorithm,
4424 args.key, args.public_key_metadata,
4425 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004426 args.prop_from_file,
4427 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004428 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004429 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004430 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004431 args.calc_max_image_size,
4432 args.signing_helper,
4433 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004434 args.internal_release_string,
4435 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004436 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004437 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004438 args.print_required_libavb_version,
4439 args.use_persistent_digest,
Jooyung Hand7221942019-06-17 13:19:57 +09004440 args.do_not_use_ab,
4441 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004442
David Zeuthen21e95262016-07-27 17:58:40 -04004443 def erase_footer(self, args):
4444 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004445 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004446
David Zeuthen1394f762019-04-30 10:20:11 -04004447 def zero_hashtree(self, args):
4448 """Implements the 'zero_hashtree' sub-command."""
4449 self.avb.zero_hashtree(args.image.name)
4450
David Zeuthen49936b42018-08-07 17:38:58 -04004451 def extract_vbmeta_image(self, args):
4452 """Implements the 'extract_vbmeta_image' sub-command."""
4453 self.avb.extract_vbmeta_image(args.output, args.image.name,
4454 args.padding_size)
4455
David Zeuthen2bc232b2017-04-19 14:25:19 -04004456 def resize_image(self, args):
4457 """Implements the 'resize_image' sub-command."""
4458 self.avb.resize_image(args.image.name, args.partition_size)
4459
David Zeuthen8b6973b2016-09-20 12:39:49 -04004460 def set_ab_metadata(self, args):
4461 """Implements the 'set_ab_metadata' sub-command."""
4462 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4463
David Zeuthen21e95262016-07-27 17:58:40 -04004464 def info_image(self, args):
4465 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004466 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004467
David Zeuthenb623d8b2017-04-04 16:05:53 -04004468 def verify_image(self, args):
4469 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004470 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004471 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004472 args.follow_chain_partitions,
4473 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004474
David Zeuthenb8643c02018-05-17 17:21:18 -04004475 def calculate_vbmeta_digest(self, args):
4476 """Implements the 'calculate_vbmeta_digest' sub-command."""
4477 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4478 args.output)
4479
David Zeuthenf7d2e752018-09-20 13:30:41 -04004480 def calculate_kernel_cmdline(self, args):
4481 """Implements the 'calculate_kernel_cmdline' sub-command."""
4482 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, args.output)
4483
Darren Krahn147b08d2016-12-20 16:38:29 -08004484 def make_atx_certificate(self, args):
4485 """Implements the 'make_atx_certificate' sub-command."""
4486 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004487 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004488 args.subject_key_version,
4489 args.subject.read(),
4490 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004491 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004492 args.signing_helper,
4493 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004494
4495 def make_atx_permanent_attributes(self, args):
4496 """Implements the 'make_atx_permanent_attributes' sub-command."""
4497 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004498 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004499 args.product_id.read())
4500
4501 def make_atx_metadata(self, args):
4502 """Implements the 'make_atx_metadata' sub-command."""
4503 self.avb.make_atx_metadata(args.output,
4504 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004505 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004506
Darren Krahnfccd64e2018-01-16 17:39:35 -08004507 def make_atx_unlock_credential(self, args):
4508 """Implements the 'make_atx_unlock_credential' sub-command."""
4509 self.avb.make_atx_unlock_credential(
4510 args.output,
4511 args.intermediate_key_certificate.read(),
4512 args.unlock_key_certificate.read(),
4513 args.challenge,
4514 args.unlock_key,
4515 args.signing_helper,
4516 args.signing_helper_with_files)
4517
David Zeuthen21e95262016-07-27 17:58:40 -04004518
4519if __name__ == '__main__':
4520 tool = AvbTool()
4521 tool.run(sys.argv)