blob: f22f4396ab44854f01806ee50fd05a5487e12118 [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 '
1759 'contents.\n'.
1760 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 Zeuthen5dfb4e92017-05-24 14:49:32 -04002223 def verify_image(self, image_filename, key_path, expected_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 Zeuthenb623d8b2017-04-04 16:05:53 -04002230 """
2231
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002232 expected_chain_partitions_map = {}
2233 if expected_chain_partitions:
2234 used_locations = {}
2235 for cp in expected_chain_partitions:
2236 cp_tokens = cp.split(':')
2237 if len(cp_tokens) != 3:
2238 raise AvbError('Malformed chained partition "{}".'.format(cp))
2239 partition_name = cp_tokens[0]
2240 rollback_index_location = int(cp_tokens[1])
2241 file_path = cp_tokens[2]
2242 pk_blob = open(file_path).read()
2243 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2244
2245 image_dir = os.path.dirname(image_filename)
2246 image_ext = os.path.splitext(image_filename)[1]
2247
2248 key_blob = None
2249 if key_path:
2250 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2251 key_blob = encode_rsa_key(key_path)
2252 else:
2253 print 'Verifying image {} using embedded public key'.format(image_filename)
2254
David Zeuthenb623d8b2017-04-04 16:05:53 -04002255 image = ImageHandler(image_filename)
2256 (footer, header, descriptors, image_size) = self._parse_image(image)
2257 offset = 0
2258 if footer:
2259 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002260
David Zeuthenb623d8b2017-04-04 16:05:53 -04002261 image.seek(offset)
David Zeuthen49936b42018-08-07 17:38:58 -04002262 vbmeta_blob = image.read(header.SIZE + header.authentication_data_block_size +
2263 header.auxiliary_data_block_size)
2264
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002265 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002266 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002267 raise AvbError('Signature check failed for {} vbmeta struct {}'
2268 .format(alg_name, image_filename))
2269
2270 if key_blob:
2271 # The embedded public key is in the auxiliary block at an offset.
2272 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002273 key_offset += header.authentication_data_block_size
2274 key_offset += header.public_key_offset
2275 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002276 if key_blob != key_blob_in_vbmeta:
2277 raise AvbError('Embedded public key does not match given key.')
2278
2279 if footer:
2280 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002281 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002282 else:
2283 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002284 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002285
2286 for desc in descriptors:
David Zeuthenf4f51eb2018-09-20 14:56:46 -04002287 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002288 raise AvbError('Error verifying descriptor.')
David Zeuthen49936b42018-08-07 17:38:58 -04002289 # Note how AvbDescriptor.verify() method verifies only the descriptor
2290 # contents which in the case of chain descriptors means checking only its
2291 # contents matches what is in |expected_chain_partitions_map|.
2292 #
2293 # Specifically AvbHashtreeDescriptor.verify(), doesn't follow chain
2294 # descriptors e.g. if it's a chain descriptor for 'system' it will not try
2295 # to verify system.img. Why? Because the whole idea of chain descriptors
2296 # is separate organizations. That is, when they are used it's assumed that
2297 # all you have is the public key, not an actual image (because if you had
2298 # the image you wouldn't need to use a chain partition in the first
2299 # place).
2300 #
2301 # However in certain situations you do have the image so it would be nice
2302 # to add something like a --follow_chain_descriptors option for 'avbtool
2303 # verify_image' which will look for and follow images specified by chain
2304 # descriptors. Maybe it should be on a per-partition basis and specificied
2305 # as part of the --expected_chain_partition paramter, maybe if the
2306 # partition name ends with a '+' or something. Something to think about.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002307
David Zeuthenb623d8b2017-04-04 16:05:53 -04002308
David Zeuthenb8643c02018-05-17 17:21:18 -04002309 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2310 """Implements the 'calculate_vbmeta_digest' command.
2311
2312 Arguments:
2313 image_filename: Image file to get information from (file object).
2314 hash_algorithm: Hash algorithm used.
2315 output: Output file to write human-readable information to (file object).
2316 """
2317
2318 image_dir = os.path.dirname(image_filename)
2319 image_ext = os.path.splitext(image_filename)[1]
2320
2321 image = ImageHandler(image_filename)
2322 (footer, header, descriptors, image_size) = self._parse_image(image)
2323 offset = 0
2324 if footer:
2325 offset = footer.vbmeta_offset
2326 size = (header.SIZE + header.authentication_data_block_size +
2327 header.auxiliary_data_block_size)
2328 image.seek(offset)
2329 vbmeta_blob = image.read(size)
2330
2331 hasher = hashlib.new(name=hash_algorithm)
2332 hasher.update(vbmeta_blob)
2333
2334 for desc in descriptors:
2335 if isinstance(desc, AvbChainPartitionDescriptor):
2336 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2337 ch_image = ImageHandler(ch_image_filename)
2338 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2339 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002340 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2341 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002342 if ch_footer:
2343 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002344 ch_image.seek(ch_offset)
2345 ch_vbmeta_blob = ch_image.read(ch_size)
2346 hasher.update(ch_vbmeta_blob)
2347
2348 digest = hasher.digest()
2349 output.write('{}\n'.format(digest.encode('hex')))
2350
2351
David Zeuthenf7d2e752018-09-20 13:30:41 -04002352 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2353 """Implements the 'calculate_kernel_cmdline' command.
2354
2355 Arguments:
2356 image_filename: Image file to get information from (file object).
2357 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2358 output: Output file to write human-readable information to (file object).
2359 """
2360
2361 image = ImageHandler(image_filename)
2362 _, _, descriptors, _ = self._parse_image(image)
2363
2364 image_dir = os.path.dirname(image_filename)
2365 image_ext = os.path.splitext(image_filename)[1]
2366
2367 cmdline_descriptors = []
2368 for desc in descriptors:
2369 if isinstance(desc, AvbChainPartitionDescriptor):
2370 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2371 ch_image = ImageHandler(ch_image_filename)
2372 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2373 for ch_desc in ch_descriptors:
2374 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2375 cmdline_descriptors.append(ch_desc)
2376 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2377 cmdline_descriptors.append(desc)
2378
2379 kernel_cmdline_snippets = []
2380 for desc in cmdline_descriptors:
2381 use_cmdline = True
2382 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) != 0:
2383 if hashtree_disabled:
2384 use_cmdline = False
2385 if (desc.flags & AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2386 if not hashtree_disabled:
2387 use_cmdline = False
2388 if use_cmdline:
2389 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2390 output.write(' '.join(kernel_cmdline_snippets))
2391
2392
David Zeuthen21e95262016-07-27 17:58:40 -04002393 def _parse_image(self, image):
2394 """Gets information about an image.
2395
2396 The image can either be a vbmeta or an image with a footer.
2397
2398 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002399 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002400
2401 Returns:
2402 A tuple where the first argument is a AvbFooter (None if there
2403 is no footer on the image), the second argument is a
2404 AvbVBMetaHeader, the third argument is a list of
2405 AvbDescriptor-derived instances, and the fourth argument is the
2406 size of |image|.
2407 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002408 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002409 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002410 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002411 try:
2412 footer = AvbFooter(image.read(AvbFooter.SIZE))
2413 except (LookupError, struct.error):
2414 # Nope, just seek back to the start.
2415 image.seek(0)
2416
2417 vbmeta_offset = 0
2418 if footer:
2419 vbmeta_offset = footer.vbmeta_offset
2420
2421 image.seek(vbmeta_offset)
2422 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2423
2424 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2425 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2426 desc_start_offset = aux_block_offset + h.descriptors_offset
2427 image.seek(desc_start_offset)
2428 descriptors = parse_descriptors(image.read(h.descriptors_size))
2429
David Zeuthen09692692016-09-30 16:16:40 -04002430 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002431
David Zeuthenb1b994d2017-03-06 18:01:31 -05002432 def _load_vbmeta_blob(self, image):
2433 """Gets the vbmeta struct and associated sections.
2434
2435 The image can either be a vbmeta.img or an image with a footer.
2436
2437 Arguments:
2438 image: An ImageHandler (vbmeta or footer).
2439
2440 Returns:
2441 A blob with the vbmeta struct and other sections.
2442 """
2443 assert isinstance(image, ImageHandler)
2444 footer = None
2445 image.seek(image.image_size - AvbFooter.SIZE)
2446 try:
2447 footer = AvbFooter(image.read(AvbFooter.SIZE))
2448 except (LookupError, struct.error):
2449 # Nope, just seek back to the start.
2450 image.seek(0)
2451
2452 vbmeta_offset = 0
2453 if footer:
2454 vbmeta_offset = footer.vbmeta_offset
2455
2456 image.seek(vbmeta_offset)
2457 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2458
2459 image.seek(vbmeta_offset)
2460 data_size = AvbVBMetaHeader.SIZE
2461 data_size += h.authentication_data_block_size
2462 data_size += h.auxiliary_data_block_size
2463 return image.read(data_size)
2464
David Zeuthen73f2afa2017-05-17 16:54:11 -04002465 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002466 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002467
2468 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002469 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002470
2471 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002472 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2473 instructions. There is one for when hashtree is not disabled and one for
2474 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002475
David Zeuthen21e95262016-07-27 17:58:40 -04002476 """
2477
David Zeuthen21e95262016-07-27 17:58:40 -04002478 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002479 c += '0' # start
2480 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2481 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2482 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2483 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2484 c += ' {}'.format(ht.data_block_size) # data_block
2485 c += ' {}'.format(ht.hash_block_size) # hash_block
2486 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2487 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2488 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2489 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2490 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2491 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002492 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002493 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002494 c += ' ignore_zero_blocks'
2495 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2496 c += ' fec_roots {}'.format(ht.fec_num_roots)
2497 # Note that fec_blocks is the size that FEC covers, *not* the
2498 # size of the FEC data. Since we use FEC for everything up until
2499 # the FEC data, it's the same as the offset.
2500 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2501 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2502 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002503 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002504 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002505 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002506 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002507
David Zeuthenfd41eb92016-11-17 12:24:47 -05002508 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002509 desc = AvbKernelCmdlineDescriptor()
2510 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002511 desc.flags = (
2512 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2513
2514 # The descriptor for when hashtree verification is disabled is a lot
2515 # simpler - we just set the root to the partition.
2516 desc_no_ht = AvbKernelCmdlineDescriptor()
2517 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2518 desc_no_ht.flags = (
2519 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2520
2521 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002522
David Zeuthen73f2afa2017-05-17 16:54:11 -04002523 def _get_cmdline_descriptors_for_dm_verity(self, image):
2524 """Generate kernel cmdline descriptors for dm-verity.
2525
2526 Arguments:
2527 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2528
2529 Returns:
2530 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2531 instructions. There is one for when hashtree is not disabled and one for
2532 when it is.
2533
2534 Raises:
2535 AvbError: If |image| doesn't have a hashtree descriptor.
2536
2537 """
2538
2539 (_, _, descriptors, _) = self._parse_image(image)
2540
2541 ht = None
2542 for desc in descriptors:
2543 if isinstance(desc, AvbHashtreeDescriptor):
2544 ht = desc
2545 break
2546
2547 if not ht:
2548 raise AvbError('No hashtree descriptor in given image')
2549
2550 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2551
David Zeuthen21e95262016-07-27 17:58:40 -04002552 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002553 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002554 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002555 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002556 include_descriptors_from_image,
2557 signing_helper,
2558 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002559 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002560 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002561 print_required_libavb_version,
2562 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002563 """Implements the 'make_vbmeta_image' command.
2564
2565 Arguments:
2566 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002567 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002568 algorithm_name: Name of algorithm to use.
2569 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002570 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002571 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002572 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002573 props: Properties to insert (list of strings of the form 'key:value').
2574 props_from_file: Properties to insert (list of strings 'key:<path>').
2575 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002576 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002577 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002578 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002579 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002580 release_string: None or avbtool release string to use instead of default.
2581 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002582 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002583 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002584
2585 Raises:
2586 AvbError: If a chained partition is malformed.
2587 """
2588
David Zeuthen1097a782017-05-31 15:53:17 -04002589 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002590 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002591 if include_descriptors_from_image:
2592 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2593 # version of all included descriptors.
2594 tmp_header = AvbVBMetaHeader()
2595 for image in include_descriptors_from_image:
2596 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2597 tmp_header.bump_required_libavb_version_minor(
2598 image_header.required_libavb_version_minor)
2599 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2600 else:
2601 # Descriptors aside, all vbmeta features are supported in 1.0.
2602 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002603 return
2604
2605 if not output:
2606 raise AvbError('No output file given')
2607
David Zeuthen21e95262016-07-27 17:58:40 -04002608 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002609 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002610 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002611 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002612 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002613 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002614 include_descriptors_from_image, signing_helper,
2615 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002616 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002617
2618 # Write entire vbmeta blob (header, authentication, auxiliary).
2619 output.seek(0)
2620 output.write(vbmeta_blob)
2621
David Zeuthen97cb5802017-06-01 16:14:05 -04002622 if padding_size > 0:
2623 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2624 padding_needed = padded_size - len(vbmeta_blob)
2625 output.write('\0' * padding_needed)
2626
David Zeuthen18666ab2016-11-15 11:18:05 -05002627 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2628 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002629 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002630 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002631 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002632 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002633 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002634 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002635 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002636 release_string, append_to_release_string,
2637 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002638 """Generates a VBMeta blob.
2639
2640 This blob contains the header (struct AvbVBMetaHeader), the
2641 authentication data block (which contains the hash and signature
2642 for the header and auxiliary block), and the auxiliary block
2643 (which contains descriptors, the public key used, and other data).
2644
2645 The |key| parameter can |None| only if the |algorithm_name| is
2646 'NONE'.
2647
2648 Arguments:
2649 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2650 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002651 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002652 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002653 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002654 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002655 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002656 props: Properties to insert (List of strings of the form 'key:value').
2657 props_from_file: Properties to insert (List of strings 'key:<path>').
2658 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002659 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002660 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002661 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2662 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002663 include_descriptors_from_image: List of file objects for which
2664 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002665 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002666 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002667 release_string: None or avbtool release string.
2668 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002669 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002670
2671 Returns:
2672 A bytearray() with the VBMeta blob.
2673
2674 Raises:
2675 Exception: If the |algorithm_name| is not found, if no key has
2676 been given and the given algorithm requires one, or the key is
2677 of the wrong size.
2678
2679 """
2680 try:
2681 alg = ALGORITHMS[algorithm_name]
2682 except KeyError:
2683 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2684
David Zeuthena5fd3a42017-02-27 16:38:54 -05002685 if not descriptors:
2686 descriptors = []
2687
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002688 h = AvbVBMetaHeader()
2689 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2690
David Zeuthena5fd3a42017-02-27 16:38:54 -05002691 # Insert chained partition descriptors, if any
2692 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002693 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002694 for cp in chain_partitions:
2695 cp_tokens = cp.split(':')
2696 if len(cp_tokens) != 3:
2697 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002698 partition_name = cp_tokens[0]
2699 rollback_index_location = int(cp_tokens[1])
2700 file_path = cp_tokens[2]
2701 # Check that the same rollback location isn't being used by
2702 # multiple chained partitions.
2703 if used_locations.get(rollback_index_location):
2704 raise AvbError('Rollback Index Location {} is already in use.'.format(
2705 rollback_index_location))
2706 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002707 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002708 desc.partition_name = partition_name
2709 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002710 if desc.rollback_index_location < 1:
2711 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002712 desc.public_key = open(file_path, 'rb').read()
2713 descriptors.append(desc)
2714
David Zeuthen21e95262016-07-27 17:58:40 -04002715 # Descriptors.
2716 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002717 for desc in descriptors:
2718 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002719
2720 # Add properties.
2721 if props:
2722 for prop in props:
2723 idx = prop.find(':')
2724 if idx == -1:
2725 raise AvbError('Malformed property "{}".'.format(prop))
2726 desc = AvbPropertyDescriptor()
2727 desc.key = prop[0:idx]
2728 desc.value = prop[(idx + 1):]
2729 encoded_descriptors.extend(desc.encode())
2730 if props_from_file:
2731 for prop in props_from_file:
2732 idx = prop.find(':')
2733 if idx == -1:
2734 raise AvbError('Malformed property "{}".'.format(prop))
2735 desc = AvbPropertyDescriptor()
2736 desc.key = prop[0:idx]
2737 desc.value = prop[(idx + 1):]
2738 file_path = prop[(idx + 1):]
2739 desc.value = open(file_path, 'rb').read()
2740 encoded_descriptors.extend(desc.encode())
2741
David Zeuthen73f2afa2017-05-17 16:54:11 -04002742 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002743 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002744 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002745 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002746 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2747 encoded_descriptors.extend(cmdline_desc[0].encode())
2748 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002749
David Zeuthen73f2afa2017-05-17 16:54:11 -04002750 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2751 if ht_desc_to_setup:
2752 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2753 ht_desc_to_setup)
2754 encoded_descriptors.extend(cmdline_desc[0].encode())
2755 encoded_descriptors.extend(cmdline_desc[1].encode())
2756
David Zeuthen21e95262016-07-27 17:58:40 -04002757 # Add kernel command-lines.
2758 if kernel_cmdlines:
2759 for i in kernel_cmdlines:
2760 desc = AvbKernelCmdlineDescriptor()
2761 desc.kernel_cmdline = i
2762 encoded_descriptors.extend(desc.encode())
2763
2764 # Add descriptors from other images.
2765 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002766 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002767 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002768 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002769 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2770 image_handler)
2771 # Bump the required libavb version to support all included descriptors.
2772 h.bump_required_libavb_version_minor(
2773 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002774 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002775 # The --include_descriptors_from_image option is used in some setups
2776 # with images A and B where both A and B contain a descriptor
2777 # for a partition with the same name. Since it's not meaningful
2778 # to include both descriptors, only include the last seen descriptor.
2779 # See bug 76386656 for details.
2780 if hasattr(desc, 'partition_name'):
2781 key = type(desc).__name__ + '_' + desc.partition_name
2782 descriptors_dict[key] = desc.encode()
2783 else:
2784 encoded_descriptors.extend(desc.encode())
2785 for key in sorted(descriptors_dict.keys()):
2786 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002787
David Zeuthen18666ab2016-11-15 11:18:05 -05002788 # Load public key metadata blob, if requested.
2789 pkmd_blob = []
2790 if public_key_metadata_path:
2791 with open(public_key_metadata_path) as f:
2792 pkmd_blob = f.read()
2793
David Zeuthen21e95262016-07-27 17:58:40 -04002794 key = None
2795 encoded_key = bytearray()
2796 if alg.public_key_num_bytes > 0:
2797 if not key_path:
2798 raise AvbError('Key is required for algorithm {}'.format(
2799 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002800 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002801 if len(encoded_key) != alg.public_key_num_bytes:
2802 raise AvbError('Key is wrong size for algorithm {}'.format(
2803 algorithm_name))
2804
David Zeuthene3cadca2017-02-22 21:25:46 -05002805 # Override release string, if requested.
2806 if isinstance(release_string, (str, unicode)):
2807 h.release_string = release_string
2808
2809 # Append to release string, if requested. Also insert a space before.
2810 if isinstance(append_to_release_string, (str, unicode)):
2811 h.release_string += ' ' + append_to_release_string
2812
David Zeuthen18666ab2016-11-15 11:18:05 -05002813 # For the Auxiliary data block, descriptors are stored at offset 0,
2814 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002815 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002816 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002817 h.descriptors_offset = 0
2818 h.descriptors_size = len(encoded_descriptors)
2819 h.public_key_offset = h.descriptors_size
2820 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002821 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2822 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002823
2824 # For the Authentication data block, the hash is first and then
2825 # the signature.
2826 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002827 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002828 h.algorithm_type = alg.algorithm_type
2829 h.hash_offset = 0
2830 h.hash_size = alg.hash_num_bytes
2831 # Signature offset and size - it's stored right after the hash
2832 # (in Authentication data block).
2833 h.signature_offset = alg.hash_num_bytes
2834 h.signature_size = alg.signature_num_bytes
2835
2836 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002837 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002838
2839 # Generate Header data block.
2840 header_data_blob = h.encode()
2841
2842 # Generate Auxiliary data block.
2843 aux_data_blob = bytearray()
2844 aux_data_blob.extend(encoded_descriptors)
2845 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002846 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002847 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2848 aux_data_blob.extend('\0' * padding_bytes)
2849
2850 # Calculate the hash.
2851 binary_hash = bytearray()
2852 binary_signature = bytearray()
2853 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002854 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002855 ha.update(header_data_blob)
2856 ha.update(aux_data_blob)
2857 binary_hash.extend(ha.digest())
2858
2859 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002860 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002861 binary_signature.extend(raw_sign(signing_helper,
2862 signing_helper_with_files,
2863 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002864 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002865 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002866
2867 # Generate Authentication data block.
2868 auth_data_blob = bytearray()
2869 auth_data_blob.extend(binary_hash)
2870 auth_data_blob.extend(binary_signature)
2871 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2872 auth_data_blob.extend('\0' * padding_bytes)
2873
2874 return header_data_blob + auth_data_blob + aux_data_blob
2875
2876 def extract_public_key(self, key_path, output):
2877 """Implements the 'extract_public_key' command.
2878
2879 Arguments:
2880 key_path: The path to a RSA private key file.
2881 output: The file to write to.
2882 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002883 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002884
David Zeuthenb1b994d2017-03-06 18:01:31 -05002885 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2886 partition_size):
2887 """Implementation of the append_vbmeta_image command.
2888
2889 Arguments:
2890 image_filename: File to add the footer to.
2891 vbmeta_image_filename: File to get vbmeta struct from.
2892 partition_size: Size of partition.
2893
2894 Raises:
2895 AvbError: If an argument is incorrect.
2896 """
2897 image = ImageHandler(image_filename)
2898
2899 if partition_size % image.block_size != 0:
2900 raise AvbError('Partition size of {} is not a multiple of the image '
2901 'block size {}.'.format(partition_size,
2902 image.block_size))
2903
2904 # If there's already a footer, truncate the image to its original
2905 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002906 if image.image_size >= AvbFooter.SIZE:
2907 image.seek(image.image_size - AvbFooter.SIZE)
2908 try:
2909 footer = AvbFooter(image.read(AvbFooter.SIZE))
2910 # Existing footer found. Just truncate.
2911 original_image_size = footer.original_image_size
2912 image.truncate(footer.original_image_size)
2913 except (LookupError, struct.error):
2914 original_image_size = image.image_size
2915 else:
2916 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002917 original_image_size = image.image_size
2918
2919 # If anything goes wrong from here-on, restore the image back to
2920 # its original size.
2921 try:
2922 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2923 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2924
2925 # If the image isn't sparse, its size might not be a multiple of
2926 # the block size. This will screw up padding later so just grow it.
2927 if image.image_size % image.block_size != 0:
2928 assert not image.is_sparse
2929 padding_needed = image.block_size - (image.image_size%image.block_size)
2930 image.truncate(image.image_size + padding_needed)
2931
2932 # The append_raw() method requires content with size being a
2933 # multiple of |block_size| so add padding as needed. Also record
2934 # where this is written to since we'll need to put that in the
2935 # footer.
2936 vbmeta_offset = image.image_size
2937 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2938 len(vbmeta_blob))
2939 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2940
2941 # Append vbmeta blob and footer
2942 image.append_raw(vbmeta_blob_with_padding)
2943 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2944
2945 # Now insert a DONT_CARE chunk with enough bytes such that the
2946 # final Footer block is at the end of partition_size..
2947 image.append_dont_care(partition_size - vbmeta_end_offset -
2948 1*image.block_size)
2949
2950 # Generate the Footer that tells where the VBMeta footer
2951 # is. Also put enough padding in the front of the footer since
2952 # we'll write out an entire block.
2953 footer = AvbFooter()
2954 footer.original_image_size = original_image_size
2955 footer.vbmeta_offset = vbmeta_offset
2956 footer.vbmeta_size = len(vbmeta_blob)
2957 footer_blob = footer.encode()
2958 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2959 footer_blob)
2960 image.append_raw(footer_blob_with_padding)
2961
2962 except:
2963 # Truncate back to original size, then re-raise
2964 image.truncate(original_image_size)
2965 raise
2966
David Zeuthena4fee8b2016-08-22 15:20:43 -04002967 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002968 hash_algorithm, salt, chain_partitions, algorithm_name,
2969 key_path,
2970 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002971 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002972 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002973 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002974 signing_helper, signing_helper_with_files,
2975 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002976 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002977 print_required_libavb_version, use_persistent_digest,
2978 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002979 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002980
2981 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002982 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002983 partition_size: Size of partition.
2984 partition_name: Name of partition (without A/B suffix).
2985 hash_algorithm: Hash algorithm to use.
2986 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002987 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002988 algorithm_name: Name of algorithm to use.
2989 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002990 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002991 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002992 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002993 props: Properties to insert (List of strings of the form 'key:value').
2994 props_from_file: Properties to insert (List of strings 'key:<path>').
2995 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002996 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002997 dm-verity kernel cmdline from.
2998 include_descriptors_from_image: List of file objects for which
2999 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003000 calc_max_image_size: Don't store the footer - instead calculate the
3001 maximum image size leaving enough room for metadata with the
3002 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003003 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003004 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003005 release_string: None or avbtool release string.
3006 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003007 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3008 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003009 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003010 use_persistent_digest: Use a persistent digest on device.
3011 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003012
3013 Raises:
3014 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003015 """
David Zeuthen1097a782017-05-31 15:53:17 -04003016
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003017 required_libavb_version_minor = 0
3018 if use_persistent_digest or do_not_use_ab:
3019 required_libavb_version_minor = 1
3020
David Zeuthen1097a782017-05-31 15:53:17 -04003021 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003022 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003023 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003024 return
3025
David Zeuthenbf562452017-05-17 18:04:43 -04003026 # First, calculate the maximum image size such that an image
3027 # this size + metadata (footer + vbmeta struct) fits in
3028 # |partition_size|.
3029 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003030 if partition_size < max_metadata_size:
3031 raise AvbError('Parition size of {} is too small. '
3032 'Needs to be at least {}'.format(
3033 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003034 max_image_size = partition_size - max_metadata_size
3035
3036 # If we're asked to only calculate the maximum image size, we're done.
3037 if calc_max_image_size:
3038 print '{}'.format(max_image_size)
3039 return
3040
David Zeuthena4fee8b2016-08-22 15:20:43 -04003041 image = ImageHandler(image_filename)
3042
3043 if partition_size % image.block_size != 0:
3044 raise AvbError('Partition size of {} is not a multiple of the image '
3045 'block size {}.'.format(partition_size,
3046 image.block_size))
3047
David Zeuthen21e95262016-07-27 17:58:40 -04003048 # If there's already a footer, truncate the image to its original
3049 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3050 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003051 if image.image_size >= AvbFooter.SIZE:
3052 image.seek(image.image_size - AvbFooter.SIZE)
3053 try:
3054 footer = AvbFooter(image.read(AvbFooter.SIZE))
3055 # Existing footer found. Just truncate.
3056 original_image_size = footer.original_image_size
3057 image.truncate(footer.original_image_size)
3058 except (LookupError, struct.error):
3059 original_image_size = image.image_size
3060 else:
3061 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003062 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003063
3064 # If anything goes wrong from here-on, restore the image back to
3065 # its original size.
3066 try:
David Zeuthen09692692016-09-30 16:16:40 -04003067 # If image size exceeds the maximum image size, fail.
3068 if image.image_size > max_image_size:
3069 raise AvbError('Image size of {} exceeds maximum image '
3070 'size of {} in order to fit in a partition '
3071 'size of {}.'.format(image.image_size, max_image_size,
3072 partition_size))
3073
David Zeuthen21e95262016-07-27 17:58:40 -04003074 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3075 if salt:
3076 salt = salt.decode('hex')
3077 else:
Bryan Henry45354282018-10-25 18:37:27 -07003078 if salt is None and not use_persistent_digest:
3079 # If salt is not explicitly specified, choose a hash that's the same
3080 # size as the hash size. Don't populate a random salt if this
3081 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003082 hash_size = digest_size
3083 salt = open('/dev/urandom').read(hash_size)
3084 else:
3085 salt = ''
3086
3087 hasher = hashlib.new(name=hash_algorithm, string=salt)
3088 # TODO(zeuthen): might want to read this in chunks to avoid
3089 # memory pressure, then again, this is only supposed to be used
3090 # on kernel/initramfs partitions. Possible optimization.
3091 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003092 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003093 digest = hasher.digest()
3094
3095 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003096 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003097 h_desc.hash_algorithm = hash_algorithm
3098 h_desc.partition_name = partition_name
3099 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003100 h_desc.flags = 0
3101 if do_not_use_ab:
3102 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3103 if not use_persistent_digest:
3104 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003105
3106 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003107 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003108 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003109 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003110 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003111 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003112 include_descriptors_from_image, signing_helper,
3113 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003114 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003115
David Zeuthend247fcb2017-02-16 12:09:27 -05003116 # Write vbmeta blob, if requested.
3117 if output_vbmeta_image:
3118 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003119
David Zeuthend247fcb2017-02-16 12:09:27 -05003120 # Append vbmeta blob and footer, unless requested not to.
3121 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003122 # If the image isn't sparse, its size might not be a multiple of
3123 # the block size. This will screw up padding later so just grow it.
3124 if image.image_size % image.block_size != 0:
3125 assert not image.is_sparse
3126 padding_needed = image.block_size - (
3127 image.image_size % image.block_size)
3128 image.truncate(image.image_size + padding_needed)
3129
3130 # The append_raw() method requires content with size being a
3131 # multiple of |block_size| so add padding as needed. Also record
3132 # where this is written to since we'll need to put that in the
3133 # footer.
3134 vbmeta_offset = image.image_size
3135 padding_needed = (
3136 round_to_multiple(len(vbmeta_blob), image.block_size) -
3137 len(vbmeta_blob))
3138 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3139
David Zeuthend247fcb2017-02-16 12:09:27 -05003140 image.append_raw(vbmeta_blob_with_padding)
3141 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3142
3143 # Now insert a DONT_CARE chunk with enough bytes such that the
3144 # final Footer block is at the end of partition_size..
3145 image.append_dont_care(partition_size - vbmeta_end_offset -
3146 1*image.block_size)
3147
3148 # Generate the Footer that tells where the VBMeta footer
3149 # is. Also put enough padding in the front of the footer since
3150 # we'll write out an entire block.
3151 footer = AvbFooter()
3152 footer.original_image_size = original_image_size
3153 footer.vbmeta_offset = vbmeta_offset
3154 footer.vbmeta_size = len(vbmeta_blob)
3155 footer_blob = footer.encode()
3156 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3157 footer_blob)
3158 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003159
David Zeuthen21e95262016-07-27 17:58:40 -04003160 except:
3161 # Truncate back to original size, then re-raise
3162 image.truncate(original_image_size)
3163 raise
3164
David Zeuthena4fee8b2016-08-22 15:20:43 -04003165 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003166 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003167 block_size, salt, chain_partitions, algorithm_name,
3168 key_path,
3169 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003170 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003171 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003172 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003173 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003174 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003175 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003176 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003177 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003178 print_required_libavb_version,
3179 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04003180 """Implements the 'add_hashtree_footer' command.
3181
3182 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3183 more information about dm-verity and these hashes.
3184
3185 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003186 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003187 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003188 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003189 generate_fec: If True, generate FEC codes.
3190 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003191 hash_algorithm: Hash algorithm to use.
3192 block_size: Block size to use.
3193 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003194 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003195 algorithm_name: Name of algorithm to use.
3196 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003197 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003198 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003199 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003200 props: Properties to insert (List of strings of the form 'key:value').
3201 props_from_file: Properties to insert (List of strings 'key:<path>').
3202 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003203 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003204 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003205 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3206 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003207 include_descriptors_from_image: List of file objects for which
3208 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003209 calc_max_image_size: Don't store the hashtree or footer - instead
3210 calculate the maximum image size leaving enough room for hashtree
3211 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003212 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003213 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003214 release_string: None or avbtool release string.
3215 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003216 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3217 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003218 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003219 use_persistent_root_digest: Use a persistent root digest on device.
3220 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003221
3222 Raises:
3223 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003224 """
David Zeuthen1097a782017-05-31 15:53:17 -04003225
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003226 required_libavb_version_minor = 0
3227 if use_persistent_root_digest or do_not_use_ab:
3228 required_libavb_version_minor = 1
3229
David Zeuthen1097a782017-05-31 15:53:17 -04003230 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003231 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003232 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003233 return
3234
David Zeuthen09692692016-09-30 16:16:40 -04003235 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3236 digest_padding = round_to_pow2(digest_size) - digest_size
3237
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003238 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3239 # size such that an image this size + the hashtree + metadata (footer +
3240 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3241 # for metadata.
3242 if partition_size > 0:
3243 (_, max_tree_size) = calc_hash_level_offsets(
3244 partition_size, block_size, digest_size + digest_padding)
3245 max_fec_size = 0
3246 if generate_fec:
3247 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3248 max_metadata_size = (max_fec_size + max_tree_size +
3249 self.MAX_VBMETA_SIZE +
3250 self.MAX_FOOTER_SIZE)
3251 max_image_size = partition_size - max_metadata_size
3252 else:
3253 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003254
3255 # If we're asked to only calculate the maximum image size, we're done.
3256 if calc_max_image_size:
3257 print '{}'.format(max_image_size)
3258 return
3259
David Zeuthena4fee8b2016-08-22 15:20:43 -04003260 image = ImageHandler(image_filename)
3261
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003262 if partition_size > 0:
3263 if partition_size % image.block_size != 0:
3264 raise AvbError('Partition size of {} is not a multiple of the image '
3265 'block size {}.'.format(partition_size,
3266 image.block_size))
3267 else:
3268 if image.image_size % image.block_size != 0:
3269 raise AvbError('File size of {} is not a multiple of the image '
3270 'block size {}.'.format(image.image_size,
3271 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003272
David Zeuthen21e95262016-07-27 17:58:40 -04003273 # If there's already a footer, truncate the image to its original
3274 # size. This way 'avbtool add_hashtree_footer' is idempotent
3275 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003276 if image.image_size >= AvbFooter.SIZE:
3277 image.seek(image.image_size - AvbFooter.SIZE)
3278 try:
3279 footer = AvbFooter(image.read(AvbFooter.SIZE))
3280 # Existing footer found. Just truncate.
3281 original_image_size = footer.original_image_size
3282 image.truncate(footer.original_image_size)
3283 except (LookupError, struct.error):
3284 original_image_size = image.image_size
3285 else:
3286 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003287 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003288
3289 # If anything goes wrong from here-on, restore the image back to
3290 # its original size.
3291 try:
3292 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003293 rounded_image_size = round_to_multiple(image.image_size, block_size)
3294 if rounded_image_size > image.image_size:
3295 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003296
David Zeuthen09692692016-09-30 16:16:40 -04003297 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003298 if partition_size > 0:
3299 if image.image_size > max_image_size:
3300 raise AvbError('Image size of {} exceeds maximum image '
3301 'size of {} in order to fit in a partition '
3302 'size of {}.'.format(image.image_size, max_image_size,
3303 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003304
3305 if salt:
3306 salt = salt.decode('hex')
3307 else:
Bryan Henry45354282018-10-25 18:37:27 -07003308 if salt is None and not use_persistent_root_digest:
3309 # If salt is not explicitly specified, choose a hash that's the same
3310 # size as the hash size. Don't populate a random salt if this
3311 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003312 hash_size = digest_size
3313 salt = open('/dev/urandom').read(hash_size)
3314 else:
3315 salt = ''
3316
David Zeuthena4fee8b2016-08-22 15:20:43 -04003317 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003318 # offsets in advance.
3319 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003320 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003321
David Zeuthena4fee8b2016-08-22 15:20:43 -04003322 # If the image isn't sparse, its size might not be a multiple of
3323 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003324 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003325 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003326 padding_needed = image.block_size - (image.image_size%image.block_size)
3327 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003328
David Zeuthena4fee8b2016-08-22 15:20:43 -04003329 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003330 tree_offset = image.image_size
3331 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003332 block_size,
3333 hash_algorithm, salt,
3334 digest_padding,
3335 hash_level_offsets,
3336 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003337
3338 # Generate HashtreeDescriptor with details about the tree we
3339 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003340 ht_desc = AvbHashtreeDescriptor()
3341 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003342 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003343 ht_desc.tree_offset = tree_offset
3344 ht_desc.tree_size = tree_size
3345 ht_desc.data_block_size = block_size
3346 ht_desc.hash_block_size = block_size
3347 ht_desc.hash_algorithm = hash_algorithm
3348 ht_desc.partition_name = partition_name
3349 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003350 if do_not_use_ab:
3351 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3352 if not use_persistent_root_digest:
3353 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003354
David Zeuthen09692692016-09-30 16:16:40 -04003355 # Write the hash tree
3356 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3357 len(hash_tree))
3358 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3359 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003360 len_hashtree_and_fec = len(hash_tree_with_padding)
3361
3362 # Generate FEC codes, if requested.
3363 if generate_fec:
3364 fec_data = generate_fec_data(image_filename, fec_num_roots)
3365 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3366 len(fec_data))
3367 fec_data_with_padding = fec_data + '\0'*padding_needed
3368 fec_offset = image.image_size
3369 image.append_raw(fec_data_with_padding)
3370 len_hashtree_and_fec += len(fec_data_with_padding)
3371 # Update the hashtree descriptor.
3372 ht_desc.fec_num_roots = fec_num_roots
3373 ht_desc.fec_offset = fec_offset
3374 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003375
David Zeuthen73f2afa2017-05-17 16:54:11 -04003376 ht_desc_to_setup = None
3377 if setup_as_rootfs_from_kernel:
3378 ht_desc_to_setup = ht_desc
3379
David Zeuthena4fee8b2016-08-22 15:20:43 -04003380 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003381 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003382 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003383 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003384 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003385 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003386 include_descriptors_from_image, signing_helper,
3387 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003388 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003389 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3390 len(vbmeta_blob))
3391 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003392
David Zeuthend247fcb2017-02-16 12:09:27 -05003393 # Write vbmeta blob, if requested.
3394 if output_vbmeta_image:
3395 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003396
David Zeuthend247fcb2017-02-16 12:09:27 -05003397 # Append vbmeta blob and footer, unless requested not to.
3398 if not do_not_append_vbmeta_image:
3399 image.append_raw(vbmeta_blob_with_padding)
3400
3401 # Now insert a DONT_CARE chunk with enough bytes such that the
3402 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003403 if partition_size > 0:
3404 image.append_dont_care(partition_size - image.image_size -
3405 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003406
3407 # Generate the Footer that tells where the VBMeta footer
3408 # is. Also put enough padding in the front of the footer since
3409 # we'll write out an entire block.
3410 footer = AvbFooter()
3411 footer.original_image_size = original_image_size
3412 footer.vbmeta_offset = vbmeta_offset
3413 footer.vbmeta_size = len(vbmeta_blob)
3414 footer_blob = footer.encode()
3415 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3416 footer_blob)
3417 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003418
David Zeuthen21e95262016-07-27 17:58:40 -04003419 except:
David Zeuthen09692692016-09-30 16:16:40 -04003420 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003421 image.truncate(original_image_size)
3422 raise
3423
David Zeuthenc68f0822017-03-31 17:22:35 -04003424 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003425 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003426 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003427 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003428 """Implements the 'make_atx_certificate' command.
3429
3430 Android Things certificates are required for Android Things public key
3431 metadata. They chain the vbmeta signing key for a particular product back to
3432 a fused, permanent root key. These certificates are fixed-length and fixed-
3433 format with the explicit goal of not parsing ASN.1 in bootloader code.
3434
3435 Arguments:
3436 output: Certificate will be written to this file on success.
3437 authority_key_path: A PEM file path with the authority private key.
3438 If None, then a certificate will be created without a
3439 signature. The signature can be created out-of-band
3440 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003441 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003442 subject_key_version: A 64-bit version value. If this is None, the number
3443 of seconds since the epoch is used.
3444 subject: A subject identifier. For Product Signing Key certificates this
3445 should be the same Product ID found in the permanent attributes.
3446 is_intermediate_authority: True if the certificate is for an intermediate
3447 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003448 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003449 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003450 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003451 """
3452 signed_data = bytearray()
3453 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003454 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003455 hasher = hashlib.sha256()
3456 hasher.update(subject)
3457 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003458 if not usage:
3459 usage = 'com.google.android.things.vboot'
3460 if is_intermediate_authority:
3461 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003462 hasher = hashlib.sha256()
3463 hasher.update(usage)
3464 signed_data.extend(hasher.digest())
3465 if not subject_key_version:
3466 subject_key_version = int(time.time())
3467 signed_data.extend(struct.pack('<Q', subject_key_version))
3468 signature = bytearray()
3469 if authority_key_path:
3470 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003471 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003472 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003473 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003474 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003475 hasher.update(signed_data)
3476 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003477 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3478 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003479 alg.signature_num_bytes, authority_key_path,
3480 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003481 output.write(signed_data)
3482 output.write(signature)
3483
David Zeuthenc68f0822017-03-31 17:22:35 -04003484 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003485 product_id):
3486 """Implements the 'make_atx_permanent_attributes' command.
3487
3488 Android Things permanent attributes are designed to be permanent for a
3489 particular product and a hash of these attributes should be fused into
3490 hardware to enforce this.
3491
3492 Arguments:
3493 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003494 root_authority_key_path: Path to a PEM or DER public key for
3495 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003496 product_id: A 16-byte Product ID.
3497
3498 Raises:
3499 AvbError: If an argument is incorrect.
3500 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003501 EXPECTED_PRODUCT_ID_SIZE = 16
3502 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003503 raise AvbError('Invalid Product ID length.')
3504 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003505 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003506 output.write(product_id)
3507
3508 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003509 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003510 """Implements the 'make_atx_metadata' command.
3511
3512 Android Things metadata are included in vbmeta images to facilitate
3513 verification. The output of this command can be used as the
3514 public_key_metadata argument to other commands.
3515
3516 Arguments:
3517 output: Metadata will be written to this file on success.
3518 intermediate_key_certificate: A certificate file as output by
3519 make_atx_certificate with
3520 is_intermediate_authority set to true.
3521 product_key_certificate: A certificate file as output by
3522 make_atx_certificate with
3523 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003524
3525 Raises:
3526 AvbError: If an argument is incorrect.
3527 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003528 EXPECTED_CERTIFICATE_SIZE = 1620
3529 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003530 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003531 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003532 raise AvbError('Invalid product key certificate length.')
3533 output.write(struct.pack('<I', 1)) # Format Version
3534 output.write(intermediate_key_certificate)
3535 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003536
Darren Krahnfccd64e2018-01-16 17:39:35 -08003537 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3538 unlock_key_certificate, challenge_path,
3539 unlock_key_path, signing_helper,
3540 signing_helper_with_files):
3541 """Implements the 'make_atx_unlock_credential' command.
3542
3543 Android Things unlock credentials can be used to authorize the unlock of AVB
3544 on a device. These credentials are presented to an Android Things bootloader
3545 via the fastboot interface in response to a 16-byte challenge. This method
3546 creates all fields of the credential except the challenge signature field
3547 (which is the last field) and can optionally create the challenge signature
3548 field as well if a challenge and the unlock_key_path is provided.
3549
3550 Arguments:
3551 output: The credential will be written to this file on success.
3552 intermediate_key_certificate: A certificate file as output by
3553 make_atx_certificate with
3554 is_intermediate_authority set to true.
3555 unlock_key_certificate: A certificate file as output by
3556 make_atx_certificate with
3557 is_intermediate_authority set to false and the
3558 usage set to
3559 'com.google.android.things.vboot.unlock'.
3560 challenge_path: [optional] A path to the challenge to sign.
3561 unlock_key_path: [optional] A PEM file path with the unlock private key.
3562 signing_helper: Program which signs a hash and returns the signature.
3563 signing_helper_with_files: Same as signing_helper but uses files instead.
3564
3565 Raises:
3566 AvbError: If an argument is incorrect.
3567 """
3568 EXPECTED_CERTIFICATE_SIZE = 1620
3569 EXPECTED_CHALLENGE_SIZE = 16
3570 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3571 raise AvbError('Invalid intermediate key certificate length.')
3572 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3573 raise AvbError('Invalid product key certificate length.')
3574 challenge = bytearray()
3575 if challenge_path:
3576 with open(challenge_path, 'r') as f:
3577 challenge = f.read()
3578 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3579 raise AvbError('Invalid unlock challenge length.')
3580 output.write(struct.pack('<I', 1)) # Format Version
3581 output.write(intermediate_key_certificate)
3582 output.write(unlock_key_certificate)
3583 if challenge_path and unlock_key_path:
3584 signature = bytearray()
3585 padding_and_hash = bytearray()
3586 algorithm_name = 'SHA512_RSA4096'
3587 alg = ALGORITHMS[algorithm_name]
3588 hasher = hashlib.sha512()
3589 padding_and_hash.extend(alg.padding)
3590 hasher.update(challenge)
3591 padding_and_hash.extend(hasher.digest())
3592 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3593 algorithm_name,
3594 alg.signature_num_bytes, unlock_key_path,
3595 padding_and_hash))
3596 output.write(signature)
3597
David Zeuthen21e95262016-07-27 17:58:40 -04003598
3599def calc_hash_level_offsets(image_size, block_size, digest_size):
3600 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3601
3602 Arguments:
3603 image_size: The size of the image to calculate a Merkle-tree for.
3604 block_size: The block size, e.g. 4096.
3605 digest_size: The size of each hash, e.g. 32 for SHA-256.
3606
3607 Returns:
3608 A tuple where the first argument is an array of offsets and the
3609 second is size of the tree, in bytes.
3610 """
3611 level_offsets = []
3612 level_sizes = []
3613 tree_size = 0
3614
3615 num_levels = 0
3616 size = image_size
3617 while size > block_size:
3618 num_blocks = (size + block_size - 1) / block_size
3619 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3620
3621 level_sizes.append(level_size)
3622 tree_size += level_size
3623 num_levels += 1
3624
3625 size = level_size
3626
3627 for n in range(0, num_levels):
3628 offset = 0
3629 for m in range(n + 1, num_levels):
3630 offset += level_sizes[m]
3631 level_offsets.append(offset)
3632
David Zeuthena4fee8b2016-08-22 15:20:43 -04003633 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003634
3635
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003636# See system/extras/libfec/include/fec/io.h for these definitions.
3637FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3638FEC_MAGIC = 0xfecfecfe
3639
3640
3641def calc_fec_data_size(image_size, num_roots):
3642 """Calculates how much space FEC data will take.
3643
3644 Args:
3645 image_size: The size of the image.
3646 num_roots: Number of roots.
3647
3648 Returns:
3649 The number of bytes needed for FEC for an image of the given size
3650 and with the requested number of FEC roots.
3651
3652 Raises:
3653 ValueError: If output from the 'fec' tool is invalid.
3654
3655 """
3656 p = subprocess.Popen(
3657 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3658 stdout=subprocess.PIPE,
3659 stderr=subprocess.PIPE)
3660 (pout, perr) = p.communicate()
3661 retcode = p.wait()
3662 if retcode != 0:
3663 raise ValueError('Error invoking fec: {}'.format(perr))
3664 return int(pout)
3665
3666
3667def generate_fec_data(image_filename, num_roots):
3668 """Generate FEC codes for an image.
3669
3670 Args:
3671 image_filename: The filename of the image.
3672 num_roots: Number of roots.
3673
3674 Returns:
3675 The FEC data blob.
3676
3677 Raises:
3678 ValueError: If output from the 'fec' tool is invalid.
3679 """
3680 fec_tmpfile = tempfile.NamedTemporaryFile()
3681 subprocess.check_call(
3682 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3683 fec_tmpfile.name],
3684 stderr=open(os.devnull))
3685 fec_data = fec_tmpfile.read()
3686 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3687 footer_data = fec_data[-footer_size:]
3688 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3689 footer_data)
3690 if magic != FEC_MAGIC:
3691 raise ValueError('Unexpected magic in FEC footer')
3692 return fec_data[0:fec_size]
3693
3694
David Zeuthen21e95262016-07-27 17:58:40 -04003695def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003696 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003697 """Generates a Merkle-tree for a file.
3698
3699 Args:
3700 image: The image, as a file.
3701 image_size: The size of the image.
3702 block_size: The block size, e.g. 4096.
3703 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3704 salt: The salt to use.
3705 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003706 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003707 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003708
3709 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003710 A tuple where the first element is the top-level hash and the
3711 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003712 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003713 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003714 hash_src_offset = 0
3715 hash_src_size = image_size
3716 level_num = 0
3717 while hash_src_size > block_size:
3718 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003719 remaining = hash_src_size
3720 while remaining > 0:
3721 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003722 # Only read from the file for the first level - for subsequent
3723 # levels, access the array we're building.
3724 if level_num == 0:
3725 image.seek(hash_src_offset + hash_src_size - remaining)
3726 data = image.read(min(remaining, block_size))
3727 else:
3728 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3729 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003730 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003731
3732 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003733 if len(data) < block_size:
3734 hasher.update('\0' * (block_size - len(data)))
3735 level_output += hasher.digest()
3736 if digest_padding > 0:
3737 level_output += '\0' * digest_padding
3738
3739 padding_needed = (round_to_multiple(
3740 len(level_output), block_size) - len(level_output))
3741 level_output += '\0' * padding_needed
3742
David Zeuthena4fee8b2016-08-22 15:20:43 -04003743 # Copy level-output into resulting tree.
3744 offset = hash_level_offsets[level_num]
3745 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003746
David Zeuthena4fee8b2016-08-22 15:20:43 -04003747 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003748 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003749 level_num += 1
3750
3751 hasher = hashlib.new(name=hash_alg_name, string=salt)
3752 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003753 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003754
3755
3756class AvbTool(object):
3757 """Object for avbtool command-line tool."""
3758
3759 def __init__(self):
3760 """Initializer method."""
3761 self.avb = Avb()
3762
3763 def _add_common_args(self, sub_parser):
3764 """Adds arguments used by several sub-commands.
3765
3766 Arguments:
3767 sub_parser: The parser to add arguments to.
3768 """
3769 sub_parser.add_argument('--algorithm',
3770 help='Algorithm to use (default: NONE)',
3771 metavar='ALGORITHM',
3772 default='NONE')
3773 sub_parser.add_argument('--key',
3774 help='Path to RSA private key file',
3775 metavar='KEY',
3776 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003777 sub_parser.add_argument('--signing_helper',
3778 help='Path to helper used for signing',
3779 metavar='APP',
3780 default=None,
3781 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003782 sub_parser.add_argument('--signing_helper_with_files',
3783 help='Path to helper used for signing using files',
3784 metavar='APP',
3785 default=None,
3786 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003787 sub_parser.add_argument('--public_key_metadata',
3788 help='Path to public key metadata file',
3789 metavar='KEY_METADATA',
3790 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003791 sub_parser.add_argument('--rollback_index',
3792 help='Rollback Index',
3793 type=parse_number,
3794 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003795 # This is used internally for unit tests. Do not include in --help output.
3796 sub_parser.add_argument('--internal_release_string',
3797 help=argparse.SUPPRESS)
3798 sub_parser.add_argument('--append_to_release_string',
3799 help='Text to append to release string',
3800 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003801 sub_parser.add_argument('--prop',
3802 help='Add property',
3803 metavar='KEY:VALUE',
3804 action='append')
3805 sub_parser.add_argument('--prop_from_file',
3806 help='Add property from file',
3807 metavar='KEY:PATH',
3808 action='append')
3809 sub_parser.add_argument('--kernel_cmdline',
3810 help='Add kernel cmdline',
3811 metavar='CMDLINE',
3812 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003813 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3814 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3815 # at some future point.
3816 sub_parser.add_argument('--setup_rootfs_from_kernel',
3817 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003818 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003819 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003820 type=argparse.FileType('rb'))
3821 sub_parser.add_argument('--include_descriptors_from_image',
3822 help='Include descriptors from image',
3823 metavar='IMAGE',
3824 action='append',
3825 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003826 sub_parser.add_argument('--print_required_libavb_version',
3827 help=('Don\'t store the footer - '
3828 'instead calculate the required libavb '
3829 'version for the given options.'),
3830 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003831 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3832 sub_parser.add_argument('--chain_partition',
3833 help='Allow signed integrity-data for partition',
3834 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3835 action='append')
3836 sub_parser.add_argument('--flags',
3837 help='VBMeta flags',
3838 type=parse_number,
3839 default=0)
3840 sub_parser.add_argument('--set_hashtree_disabled_flag',
3841 help='Set the HASHTREE_DISABLED flag',
3842 action='store_true')
3843
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003844 def _add_common_footer_args(self, sub_parser):
3845 """Adds arguments used by add_*_footer sub-commands.
3846
3847 Arguments:
3848 sub_parser: The parser to add arguments to.
3849 """
3850 sub_parser.add_argument('--use_persistent_digest',
3851 help='Use a persistent digest on device instead of '
3852 'storing the digest in the descriptor. This '
3853 'cannot be used with A/B so must be combined '
3854 'with --do_not_use_ab when an A/B suffix is '
3855 'expected at runtime.',
3856 action='store_true')
3857 sub_parser.add_argument('--do_not_use_ab',
3858 help='The partition does not use A/B even when an '
3859 'A/B suffix is present. This must not be used '
3860 'for vbmeta or chained partitions.',
3861 action='store_true')
3862
David Zeuthena5fd3a42017-02-27 16:38:54 -05003863 def _fixup_common_args(self, args):
3864 """Common fixups needed by subcommands.
3865
3866 Arguments:
3867 args: Arguments to modify.
3868
3869 Returns:
3870 The modified arguments.
3871 """
3872 if args.set_hashtree_disabled_flag:
3873 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3874 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003875
3876 def run(self, argv):
3877 """Command-line processor.
3878
3879 Arguments:
3880 argv: Pass sys.argv from main.
3881 """
3882 parser = argparse.ArgumentParser()
3883 subparsers = parser.add_subparsers(title='subcommands')
3884
3885 sub_parser = subparsers.add_parser('version',
3886 help='Prints version of avbtool.')
3887 sub_parser.set_defaults(func=self.version)
3888
3889 sub_parser = subparsers.add_parser('extract_public_key',
3890 help='Extract public key.')
3891 sub_parser.add_argument('--key',
3892 help='Path to RSA private key file',
3893 required=True)
3894 sub_parser.add_argument('--output',
3895 help='Output file name',
3896 type=argparse.FileType('wb'),
3897 required=True)
3898 sub_parser.set_defaults(func=self.extract_public_key)
3899
3900 sub_parser = subparsers.add_parser('make_vbmeta_image',
3901 help='Makes a vbmeta image.')
3902 sub_parser.add_argument('--output',
3903 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003904 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003905 sub_parser.add_argument('--padding_size',
3906 metavar='NUMBER',
3907 help='If non-zero, pads output with NUL bytes so '
3908 'its size is a multiple of NUMBER (default: 0)',
3909 type=parse_number,
3910 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003911 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003912 sub_parser.set_defaults(func=self.make_vbmeta_image)
3913
3914 sub_parser = subparsers.add_parser('add_hash_footer',
3915 help='Add hashes and footer to image.')
3916 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003917 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003918 type=argparse.FileType('rab+'))
3919 sub_parser.add_argument('--partition_size',
3920 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003921 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003922 sub_parser.add_argument('--partition_name',
3923 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003924 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003925 sub_parser.add_argument('--hash_algorithm',
3926 help='Hash algorithm to use (default: sha256)',
3927 default='sha256')
3928 sub_parser.add_argument('--salt',
3929 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003930 sub_parser.add_argument('--calc_max_image_size',
3931 help=('Don\'t store the footer - '
3932 'instead calculate the maximum image size '
3933 'leaving enough room for metadata with '
3934 'the given partition size.'),
3935 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003936 sub_parser.add_argument('--output_vbmeta_image',
3937 help='Also write vbmeta struct to file',
3938 type=argparse.FileType('wb'))
3939 sub_parser.add_argument('--do_not_append_vbmeta_image',
3940 help=('Do not append vbmeta struct or footer '
3941 'to the image'),
3942 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003943 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003944 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003945 sub_parser.set_defaults(func=self.add_hash_footer)
3946
David Zeuthenb1b994d2017-03-06 18:01:31 -05003947 sub_parser = subparsers.add_parser('append_vbmeta_image',
3948 help='Append vbmeta image to image.')
3949 sub_parser.add_argument('--image',
3950 help='Image to append vbmeta blob to',
3951 type=argparse.FileType('rab+'))
3952 sub_parser.add_argument('--partition_size',
3953 help='Partition size',
3954 type=parse_number,
3955 required=True)
3956 sub_parser.add_argument('--vbmeta_image',
3957 help='Image with vbmeta blob to append',
3958 type=argparse.FileType('rb'))
3959 sub_parser.set_defaults(func=self.append_vbmeta_image)
3960
David Zeuthen21e95262016-07-27 17:58:40 -04003961 sub_parser = subparsers.add_parser('add_hashtree_footer',
3962 help='Add hashtree and footer to image.')
3963 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003964 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003965 type=argparse.FileType('rab+'))
3966 sub_parser.add_argument('--partition_size',
3967 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003968 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04003969 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003970 sub_parser.add_argument('--partition_name',
3971 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003972 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04003973 sub_parser.add_argument('--hash_algorithm',
3974 help='Hash algorithm to use (default: sha1)',
3975 default='sha1')
3976 sub_parser.add_argument('--salt',
3977 help='Salt in hex (default: /dev/urandom)')
3978 sub_parser.add_argument('--block_size',
3979 help='Block size (default: 4096)',
3980 type=parse_number,
3981 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003982 # TODO(zeuthen): The --generate_fec option was removed when we
3983 # moved to generating FEC by default. To avoid breaking existing
3984 # users needing to transition we simply just print a warning below
3985 # in add_hashtree_footer(). Remove this option and the warning at
3986 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003987 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003988 help=argparse.SUPPRESS,
3989 action='store_true')
3990 sub_parser.add_argument('--do_not_generate_fec',
3991 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003992 action='store_true')
3993 sub_parser.add_argument('--fec_num_roots',
3994 help='Number of roots for FEC (default: 2)',
3995 type=parse_number,
3996 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003997 sub_parser.add_argument('--calc_max_image_size',
3998 help=('Don\'t store the hashtree or footer - '
3999 'instead calculate the maximum image size '
4000 'leaving enough room for hashtree '
4001 'and metadata with the given partition '
4002 'size.'),
4003 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004004 sub_parser.add_argument('--output_vbmeta_image',
4005 help='Also write vbmeta struct to file',
4006 type=argparse.FileType('wb'))
4007 sub_parser.add_argument('--do_not_append_vbmeta_image',
4008 help=('Do not append vbmeta struct or footer '
4009 'to the image'),
4010 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004011 # This is different from --setup_rootfs_from_kernel insofar that
4012 # it doesn't take an IMAGE, the generated cmdline will be for the
4013 # hashtree we're adding.
4014 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4015 action='store_true',
4016 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04004017 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004018 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004019 sub_parser.set_defaults(func=self.add_hashtree_footer)
4020
4021 sub_parser = subparsers.add_parser('erase_footer',
4022 help='Erase footer from an image.')
4023 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004024 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004025 type=argparse.FileType('rwb+'),
4026 required=True)
4027 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004028 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004029 action='store_true')
4030 sub_parser.set_defaults(func=self.erase_footer)
4031
David Zeuthen49936b42018-08-07 17:38:58 -04004032 sub_parser = subparsers.add_parser('extract_vbmeta_image',
4033 help='Extracts vbmeta from an image with a footer.')
4034 sub_parser.add_argument('--image',
4035 help='Image with footer',
4036 type=argparse.FileType('rb'),
4037 required=True)
4038 sub_parser.add_argument('--output',
4039 help='Output file name',
4040 type=argparse.FileType('wb'))
4041 sub_parser.add_argument('--padding_size',
4042 metavar='NUMBER',
4043 help='If non-zero, pads output with NUL bytes so '
4044 'its size is a multiple of NUMBER (default: 0)',
4045 type=parse_number,
4046 default=0)
4047 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4048
David Zeuthen2bc232b2017-04-19 14:25:19 -04004049 sub_parser = subparsers.add_parser('resize_image',
4050 help='Resize image with a footer.')
4051 sub_parser.add_argument('--image',
4052 help='Image with a footer',
4053 type=argparse.FileType('rwb+'),
4054 required=True)
4055 sub_parser.add_argument('--partition_size',
4056 help='New partition size',
4057 type=parse_number)
4058 sub_parser.set_defaults(func=self.resize_image)
4059
David Zeuthen21e95262016-07-27 17:58:40 -04004060 sub_parser = subparsers.add_parser(
4061 'info_image',
4062 help='Show information about vbmeta or footer.')
4063 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004064 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004065 type=argparse.FileType('rb'),
4066 required=True)
4067 sub_parser.add_argument('--output',
4068 help='Write info to file',
4069 type=argparse.FileType('wt'),
4070 default=sys.stdout)
4071 sub_parser.set_defaults(func=self.info_image)
4072
David Zeuthenb623d8b2017-04-04 16:05:53 -04004073 sub_parser = subparsers.add_parser(
4074 'verify_image',
4075 help='Verify an image.')
4076 sub_parser.add_argument('--image',
4077 help='Image to verify',
4078 type=argparse.FileType('rb'),
4079 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004080 sub_parser.add_argument('--key',
4081 help='Check embedded public key matches KEY',
4082 metavar='KEY',
4083 required=False)
4084 sub_parser.add_argument('--expected_chain_partition',
4085 help='Expected chain partition',
4086 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4087 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004088 sub_parser.set_defaults(func=self.verify_image)
4089
David Zeuthenb8643c02018-05-17 17:21:18 -04004090 sub_parser = subparsers.add_parser(
4091 'calculate_vbmeta_digest',
4092 help='Calculate vbmeta digest.')
4093 sub_parser.add_argument('--image',
4094 help='Image to calculate digest for',
4095 type=argparse.FileType('rb'),
4096 required=True)
4097 sub_parser.add_argument('--hash_algorithm',
4098 help='Hash algorithm to use (default: sha256)',
4099 default='sha256')
4100 sub_parser.add_argument('--output',
4101 help='Write hex digest to file (default: stdout)',
4102 type=argparse.FileType('wt'),
4103 default=sys.stdout)
4104 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4105
David Zeuthenf7d2e752018-09-20 13:30:41 -04004106 sub_parser = subparsers.add_parser(
4107 'calculate_kernel_cmdline',
4108 help='Calculate kernel cmdline.')
4109 sub_parser.add_argument('--image',
4110 help='Image to calculate kernel cmdline for',
4111 type=argparse.FileType('rb'),
4112 required=True)
4113 sub_parser.add_argument('--hashtree_disabled',
4114 help='Return the cmdline for hashtree disabled',
4115 action='store_true')
4116 sub_parser.add_argument('--output',
4117 help='Write cmdline to file (default: stdout)',
4118 type=argparse.FileType('wt'),
4119 default=sys.stdout)
4120 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4121
David Zeuthen8b6973b2016-09-20 12:39:49 -04004122 sub_parser = subparsers.add_parser('set_ab_metadata',
4123 help='Set A/B metadata.')
4124 sub_parser.add_argument('--misc_image',
4125 help=('The misc image to modify. If the image does '
4126 'not exist, it will be created.'),
4127 type=argparse.FileType('r+b'),
4128 required=True)
4129 sub_parser.add_argument('--slot_data',
4130 help=('Slot data of the form "priority", '
4131 '"tries_remaining", "sucessful_boot" for '
4132 'slot A followed by the same for slot B, '
4133 'separated by colons. The default value '
4134 'is 15:7:0:14:7:0.'),
4135 default='15:7:0:14:7:0')
4136 sub_parser.set_defaults(func=self.set_ab_metadata)
4137
Darren Krahn147b08d2016-12-20 16:38:29 -08004138 sub_parser = subparsers.add_parser(
4139 'make_atx_certificate',
4140 help='Create an Android Things eXtension (ATX) certificate.')
4141 sub_parser.add_argument('--output',
4142 help='Write certificate to file',
4143 type=argparse.FileType('wb'),
4144 default=sys.stdout)
4145 sub_parser.add_argument('--subject',
4146 help=('Path to subject file'),
4147 type=argparse.FileType('rb'),
4148 required=True)
4149 sub_parser.add_argument('--subject_key',
4150 help=('Path to subject RSA public key file'),
4151 type=argparse.FileType('rb'),
4152 required=True)
4153 sub_parser.add_argument('--subject_key_version',
4154 help=('Version of the subject key'),
4155 type=parse_number,
4156 required=False)
4157 sub_parser.add_argument('--subject_is_intermediate_authority',
4158 help=('Generate an intermediate authority '
4159 'certificate'),
4160 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004161 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004162 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004163 'string'),
4164 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004165 sub_parser.add_argument('--authority_key',
4166 help='Path to authority RSA private key file',
4167 required=False)
4168 sub_parser.add_argument('--signing_helper',
4169 help='Path to helper used for signing',
4170 metavar='APP',
4171 default=None,
4172 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004173 sub_parser.add_argument('--signing_helper_with_files',
4174 help='Path to helper used for signing using files',
4175 metavar='APP',
4176 default=None,
4177 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004178 sub_parser.set_defaults(func=self.make_atx_certificate)
4179
4180 sub_parser = subparsers.add_parser(
4181 'make_atx_permanent_attributes',
4182 help='Create Android Things eXtension (ATX) permanent attributes.')
4183 sub_parser.add_argument('--output',
4184 help='Write attributes to file',
4185 type=argparse.FileType('wb'),
4186 default=sys.stdout)
4187 sub_parser.add_argument('--root_authority_key',
4188 help='Path to authority RSA public key file',
4189 type=argparse.FileType('rb'),
4190 required=True)
4191 sub_parser.add_argument('--product_id',
4192 help=('Path to Product ID file'),
4193 type=argparse.FileType('rb'),
4194 required=True)
4195 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4196
4197 sub_parser = subparsers.add_parser(
4198 'make_atx_metadata',
4199 help='Create Android Things eXtension (ATX) metadata.')
4200 sub_parser.add_argument('--output',
4201 help='Write metadata to file',
4202 type=argparse.FileType('wb'),
4203 default=sys.stdout)
4204 sub_parser.add_argument('--intermediate_key_certificate',
4205 help='Path to intermediate key certificate file',
4206 type=argparse.FileType('rb'),
4207 required=True)
4208 sub_parser.add_argument('--product_key_certificate',
4209 help='Path to product key certificate file',
4210 type=argparse.FileType('rb'),
4211 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004212 sub_parser.set_defaults(func=self.make_atx_metadata)
4213
Darren Krahnfccd64e2018-01-16 17:39:35 -08004214 sub_parser = subparsers.add_parser(
4215 'make_atx_unlock_credential',
4216 help='Create an Android Things eXtension (ATX) unlock credential.')
4217 sub_parser.add_argument('--output',
4218 help='Write credential to file',
4219 type=argparse.FileType('wb'),
4220 default=sys.stdout)
4221 sub_parser.add_argument('--intermediate_key_certificate',
4222 help='Path to intermediate key certificate file',
4223 type=argparse.FileType('rb'),
4224 required=True)
4225 sub_parser.add_argument('--unlock_key_certificate',
4226 help='Path to unlock key certificate file',
4227 type=argparse.FileType('rb'),
4228 required=True)
4229 sub_parser.add_argument('--challenge',
4230 help='Path to the challenge to sign (optional). If '
4231 'this is not provided the challenge signature '
4232 'field is omitted and can be concatenated '
4233 'later.',
4234 required=False)
4235 sub_parser.add_argument('--unlock_key',
4236 help='Path to unlock key (optional). Must be '
4237 'provided if using --challenge.',
4238 required=False)
4239 sub_parser.add_argument('--signing_helper',
4240 help='Path to helper used for signing',
4241 metavar='APP',
4242 default=None,
4243 required=False)
4244 sub_parser.add_argument('--signing_helper_with_files',
4245 help='Path to helper used for signing using files',
4246 metavar='APP',
4247 default=None,
4248 required=False)
4249 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4250
David Zeuthen21e95262016-07-27 17:58:40 -04004251 args = parser.parse_args(argv[1:])
4252 try:
4253 args.func(args)
4254 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004255 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004256 sys.exit(1)
4257
4258 def version(self, _):
4259 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004260 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004261
4262 def extract_public_key(self, args):
4263 """Implements the 'extract_public_key' sub-command."""
4264 self.avb.extract_public_key(args.key, args.output)
4265
4266 def make_vbmeta_image(self, args):
4267 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004268 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004269 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004270 args.algorithm, args.key,
4271 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004272 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004273 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004274 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004275 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004276 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004277 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004278 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004279 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004280 args.print_required_libavb_version,
4281 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004282
David Zeuthenb1b994d2017-03-06 18:01:31 -05004283 def append_vbmeta_image(self, args):
4284 """Implements the 'append_vbmeta_image' sub-command."""
4285 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4286 args.partition_size)
4287
David Zeuthen21e95262016-07-27 17:58:40 -04004288 def add_hash_footer(self, args):
4289 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004290 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004291 self.avb.add_hash_footer(args.image.name if args.image else None,
4292 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004293 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004294 args.salt, args.chain_partition, args.algorithm,
4295 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004296 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004297 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004298 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004299 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004300 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004301 args.calc_max_image_size,
4302 args.signing_helper,
4303 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004304 args.internal_release_string,
4305 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004306 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004307 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004308 args.print_required_libavb_version,
4309 args.use_persistent_digest,
4310 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004311
4312 def add_hashtree_footer(self, args):
4313 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004314 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004315 # TODO(zeuthen): Remove when removing support for the
4316 # '--generate_fec' option above.
4317 if args.generate_fec:
4318 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4319 'is now generated by default. Use the option '
4320 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004321 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4322 args.partition_size,
4323 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004324 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004325 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004326 args.salt, args.chain_partition, args.algorithm,
4327 args.key, args.public_key_metadata,
4328 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004329 args.prop_from_file,
4330 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004331 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004332 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004333 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004334 args.calc_max_image_size,
4335 args.signing_helper,
4336 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004337 args.internal_release_string,
4338 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004339 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004340 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004341 args.print_required_libavb_version,
4342 args.use_persistent_digest,
4343 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05004344
David Zeuthen21e95262016-07-27 17:58:40 -04004345 def erase_footer(self, args):
4346 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004347 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004348
David Zeuthen49936b42018-08-07 17:38:58 -04004349 def extract_vbmeta_image(self, args):
4350 """Implements the 'extract_vbmeta_image' sub-command."""
4351 self.avb.extract_vbmeta_image(args.output, args.image.name,
4352 args.padding_size)
4353
David Zeuthen2bc232b2017-04-19 14:25:19 -04004354 def resize_image(self, args):
4355 """Implements the 'resize_image' sub-command."""
4356 self.avb.resize_image(args.image.name, args.partition_size)
4357
David Zeuthen8b6973b2016-09-20 12:39:49 -04004358 def set_ab_metadata(self, args):
4359 """Implements the 'set_ab_metadata' sub-command."""
4360 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4361
David Zeuthen21e95262016-07-27 17:58:40 -04004362 def info_image(self, args):
4363 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004364 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004365
David Zeuthenb623d8b2017-04-04 16:05:53 -04004366 def verify_image(self, args):
4367 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004368 self.avb.verify_image(args.image.name, args.key,
4369 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004370
David Zeuthenb8643c02018-05-17 17:21:18 -04004371 def calculate_vbmeta_digest(self, args):
4372 """Implements the 'calculate_vbmeta_digest' sub-command."""
4373 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4374 args.output)
4375
David Zeuthenf7d2e752018-09-20 13:30:41 -04004376 def calculate_kernel_cmdline(self, args):
4377 """Implements the 'calculate_kernel_cmdline' sub-command."""
4378 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, args.output)
4379
Darren Krahn147b08d2016-12-20 16:38:29 -08004380 def make_atx_certificate(self, args):
4381 """Implements the 'make_atx_certificate' sub-command."""
4382 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004383 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004384 args.subject_key_version,
4385 args.subject.read(),
4386 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004387 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004388 args.signing_helper,
4389 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004390
4391 def make_atx_permanent_attributes(self, args):
4392 """Implements the 'make_atx_permanent_attributes' sub-command."""
4393 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004394 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004395 args.product_id.read())
4396
4397 def make_atx_metadata(self, args):
4398 """Implements the 'make_atx_metadata' sub-command."""
4399 self.avb.make_atx_metadata(args.output,
4400 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004401 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004402
Darren Krahnfccd64e2018-01-16 17:39:35 -08004403 def make_atx_unlock_credential(self, args):
4404 """Implements the 'make_atx_unlock_credential' sub-command."""
4405 self.avb.make_atx_unlock_credential(
4406 args.output,
4407 args.intermediate_key_certificate.read(),
4408 args.unlock_key_certificate.read(),
4409 args.challenge,
4410 args.unlock_key,
4411 args.signing_helper,
4412 args.signing_helper_with_files)
4413
David Zeuthen21e95262016-07-27 17:58:40 -04004414
4415if __name__ == '__main__':
4416 tool = AvbTool()
4417 tool.run(sys.argv)