blob: 610cf19ef234c9458e9962616bd28291d445f518 [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,
1124 image_containing_descriptor):
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 Zeuthen5dfb4e92017-05-24 14:49:32 -04001133
1134 Returns:
1135 True if the descriptor verifies, False otherwise.
1136 """
1137 # Nothing to do.
1138 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001139
1140class AvbPropertyDescriptor(AvbDescriptor):
1141 """A class for property descriptors.
1142
1143 See the |AvbPropertyDescriptor| C struct for more information.
1144
1145 Attributes:
1146 key: The key.
1147 value: The key.
1148 """
1149
1150 TAG = 0
1151 SIZE = 32
1152 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1153 'Q' # key size (bytes)
1154 'Q') # value size (bytes)
1155
1156 def __init__(self, data=None):
1157 """Initializes a new property descriptor.
1158
1159 Arguments:
1160 data: If not None, must be a bytearray of size |SIZE|.
1161
1162 Raises:
1163 LookupError: If the given descriptor is malformed.
1164 """
1165 AvbDescriptor.__init__(self, None)
1166 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1167
1168 if data:
1169 (tag, num_bytes_following, key_size,
1170 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1171 expected_size = round_to_multiple(
1172 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1173 if tag != self.TAG or num_bytes_following != expected_size:
1174 raise LookupError('Given data does not look like a property '
1175 'descriptor.')
1176 self.key = data[self.SIZE:(self.SIZE + key_size)]
1177 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1178 value_size)]
1179 else:
1180 self.key = ''
1181 self.value = ''
1182
1183 def print_desc(self, o):
1184 """Print the descriptor.
1185
1186 Arguments:
1187 o: The object to write the output to.
1188 """
1189 if len(self.value) < 256:
1190 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1191 else:
1192 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1193
1194 def encode(self):
1195 """Serializes the descriptor.
1196
1197 Returns:
1198 A bytearray() with the descriptor data.
1199 """
1200 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1201 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1202 padding_size = nbf_with_padding - num_bytes_following
1203 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1204 len(self.key), len(self.value))
1205 padding = struct.pack(str(padding_size) + 'x')
1206 ret = desc + self.key + '\0' + self.value + '\0' + padding
1207 return bytearray(ret)
1208
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001209 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1210 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001211 """Verifies contents of the descriptor - used in verify_image sub-command.
1212
1213 Arguments:
1214 image_dir: The directory of the file being verified.
1215 image_ext: The extension of the file being verified (e.g. '.img').
1216 expected_chain_partitions_map: A map from partition name to the
1217 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001218 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001219
1220 Returns:
1221 True if the descriptor verifies, False otherwise.
1222 """
1223 # Nothing to do.
1224 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001225
1226class AvbHashtreeDescriptor(AvbDescriptor):
1227 """A class for hashtree descriptors.
1228
1229 See the |AvbHashtreeDescriptor| C struct for more information.
1230
1231 Attributes:
1232 dm_verity_version: dm-verity version used.
1233 image_size: Size of the image, after rounding up to |block_size|.
1234 tree_offset: Offset of the hash tree in the file.
1235 tree_size: Size of the tree.
1236 data_block_size: Data block size
1237 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001238 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1239 fec_offset: Offset of FEC data (0 if FEC is not used).
1240 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001241 hash_algorithm: Hash algorithm used.
1242 partition_name: Partition name.
1243 salt: Salt used.
1244 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001245 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001246 """
1247
1248 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001249 RESERVED = 60
1250 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001251 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1252 'L' # dm-verity version used
1253 'Q' # image size (bytes)
1254 'Q' # tree offset (bytes)
1255 'Q' # tree size (bytes)
1256 'L' # data block size (bytes)
1257 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001258 'L' # FEC number of roots
1259 'Q' # FEC offset (bytes)
1260 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001261 '32s' # hash algorithm used
1262 'L' # partition name (bytes)
1263 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001264 'L' # root digest length (bytes)
1265 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001266 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001267
1268 def __init__(self, data=None):
1269 """Initializes a new hashtree descriptor.
1270
1271 Arguments:
1272 data: If not None, must be a bytearray of size |SIZE|.
1273
1274 Raises:
1275 LookupError: If the given descriptor is malformed.
1276 """
1277 AvbDescriptor.__init__(self, None)
1278 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1279
1280 if data:
1281 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1282 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001283 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1284 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001285 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1286 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001287 expected_size = round_to_multiple(
1288 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1289 if tag != self.TAG or num_bytes_following != expected_size:
1290 raise LookupError('Given data does not look like a hashtree '
1291 'descriptor.')
1292 # Nuke NUL-bytes at the end.
1293 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1294 o = 0
1295 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1296 partition_name_len)])
1297 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1298 self.partition_name.decode('utf-8')
1299 o += partition_name_len
1300 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1301 o += salt_len
1302 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1303 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001304 if root_digest_len != 0:
1305 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001306
1307 else:
1308 self.dm_verity_version = 0
1309 self.image_size = 0
1310 self.tree_offset = 0
1311 self.tree_size = 0
1312 self.data_block_size = 0
1313 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001314 self.fec_num_roots = 0
1315 self.fec_offset = 0
1316 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001317 self.hash_algorithm = ''
1318 self.partition_name = ''
1319 self.salt = bytearray()
1320 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001321 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001322
1323 def print_desc(self, o):
1324 """Print the descriptor.
1325
1326 Arguments:
1327 o: The object to write the output to.
1328 """
1329 o.write(' Hashtree descriptor:\n')
1330 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1331 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1332 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1333 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1334 o.write(' Data Block Size: {} bytes\n'.format(
1335 self.data_block_size))
1336 o.write(' Hash Block Size: {} bytes\n'.format(
1337 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001338 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1339 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1340 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001341 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1342 o.write(' Partition Name: {}\n'.format(self.partition_name))
1343 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1344 'hex')))
1345 o.write(' Root Digest: {}\n'.format(str(
1346 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001347 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001348
1349 def encode(self):
1350 """Serializes the descriptor.
1351
1352 Returns:
1353 A bytearray() with the descriptor data.
1354 """
1355 encoded_name = self.partition_name.encode('utf-8')
1356 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1357 len(self.root_digest) - 16)
1358 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1359 padding_size = nbf_with_padding - num_bytes_following
1360 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1361 self.dm_verity_version, self.image_size,
1362 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001363 self.hash_block_size, self.fec_num_roots,
1364 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001365 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001366 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001367 padding = struct.pack(str(padding_size) + 'x')
1368 ret = desc + encoded_name + self.salt + self.root_digest + padding
1369 return bytearray(ret)
1370
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001371 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1372 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001373 """Verifies contents of the descriptor - used in verify_image sub-command.
1374
1375 Arguments:
1376 image_dir: The directory of the file being verified.
1377 image_ext: The extension of the file being verified (e.g. '.img').
1378 expected_chain_partitions_map: A map from partition name to the
1379 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001380 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001381
1382 Returns:
1383 True if the descriptor verifies, False otherwise.
1384 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001385 if self.partition_name == '':
1386 image = image_containing_descriptor
1387 else:
1388 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1389 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001390 # Generate the hashtree and checks that it matches what's in the file.
1391 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1392 digest_padding = round_to_pow2(digest_size) - digest_size
1393 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1394 self.image_size, self.data_block_size, digest_size + digest_padding)
1395 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1396 self.data_block_size,
1397 self.hash_algorithm, self.salt,
1398 digest_padding,
1399 hash_level_offsets,
1400 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001401 # The root digest must match unless it is not embedded in the descriptor.
1402 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001403 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1404 format(image_filename))
1405 return False
1406 # ... also check that the on-disk hashtree matches
1407 image.seek(self.tree_offset)
1408 hash_tree_ondisk = image.read(self.tree_size)
1409 if hash_tree != hash_tree_ondisk:
1410 sys.stderr.write('hashtree of {} contains invalid data\n'.
1411 format(image_filename))
1412 return False
1413 # TODO: we could also verify that the FEC stored in the image is
1414 # correct but this a) currently requires the 'fec' binary; and b)
1415 # takes a long time; and c) is not strictly needed for
1416 # verification purposes as we've already verified the root hash.
1417 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001418 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001419 self.image_size))
1420 return True
1421
David Zeuthen21e95262016-07-27 17:58:40 -04001422
1423class AvbHashDescriptor(AvbDescriptor):
1424 """A class for hash descriptors.
1425
1426 See the |AvbHashDescriptor| C struct for more information.
1427
1428 Attributes:
1429 image_size: Image size, in bytes.
1430 hash_algorithm: Hash algorithm used.
1431 partition_name: Partition name.
1432 salt: Salt used.
1433 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001434 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001435 """
1436
1437 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001438 RESERVED = 60
1439 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001440 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1441 'Q' # image size (bytes)
1442 '32s' # hash algorithm used
1443 'L' # partition name (bytes)
1444 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001445 'L' # digest length (bytes)
1446 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001447 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001448
1449 def __init__(self, data=None):
1450 """Initializes a new hash descriptor.
1451
1452 Arguments:
1453 data: If not None, must be a bytearray of size |SIZE|.
1454
1455 Raises:
1456 LookupError: If the given descriptor is malformed.
1457 """
1458 AvbDescriptor.__init__(self, None)
1459 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1460
1461 if data:
1462 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1463 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001464 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1465 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001466 expected_size = round_to_multiple(
1467 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1468 if tag != self.TAG or num_bytes_following != expected_size:
1469 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1470 # Nuke NUL-bytes at the end.
1471 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1472 o = 0
1473 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1474 partition_name_len)])
1475 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1476 self.partition_name.decode('utf-8')
1477 o += partition_name_len
1478 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1479 o += salt_len
1480 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1481 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001482 if digest_len != 0:
1483 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001484
1485 else:
1486 self.image_size = 0
1487 self.hash_algorithm = ''
1488 self.partition_name = ''
1489 self.salt = bytearray()
1490 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001491 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001492
1493 def print_desc(self, o):
1494 """Print the descriptor.
1495
1496 Arguments:
1497 o: The object to write the output to.
1498 """
1499 o.write(' Hash descriptor:\n')
1500 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1501 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1502 o.write(' Partition Name: {}\n'.format(self.partition_name))
1503 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1504 'hex')))
1505 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1506 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001507 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001508
1509 def encode(self):
1510 """Serializes the descriptor.
1511
1512 Returns:
1513 A bytearray() with the descriptor data.
1514 """
1515 encoded_name = self.partition_name.encode('utf-8')
1516 num_bytes_following = (
1517 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1518 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1519 padding_size = nbf_with_padding - num_bytes_following
1520 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1521 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001522 len(self.salt), len(self.digest), self.flags,
1523 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001524 padding = struct.pack(str(padding_size) + 'x')
1525 ret = desc + encoded_name + self.salt + self.digest + padding
1526 return bytearray(ret)
1527
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001528 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1529 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001530 """Verifies contents of the descriptor - used in verify_image sub-command.
1531
1532 Arguments:
1533 image_dir: The directory of the file being verified.
1534 image_ext: The extension of the file being verified (e.g. '.img').
1535 expected_chain_partitions_map: A map from partition name to the
1536 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001537 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001538
1539 Returns:
1540 True if the descriptor verifies, False otherwise.
1541 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001542 if self.partition_name == '':
1543 image = image_containing_descriptor
1544 else:
1545 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1546 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001547 data = image.read(self.image_size)
1548 ha = hashlib.new(self.hash_algorithm)
1549 ha.update(self.salt)
1550 ha.update(data)
1551 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001552 # The digest must match unless there is no digest in the descriptor.
1553 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001554 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1555 format(self.hash_algorithm, image_filename))
1556 return False
1557 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001558 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001559 self.image_size))
1560 return True
1561
David Zeuthen21e95262016-07-27 17:58:40 -04001562
1563class AvbKernelCmdlineDescriptor(AvbDescriptor):
1564 """A class for kernel command-line descriptors.
1565
1566 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1567
1568 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001569 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001570 kernel_cmdline: The kernel command-line.
1571 """
1572
1573 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001574 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001575 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001576 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001577 'L') # cmdline length (bytes)
1578
David Zeuthenfd41eb92016-11-17 12:24:47 -05001579 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1580 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1581
David Zeuthen21e95262016-07-27 17:58:40 -04001582 def __init__(self, data=None):
1583 """Initializes a new kernel cmdline descriptor.
1584
1585 Arguments:
1586 data: If not None, must be a bytearray of size |SIZE|.
1587
1588 Raises:
1589 LookupError: If the given descriptor is malformed.
1590 """
1591 AvbDescriptor.__init__(self, None)
1592 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1593
1594 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001595 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001596 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1597 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1598 8)
1599 if tag != self.TAG or num_bytes_following != expected_size:
1600 raise LookupError('Given data does not look like a kernel cmdline '
1601 'descriptor.')
1602 # Nuke NUL-bytes at the end.
1603 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1604 kernel_cmdline_length)])
1605 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1606 self.kernel_cmdline.decode('utf-8')
1607 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001608 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001609 self.kernel_cmdline = ''
1610
1611 def print_desc(self, o):
1612 """Print the descriptor.
1613
1614 Arguments:
1615 o: The object to write the output to.
1616 """
1617 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001618 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001619 o.write(' Kernel Cmdline: {}\n'.format(repr(
1620 self.kernel_cmdline)))
1621
1622 def encode(self):
1623 """Serializes the descriptor.
1624
1625 Returns:
1626 A bytearray() with the descriptor data.
1627 """
1628 encoded_str = self.kernel_cmdline.encode('utf-8')
1629 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1630 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1631 padding_size = nbf_with_padding - num_bytes_following
1632 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001633 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001634 padding = struct.pack(str(padding_size) + 'x')
1635 ret = desc + encoded_str + padding
1636 return bytearray(ret)
1637
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001638 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1639 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001640 """Verifies contents of the descriptor - used in verify_image sub-command.
1641
1642 Arguments:
1643 image_dir: The directory of the file being verified.
1644 image_ext: The extension of the file being verified (e.g. '.img').
1645 expected_chain_partitions_map: A map from partition name to the
1646 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001647 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001648
1649 Returns:
1650 True if the descriptor verifies, False otherwise.
1651 """
1652 # Nothing to verify.
1653 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001654
1655class AvbChainPartitionDescriptor(AvbDescriptor):
1656 """A class for chained partition descriptors.
1657
1658 See the |AvbChainPartitionDescriptor| C struct for more information.
1659
1660 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001661 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001662 partition_name: Partition name.
1663 public_key: Bytes for the public key.
1664 """
1665
1666 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001667 RESERVED = 64
1668 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001669 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001670 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001671 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001672 'L' + # public_key_size (bytes)
1673 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001674
1675 def __init__(self, data=None):
1676 """Initializes a new chain partition descriptor.
1677
1678 Arguments:
1679 data: If not None, must be a bytearray of size |SIZE|.
1680
1681 Raises:
1682 LookupError: If the given descriptor is malformed.
1683 """
1684 AvbDescriptor.__init__(self, None)
1685 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1686
1687 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001688 (tag, num_bytes_following, self.rollback_index_location,
1689 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001690 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001691 expected_size = round_to_multiple(
1692 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1693 if tag != self.TAG or num_bytes_following != expected_size:
1694 raise LookupError('Given data does not look like a chain partition '
1695 'descriptor.')
1696 o = 0
1697 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1698 partition_name_len)])
1699 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1700 self.partition_name.decode('utf-8')
1701 o += partition_name_len
1702 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1703
1704 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001705 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001706 self.partition_name = ''
1707 self.public_key = bytearray()
1708
1709 def print_desc(self, o):
1710 """Print the descriptor.
1711
1712 Arguments:
1713 o: The object to write the output to.
1714 """
1715 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001716 o.write(' Partition Name: {}\n'.format(self.partition_name))
1717 o.write(' Rollback Index Location: {}\n'.format(
1718 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001719 # Just show the SHA1 of the key, for size reasons.
1720 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001721 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001722
1723 def encode(self):
1724 """Serializes the descriptor.
1725
1726 Returns:
1727 A bytearray() with the descriptor data.
1728 """
1729 encoded_name = self.partition_name.encode('utf-8')
1730 num_bytes_following = (
1731 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1732 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1733 padding_size = nbf_with_padding - num_bytes_following
1734 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001735 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001736 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001737 padding = struct.pack(str(padding_size) + 'x')
1738 ret = desc + encoded_name + self.public_key + padding
1739 return bytearray(ret)
1740
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001741 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1742 image_containing_descriptor):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001743 """Verifies contents of the descriptor - used in verify_image sub-command.
1744
1745 Arguments:
1746 image_dir: The directory of the file being verified.
1747 image_ext: The extension of the file being verified (e.g. '.img').
1748 expected_chain_partitions_map: A map from partition name to the
1749 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001750 image_containing_descriptor: The image the descriptor is in.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001751
1752 Returns:
1753 True if the descriptor verifies, False otherwise.
1754 """
1755 value = expected_chain_partitions_map.get(self.partition_name)
1756 if not value:
1757 sys.stderr.write('No expected chain partition for partition {}. Use '
1758 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001759 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001760 format(self.partition_name))
1761 return False
1762 rollback_index_location, pk_blob = value
1763
1764 if self.rollback_index_location != rollback_index_location:
1765 sys.stderr.write('Expected rollback_index_location {} does not '
1766 'match {} in descriptor for partition {}\n'.
1767 format(rollback_index_location,
1768 self.rollback_index_location,
1769 self.partition_name))
1770 return False
1771
1772 if self.public_key != pk_blob:
1773 sys.stderr.write('Expected public key blob does not match public '
1774 'key blob in descriptor for partition {}\n'.
1775 format(self.partition_name))
1776 return False
1777
1778 print ('{}: Successfully verified chain partition descriptor matches '
1779 'expected data'.format(self.partition_name))
1780
1781 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001782
1783DESCRIPTOR_CLASSES = [
1784 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1785 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1786]
1787
1788
1789def parse_descriptors(data):
1790 """Parses a blob of data into descriptors.
1791
1792 Arguments:
1793 data: A bytearray() with encoded descriptors.
1794
1795 Returns:
1796 A list of instances of objects derived from AvbDescriptor. For
1797 unknown descriptors, the class AvbDescriptor is used.
1798 """
1799 o = 0
1800 ret = []
1801 while o < len(data):
1802 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1803 if tag < len(DESCRIPTOR_CLASSES):
1804 c = DESCRIPTOR_CLASSES[tag]
1805 else:
1806 c = AvbDescriptor
1807 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1808 o += 16 + nb_following
1809 return ret
1810
1811
1812class AvbFooter(object):
1813 """A class for parsing and writing footers.
1814
1815 Footers are stored at the end of partitions and point to where the
1816 AvbVBMeta blob is located. They also contain the original size of
1817 the image before AVB information was added.
1818
1819 Attributes:
1820 magic: Magic for identifying the footer, see |MAGIC|.
1821 version_major: The major version of avbtool that wrote the footer.
1822 version_minor: The minor version of avbtool that wrote the footer.
1823 original_image_size: Original image size.
1824 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1825 vbmeta_size: Size of the AvbVBMeta blob.
1826 """
1827
1828 MAGIC = 'AVBf'
1829 SIZE = 64
1830 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001831 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1832 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001833 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1834 'Q' # Original image size.
1835 'Q' # Offset of VBMeta blob.
1836 'Q' + # Size of VBMeta blob.
1837 str(RESERVED) + 'x') # padding for reserved bytes
1838
1839 def __init__(self, data=None):
1840 """Initializes a new footer object.
1841
1842 Arguments:
1843 data: If not None, must be a bytearray of size 4096.
1844
1845 Raises:
1846 LookupError: If the given footer is malformed.
1847 struct.error: If the given data has no footer.
1848 """
1849 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1850
1851 if data:
1852 (self.magic, self.version_major, self.version_minor,
1853 self.original_image_size, self.vbmeta_offset,
1854 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1855 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001856 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001857 else:
1858 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001859 self.version_major = self.FOOTER_VERSION_MAJOR
1860 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001861 self.original_image_size = 0
1862 self.vbmeta_offset = 0
1863 self.vbmeta_size = 0
1864
David Zeuthena4fee8b2016-08-22 15:20:43 -04001865 def encode(self):
1866 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001867
David Zeuthena4fee8b2016-08-22 15:20:43 -04001868 Returns:
1869 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001870 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001871 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1872 self.version_minor, self.original_image_size,
1873 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001874
1875
1876class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001877 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001878
1879 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07001880 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1881 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04001882 """
1883
1884 SIZE = 256
1885
David Zeuthene3cadca2017-02-22 21:25:46 -05001886 # Keep in sync with |reserved0| and |reserved| field of
1887 # |AvbVBMetaImageHeader|.
1888 RESERVED0 = 4
1889 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001890
1891 # Keep in sync with |AvbVBMetaImageHeader|.
1892 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1893 '2Q' # 2 x block size
1894 'L' # algorithm type
1895 '2Q' # offset, size (hash)
1896 '2Q' # offset, size (signature)
1897 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001898 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001899 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001900 'Q' # rollback_index
1901 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001902 str(RESERVED0) + 'x' + # padding for reserved bytes
1903 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001904 str(RESERVED) + 'x') # padding for reserved bytes
1905
1906 def __init__(self, data=None):
1907 """Initializes a new header object.
1908
1909 Arguments:
1910 data: If not None, must be a bytearray of size 8192.
1911
1912 Raises:
1913 Exception: If the given data is malformed.
1914 """
1915 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1916
1917 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001918 (self.magic, self.required_libavb_version_major,
1919 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001920 self.authentication_data_block_size, self.auxiliary_data_block_size,
1921 self.algorithm_type, self.hash_offset, self.hash_size,
1922 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001923 self.public_key_size, self.public_key_metadata_offset,
1924 self.public_key_metadata_size, self.descriptors_offset,
1925 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001926 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001927 self.flags,
1928 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001929 # Nuke NUL-bytes at the end of the string.
1930 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001931 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001932 else:
1933 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001934 # Start by just requiring version 1.0. Code that adds features
1935 # in a future version can use bump_required_libavb_version_minor() to
1936 # bump the minor.
1937 self.required_libavb_version_major = AVB_VERSION_MAJOR
1938 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001939 self.authentication_data_block_size = 0
1940 self.auxiliary_data_block_size = 0
1941 self.algorithm_type = 0
1942 self.hash_offset = 0
1943 self.hash_size = 0
1944 self.signature_offset = 0
1945 self.signature_size = 0
1946 self.public_key_offset = 0
1947 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001948 self.public_key_metadata_offset = 0
1949 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001950 self.descriptors_offset = 0
1951 self.descriptors_size = 0
1952 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001953 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001954 self.release_string = get_release_string()
1955
1956 def bump_required_libavb_version_minor(self, minor):
1957 """Function to bump required_libavb_version_minor.
1958
1959 Call this when writing data that requires a specific libavb
1960 version to parse it.
1961
1962 Arguments:
1963 minor: The minor version of libavb that has support for the feature.
1964 """
1965 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001966 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001967
1968 def save(self, output):
1969 """Serializes the header (256 bytes) to disk.
1970
1971 Arguments:
1972 output: The object to write the output to.
1973 """
1974 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001975 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1976 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001977 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1978 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001979 self.public_key_offset, self.public_key_size,
1980 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001981 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001982 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001983
1984 def encode(self):
1985 """Serializes the header (256) to a bytearray().
1986
1987 Returns:
1988 A bytearray() with the encoded header.
1989 """
1990 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001991 self.required_libavb_version_major,
1992 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001993 self.authentication_data_block_size,
1994 self.auxiliary_data_block_size, self.algorithm_type,
1995 self.hash_offset, self.hash_size, self.signature_offset,
1996 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001997 self.public_key_size, self.public_key_metadata_offset,
1998 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001999 self.descriptors_size, self.rollback_index, self.flags,
2000 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002001
2002
2003class Avb(object):
2004 """Business logic for avbtool command-line tool."""
2005
David Zeuthen8b6973b2016-09-20 12:39:49 -04002006 # Keep in sync with avb_ab_flow.h.
2007 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2008 AB_MAGIC = '\0AB0'
2009 AB_MAJOR_VERSION = 1
2010 AB_MINOR_VERSION = 0
2011 AB_MISC_METADATA_OFFSET = 2048
2012
David Zeuthen09692692016-09-30 16:16:40 -04002013 # Constants for maximum metadata size. These are used to give
2014 # meaningful errors if the value passed in via --partition_size is
2015 # too small and when --calc_max_image_size is used. We use
2016 # conservative figures.
2017 MAX_VBMETA_SIZE = 64 * 1024
2018 MAX_FOOTER_SIZE = 4096
2019
David Zeuthen49936b42018-08-07 17:38:58 -04002020 def extract_vbmeta_image(self, output, image_filename, padding_size):
2021 """Implements the 'extract_vbmeta_image' command.
2022
2023 Arguments:
2024 output: Write vbmeta struct to this file.
2025 image_filename: File to extract vbmeta data from (with a footer).
2026 padding_size: If not 0, pads output so size is a multiple of the number.
2027
2028 Raises:
2029 AvbError: If there's no footer in the image.
2030 """
2031 image = ImageHandler(image_filename)
2032
2033 (footer, _, _, _) = self._parse_image(image)
2034
2035 if not footer:
2036 raise AvbError('Given image does not have a footer.')
2037
2038 image.seek(footer.vbmeta_offset)
2039 vbmeta_blob = image.read(footer.vbmeta_size)
2040 output.write(vbmeta_blob)
2041
2042 if padding_size > 0:
2043 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2044 padding_needed = padded_size - len(vbmeta_blob)
2045 output.write('\0' * padding_needed)
2046
David Zeuthena4fee8b2016-08-22 15:20:43 -04002047 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002048 """Implements the 'erase_footer' command.
2049
2050 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002051 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002052 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002053
2054 Raises:
2055 AvbError: If there's no footer in the image.
2056 """
2057
David Zeuthena4fee8b2016-08-22 15:20:43 -04002058 image = ImageHandler(image_filename)
2059
David Zeuthen21e95262016-07-27 17:58:40 -04002060 (footer, _, descriptors, _) = self._parse_image(image)
2061
2062 if not footer:
2063 raise AvbError('Given image does not have a footer.')
2064
2065 new_image_size = None
2066 if not keep_hashtree:
2067 new_image_size = footer.original_image_size
2068 else:
2069 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002070 # descriptor to figure out the location and size of the hashtree
2071 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002072 for desc in descriptors:
2073 if isinstance(desc, AvbHashtreeDescriptor):
2074 # The hashtree is always just following the main data so the
2075 # new size is easily derived.
2076 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002077 # If the image has FEC codes, also keep those.
2078 if desc.fec_offset > 0:
2079 fec_end = desc.fec_offset + desc.fec_size
2080 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002081 break
2082 if not new_image_size:
2083 raise AvbError('Requested to keep hashtree but no hashtree '
2084 'descriptor was found.')
2085
2086 # And cut...
2087 image.truncate(new_image_size)
2088
David Zeuthen2bc232b2017-04-19 14:25:19 -04002089 def resize_image(self, image_filename, partition_size):
2090 """Implements the 'resize_image' command.
2091
2092 Arguments:
2093 image_filename: File with footer to resize.
2094 partition_size: The new size of the image.
2095
2096 Raises:
2097 AvbError: If there's no footer in the image.
2098 """
2099
2100 image = ImageHandler(image_filename)
2101
2102 if partition_size % image.block_size != 0:
2103 raise AvbError('Partition size of {} is not a multiple of the image '
2104 'block size {}.'.format(partition_size,
2105 image.block_size))
2106
2107 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2108
2109 if not footer:
2110 raise AvbError('Given image does not have a footer.')
2111
2112 # The vbmeta blob is always at the end of the data so resizing an
2113 # image amounts to just moving the footer around.
2114
2115 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2116 if vbmeta_end_offset % image.block_size != 0:
2117 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2118
2119 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002120 raise AvbError('Requested size of {} is too small for an image '
2121 'of size {}.'
2122 .format(partition_size,
2123 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002124
2125 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2126 # with enough bytes such that the final Footer block is at the end
2127 # of partition_size.
2128 image.truncate(vbmeta_end_offset)
2129 image.append_dont_care(partition_size - vbmeta_end_offset -
2130 1*image.block_size)
2131
2132 # Just reuse the same footer - only difference is that we're
2133 # writing it in a different place.
2134 footer_blob = footer.encode()
2135 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2136 footer_blob)
2137 image.append_raw(footer_blob_with_padding)
2138
David Zeuthen8b6973b2016-09-20 12:39:49 -04002139 def set_ab_metadata(self, misc_image, slot_data):
2140 """Implements the 'set_ab_metadata' command.
2141
2142 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2143 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2144
2145 Arguments:
2146 misc_image: The misc image to write to.
2147 slot_data: Slot data as a string
2148
2149 Raises:
2150 AvbError: If slot data is malformed.
2151 """
2152 tokens = slot_data.split(':')
2153 if len(tokens) != 6:
2154 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2155 a_priority = int(tokens[0])
2156 a_tries_remaining = int(tokens[1])
2157 a_success = True if int(tokens[2]) != 0 else False
2158 b_priority = int(tokens[3])
2159 b_tries_remaining = int(tokens[4])
2160 b_success = True if int(tokens[5]) != 0 else False
2161
2162 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2163 self.AB_MAGIC,
2164 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2165 a_priority, a_tries_remaining, a_success,
2166 b_priority, b_tries_remaining, b_success)
2167 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2168 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2169 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2170 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2171 misc_image.write(ab_data)
2172
David Zeuthena4fee8b2016-08-22 15:20:43 -04002173 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002174 """Implements the 'info_image' command.
2175
2176 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002177 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002178 output: Output file to write human-readable information to (file object).
2179 """
2180
David Zeuthena4fee8b2016-08-22 15:20:43 -04002181 image = ImageHandler(image_filename)
2182
David Zeuthen21e95262016-07-27 17:58:40 -04002183 o = output
2184
2185 (footer, header, descriptors, image_size) = self._parse_image(image)
2186
2187 if footer:
2188 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2189 footer.version_minor))
2190 o.write('Image size: {} bytes\n'.format(image_size))
2191 o.write('Original image size: {} bytes\n'.format(
2192 footer.original_image_size))
2193 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2194 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2195 o.write('--\n')
2196
2197 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2198
David Zeuthene3cadca2017-02-22 21:25:46 -05002199 o.write('Minimum libavb version: {}.{}{}\n'.format(
2200 header.required_libavb_version_major,
2201 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002202 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002203 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2204 o.write('Authentication Block: {} bytes\n'.format(
2205 header.authentication_data_block_size))
2206 o.write('Auxiliary Block: {} bytes\n'.format(
2207 header.auxiliary_data_block_size))
2208 o.write('Algorithm: {}\n'.format(alg_name))
2209 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002210 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002211 o.write('Release String: \'{}\'\n'.format(
2212 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002213
2214 # Print descriptors.
2215 num_printed = 0
2216 o.write('Descriptors:\n')
2217 for desc in descriptors:
2218 desc.print_desc(o)
2219 num_printed += 1
2220 if num_printed == 0:
2221 o.write(' (none)\n')
2222
David Zeuthene947cb62019-01-25 15:27:08 -05002223 def verify_image(self, image_filename, key_path, expected_chain_partitions, follow_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002224 """Implements the 'verify_image' command.
2225
2226 Arguments:
2227 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002228 key_path: None or check that embedded public key matches key at given path.
2229 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthene947cb62019-01-25 15:27:08 -05002230 follow_chain_partitions: If True, will follows chain partitions even when not
2231 specified with the --expected_chain_partition option
David Zeuthenb623d8b2017-04-04 16:05:53 -04002232 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002233 expected_chain_partitions_map = {}
2234 if expected_chain_partitions:
2235 used_locations = {}
2236 for cp in expected_chain_partitions:
2237 cp_tokens = cp.split(':')
2238 if len(cp_tokens) != 3:
2239 raise AvbError('Malformed chained partition "{}".'.format(cp))
2240 partition_name = cp_tokens[0]
2241 rollback_index_location = int(cp_tokens[1])
2242 file_path = cp_tokens[2]
2243 pk_blob = open(file_path).read()
2244 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2245
2246 image_dir = os.path.dirname(image_filename)
2247 image_ext = os.path.splitext(image_filename)[1]
2248
2249 key_blob = None
2250 if key_path:
2251 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2252 key_blob = encode_rsa_key(key_path)
2253 else:
2254 print 'Verifying image {} using embedded public key'.format(image_filename)
2255
David Zeuthenb623d8b2017-04-04 16:05:53 -04002256 image = ImageHandler(image_filename)
2257 (footer, header, descriptors, image_size) = self._parse_image(image)
2258 offset = 0
2259 if footer:
2260 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002261
David Zeuthenb623d8b2017-04-04 16:05:53 -04002262 image.seek(offset)
David Zeuthen49936b42018-08-07 17:38:58 -04002263 vbmeta_blob = image.read(header.SIZE + header.authentication_data_block_size +
2264 header.auxiliary_data_block_size)
2265
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002266 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002267 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002268 raise AvbError('Signature check failed for {} vbmeta struct {}'
2269 .format(alg_name, image_filename))
2270
2271 if key_blob:
2272 # The embedded public key is in the auxiliary block at an offset.
2273 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002274 key_offset += header.authentication_data_block_size
2275 key_offset += header.public_key_offset
2276 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002277 if key_blob != key_blob_in_vbmeta:
2278 raise AvbError('Embedded public key does not match given key.')
2279
2280 if footer:
2281 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002282 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002283 else:
2284 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002285 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002286
2287 for desc in descriptors:
David Zeuthene947cb62019-01-25 15:27:08 -05002288 if (isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions and
2289 expected_chain_partitions_map.get(desc.partition_name) == None):
2290 # In this case we're processing a chain descriptor but don't have a
2291 # --expect_chain_partition ... however --follow_chain_partitions was
2292 # specified so we shouldn't error out in desc.verify().
2293 print ('{}: Chained but ROLLBACK_SLOT (which is {}) and KEY (which has sha1 {}) not specified'
2294 .format(desc.partition_name, desc.rollback_index_location,
2295 hashlib.sha1(desc.public_key).hexdigest()))
2296 else:
2297 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image):
2298 raise AvbError('Error verifying descriptor.')
2299 # Honor --follow_chain_partitions - add '--' to make the output more readable.
2300 if isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions:
2301 print '--'
2302 chained_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2303 self.verify_image(chained_image_filename, key_path, None, False)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002304
David Zeuthenb623d8b2017-04-04 16:05:53 -04002305
David Zeuthenb8643c02018-05-17 17:21:18 -04002306 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2307 """Implements the 'calculate_vbmeta_digest' command.
2308
2309 Arguments:
2310 image_filename: Image file to get information from (file object).
2311 hash_algorithm: Hash algorithm used.
2312 output: Output file to write human-readable information to (file object).
2313 """
2314
2315 image_dir = os.path.dirname(image_filename)
2316 image_ext = os.path.splitext(image_filename)[1]
2317
2318 image = ImageHandler(image_filename)
2319 (footer, header, descriptors, image_size) = self._parse_image(image)
2320 offset = 0
2321 if footer:
2322 offset = footer.vbmeta_offset
2323 size = (header.SIZE + header.authentication_data_block_size +
2324 header.auxiliary_data_block_size)
2325 image.seek(offset)
2326 vbmeta_blob = image.read(size)
2327
2328 hasher = hashlib.new(name=hash_algorithm)
2329 hasher.update(vbmeta_blob)
2330
2331 for desc in descriptors:
2332 if isinstance(desc, AvbChainPartitionDescriptor):
2333 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2334 ch_image = ImageHandler(ch_image_filename)
2335 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2336 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002337 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2338 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002339 if ch_footer:
2340 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002341 ch_image.seek(ch_offset)
2342 ch_vbmeta_blob = ch_image.read(ch_size)
2343 hasher.update(ch_vbmeta_blob)
2344
2345 digest = hasher.digest()
2346 output.write('{}\n'.format(digest.encode('hex')))
2347
2348
David Zeuthenf7d2e752018-09-20 13:30:41 -04002349 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2350 """Implements the 'calculate_kernel_cmdline' command.
2351
2352 Arguments:
2353 image_filename: Image file to get information from (file object).
2354 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2355 output: Output file to write human-readable information to (file object).
2356 """
2357
2358 image = ImageHandler(image_filename)
2359 _, _, descriptors, _ = self._parse_image(image)
2360
2361 image_dir = os.path.dirname(image_filename)
2362 image_ext = os.path.splitext(image_filename)[1]
2363
2364 cmdline_descriptors = []
2365 for desc in descriptors:
2366 if isinstance(desc, AvbChainPartitionDescriptor):
2367 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2368 ch_image = ImageHandler(ch_image_filename)
2369 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2370 for ch_desc in ch_descriptors:
2371 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2372 cmdline_descriptors.append(ch_desc)
2373 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2374 cmdline_descriptors.append(desc)
2375
2376 kernel_cmdline_snippets = []
2377 for desc in cmdline_descriptors:
2378 use_cmdline = True
2379 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) != 0:
2380 if hashtree_disabled:
2381 use_cmdline = False
2382 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2383 if not hashtree_disabled:
2384 use_cmdline = False
2385 if use_cmdline:
2386 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2387 output.write(' '.join(kernel_cmdline_snippets))
2388
2389
David Zeuthen21e95262016-07-27 17:58:40 -04002390 def _parse_image(self, image):
2391 """Gets information about an image.
2392
2393 The image can either be a vbmeta or an image with a footer.
2394
2395 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002396 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002397
2398 Returns:
2399 A tuple where the first argument is a AvbFooter (None if there
2400 is no footer on the image), the second argument is a
2401 AvbVBMetaHeader, the third argument is a list of
2402 AvbDescriptor-derived instances, and the fourth argument is the
2403 size of |image|.
2404 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002405 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002406 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002407 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002408 try:
2409 footer = AvbFooter(image.read(AvbFooter.SIZE))
2410 except (LookupError, struct.error):
2411 # Nope, just seek back to the start.
2412 image.seek(0)
2413
2414 vbmeta_offset = 0
2415 if footer:
2416 vbmeta_offset = footer.vbmeta_offset
2417
2418 image.seek(vbmeta_offset)
2419 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2420
2421 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2422 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2423 desc_start_offset = aux_block_offset + h.descriptors_offset
2424 image.seek(desc_start_offset)
2425 descriptors = parse_descriptors(image.read(h.descriptors_size))
2426
David Zeuthen09692692016-09-30 16:16:40 -04002427 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002428
David Zeuthenb1b994d2017-03-06 18:01:31 -05002429 def _load_vbmeta_blob(self, image):
2430 """Gets the vbmeta struct and associated sections.
2431
2432 The image can either be a vbmeta.img or an image with a footer.
2433
2434 Arguments:
2435 image: An ImageHandler (vbmeta or footer).
2436
2437 Returns:
2438 A blob with the vbmeta struct and other sections.
2439 """
2440 assert isinstance(image, ImageHandler)
2441 footer = None
2442 image.seek(image.image_size - AvbFooter.SIZE)
2443 try:
2444 footer = AvbFooter(image.read(AvbFooter.SIZE))
2445 except (LookupError, struct.error):
2446 # Nope, just seek back to the start.
2447 image.seek(0)
2448
2449 vbmeta_offset = 0
2450 if footer:
2451 vbmeta_offset = footer.vbmeta_offset
2452
2453 image.seek(vbmeta_offset)
2454 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2455
2456 image.seek(vbmeta_offset)
2457 data_size = AvbVBMetaHeader.SIZE
2458 data_size += h.authentication_data_block_size
2459 data_size += h.auxiliary_data_block_size
2460 return image.read(data_size)
2461
David Zeuthen73f2afa2017-05-17 16:54:11 -04002462 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002463 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002464
2465 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002466 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002467
2468 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002469 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2470 instructions. There is one for when hashtree is not disabled and one for
2471 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002472
David Zeuthen21e95262016-07-27 17:58:40 -04002473 """
2474
David Zeuthen21e95262016-07-27 17:58:40 -04002475 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002476 c += '0' # start
2477 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2478 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2479 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2480 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2481 c += ' {}'.format(ht.data_block_size) # data_block
2482 c += ' {}'.format(ht.hash_block_size) # hash_block
2483 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2484 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2485 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2486 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2487 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2488 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002489 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002490 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002491 c += ' ignore_zero_blocks'
2492 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2493 c += ' fec_roots {}'.format(ht.fec_num_roots)
2494 # Note that fec_blocks is the size that FEC covers, *not* the
2495 # size of the FEC data. Since we use FEC for everything up until
2496 # the FEC data, it's the same as the offset.
2497 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2498 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2499 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002500 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002501 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002502 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002503 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002504
David Zeuthenfd41eb92016-11-17 12:24:47 -05002505 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002506 desc = AvbKernelCmdlineDescriptor()
2507 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002508 desc.flags = (
2509 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2510
2511 # The descriptor for when hashtree verification is disabled is a lot
2512 # simpler - we just set the root to the partition.
2513 desc_no_ht = AvbKernelCmdlineDescriptor()
2514 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2515 desc_no_ht.flags = (
2516 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2517
2518 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002519
David Zeuthen73f2afa2017-05-17 16:54:11 -04002520 def _get_cmdline_descriptors_for_dm_verity(self, image):
2521 """Generate kernel cmdline descriptors for dm-verity.
2522
2523 Arguments:
2524 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2525
2526 Returns:
2527 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2528 instructions. There is one for when hashtree is not disabled and one for
2529 when it is.
2530
2531 Raises:
2532 AvbError: If |image| doesn't have a hashtree descriptor.
2533
2534 """
2535
2536 (_, _, descriptors, _) = self._parse_image(image)
2537
2538 ht = None
2539 for desc in descriptors:
2540 if isinstance(desc, AvbHashtreeDescriptor):
2541 ht = desc
2542 break
2543
2544 if not ht:
2545 raise AvbError('No hashtree descriptor in given image')
2546
2547 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2548
David Zeuthen21e95262016-07-27 17:58:40 -04002549 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002550 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002551 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002552 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002553 include_descriptors_from_image,
2554 signing_helper,
2555 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002556 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002557 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002558 print_required_libavb_version,
2559 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002560 """Implements the 'make_vbmeta_image' command.
2561
2562 Arguments:
2563 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002564 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002565 algorithm_name: Name of algorithm to use.
2566 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002567 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002568 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002569 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002570 props: Properties to insert (list of strings of the form 'key:value').
2571 props_from_file: Properties to insert (list of strings 'key:<path>').
2572 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002573 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002574 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002575 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002576 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002577 release_string: None or avbtool release string to use instead of default.
2578 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002579 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002580 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002581
2582 Raises:
2583 AvbError: If a chained partition is malformed.
2584 """
2585
David Zeuthen1097a782017-05-31 15:53:17 -04002586 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002587 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002588 if include_descriptors_from_image:
2589 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2590 # version of all included descriptors.
2591 tmp_header = AvbVBMetaHeader()
2592 for image in include_descriptors_from_image:
2593 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2594 tmp_header.bump_required_libavb_version_minor(
2595 image_header.required_libavb_version_minor)
2596 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2597 else:
2598 # Descriptors aside, all vbmeta features are supported in 1.0.
2599 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002600 return
2601
2602 if not output:
2603 raise AvbError('No output file given')
2604
David Zeuthen21e95262016-07-27 17:58:40 -04002605 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002606 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002607 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002608 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002609 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002610 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002611 include_descriptors_from_image, signing_helper,
2612 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002613 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002614
2615 # Write entire vbmeta blob (header, authentication, auxiliary).
2616 output.seek(0)
2617 output.write(vbmeta_blob)
2618
David Zeuthen97cb5802017-06-01 16:14:05 -04002619 if padding_size > 0:
2620 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2621 padding_needed = padded_size - len(vbmeta_blob)
2622 output.write('\0' * padding_needed)
2623
David Zeuthen18666ab2016-11-15 11:18:05 -05002624 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2625 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002626 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002627 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002628 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002629 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002630 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002631 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002632 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002633 release_string, append_to_release_string,
2634 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002635 """Generates a VBMeta blob.
2636
2637 This blob contains the header (struct AvbVBMetaHeader), the
2638 authentication data block (which contains the hash and signature
2639 for the header and auxiliary block), and the auxiliary block
2640 (which contains descriptors, the public key used, and other data).
2641
2642 The |key| parameter can |None| only if the |algorithm_name| is
2643 'NONE'.
2644
2645 Arguments:
2646 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2647 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002648 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002649 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002650 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002651 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002652 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002653 props: Properties to insert (List of strings of the form 'key:value').
2654 props_from_file: Properties to insert (List of strings 'key:<path>').
2655 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002656 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002657 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002658 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2659 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002660 include_descriptors_from_image: List of file objects for which
2661 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002662 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002663 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002664 release_string: None or avbtool release string.
2665 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002666 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002667
2668 Returns:
2669 A bytearray() with the VBMeta blob.
2670
2671 Raises:
2672 Exception: If the |algorithm_name| is not found, if no key has
2673 been given and the given algorithm requires one, or the key is
2674 of the wrong size.
2675
2676 """
2677 try:
2678 alg = ALGORITHMS[algorithm_name]
2679 except KeyError:
2680 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2681
David Zeuthena5fd3a42017-02-27 16:38:54 -05002682 if not descriptors:
2683 descriptors = []
2684
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002685 h = AvbVBMetaHeader()
2686 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2687
David Zeuthena5fd3a42017-02-27 16:38:54 -05002688 # Insert chained partition descriptors, if any
2689 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002690 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002691 for cp in chain_partitions:
2692 cp_tokens = cp.split(':')
2693 if len(cp_tokens) != 3:
2694 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002695 partition_name = cp_tokens[0]
2696 rollback_index_location = int(cp_tokens[1])
2697 file_path = cp_tokens[2]
2698 # Check that the same rollback location isn't being used by
2699 # multiple chained partitions.
2700 if used_locations.get(rollback_index_location):
2701 raise AvbError('Rollback Index Location {} is already in use.'.format(
2702 rollback_index_location))
2703 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002704 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002705 desc.partition_name = partition_name
2706 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002707 if desc.rollback_index_location < 1:
2708 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002709 desc.public_key = open(file_path, 'rb').read()
2710 descriptors.append(desc)
2711
David Zeuthen21e95262016-07-27 17:58:40 -04002712 # Descriptors.
2713 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002714 for desc in descriptors:
2715 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002716
2717 # Add properties.
2718 if props:
2719 for prop in props:
2720 idx = prop.find(':')
2721 if idx == -1:
2722 raise AvbError('Malformed property "{}".'.format(prop))
2723 desc = AvbPropertyDescriptor()
2724 desc.key = prop[0:idx]
2725 desc.value = prop[(idx + 1):]
2726 encoded_descriptors.extend(desc.encode())
2727 if props_from_file:
2728 for prop in props_from_file:
2729 idx = prop.find(':')
2730 if idx == -1:
2731 raise AvbError('Malformed property "{}".'.format(prop))
2732 desc = AvbPropertyDescriptor()
2733 desc.key = prop[0:idx]
2734 desc.value = prop[(idx + 1):]
2735 file_path = prop[(idx + 1):]
2736 desc.value = open(file_path, 'rb').read()
2737 encoded_descriptors.extend(desc.encode())
2738
David Zeuthen73f2afa2017-05-17 16:54:11 -04002739 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002740 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002741 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002742 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002743 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2744 encoded_descriptors.extend(cmdline_desc[0].encode())
2745 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002746
David Zeuthen73f2afa2017-05-17 16:54:11 -04002747 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2748 if ht_desc_to_setup:
2749 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2750 ht_desc_to_setup)
2751 encoded_descriptors.extend(cmdline_desc[0].encode())
2752 encoded_descriptors.extend(cmdline_desc[1].encode())
2753
David Zeuthen21e95262016-07-27 17:58:40 -04002754 # Add kernel command-lines.
2755 if kernel_cmdlines:
2756 for i in kernel_cmdlines:
2757 desc = AvbKernelCmdlineDescriptor()
2758 desc.kernel_cmdline = i
2759 encoded_descriptors.extend(desc.encode())
2760
2761 # Add descriptors from other images.
2762 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002763 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002764 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002765 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002766 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2767 image_handler)
2768 # Bump the required libavb version to support all included descriptors.
2769 h.bump_required_libavb_version_minor(
2770 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002771 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002772 # The --include_descriptors_from_image option is used in some setups
2773 # with images A and B where both A and B contain a descriptor
2774 # for a partition with the same name. Since it's not meaningful
2775 # to include both descriptors, only include the last seen descriptor.
2776 # See bug 76386656 for details.
2777 if hasattr(desc, 'partition_name'):
2778 key = type(desc).__name__ + '_' + desc.partition_name
2779 descriptors_dict[key] = desc.encode()
2780 else:
2781 encoded_descriptors.extend(desc.encode())
2782 for key in sorted(descriptors_dict.keys()):
2783 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002784
David Zeuthen18666ab2016-11-15 11:18:05 -05002785 # Load public key metadata blob, if requested.
2786 pkmd_blob = []
2787 if public_key_metadata_path:
2788 with open(public_key_metadata_path) as f:
2789 pkmd_blob = f.read()
2790
David Zeuthen21e95262016-07-27 17:58:40 -04002791 key = None
2792 encoded_key = bytearray()
2793 if alg.public_key_num_bytes > 0:
2794 if not key_path:
2795 raise AvbError('Key is required for algorithm {}'.format(
2796 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002797 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002798 if len(encoded_key) != alg.public_key_num_bytes:
2799 raise AvbError('Key is wrong size for algorithm {}'.format(
2800 algorithm_name))
2801
David Zeuthene3cadca2017-02-22 21:25:46 -05002802 # Override release string, if requested.
2803 if isinstance(release_string, (str, unicode)):
2804 h.release_string = release_string
2805
2806 # Append to release string, if requested. Also insert a space before.
2807 if isinstance(append_to_release_string, (str, unicode)):
2808 h.release_string += ' ' + append_to_release_string
2809
David Zeuthen18666ab2016-11-15 11:18:05 -05002810 # For the Auxiliary data block, descriptors are stored at offset 0,
2811 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002812 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002813 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002814 h.descriptors_offset = 0
2815 h.descriptors_size = len(encoded_descriptors)
2816 h.public_key_offset = h.descriptors_size
2817 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002818 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2819 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002820
2821 # For the Authentication data block, the hash is first and then
2822 # the signature.
2823 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002824 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002825 h.algorithm_type = alg.algorithm_type
2826 h.hash_offset = 0
2827 h.hash_size = alg.hash_num_bytes
2828 # Signature offset and size - it's stored right after the hash
2829 # (in Authentication data block).
2830 h.signature_offset = alg.hash_num_bytes
2831 h.signature_size = alg.signature_num_bytes
2832
2833 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002834 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002835
2836 # Generate Header data block.
2837 header_data_blob = h.encode()
2838
2839 # Generate Auxiliary data block.
2840 aux_data_blob = bytearray()
2841 aux_data_blob.extend(encoded_descriptors)
2842 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002843 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002844 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2845 aux_data_blob.extend('\0' * padding_bytes)
2846
2847 # Calculate the hash.
2848 binary_hash = bytearray()
2849 binary_signature = bytearray()
2850 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002851 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002852 ha.update(header_data_blob)
2853 ha.update(aux_data_blob)
2854 binary_hash.extend(ha.digest())
2855
2856 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002857 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002858 binary_signature.extend(raw_sign(signing_helper,
2859 signing_helper_with_files,
2860 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002861 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002862 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002863
2864 # Generate Authentication data block.
2865 auth_data_blob = bytearray()
2866 auth_data_blob.extend(binary_hash)
2867 auth_data_blob.extend(binary_signature)
2868 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2869 auth_data_blob.extend('\0' * padding_bytes)
2870
2871 return header_data_blob + auth_data_blob + aux_data_blob
2872
2873 def extract_public_key(self, key_path, output):
2874 """Implements the 'extract_public_key' command.
2875
2876 Arguments:
2877 key_path: The path to a RSA private key file.
2878 output: The file to write to.
2879 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002880 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002881
David Zeuthenb1b994d2017-03-06 18:01:31 -05002882 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2883 partition_size):
2884 """Implementation of the append_vbmeta_image command.
2885
2886 Arguments:
2887 image_filename: File to add the footer to.
2888 vbmeta_image_filename: File to get vbmeta struct from.
2889 partition_size: Size of partition.
2890
2891 Raises:
2892 AvbError: If an argument is incorrect.
2893 """
2894 image = ImageHandler(image_filename)
2895
2896 if partition_size % image.block_size != 0:
2897 raise AvbError('Partition size of {} is not a multiple of the image '
2898 'block size {}.'.format(partition_size,
2899 image.block_size))
2900
2901 # If there's already a footer, truncate the image to its original
2902 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002903 if image.image_size >= AvbFooter.SIZE:
2904 image.seek(image.image_size - AvbFooter.SIZE)
2905 try:
2906 footer = AvbFooter(image.read(AvbFooter.SIZE))
2907 # Existing footer found. Just truncate.
2908 original_image_size = footer.original_image_size
2909 image.truncate(footer.original_image_size)
2910 except (LookupError, struct.error):
2911 original_image_size = image.image_size
2912 else:
2913 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002914 original_image_size = image.image_size
2915
2916 # If anything goes wrong from here-on, restore the image back to
2917 # its original size.
2918 try:
2919 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2920 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2921
2922 # If the image isn't sparse, its size might not be a multiple of
2923 # the block size. This will screw up padding later so just grow it.
2924 if image.image_size % image.block_size != 0:
2925 assert not image.is_sparse
2926 padding_needed = image.block_size - (image.image_size%image.block_size)
2927 image.truncate(image.image_size + padding_needed)
2928
2929 # The append_raw() method requires content with size being a
2930 # multiple of |block_size| so add padding as needed. Also record
2931 # where this is written to since we'll need to put that in the
2932 # footer.
2933 vbmeta_offset = image.image_size
2934 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2935 len(vbmeta_blob))
2936 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2937
2938 # Append vbmeta blob and footer
2939 image.append_raw(vbmeta_blob_with_padding)
2940 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2941
2942 # Now insert a DONT_CARE chunk with enough bytes such that the
2943 # final Footer block is at the end of partition_size..
2944 image.append_dont_care(partition_size - vbmeta_end_offset -
2945 1*image.block_size)
2946
2947 # Generate the Footer that tells where the VBMeta footer
2948 # is. Also put enough padding in the front of the footer since
2949 # we'll write out an entire block.
2950 footer = AvbFooter()
2951 footer.original_image_size = original_image_size
2952 footer.vbmeta_offset = vbmeta_offset
2953 footer.vbmeta_size = len(vbmeta_blob)
2954 footer_blob = footer.encode()
2955 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2956 footer_blob)
2957 image.append_raw(footer_blob_with_padding)
2958
2959 except:
2960 # Truncate back to original size, then re-raise
2961 image.truncate(original_image_size)
2962 raise
2963
David Zeuthena4fee8b2016-08-22 15:20:43 -04002964 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002965 hash_algorithm, salt, chain_partitions, algorithm_name,
2966 key_path,
2967 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002968 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002969 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002970 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002971 signing_helper, signing_helper_with_files,
2972 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002973 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002974 print_required_libavb_version, use_persistent_digest,
2975 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002976 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002977
2978 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002979 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002980 partition_size: Size of partition.
2981 partition_name: Name of partition (without A/B suffix).
2982 hash_algorithm: Hash algorithm to use.
2983 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002984 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002985 algorithm_name: Name of algorithm to use.
2986 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002987 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002988 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002989 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002990 props: Properties to insert (List of strings of the form 'key:value').
2991 props_from_file: Properties to insert (List of strings 'key:<path>').
2992 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002993 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002994 dm-verity kernel cmdline from.
2995 include_descriptors_from_image: List of file objects for which
2996 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002997 calc_max_image_size: Don't store the footer - instead calculate the
2998 maximum image size leaving enough room for metadata with the
2999 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003000 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003001 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003002 release_string: None or avbtool release string.
3003 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003004 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3005 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003006 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003007 use_persistent_digest: Use a persistent digest on device.
3008 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003009
3010 Raises:
3011 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003012 """
David Zeuthen1097a782017-05-31 15:53:17 -04003013
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003014 required_libavb_version_minor = 0
3015 if use_persistent_digest or do_not_use_ab:
3016 required_libavb_version_minor = 1
3017
David Zeuthen1097a782017-05-31 15:53:17 -04003018 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003019 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003020 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003021 return
3022
David Zeuthenbf562452017-05-17 18:04:43 -04003023 # First, calculate the maximum image size such that an image
3024 # this size + metadata (footer + vbmeta struct) fits in
3025 # |partition_size|.
3026 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003027 if partition_size < max_metadata_size:
3028 raise AvbError('Parition size of {} is too small. '
3029 'Needs to be at least {}'.format(
3030 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003031 max_image_size = partition_size - max_metadata_size
3032
3033 # If we're asked to only calculate the maximum image size, we're done.
3034 if calc_max_image_size:
3035 print '{}'.format(max_image_size)
3036 return
3037
David Zeuthena4fee8b2016-08-22 15:20:43 -04003038 image = ImageHandler(image_filename)
3039
3040 if partition_size % image.block_size != 0:
3041 raise AvbError('Partition size of {} is not a multiple of the image '
3042 'block size {}.'.format(partition_size,
3043 image.block_size))
3044
David Zeuthen21e95262016-07-27 17:58:40 -04003045 # If there's already a footer, truncate the image to its original
3046 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3047 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003048 if image.image_size >= AvbFooter.SIZE:
3049 image.seek(image.image_size - AvbFooter.SIZE)
3050 try:
3051 footer = AvbFooter(image.read(AvbFooter.SIZE))
3052 # Existing footer found. Just truncate.
3053 original_image_size = footer.original_image_size
3054 image.truncate(footer.original_image_size)
3055 except (LookupError, struct.error):
3056 original_image_size = image.image_size
3057 else:
3058 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003059 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003060
3061 # If anything goes wrong from here-on, restore the image back to
3062 # its original size.
3063 try:
David Zeuthen09692692016-09-30 16:16:40 -04003064 # If image size exceeds the maximum image size, fail.
3065 if image.image_size > max_image_size:
3066 raise AvbError('Image size of {} exceeds maximum image '
3067 'size of {} in order to fit in a partition '
3068 'size of {}.'.format(image.image_size, max_image_size,
3069 partition_size))
3070
David Zeuthen21e95262016-07-27 17:58:40 -04003071 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3072 if salt:
3073 salt = salt.decode('hex')
3074 else:
Bryan Henry45354282018-10-25 18:37:27 -07003075 if salt is None and not use_persistent_digest:
3076 # If salt is not explicitly specified, choose a hash that's the same
3077 # size as the hash size. Don't populate a random salt if this
3078 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003079 hash_size = digest_size
3080 salt = open('/dev/urandom').read(hash_size)
3081 else:
3082 salt = ''
3083
3084 hasher = hashlib.new(name=hash_algorithm, string=salt)
3085 # TODO(zeuthen): might want to read this in chunks to avoid
3086 # memory pressure, then again, this is only supposed to be used
3087 # on kernel/initramfs partitions. Possible optimization.
3088 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003089 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003090 digest = hasher.digest()
3091
3092 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003093 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003094 h_desc.hash_algorithm = hash_algorithm
3095 h_desc.partition_name = partition_name
3096 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003097 h_desc.flags = 0
3098 if do_not_use_ab:
3099 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3100 if not use_persistent_digest:
3101 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003102
3103 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003104 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003105 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003106 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003107 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003108 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003109 include_descriptors_from_image, signing_helper,
3110 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003111 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003112
David Zeuthend247fcb2017-02-16 12:09:27 -05003113 # Write vbmeta blob, if requested.
3114 if output_vbmeta_image:
3115 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003116
David Zeuthend247fcb2017-02-16 12:09:27 -05003117 # Append vbmeta blob and footer, unless requested not to.
3118 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003119 # If the image isn't sparse, its size might not be a multiple of
3120 # the block size. This will screw up padding later so just grow it.
3121 if image.image_size % image.block_size != 0:
3122 assert not image.is_sparse
3123 padding_needed = image.block_size - (
3124 image.image_size % image.block_size)
3125 image.truncate(image.image_size + padding_needed)
3126
3127 # The append_raw() method requires content with size being a
3128 # multiple of |block_size| so add padding as needed. Also record
3129 # where this is written to since we'll need to put that in the
3130 # footer.
3131 vbmeta_offset = image.image_size
3132 padding_needed = (
3133 round_to_multiple(len(vbmeta_blob), image.block_size) -
3134 len(vbmeta_blob))
3135 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3136
David Zeuthend247fcb2017-02-16 12:09:27 -05003137 image.append_raw(vbmeta_blob_with_padding)
3138 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3139
3140 # Now insert a DONT_CARE chunk with enough bytes such that the
3141 # final Footer block is at the end of partition_size..
3142 image.append_dont_care(partition_size - vbmeta_end_offset -
3143 1*image.block_size)
3144
3145 # Generate the Footer that tells where the VBMeta footer
3146 # is. Also put enough padding in the front of the footer since
3147 # we'll write out an entire block.
3148 footer = AvbFooter()
3149 footer.original_image_size = original_image_size
3150 footer.vbmeta_offset = vbmeta_offset
3151 footer.vbmeta_size = len(vbmeta_blob)
3152 footer_blob = footer.encode()
3153 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3154 footer_blob)
3155 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003156
David Zeuthen21e95262016-07-27 17:58:40 -04003157 except:
3158 # Truncate back to original size, then re-raise
3159 image.truncate(original_image_size)
3160 raise
3161
David Zeuthena4fee8b2016-08-22 15:20:43 -04003162 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003163 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003164 block_size, salt, chain_partitions, algorithm_name,
3165 key_path,
3166 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003167 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003168 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003169 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003170 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003171 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003172 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003173 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003174 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003175 print_required_libavb_version,
3176 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04003177 """Implements the 'add_hashtree_footer' command.
3178
3179 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3180 more information about dm-verity and these hashes.
3181
3182 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003183 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003184 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003185 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003186 generate_fec: If True, generate FEC codes.
3187 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003188 hash_algorithm: Hash algorithm to use.
3189 block_size: Block size to use.
3190 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003191 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003192 algorithm_name: Name of algorithm to use.
3193 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003194 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003195 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003196 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003197 props: Properties to insert (List of strings of the form 'key:value').
3198 props_from_file: Properties to insert (List of strings 'key:<path>').
3199 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003200 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003201 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003202 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3203 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003204 include_descriptors_from_image: List of file objects for which
3205 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003206 calc_max_image_size: Don't store the hashtree or footer - instead
3207 calculate the maximum image size leaving enough room for hashtree
3208 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003209 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003210 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003211 release_string: None or avbtool release string.
3212 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003213 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3214 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003215 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003216 use_persistent_root_digest: Use a persistent root digest on device.
3217 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003218
3219 Raises:
3220 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003221 """
David Zeuthen1097a782017-05-31 15:53:17 -04003222
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003223 required_libavb_version_minor = 0
3224 if use_persistent_root_digest or do_not_use_ab:
3225 required_libavb_version_minor = 1
3226
David Zeuthen1097a782017-05-31 15:53:17 -04003227 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003228 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003229 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003230 return
3231
David Zeuthen09692692016-09-30 16:16:40 -04003232 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3233 digest_padding = round_to_pow2(digest_size) - digest_size
3234
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003235 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3236 # size such that an image this size + the hashtree + metadata (footer +
3237 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3238 # for metadata.
3239 if partition_size > 0:
3240 (_, max_tree_size) = calc_hash_level_offsets(
3241 partition_size, block_size, digest_size + digest_padding)
3242 max_fec_size = 0
3243 if generate_fec:
3244 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3245 max_metadata_size = (max_fec_size + max_tree_size +
3246 self.MAX_VBMETA_SIZE +
3247 self.MAX_FOOTER_SIZE)
3248 max_image_size = partition_size - max_metadata_size
3249 else:
3250 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003251
3252 # If we're asked to only calculate the maximum image size, we're done.
3253 if calc_max_image_size:
3254 print '{}'.format(max_image_size)
3255 return
3256
David Zeuthena4fee8b2016-08-22 15:20:43 -04003257 image = ImageHandler(image_filename)
3258
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003259 if partition_size > 0:
3260 if partition_size % image.block_size != 0:
3261 raise AvbError('Partition size of {} is not a multiple of the image '
3262 'block size {}.'.format(partition_size,
3263 image.block_size))
3264 else:
3265 if image.image_size % image.block_size != 0:
3266 raise AvbError('File size of {} is not a multiple of the image '
3267 'block size {}.'.format(image.image_size,
3268 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003269
David Zeuthen21e95262016-07-27 17:58:40 -04003270 # If there's already a footer, truncate the image to its original
3271 # size. This way 'avbtool add_hashtree_footer' is idempotent
3272 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003273 if image.image_size >= AvbFooter.SIZE:
3274 image.seek(image.image_size - AvbFooter.SIZE)
3275 try:
3276 footer = AvbFooter(image.read(AvbFooter.SIZE))
3277 # Existing footer found. Just truncate.
3278 original_image_size = footer.original_image_size
3279 image.truncate(footer.original_image_size)
3280 except (LookupError, struct.error):
3281 original_image_size = image.image_size
3282 else:
3283 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003284 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003285
3286 # If anything goes wrong from here-on, restore the image back to
3287 # its original size.
3288 try:
3289 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003290 rounded_image_size = round_to_multiple(image.image_size, block_size)
3291 if rounded_image_size > image.image_size:
3292 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003293
David Zeuthen09692692016-09-30 16:16:40 -04003294 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003295 if partition_size > 0:
3296 if image.image_size > max_image_size:
3297 raise AvbError('Image size of {} exceeds maximum image '
3298 'size of {} in order to fit in a partition '
3299 'size of {}.'.format(image.image_size, max_image_size,
3300 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003301
3302 if salt:
3303 salt = salt.decode('hex')
3304 else:
Bryan Henry45354282018-10-25 18:37:27 -07003305 if salt is None and not use_persistent_root_digest:
3306 # If salt is not explicitly specified, choose a hash that's the same
3307 # size as the hash size. Don't populate a random salt if this
3308 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003309 hash_size = digest_size
3310 salt = open('/dev/urandom').read(hash_size)
3311 else:
3312 salt = ''
3313
David Zeuthena4fee8b2016-08-22 15:20:43 -04003314 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003315 # offsets in advance.
3316 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003317 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003318
David Zeuthena4fee8b2016-08-22 15:20:43 -04003319 # If the image isn't sparse, its size might not be a multiple of
3320 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003321 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003322 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003323 padding_needed = image.block_size - (image.image_size%image.block_size)
3324 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003325
David Zeuthena4fee8b2016-08-22 15:20:43 -04003326 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003327 tree_offset = image.image_size
3328 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003329 block_size,
3330 hash_algorithm, salt,
3331 digest_padding,
3332 hash_level_offsets,
3333 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003334
3335 # Generate HashtreeDescriptor with details about the tree we
3336 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003337 ht_desc = AvbHashtreeDescriptor()
3338 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003339 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003340 ht_desc.tree_offset = tree_offset
3341 ht_desc.tree_size = tree_size
3342 ht_desc.data_block_size = block_size
3343 ht_desc.hash_block_size = block_size
3344 ht_desc.hash_algorithm = hash_algorithm
3345 ht_desc.partition_name = partition_name
3346 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003347 if do_not_use_ab:
3348 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3349 if not use_persistent_root_digest:
3350 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003351
David Zeuthen09692692016-09-30 16:16:40 -04003352 # Write the hash tree
3353 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3354 len(hash_tree))
3355 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3356 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003357 len_hashtree_and_fec = len(hash_tree_with_padding)
3358
3359 # Generate FEC codes, if requested.
3360 if generate_fec:
3361 fec_data = generate_fec_data(image_filename, fec_num_roots)
3362 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3363 len(fec_data))
3364 fec_data_with_padding = fec_data + '\0'*padding_needed
3365 fec_offset = image.image_size
3366 image.append_raw(fec_data_with_padding)
3367 len_hashtree_and_fec += len(fec_data_with_padding)
3368 # Update the hashtree descriptor.
3369 ht_desc.fec_num_roots = fec_num_roots
3370 ht_desc.fec_offset = fec_offset
3371 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003372
David Zeuthen73f2afa2017-05-17 16:54:11 -04003373 ht_desc_to_setup = None
3374 if setup_as_rootfs_from_kernel:
3375 ht_desc_to_setup = ht_desc
3376
David Zeuthena4fee8b2016-08-22 15:20:43 -04003377 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003378 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003379 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003380 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003381 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003382 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003383 include_descriptors_from_image, signing_helper,
3384 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003385 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003386 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3387 len(vbmeta_blob))
3388 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003389
David Zeuthend247fcb2017-02-16 12:09:27 -05003390 # Write vbmeta blob, if requested.
3391 if output_vbmeta_image:
3392 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003393
David Zeuthend247fcb2017-02-16 12:09:27 -05003394 # Append vbmeta blob and footer, unless requested not to.
3395 if not do_not_append_vbmeta_image:
3396 image.append_raw(vbmeta_blob_with_padding)
3397
3398 # Now insert a DONT_CARE chunk with enough bytes such that the
3399 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003400 if partition_size > 0:
3401 image.append_dont_care(partition_size - image.image_size -
3402 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003403
3404 # Generate the Footer that tells where the VBMeta footer
3405 # is. Also put enough padding in the front of the footer since
3406 # we'll write out an entire block.
3407 footer = AvbFooter()
3408 footer.original_image_size = original_image_size
3409 footer.vbmeta_offset = vbmeta_offset
3410 footer.vbmeta_size = len(vbmeta_blob)
3411 footer_blob = footer.encode()
3412 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3413 footer_blob)
3414 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003415
David Zeuthen21e95262016-07-27 17:58:40 -04003416 except:
David Zeuthen09692692016-09-30 16:16:40 -04003417 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003418 image.truncate(original_image_size)
3419 raise
3420
David Zeuthenc68f0822017-03-31 17:22:35 -04003421 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003422 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003423 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003424 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003425 """Implements the 'make_atx_certificate' command.
3426
3427 Android Things certificates are required for Android Things public key
3428 metadata. They chain the vbmeta signing key for a particular product back to
3429 a fused, permanent root key. These certificates are fixed-length and fixed-
3430 format with the explicit goal of not parsing ASN.1 in bootloader code.
3431
3432 Arguments:
3433 output: Certificate will be written to this file on success.
3434 authority_key_path: A PEM file path with the authority private key.
3435 If None, then a certificate will be created without a
3436 signature. The signature can be created out-of-band
3437 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003438 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003439 subject_key_version: A 64-bit version value. If this is None, the number
3440 of seconds since the epoch is used.
3441 subject: A subject identifier. For Product Signing Key certificates this
3442 should be the same Product ID found in the permanent attributes.
3443 is_intermediate_authority: True if the certificate is for an intermediate
3444 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003445 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003446 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003447 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003448 """
3449 signed_data = bytearray()
3450 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003451 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003452 hasher = hashlib.sha256()
3453 hasher.update(subject)
3454 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003455 if not usage:
3456 usage = 'com.google.android.things.vboot'
3457 if is_intermediate_authority:
3458 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003459 hasher = hashlib.sha256()
3460 hasher.update(usage)
3461 signed_data.extend(hasher.digest())
3462 if not subject_key_version:
3463 subject_key_version = int(time.time())
3464 signed_data.extend(struct.pack('<Q', subject_key_version))
3465 signature = bytearray()
3466 if authority_key_path:
3467 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003468 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003469 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003470 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003471 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003472 hasher.update(signed_data)
3473 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003474 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3475 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003476 alg.signature_num_bytes, authority_key_path,
3477 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003478 output.write(signed_data)
3479 output.write(signature)
3480
David Zeuthenc68f0822017-03-31 17:22:35 -04003481 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003482 product_id):
3483 """Implements the 'make_atx_permanent_attributes' command.
3484
3485 Android Things permanent attributes are designed to be permanent for a
3486 particular product and a hash of these attributes should be fused into
3487 hardware to enforce this.
3488
3489 Arguments:
3490 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003491 root_authority_key_path: Path to a PEM or DER public key for
3492 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003493 product_id: A 16-byte Product ID.
3494
3495 Raises:
3496 AvbError: If an argument is incorrect.
3497 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003498 EXPECTED_PRODUCT_ID_SIZE = 16
3499 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003500 raise AvbError('Invalid Product ID length.')
3501 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003502 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003503 output.write(product_id)
3504
3505 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003506 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003507 """Implements the 'make_atx_metadata' command.
3508
3509 Android Things metadata are included in vbmeta images to facilitate
3510 verification. The output of this command can be used as the
3511 public_key_metadata argument to other commands.
3512
3513 Arguments:
3514 output: Metadata will be written to this file on success.
3515 intermediate_key_certificate: A certificate file as output by
3516 make_atx_certificate with
3517 is_intermediate_authority set to true.
3518 product_key_certificate: A certificate file as output by
3519 make_atx_certificate with
3520 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003521
3522 Raises:
3523 AvbError: If an argument is incorrect.
3524 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003525 EXPECTED_CERTIFICATE_SIZE = 1620
3526 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003527 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003528 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003529 raise AvbError('Invalid product key certificate length.')
3530 output.write(struct.pack('<I', 1)) # Format Version
3531 output.write(intermediate_key_certificate)
3532 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003533
Darren Krahnfccd64e2018-01-16 17:39:35 -08003534 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3535 unlock_key_certificate, challenge_path,
3536 unlock_key_path, signing_helper,
3537 signing_helper_with_files):
3538 """Implements the 'make_atx_unlock_credential' command.
3539
3540 Android Things unlock credentials can be used to authorize the unlock of AVB
3541 on a device. These credentials are presented to an Android Things bootloader
3542 via the fastboot interface in response to a 16-byte challenge. This method
3543 creates all fields of the credential except the challenge signature field
3544 (which is the last field) and can optionally create the challenge signature
3545 field as well if a challenge and the unlock_key_path is provided.
3546
3547 Arguments:
3548 output: The credential will be written to this file on success.
3549 intermediate_key_certificate: A certificate file as output by
3550 make_atx_certificate with
3551 is_intermediate_authority set to true.
3552 unlock_key_certificate: A certificate file as output by
3553 make_atx_certificate with
3554 is_intermediate_authority set to false and the
3555 usage set to
3556 'com.google.android.things.vboot.unlock'.
3557 challenge_path: [optional] A path to the challenge to sign.
3558 unlock_key_path: [optional] A PEM file path with the unlock private key.
3559 signing_helper: Program which signs a hash and returns the signature.
3560 signing_helper_with_files: Same as signing_helper but uses files instead.
3561
3562 Raises:
3563 AvbError: If an argument is incorrect.
3564 """
3565 EXPECTED_CERTIFICATE_SIZE = 1620
3566 EXPECTED_CHALLENGE_SIZE = 16
3567 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3568 raise AvbError('Invalid intermediate key certificate length.')
3569 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3570 raise AvbError('Invalid product key certificate length.')
3571 challenge = bytearray()
3572 if challenge_path:
3573 with open(challenge_path, 'r') as f:
3574 challenge = f.read()
3575 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3576 raise AvbError('Invalid unlock challenge length.')
3577 output.write(struct.pack('<I', 1)) # Format Version
3578 output.write(intermediate_key_certificate)
3579 output.write(unlock_key_certificate)
3580 if challenge_path and unlock_key_path:
3581 signature = bytearray()
3582 padding_and_hash = bytearray()
3583 algorithm_name = 'SHA512_RSA4096'
3584 alg = ALGORITHMS[algorithm_name]
3585 hasher = hashlib.sha512()
3586 padding_and_hash.extend(alg.padding)
3587 hasher.update(challenge)
3588 padding_and_hash.extend(hasher.digest())
3589 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3590 algorithm_name,
3591 alg.signature_num_bytes, unlock_key_path,
3592 padding_and_hash))
3593 output.write(signature)
3594
David Zeuthen21e95262016-07-27 17:58:40 -04003595
3596def calc_hash_level_offsets(image_size, block_size, digest_size):
3597 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3598
3599 Arguments:
3600 image_size: The size of the image to calculate a Merkle-tree for.
3601 block_size: The block size, e.g. 4096.
3602 digest_size: The size of each hash, e.g. 32 for SHA-256.
3603
3604 Returns:
3605 A tuple where the first argument is an array of offsets and the
3606 second is size of the tree, in bytes.
3607 """
3608 level_offsets = []
3609 level_sizes = []
3610 tree_size = 0
3611
3612 num_levels = 0
3613 size = image_size
3614 while size > block_size:
3615 num_blocks = (size + block_size - 1) / block_size
3616 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3617
3618 level_sizes.append(level_size)
3619 tree_size += level_size
3620 num_levels += 1
3621
3622 size = level_size
3623
3624 for n in range(0, num_levels):
3625 offset = 0
3626 for m in range(n + 1, num_levels):
3627 offset += level_sizes[m]
3628 level_offsets.append(offset)
3629
David Zeuthena4fee8b2016-08-22 15:20:43 -04003630 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003631
3632
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003633# See system/extras/libfec/include/fec/io.h for these definitions.
3634FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3635FEC_MAGIC = 0xfecfecfe
3636
3637
3638def calc_fec_data_size(image_size, num_roots):
3639 """Calculates how much space FEC data will take.
3640
3641 Args:
3642 image_size: The size of the image.
3643 num_roots: Number of roots.
3644
3645 Returns:
3646 The number of bytes needed for FEC for an image of the given size
3647 and with the requested number of FEC roots.
3648
3649 Raises:
3650 ValueError: If output from the 'fec' tool is invalid.
3651
3652 """
3653 p = subprocess.Popen(
3654 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3655 stdout=subprocess.PIPE,
3656 stderr=subprocess.PIPE)
3657 (pout, perr) = p.communicate()
3658 retcode = p.wait()
3659 if retcode != 0:
3660 raise ValueError('Error invoking fec: {}'.format(perr))
3661 return int(pout)
3662
3663
3664def generate_fec_data(image_filename, num_roots):
3665 """Generate FEC codes for an image.
3666
3667 Args:
3668 image_filename: The filename of the image.
3669 num_roots: Number of roots.
3670
3671 Returns:
3672 The FEC data blob.
3673
3674 Raises:
3675 ValueError: If output from the 'fec' tool is invalid.
3676 """
3677 fec_tmpfile = tempfile.NamedTemporaryFile()
3678 subprocess.check_call(
3679 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3680 fec_tmpfile.name],
3681 stderr=open(os.devnull))
3682 fec_data = fec_tmpfile.read()
3683 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3684 footer_data = fec_data[-footer_size:]
3685 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3686 footer_data)
3687 if magic != FEC_MAGIC:
3688 raise ValueError('Unexpected magic in FEC footer')
3689 return fec_data[0:fec_size]
3690
3691
David Zeuthen21e95262016-07-27 17:58:40 -04003692def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003693 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003694 """Generates a Merkle-tree for a file.
3695
3696 Args:
3697 image: The image, as a file.
3698 image_size: The size of the image.
3699 block_size: The block size, e.g. 4096.
3700 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3701 salt: The salt to use.
3702 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003703 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003704 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003705
3706 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003707 A tuple where the first element is the top-level hash and the
3708 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003709 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003710 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003711 hash_src_offset = 0
3712 hash_src_size = image_size
3713 level_num = 0
3714 while hash_src_size > block_size:
3715 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003716 remaining = hash_src_size
3717 while remaining > 0:
3718 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003719 # Only read from the file for the first level - for subsequent
3720 # levels, access the array we're building.
3721 if level_num == 0:
3722 image.seek(hash_src_offset + hash_src_size - remaining)
3723 data = image.read(min(remaining, block_size))
3724 else:
3725 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3726 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003727 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003728
3729 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003730 if len(data) < block_size:
3731 hasher.update('\0' * (block_size - len(data)))
3732 level_output += hasher.digest()
3733 if digest_padding > 0:
3734 level_output += '\0' * digest_padding
3735
3736 padding_needed = (round_to_multiple(
3737 len(level_output), block_size) - len(level_output))
3738 level_output += '\0' * padding_needed
3739
David Zeuthena4fee8b2016-08-22 15:20:43 -04003740 # Copy level-output into resulting tree.
3741 offset = hash_level_offsets[level_num]
3742 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003743
David Zeuthena4fee8b2016-08-22 15:20:43 -04003744 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003745 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003746 level_num += 1
3747
3748 hasher = hashlib.new(name=hash_alg_name, string=salt)
3749 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003750 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003751
3752
3753class AvbTool(object):
3754 """Object for avbtool command-line tool."""
3755
3756 def __init__(self):
3757 """Initializer method."""
3758 self.avb = Avb()
3759
3760 def _add_common_args(self, sub_parser):
3761 """Adds arguments used by several sub-commands.
3762
3763 Arguments:
3764 sub_parser: The parser to add arguments to.
3765 """
3766 sub_parser.add_argument('--algorithm',
3767 help='Algorithm to use (default: NONE)',
3768 metavar='ALGORITHM',
3769 default='NONE')
3770 sub_parser.add_argument('--key',
3771 help='Path to RSA private key file',
3772 metavar='KEY',
3773 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003774 sub_parser.add_argument('--signing_helper',
3775 help='Path to helper used for signing',
3776 metavar='APP',
3777 default=None,
3778 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003779 sub_parser.add_argument('--signing_helper_with_files',
3780 help='Path to helper used for signing using files',
3781 metavar='APP',
3782 default=None,
3783 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003784 sub_parser.add_argument('--public_key_metadata',
3785 help='Path to public key metadata file',
3786 metavar='KEY_METADATA',
3787 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003788 sub_parser.add_argument('--rollback_index',
3789 help='Rollback Index',
3790 type=parse_number,
3791 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003792 # This is used internally for unit tests. Do not include in --help output.
3793 sub_parser.add_argument('--internal_release_string',
3794 help=argparse.SUPPRESS)
3795 sub_parser.add_argument('--append_to_release_string',
3796 help='Text to append to release string',
3797 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003798 sub_parser.add_argument('--prop',
3799 help='Add property',
3800 metavar='KEY:VALUE',
3801 action='append')
3802 sub_parser.add_argument('--prop_from_file',
3803 help='Add property from file',
3804 metavar='KEY:PATH',
3805 action='append')
3806 sub_parser.add_argument('--kernel_cmdline',
3807 help='Add kernel cmdline',
3808 metavar='CMDLINE',
3809 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003810 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3811 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3812 # at some future point.
3813 sub_parser.add_argument('--setup_rootfs_from_kernel',
3814 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003815 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003816 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003817 type=argparse.FileType('rb'))
3818 sub_parser.add_argument('--include_descriptors_from_image',
3819 help='Include descriptors from image',
3820 metavar='IMAGE',
3821 action='append',
3822 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003823 sub_parser.add_argument('--print_required_libavb_version',
3824 help=('Don\'t store the footer - '
3825 'instead calculate the required libavb '
3826 'version for the given options.'),
3827 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003828 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3829 sub_parser.add_argument('--chain_partition',
3830 help='Allow signed integrity-data for partition',
3831 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3832 action='append')
3833 sub_parser.add_argument('--flags',
3834 help='VBMeta flags',
3835 type=parse_number,
3836 default=0)
3837 sub_parser.add_argument('--set_hashtree_disabled_flag',
3838 help='Set the HASHTREE_DISABLED flag',
3839 action='store_true')
3840
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003841 def _add_common_footer_args(self, sub_parser):
3842 """Adds arguments used by add_*_footer sub-commands.
3843
3844 Arguments:
3845 sub_parser: The parser to add arguments to.
3846 """
3847 sub_parser.add_argument('--use_persistent_digest',
3848 help='Use a persistent digest on device instead of '
3849 'storing the digest in the descriptor. This '
3850 'cannot be used with A/B so must be combined '
3851 'with --do_not_use_ab when an A/B suffix is '
3852 'expected at runtime.',
3853 action='store_true')
3854 sub_parser.add_argument('--do_not_use_ab',
3855 help='The partition does not use A/B even when an '
3856 'A/B suffix is present. This must not be used '
3857 'for vbmeta or chained partitions.',
3858 action='store_true')
3859
David Zeuthena5fd3a42017-02-27 16:38:54 -05003860 def _fixup_common_args(self, args):
3861 """Common fixups needed by subcommands.
3862
3863 Arguments:
3864 args: Arguments to modify.
3865
3866 Returns:
3867 The modified arguments.
3868 """
3869 if args.set_hashtree_disabled_flag:
3870 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3871 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003872
3873 def run(self, argv):
3874 """Command-line processor.
3875
3876 Arguments:
3877 argv: Pass sys.argv from main.
3878 """
3879 parser = argparse.ArgumentParser()
3880 subparsers = parser.add_subparsers(title='subcommands')
3881
3882 sub_parser = subparsers.add_parser('version',
3883 help='Prints version of avbtool.')
3884 sub_parser.set_defaults(func=self.version)
3885
3886 sub_parser = subparsers.add_parser('extract_public_key',
3887 help='Extract public key.')
3888 sub_parser.add_argument('--key',
3889 help='Path to RSA private key file',
3890 required=True)
3891 sub_parser.add_argument('--output',
3892 help='Output file name',
3893 type=argparse.FileType('wb'),
3894 required=True)
3895 sub_parser.set_defaults(func=self.extract_public_key)
3896
3897 sub_parser = subparsers.add_parser('make_vbmeta_image',
3898 help='Makes a vbmeta image.')
3899 sub_parser.add_argument('--output',
3900 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003901 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003902 sub_parser.add_argument('--padding_size',
3903 metavar='NUMBER',
3904 help='If non-zero, pads output with NUL bytes so '
3905 'its size is a multiple of NUMBER (default: 0)',
3906 type=parse_number,
3907 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003908 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003909 sub_parser.set_defaults(func=self.make_vbmeta_image)
3910
3911 sub_parser = subparsers.add_parser('add_hash_footer',
3912 help='Add hashes and footer to image.')
3913 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003914 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003915 type=argparse.FileType('rab+'))
3916 sub_parser.add_argument('--partition_size',
3917 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003918 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003919 sub_parser.add_argument('--partition_name',
3920 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003921 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003922 sub_parser.add_argument('--hash_algorithm',
3923 help='Hash algorithm to use (default: sha256)',
3924 default='sha256')
3925 sub_parser.add_argument('--salt',
3926 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003927 sub_parser.add_argument('--calc_max_image_size',
3928 help=('Don\'t store the footer - '
3929 'instead calculate the maximum image size '
3930 'leaving enough room for metadata with '
3931 'the given partition size.'),
3932 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003933 sub_parser.add_argument('--output_vbmeta_image',
3934 help='Also write vbmeta struct to file',
3935 type=argparse.FileType('wb'))
3936 sub_parser.add_argument('--do_not_append_vbmeta_image',
3937 help=('Do not append vbmeta struct or footer '
3938 'to the image'),
3939 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003940 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003941 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003942 sub_parser.set_defaults(func=self.add_hash_footer)
3943
David Zeuthenb1b994d2017-03-06 18:01:31 -05003944 sub_parser = subparsers.add_parser('append_vbmeta_image',
3945 help='Append vbmeta image to image.')
3946 sub_parser.add_argument('--image',
3947 help='Image to append vbmeta blob to',
3948 type=argparse.FileType('rab+'))
3949 sub_parser.add_argument('--partition_size',
3950 help='Partition size',
3951 type=parse_number,
3952 required=True)
3953 sub_parser.add_argument('--vbmeta_image',
3954 help='Image with vbmeta blob to append',
3955 type=argparse.FileType('rb'))
3956 sub_parser.set_defaults(func=self.append_vbmeta_image)
3957
David Zeuthen21e95262016-07-27 17:58:40 -04003958 sub_parser = subparsers.add_parser('add_hashtree_footer',
3959 help='Add hashtree and footer to image.')
3960 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003961 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003962 type=argparse.FileType('rab+'))
3963 sub_parser.add_argument('--partition_size',
3964 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003965 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04003966 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003967 sub_parser.add_argument('--partition_name',
3968 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003969 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04003970 sub_parser.add_argument('--hash_algorithm',
3971 help='Hash algorithm to use (default: sha1)',
3972 default='sha1')
3973 sub_parser.add_argument('--salt',
3974 help='Salt in hex (default: /dev/urandom)')
3975 sub_parser.add_argument('--block_size',
3976 help='Block size (default: 4096)',
3977 type=parse_number,
3978 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003979 # TODO(zeuthen): The --generate_fec option was removed when we
3980 # moved to generating FEC by default. To avoid breaking existing
3981 # users needing to transition we simply just print a warning below
3982 # in add_hashtree_footer(). Remove this option and the warning at
3983 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003984 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003985 help=argparse.SUPPRESS,
3986 action='store_true')
3987 sub_parser.add_argument('--do_not_generate_fec',
3988 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003989 action='store_true')
3990 sub_parser.add_argument('--fec_num_roots',
3991 help='Number of roots for FEC (default: 2)',
3992 type=parse_number,
3993 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003994 sub_parser.add_argument('--calc_max_image_size',
3995 help=('Don\'t store the hashtree or footer - '
3996 'instead calculate the maximum image size '
3997 'leaving enough room for hashtree '
3998 'and metadata with the given partition '
3999 'size.'),
4000 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004001 sub_parser.add_argument('--output_vbmeta_image',
4002 help='Also write vbmeta struct to file',
4003 type=argparse.FileType('wb'))
4004 sub_parser.add_argument('--do_not_append_vbmeta_image',
4005 help=('Do not append vbmeta struct or footer '
4006 'to the image'),
4007 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004008 # This is different from --setup_rootfs_from_kernel insofar that
4009 # it doesn't take an IMAGE, the generated cmdline will be for the
4010 # hashtree we're adding.
4011 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4012 action='store_true',
4013 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04004014 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004015 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004016 sub_parser.set_defaults(func=self.add_hashtree_footer)
4017
4018 sub_parser = subparsers.add_parser('erase_footer',
4019 help='Erase footer from an image.')
4020 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004021 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004022 type=argparse.FileType('rwb+'),
4023 required=True)
4024 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004025 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004026 action='store_true')
4027 sub_parser.set_defaults(func=self.erase_footer)
4028
David Zeuthen49936b42018-08-07 17:38:58 -04004029 sub_parser = subparsers.add_parser('extract_vbmeta_image',
4030 help='Extracts vbmeta from an image with a footer.')
4031 sub_parser.add_argument('--image',
4032 help='Image with footer',
4033 type=argparse.FileType('rb'),
4034 required=True)
4035 sub_parser.add_argument('--output',
4036 help='Output file name',
4037 type=argparse.FileType('wb'))
4038 sub_parser.add_argument('--padding_size',
4039 metavar='NUMBER',
4040 help='If non-zero, pads output with NUL bytes so '
4041 'its size is a multiple of NUMBER (default: 0)',
4042 type=parse_number,
4043 default=0)
4044 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4045
David Zeuthen2bc232b2017-04-19 14:25:19 -04004046 sub_parser = subparsers.add_parser('resize_image',
4047 help='Resize image with a footer.')
4048 sub_parser.add_argument('--image',
4049 help='Image with a footer',
4050 type=argparse.FileType('rwb+'),
4051 required=True)
4052 sub_parser.add_argument('--partition_size',
4053 help='New partition size',
4054 type=parse_number)
4055 sub_parser.set_defaults(func=self.resize_image)
4056
David Zeuthen21e95262016-07-27 17:58:40 -04004057 sub_parser = subparsers.add_parser(
4058 'info_image',
4059 help='Show information about vbmeta or footer.')
4060 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004061 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004062 type=argparse.FileType('rb'),
4063 required=True)
4064 sub_parser.add_argument('--output',
4065 help='Write info to file',
4066 type=argparse.FileType('wt'),
4067 default=sys.stdout)
4068 sub_parser.set_defaults(func=self.info_image)
4069
David Zeuthenb623d8b2017-04-04 16:05:53 -04004070 sub_parser = subparsers.add_parser(
4071 'verify_image',
4072 help='Verify an image.')
4073 sub_parser.add_argument('--image',
4074 help='Image to verify',
4075 type=argparse.FileType('rb'),
4076 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004077 sub_parser.add_argument('--key',
4078 help='Check embedded public key matches KEY',
4079 metavar='KEY',
4080 required=False)
4081 sub_parser.add_argument('--expected_chain_partition',
4082 help='Expected chain partition',
4083 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4084 action='append')
David Zeuthene947cb62019-01-25 15:27:08 -05004085 sub_parser.add_argument('--follow_chain_partitions',
4086 help=('Follows chain partitions even when not '
4087 'specified with the --expected_chain_partition option'),
4088 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004089 sub_parser.set_defaults(func=self.verify_image)
4090
David Zeuthenb8643c02018-05-17 17:21:18 -04004091 sub_parser = subparsers.add_parser(
4092 'calculate_vbmeta_digest',
4093 help='Calculate vbmeta digest.')
4094 sub_parser.add_argument('--image',
4095 help='Image to calculate digest for',
4096 type=argparse.FileType('rb'),
4097 required=True)
4098 sub_parser.add_argument('--hash_algorithm',
4099 help='Hash algorithm to use (default: sha256)',
4100 default='sha256')
4101 sub_parser.add_argument('--output',
4102 help='Write hex digest to file (default: stdout)',
4103 type=argparse.FileType('wt'),
4104 default=sys.stdout)
4105 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4106
David Zeuthenf7d2e752018-09-20 13:30:41 -04004107 sub_parser = subparsers.add_parser(
4108 'calculate_kernel_cmdline',
4109 help='Calculate kernel cmdline.')
4110 sub_parser.add_argument('--image',
4111 help='Image to calculate kernel cmdline for',
4112 type=argparse.FileType('rb'),
4113 required=True)
4114 sub_parser.add_argument('--hashtree_disabled',
4115 help='Return the cmdline for hashtree disabled',
4116 action='store_true')
4117 sub_parser.add_argument('--output',
4118 help='Write cmdline to file (default: stdout)',
4119 type=argparse.FileType('wt'),
4120 default=sys.stdout)
4121 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4122
David Zeuthen8b6973b2016-09-20 12:39:49 -04004123 sub_parser = subparsers.add_parser('set_ab_metadata',
4124 help='Set A/B metadata.')
4125 sub_parser.add_argument('--misc_image',
4126 help=('The misc image to modify. If the image does '
4127 'not exist, it will be created.'),
4128 type=argparse.FileType('r+b'),
4129 required=True)
4130 sub_parser.add_argument('--slot_data',
4131 help=('Slot data of the form "priority", '
4132 '"tries_remaining", "sucessful_boot" for '
4133 'slot A followed by the same for slot B, '
4134 'separated by colons. The default value '
4135 'is 15:7:0:14:7:0.'),
4136 default='15:7:0:14:7:0')
4137 sub_parser.set_defaults(func=self.set_ab_metadata)
4138
Darren Krahn147b08d2016-12-20 16:38:29 -08004139 sub_parser = subparsers.add_parser(
4140 'make_atx_certificate',
4141 help='Create an Android Things eXtension (ATX) certificate.')
4142 sub_parser.add_argument('--output',
4143 help='Write certificate to file',
4144 type=argparse.FileType('wb'),
4145 default=sys.stdout)
4146 sub_parser.add_argument('--subject',
4147 help=('Path to subject file'),
4148 type=argparse.FileType('rb'),
4149 required=True)
4150 sub_parser.add_argument('--subject_key',
4151 help=('Path to subject RSA public key file'),
4152 type=argparse.FileType('rb'),
4153 required=True)
4154 sub_parser.add_argument('--subject_key_version',
4155 help=('Version of the subject key'),
4156 type=parse_number,
4157 required=False)
4158 sub_parser.add_argument('--subject_is_intermediate_authority',
4159 help=('Generate an intermediate authority '
4160 'certificate'),
4161 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004162 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004163 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004164 'string'),
4165 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004166 sub_parser.add_argument('--authority_key',
4167 help='Path to authority RSA private key file',
4168 required=False)
4169 sub_parser.add_argument('--signing_helper',
4170 help='Path to helper used for signing',
4171 metavar='APP',
4172 default=None,
4173 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004174 sub_parser.add_argument('--signing_helper_with_files',
4175 help='Path to helper used for signing using files',
4176 metavar='APP',
4177 default=None,
4178 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004179 sub_parser.set_defaults(func=self.make_atx_certificate)
4180
4181 sub_parser = subparsers.add_parser(
4182 'make_atx_permanent_attributes',
4183 help='Create Android Things eXtension (ATX) permanent attributes.')
4184 sub_parser.add_argument('--output',
4185 help='Write attributes to file',
4186 type=argparse.FileType('wb'),
4187 default=sys.stdout)
4188 sub_parser.add_argument('--root_authority_key',
4189 help='Path to authority RSA public key file',
4190 type=argparse.FileType('rb'),
4191 required=True)
4192 sub_parser.add_argument('--product_id',
4193 help=('Path to Product ID file'),
4194 type=argparse.FileType('rb'),
4195 required=True)
4196 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4197
4198 sub_parser = subparsers.add_parser(
4199 'make_atx_metadata',
4200 help='Create Android Things eXtension (ATX) metadata.')
4201 sub_parser.add_argument('--output',
4202 help='Write metadata to file',
4203 type=argparse.FileType('wb'),
4204 default=sys.stdout)
4205 sub_parser.add_argument('--intermediate_key_certificate',
4206 help='Path to intermediate key certificate file',
4207 type=argparse.FileType('rb'),
4208 required=True)
4209 sub_parser.add_argument('--product_key_certificate',
4210 help='Path to product key certificate file',
4211 type=argparse.FileType('rb'),
4212 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004213 sub_parser.set_defaults(func=self.make_atx_metadata)
4214
Darren Krahnfccd64e2018-01-16 17:39:35 -08004215 sub_parser = subparsers.add_parser(
4216 'make_atx_unlock_credential',
4217 help='Create an Android Things eXtension (ATX) unlock credential.')
4218 sub_parser.add_argument('--output',
4219 help='Write credential to file',
4220 type=argparse.FileType('wb'),
4221 default=sys.stdout)
4222 sub_parser.add_argument('--intermediate_key_certificate',
4223 help='Path to intermediate key certificate file',
4224 type=argparse.FileType('rb'),
4225 required=True)
4226 sub_parser.add_argument('--unlock_key_certificate',
4227 help='Path to unlock key certificate file',
4228 type=argparse.FileType('rb'),
4229 required=True)
4230 sub_parser.add_argument('--challenge',
4231 help='Path to the challenge to sign (optional). If '
4232 'this is not provided the challenge signature '
4233 'field is omitted and can be concatenated '
4234 'later.',
4235 required=False)
4236 sub_parser.add_argument('--unlock_key',
4237 help='Path to unlock key (optional). Must be '
4238 'provided if using --challenge.',
4239 required=False)
4240 sub_parser.add_argument('--signing_helper',
4241 help='Path to helper used for signing',
4242 metavar='APP',
4243 default=None,
4244 required=False)
4245 sub_parser.add_argument('--signing_helper_with_files',
4246 help='Path to helper used for signing using files',
4247 metavar='APP',
4248 default=None,
4249 required=False)
4250 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4251
David Zeuthen21e95262016-07-27 17:58:40 -04004252 args = parser.parse_args(argv[1:])
4253 try:
4254 args.func(args)
4255 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004256 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004257 sys.exit(1)
4258
4259 def version(self, _):
4260 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004261 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004262
4263 def extract_public_key(self, args):
4264 """Implements the 'extract_public_key' sub-command."""
4265 self.avb.extract_public_key(args.key, args.output)
4266
4267 def make_vbmeta_image(self, args):
4268 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004269 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004270 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004271 args.algorithm, args.key,
4272 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004273 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004274 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004275 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004276 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004277 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004278 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004279 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004280 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004281 args.print_required_libavb_version,
4282 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004283
David Zeuthenb1b994d2017-03-06 18:01:31 -05004284 def append_vbmeta_image(self, args):
4285 """Implements the 'append_vbmeta_image' sub-command."""
4286 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4287 args.partition_size)
4288
David Zeuthen21e95262016-07-27 17:58:40 -04004289 def add_hash_footer(self, args):
4290 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004291 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004292 self.avb.add_hash_footer(args.image.name if args.image else None,
4293 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004294 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004295 args.salt, args.chain_partition, args.algorithm,
4296 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004297 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004298 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004299 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004300 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004301 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004302 args.calc_max_image_size,
4303 args.signing_helper,
4304 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004305 args.internal_release_string,
4306 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004307 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004308 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004309 args.print_required_libavb_version,
4310 args.use_persistent_digest,
4311 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004312
4313 def add_hashtree_footer(self, args):
4314 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004315 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004316 # TODO(zeuthen): Remove when removing support for the
4317 # '--generate_fec' option above.
4318 if args.generate_fec:
4319 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4320 'is now generated by default. Use the option '
4321 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004322 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4323 args.partition_size,
4324 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004325 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004326 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004327 args.salt, args.chain_partition, args.algorithm,
4328 args.key, args.public_key_metadata,
4329 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004330 args.prop_from_file,
4331 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004332 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004333 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004334 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004335 args.calc_max_image_size,
4336 args.signing_helper,
4337 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004338 args.internal_release_string,
4339 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004340 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004341 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004342 args.print_required_libavb_version,
4343 args.use_persistent_digest,
4344 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05004345
David Zeuthen21e95262016-07-27 17:58:40 -04004346 def erase_footer(self, args):
4347 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004348 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004349
David Zeuthen49936b42018-08-07 17:38:58 -04004350 def extract_vbmeta_image(self, args):
4351 """Implements the 'extract_vbmeta_image' sub-command."""
4352 self.avb.extract_vbmeta_image(args.output, args.image.name,
4353 args.padding_size)
4354
David Zeuthen2bc232b2017-04-19 14:25:19 -04004355 def resize_image(self, args):
4356 """Implements the 'resize_image' sub-command."""
4357 self.avb.resize_image(args.image.name, args.partition_size)
4358
David Zeuthen8b6973b2016-09-20 12:39:49 -04004359 def set_ab_metadata(self, args):
4360 """Implements the 'set_ab_metadata' sub-command."""
4361 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4362
David Zeuthen21e95262016-07-27 17:58:40 -04004363 def info_image(self, args):
4364 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004365 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004366
David Zeuthenb623d8b2017-04-04 16:05:53 -04004367 def verify_image(self, args):
4368 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004369 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004370 args.expected_chain_partition,
4371 args.follow_chain_partitions)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004372
David Zeuthenb8643c02018-05-17 17:21:18 -04004373 def calculate_vbmeta_digest(self, args):
4374 """Implements the 'calculate_vbmeta_digest' sub-command."""
4375 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4376 args.output)
4377
David Zeuthenf7d2e752018-09-20 13:30:41 -04004378 def calculate_kernel_cmdline(self, args):
4379 """Implements the 'calculate_kernel_cmdline' sub-command."""
4380 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, args.output)
4381
Darren Krahn147b08d2016-12-20 16:38:29 -08004382 def make_atx_certificate(self, args):
4383 """Implements the 'make_atx_certificate' sub-command."""
4384 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004385 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004386 args.subject_key_version,
4387 args.subject.read(),
4388 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004389 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004390 args.signing_helper,
4391 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004392
4393 def make_atx_permanent_attributes(self, args):
4394 """Implements the 'make_atx_permanent_attributes' sub-command."""
4395 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004396 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004397 args.product_id.read())
4398
4399 def make_atx_metadata(self, args):
4400 """Implements the 'make_atx_metadata' sub-command."""
4401 self.avb.make_atx_metadata(args.output,
4402 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004403 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004404
Darren Krahnfccd64e2018-01-16 17:39:35 -08004405 def make_atx_unlock_credential(self, args):
4406 """Implements the 'make_atx_unlock_credential' sub-command."""
4407 self.avb.make_atx_unlock_credential(
4408 args.output,
4409 args.intermediate_key_certificate.read(),
4410 args.unlock_key_certificate.read(),
4411 args.challenge,
4412 args.unlock_key,
4413 args.signing_helper,
4414 args.signing_helper_with_files)
4415
David Zeuthen21e95262016-07-27 17:58:40 -04004416
4417if __name__ == '__main__':
4418 tool = AvbTool()
4419 tool.run(sys.argv)