blob: b6d5a224c26789d2c81030514e513a5f12962b8b [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040031import math
David Zeuthen21e95262016-07-27 17:58:40 -040032import os
33import struct
34import subprocess
35import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040036import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080037import time
David Zeuthen21e95262016-07-27 17:58:40 -040038
David Zeuthene3cadca2017-02-22 21:25:46 -050039# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040040AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080041AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080044# Keep in sync with libavb/avb_footer.h.
45AVB_FOOTER_VERSION_MAJOR = 1
46AVB_FOOTER_VERSION_MINOR = 0
47
David Zeuthen58305522017-01-11 17:42:47 -050048AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040049
David Zeuthene3cadca2017-02-22 21:25:46 -050050
David Zeuthen21e95262016-07-27 17:58:40 -040051class AvbError(Exception):
52 """Application-specific errors.
53
54 These errors represent issues for which a stack-trace should not be
55 presented.
56
57 Attributes:
58 message: Error message.
59 """
60
61 def __init__(self, message):
62 Exception.__init__(self, message)
63
64
65class Algorithm(object):
66 """Contains details about an algorithm.
67
Tao Bao80418a52018-07-20 11:41:22 -070068 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040069
70 The constant |ALGORITHMS| is a dictionary from human-readable
71 names (e.g 'SHA256_RSA2048') to instances of this class.
72
73 Attributes:
74 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040075 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040076 hash_num_bytes: Number of bytes used to store the hash.
77 signature_num_bytes: Number of bytes used to store the signature.
78 public_key_num_bytes: Number of bytes used to store the public key.
79 padding: Padding used for signature, if any.
80 """
81
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
83 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040084 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040085 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.hash_num_bytes = hash_num_bytes
87 self.signature_num_bytes = signature_num_bytes
88 self.public_key_num_bytes = public_key_num_bytes
89 self.padding = padding
90
David Zeuthenb623d8b2017-04-04 16:05:53 -040091
David Zeuthen21e95262016-07-27 17:58:40 -040092# This must be kept in sync with the avb_crypto.h file.
93#
94# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
95# obtained from section 5.2.2 of RFC 4880.
96ALGORITHMS = {
97 'NONE': Algorithm(
98 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040099 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400100 hash_num_bytes=0,
101 signature_num_bytes=0,
102 public_key_num_bytes=0,
103 padding=[]),
104 'SHA256_RSA2048': Algorithm(
105 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400106 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400107 hash_num_bytes=32,
108 signature_num_bytes=256,
109 public_key_num_bytes=8 + 2*2048/8,
110 padding=[
111 # PKCS1-v1_5 padding
112 0x00, 0x01] + [0xff]*202 + [0x00] + [
113 # ASN.1 header
114 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
115 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
116 0x00, 0x04, 0x20,
117 ]),
118 'SHA256_RSA4096': Algorithm(
119 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400120 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400121 hash_num_bytes=32,
122 signature_num_bytes=512,
123 public_key_num_bytes=8 + 2*4096/8,
124 padding=[
125 # PKCS1-v1_5 padding
126 0x00, 0x01] + [0xff]*458 + [0x00] + [
127 # ASN.1 header
128 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
129 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
130 0x00, 0x04, 0x20,
131 ]),
132 'SHA256_RSA8192': Algorithm(
133 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400134 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400135 hash_num_bytes=32,
136 signature_num_bytes=1024,
137 public_key_num_bytes=8 + 2*8192/8,
138 padding=[
139 # PKCS1-v1_5 padding
140 0x00, 0x01] + [0xff]*970 + [0x00] + [
141 # ASN.1 header
142 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
143 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
144 0x00, 0x04, 0x20,
145 ]),
146 'SHA512_RSA2048': Algorithm(
147 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400148 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400149 hash_num_bytes=64,
150 signature_num_bytes=256,
151 public_key_num_bytes=8 + 2*2048/8,
152 padding=[
153 # PKCS1-v1_5 padding
154 0x00, 0x01] + [0xff]*170 + [0x00] + [
155 # ASN.1 header
156 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
157 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
158 0x00, 0x04, 0x40
159 ]),
160 'SHA512_RSA4096': Algorithm(
161 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400162 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400163 hash_num_bytes=64,
164 signature_num_bytes=512,
165 public_key_num_bytes=8 + 2*4096/8,
166 padding=[
167 # PKCS1-v1_5 padding
168 0x00, 0x01] + [0xff]*426 + [0x00] + [
169 # ASN.1 header
170 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
171 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
172 0x00, 0x04, 0x40
173 ]),
174 'SHA512_RSA8192': Algorithm(
175 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400176 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400177 hash_num_bytes=64,
178 signature_num_bytes=1024,
179 public_key_num_bytes=8 + 2*8192/8,
180 padding=[
181 # PKCS1-v1_5 padding
182 0x00, 0x01] + [0xff]*938 + [0x00] + [
183 # ASN.1 header
184 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
185 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
186 0x00, 0x04, 0x40
187 ]),
188}
189
190
David Zeuthene3cadca2017-02-22 21:25:46 -0500191def get_release_string():
192 """Calculates the release string to use in the VBMeta struct."""
193 # Keep in sync with libavb/avb_version.c:avb_version_string().
194 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
195 AVB_VERSION_MINOR,
196 AVB_VERSION_SUB)
197
198
David Zeuthen21e95262016-07-27 17:58:40 -0400199def round_to_multiple(number, size):
200 """Rounds a number up to nearest multiple of another number.
201
202 Args:
203 number: The number to round up.
204 size: The multiple to round up to.
205
206 Returns:
207 If |number| is a multiple of |size|, returns |number|, otherwise
208 returns |number| + |size|.
209 """
210 remainder = number % size
211 if remainder == 0:
212 return number
213 return number + size - remainder
214
215
216def round_to_pow2(number):
217 """Rounds a number up to the next power of 2.
218
219 Args:
220 number: The number to round up.
221
222 Returns:
223 If |number| is already a power of 2 then |number| is
224 returned. Otherwise the smallest power of 2 greater than |number|
225 is returned.
226 """
227 return 2**((number - 1).bit_length())
228
229
David Zeuthen21e95262016-07-27 17:58:40 -0400230def encode_long(num_bits, value):
231 """Encodes a long to a bytearray() using a given amount of bits.
232
233 This number is written big-endian, e.g. with the most significant
234 bit first.
235
David Zeuthenb623d8b2017-04-04 16:05:53 -0400236 This is the reverse of decode_long().
237
David Zeuthen21e95262016-07-27 17:58:40 -0400238 Arguments:
239 num_bits: The number of bits to write, e.g. 2048.
240 value: The value to write.
241
242 Returns:
243 A bytearray() with the encoded long.
244 """
245 ret = bytearray()
246 for bit_pos in range(num_bits, 0, -8):
247 octet = (value >> (bit_pos - 8)) & 0xff
248 ret.extend(struct.pack('!B', octet))
249 return ret
250
251
David Zeuthenb623d8b2017-04-04 16:05:53 -0400252def decode_long(blob):
253 """Decodes a long from a bytearray() using a given amount of bits.
254
255 This number is expected to be in big-endian, e.g. with the most
256 significant bit first.
257
258 This is the reverse of encode_long().
259
260 Arguments:
261 value: A bytearray() with the encoded long.
262
263 Returns:
264 The decoded value.
265 """
266 ret = 0
267 for b in bytearray(blob):
268 ret *= 256
269 ret += b
270 return ret
271
272
David Zeuthen21e95262016-07-27 17:58:40 -0400273def egcd(a, b):
274 """Calculate greatest common divisor of two numbers.
275
276 This implementation uses a recursive version of the extended
277 Euclidian algorithm.
278
279 Arguments:
280 a: First number.
281 b: Second number.
282
283 Returns:
284 A tuple (gcd, x, y) that where |gcd| is the greatest common
285 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
286 """
287 if a == 0:
288 return (b, 0, 1)
289 else:
290 g, y, x = egcd(b % a, a)
291 return (g, x - (b // a) * y, y)
292
293
294def modinv(a, m):
295 """Calculate modular multiplicative inverse of |a| modulo |m|.
296
297 This calculates the number |x| such that |a| * |x| == 1 (modulo
298 |m|). This number only exists if |a| and |m| are co-prime - |None|
299 is returned if this isn't true.
300
301 Arguments:
302 a: The number to calculate a modular inverse of.
303 m: The modulo to use.
304
305 Returns:
306 The modular multiplicative inverse of |a| and |m| or |None| if
307 these numbers are not co-prime.
308 """
309 gcd, x, _ = egcd(a, m)
310 if gcd != 1:
311 return None # modular inverse does not exist
312 else:
313 return x % m
314
315
316def parse_number(string):
317 """Parse a string as a number.
318
319 This is just a short-hand for int(string, 0) suitable for use in the
320 |type| parameter of |ArgumentParser|'s add_argument() function. An
321 improvement to just using type=int is that this function supports
322 numbers in other bases, e.g. "0x1234".
323
324 Arguments:
325 string: The string to parse.
326
327 Returns:
328 The parsed integer.
329
330 Raises:
331 ValueError: If the number could not be parsed.
332 """
333 return int(string, 0)
334
335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336class RSAPublicKey(object):
337 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339 Attributes:
340 exponent: The key exponent.
341 modulus: The key modulus.
342 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400343 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400344
345 MODULUS_PREFIX = 'modulus='
346
347 def __init__(self, key_path):
348 """Loads and parses an RSA key from either a private or public key file.
349
350 Arguments:
351 key_path: The path to a key file.
352 """
353 # We used to have something as simple as this:
354 #
355 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
356 # self.exponent = key.e
357 # self.modulus = key.n
358 # self.num_bits = key.size() + 1
359 #
360 # but unfortunately PyCrypto is not available in the builder. So
361 # instead just parse openssl(1) output to get this
362 # information. It's ugly but...
363 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
364 p = subprocess.Popen(args,
365 stdin=subprocess.PIPE,
366 stdout=subprocess.PIPE,
367 stderr=subprocess.PIPE)
368 (pout, perr) = p.communicate()
369 if p.wait() != 0:
370 # Could be just a public key is passed, try that.
371 args.append('-pubin')
372 p = subprocess.Popen(args,
373 stdin=subprocess.PIPE,
374 stdout=subprocess.PIPE,
375 stderr=subprocess.PIPE)
376 (pout, perr) = p.communicate()
377 if p.wait() != 0:
378 raise AvbError('Error getting public key: {}'.format(perr))
379
380 if not pout.lower().startswith(self.MODULUS_PREFIX):
381 raise AvbError('Unexpected modulus output')
382
383 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
384
385 # The exponent is assumed to always be 65537 and the number of
386 # bits can be derived from the modulus by rounding up to the
387 # nearest power of 2.
388 self.modulus = int(modulus_hexstr, 16)
389 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
390 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400391
392
David Zeuthenc68f0822017-03-31 17:22:35 -0400393def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400394 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
395
396 This creates a |AvbRSAPublicKeyHeader| as well as the two large
397 numbers (|key_num_bits| bits long) following it.
398
399 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400400 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400401
402 Returns:
403 A bytearray() with the |AvbRSAPublicKeyHeader|.
404 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400405 key = RSAPublicKey(key_path)
406 if key.exponent != 65537:
407 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400408 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400409 # Calculate n0inv = -1/n[0] (mod 2^32)
410 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400411 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400412 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400413 r = 2L**key.modulus.bit_length()
414 rrmodn = r * r % key.modulus
415 ret.extend(struct.pack('!II', key.num_bits, n0inv))
416 ret.extend(encode_long(key.num_bits, key.modulus))
417 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400418 return ret
419
420
421def lookup_algorithm_by_type(alg_type):
422 """Looks up algorithm by type.
423
424 Arguments:
425 alg_type: The integer representing the type.
426
427 Returns:
428 A tuple with the algorithm name and an |Algorithm| instance.
429
430 Raises:
431 Exception: If the algorithm cannot be found
432 """
433 for alg_name in ALGORITHMS:
434 alg_data = ALGORITHMS[alg_name]
435 if alg_data.algorithm_type == alg_type:
436 return (alg_name, alg_data)
437 raise AvbError('Unknown algorithm type {}'.format(alg_type))
438
439
David Zeuthena156d3d2017-06-01 12:08:09 -0400440def raw_sign(signing_helper, signing_helper_with_files,
441 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900442 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800443 """Computes a raw RSA signature using |signing_helper| or openssl.
444
445 Arguments:
446 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400447 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800448 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900449 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800450 key_path: Path to the private key file. Must be PEM format.
451 raw_data_to_sign: Data to sign (bytearray or str expected).
452
453 Returns:
454 A bytearray containing the signature.
455
456 Raises:
457 Exception: If an error occurs.
458 """
459 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400460 if signing_helper_with_files is not None:
461 signing_file = tempfile.NamedTemporaryFile()
462 signing_file.write(str(raw_data_to_sign))
463 signing_file.flush()
David Zeuthene3cadca2017-02-22 21:25:46 -0500464 p = subprocess.Popen(
David Zeuthena156d3d2017-06-01 12:08:09 -0400465 [signing_helper_with_files, algorithm_name, key_path, signing_file.name])
466 retcode = p.wait()
467 if retcode != 0:
468 raise AvbError('Error signing')
469 signing_file.seek(0)
470 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400472 if signing_helper is not None:
473 p = subprocess.Popen(
474 [signing_helper, algorithm_name, key_path],
475 stdin=subprocess.PIPE,
476 stdout=subprocess.PIPE,
477 stderr=subprocess.PIPE)
478 else:
479 p = subprocess.Popen(
480 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
481 stdin=subprocess.PIPE,
482 stdout=subprocess.PIPE,
483 stderr=subprocess.PIPE)
484 (pout, perr) = p.communicate(str(raw_data_to_sign))
485 retcode = p.wait()
486 if retcode != 0:
487 raise AvbError('Error signing: {}'.format(perr))
488 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900489 if len(signature) != signature_num_bytes:
490 raise AvbError('Error signing: Invalid length of signature')
491 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800492
493
David Zeuthenb623d8b2017-04-04 16:05:53 -0400494def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
495 """Checks that the signature in a vbmeta blob was made by
496 the embedded public key.
497
498 Arguments:
499 vbmeta_header: A AvbVBMetaHeader.
500 vbmeta_blob: The whole vbmeta blob, including the header.
501
502 Returns:
503 True if the signature is valid and corresponds to the embedded
504 public key. Also returns True if the vbmeta blob is not signed.
505 """
506 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
507 if alg.hash_name == '':
508 return True
509 header_blob = vbmeta_blob[0:256]
510 auth_offset = 256
511 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
512 aux_size = vbmeta_header.auxiliary_data_block_size
513 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
514 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
515 pubkey_size = vbmeta_header.public_key_size
516 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
517
518 digest_offset = auth_offset + vbmeta_header.hash_offset
519 digest_size = vbmeta_header.hash_size
520 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
521
522 sig_offset = auth_offset + vbmeta_header.signature_offset
523 sig_size = vbmeta_header.signature_size
524 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
525
526 # Now that we've got the stored digest, public key, and signature
527 # all we need to do is to verify. This is the exactly the same
528 # steps as performed in the avb_vbmeta_image_verify() function in
529 # libavb/avb_vbmeta_image.c.
530
531 ha = hashlib.new(alg.hash_name)
532 ha.update(header_blob)
533 ha.update(aux_blob)
534 computed_digest = ha.digest()
535
536 if computed_digest != digest_blob:
537 return False
538
539 padding_and_digest = bytearray(alg.padding)
540 padding_and_digest.extend(computed_digest)
541
542 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
543 modulus_blob = pubkey_blob[8:8 + num_bits/8]
544 modulus = decode_long(modulus_blob)
545 exponent = 65537
546
547 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
548 # is OK since 'avbtool verify_image' is not expected to run on the
549 # Android builders (see bug #36809096).
550 import Crypto.PublicKey.RSA
551 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
552 if not key.verify(decode_long(padding_and_digest),
553 (decode_long(sig_blob), None)):
554 return False
555 return True
556
557
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558class ImageChunk(object):
559 """Data structure used for representing chunks in Android sparse files.
560
561 Attributes:
562 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
563 chunk_offset: Offset in the sparse file where this chunk begins.
564 output_offset: Offset in de-sparsified file where output begins.
565 output_size: Number of bytes in output.
566 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
567 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
568 """
569
570 FORMAT = '<2H2I'
571 TYPE_RAW = 0xcac1
572 TYPE_FILL = 0xcac2
573 TYPE_DONT_CARE = 0xcac3
574 TYPE_CRC32 = 0xcac4
575
576 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
577 input_offset, fill_data):
578 """Initializes an ImageChunk object.
579
580 Arguments:
581 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
582 chunk_offset: Offset in the sparse file where this chunk begins.
583 output_offset: Offset in de-sparsified file.
584 output_size: Number of bytes in output.
585 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
586 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
587
588 Raises:
589 ValueError: If data is not well-formed.
590 """
591 self.chunk_type = chunk_type
592 self.chunk_offset = chunk_offset
593 self.output_offset = output_offset
594 self.output_size = output_size
595 self.input_offset = input_offset
596 self.fill_data = fill_data
597 # Check invariants.
598 if self.chunk_type == self.TYPE_RAW:
599 if self.fill_data is not None:
600 raise ValueError('RAW chunk cannot have fill_data set.')
601 if not self.input_offset:
602 raise ValueError('RAW chunk must have input_offset set.')
603 elif self.chunk_type == self.TYPE_FILL:
604 if self.fill_data is None:
605 raise ValueError('FILL chunk must have fill_data set.')
606 if self.input_offset:
607 raise ValueError('FILL chunk cannot have input_offset set.')
608 elif self.chunk_type == self.TYPE_DONT_CARE:
609 if self.fill_data is not None:
610 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
611 if self.input_offset:
612 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
613 else:
614 raise ValueError('Invalid chunk type')
615
616
617class ImageHandler(object):
618 """Abstraction for image I/O with support for Android sparse images.
619
620 This class provides an interface for working with image files that
621 may be using the Android Sparse Image format. When an instance is
622 constructed, we test whether it's an Android sparse file. If so,
623 operations will be on the sparse file by interpreting the sparse
624 format, otherwise they will be directly on the file. Either way the
625 operations do the same.
626
627 For reading, this interface mimics a file object - it has seek(),
628 tell(), and read() methods. For writing, only truncation
629 (truncate()) and appending is supported (append_raw() and
630 append_dont_care()). Additionally, data can only be written in units
631 of the block size.
632
633 Attributes:
634 is_sparse: Whether the file being operated on is sparse.
635 block_size: The block size, typically 4096.
636 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400637 """
638 # See system/core/libsparse/sparse_format.h for details.
639 MAGIC = 0xed26ff3a
640 HEADER_FORMAT = '<I4H4I'
641
642 # These are formats and offset of just the |total_chunks| and
643 # |total_blocks| fields.
644 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
645 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
646
647 def __init__(self, image_filename):
648 """Initializes an image handler.
649
650 Arguments:
651 image_filename: The name of the file to operate on.
652
653 Raises:
654 ValueError: If data in the file is invalid.
655 """
656 self._image_filename = image_filename
657 self._read_header()
658
659 def _read_header(self):
660 """Initializes internal data structures used for reading file.
661
662 This may be called multiple times and is typically called after
663 modifying the file (e.g. appending, truncation).
664
665 Raises:
666 ValueError: If data in the file is invalid.
667 """
668 self.is_sparse = False
669 self.block_size = 4096
670 self._file_pos = 0
671 self._image = open(self._image_filename, 'r+b')
672 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400673 self.image_size = self._image.tell()
674
675 self._image.seek(0, os.SEEK_SET)
676 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
677 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
678 block_size, self._num_total_blocks, self._num_total_chunks,
679 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
680 if magic != self.MAGIC:
681 # Not a sparse image, our job here is done.
682 return
683 if not (major_version == 1 and minor_version == 0):
684 raise ValueError('Encountered sparse image format version {}.{} but '
685 'only 1.0 is supported'.format(major_version,
686 minor_version))
687 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
688 raise ValueError('Unexpected file_hdr_sz value {}.'.
689 format(file_hdr_sz))
690 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
691 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
692 format(chunk_hdr_sz))
693
694 self.block_size = block_size
695
696 # Build an list of chunks by parsing the file.
697 self._chunks = []
698
699 # Find the smallest offset where only "Don't care" chunks
700 # follow. This will be the size of the content in the sparse
701 # image.
702 offset = 0
703 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400704 for _ in xrange(1, self._num_total_chunks + 1):
705 chunk_offset = self._image.tell()
706
707 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
708 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
709 header_bin)
710 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
711
David Zeuthena4fee8b2016-08-22 15:20:43 -0400712 if chunk_type == ImageChunk.TYPE_RAW:
713 if data_sz != (chunk_sz * self.block_size):
714 raise ValueError('Raw chunk input size ({}) does not match output '
715 'size ({})'.
716 format(data_sz, chunk_sz*self.block_size))
717 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
718 chunk_offset,
719 output_offset,
720 chunk_sz*self.block_size,
721 self._image.tell(),
722 None))
723 self._image.read(data_sz)
724
725 elif chunk_type == ImageChunk.TYPE_FILL:
726 if data_sz != 4:
727 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
728 'has {}'.format(data_sz))
729 fill_data = self._image.read(4)
730 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
731 chunk_offset,
732 output_offset,
733 chunk_sz*self.block_size,
734 None,
735 fill_data))
736 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
737 if data_sz != 0:
738 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
739 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400740 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
741 chunk_offset,
742 output_offset,
743 chunk_sz*self.block_size,
744 None,
745 None))
746 elif chunk_type == ImageChunk.TYPE_CRC32:
747 if data_sz != 4:
748 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
749 'this has {}'.format(data_sz))
750 self._image.read(4)
751 else:
752 raise ValueError('Unknown chunk type {}'.format(chunk_type))
753
754 offset += chunk_sz
755 output_offset += chunk_sz*self.block_size
756
757 # Record where sparse data end.
758 self._sparse_end = self._image.tell()
759
760 # Now that we've traversed all chunks, sanity check.
761 if self._num_total_blocks != offset:
762 raise ValueError('The header said we should have {} output blocks, '
763 'but we saw {}'.format(self._num_total_blocks, offset))
764 junk_len = len(self._image.read())
765 if junk_len > 0:
766 raise ValueError('There were {} bytes of extra data at the end of the '
767 'file.'.format(junk_len))
768
David Zeuthen09692692016-09-30 16:16:40 -0400769 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400770 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400771
772 # This is used when bisecting in read() to find the initial slice.
773 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
774
775 self.is_sparse = True
776
777 def _update_chunks_and_blocks(self):
778 """Helper function to update the image header.
779
780 The the |total_chunks| and |total_blocks| fields in the header
781 will be set to value of the |_num_total_blocks| and
782 |_num_total_chunks| attributes.
783
784 """
785 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
786 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
787 self._num_total_blocks,
788 self._num_total_chunks))
789
790 def append_dont_care(self, num_bytes):
791 """Appends a DONT_CARE chunk to the sparse file.
792
793 The given number of bytes must be a multiple of the block size.
794
795 Arguments:
796 num_bytes: Size in number of bytes of the DONT_CARE chunk.
797 """
798 assert num_bytes % self.block_size == 0
799
800 if not self.is_sparse:
801 self._image.seek(0, os.SEEK_END)
802 # This is more efficient that writing NUL bytes since it'll add
803 # a hole on file systems that support sparse files (native
804 # sparse, not Android sparse).
805 self._image.truncate(self._image.tell() + num_bytes)
806 self._read_header()
807 return
808
809 self._num_total_chunks += 1
810 self._num_total_blocks += num_bytes / self.block_size
811 self._update_chunks_and_blocks()
812
813 self._image.seek(self._sparse_end, os.SEEK_SET)
814 self._image.write(struct.pack(ImageChunk.FORMAT,
815 ImageChunk.TYPE_DONT_CARE,
816 0, # Reserved
817 num_bytes / self.block_size,
818 struct.calcsize(ImageChunk.FORMAT)))
819 self._read_header()
820
821 def append_raw(self, data):
822 """Appends a RAW chunk to the sparse file.
823
824 The length of the given data must be a multiple of the block size.
825
826 Arguments:
827 data: Data to append.
828 """
829 assert len(data) % self.block_size == 0
830
831 if not self.is_sparse:
832 self._image.seek(0, os.SEEK_END)
833 self._image.write(data)
834 self._read_header()
835 return
836
837 self._num_total_chunks += 1
838 self._num_total_blocks += len(data) / self.block_size
839 self._update_chunks_and_blocks()
840
841 self._image.seek(self._sparse_end, os.SEEK_SET)
842 self._image.write(struct.pack(ImageChunk.FORMAT,
843 ImageChunk.TYPE_RAW,
844 0, # Reserved
845 len(data) / self.block_size,
846 len(data) +
847 struct.calcsize(ImageChunk.FORMAT)))
848 self._image.write(data)
849 self._read_header()
850
851 def append_fill(self, fill_data, size):
852 """Appends a fill chunk to the sparse file.
853
854 The total length of the fill data must be a multiple of the block size.
855
856 Arguments:
857 fill_data: Fill data to append - must be four bytes.
858 size: Number of chunk - must be a multiple of four and the block size.
859 """
860 assert len(fill_data) == 4
861 assert size % 4 == 0
862 assert size % self.block_size == 0
863
864 if not self.is_sparse:
865 self._image.seek(0, os.SEEK_END)
866 self._image.write(fill_data * (size/4))
867 self._read_header()
868 return
869
870 self._num_total_chunks += 1
871 self._num_total_blocks += size / self.block_size
872 self._update_chunks_and_blocks()
873
874 self._image.seek(self._sparse_end, os.SEEK_SET)
875 self._image.write(struct.pack(ImageChunk.FORMAT,
876 ImageChunk.TYPE_FILL,
877 0, # Reserved
878 size / self.block_size,
879 4 + struct.calcsize(ImageChunk.FORMAT)))
880 self._image.write(fill_data)
881 self._read_header()
882
883 def seek(self, offset):
884 """Sets the cursor position for reading from unsparsified file.
885
886 Arguments:
887 offset: Offset to seek to from the beginning of the file.
888 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700889 if offset < 0:
890 raise RuntimeError("Seeking with negative offset: %d" % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400891 self._file_pos = offset
892
893 def read(self, size):
894 """Reads data from the unsparsified file.
895
896 This method may return fewer than |size| bytes of data if the end
897 of the file was encountered.
898
899 The file cursor for reading is advanced by the number of bytes
900 read.
901
902 Arguments:
903 size: Number of bytes to read.
904
905 Returns:
906 The data.
907
908 """
909 if not self.is_sparse:
910 self._image.seek(self._file_pos)
911 data = self._image.read(size)
912 self._file_pos += len(data)
913 return data
914
915 # Iterate over all chunks.
916 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
917 self._file_pos) - 1
918 data = bytearray()
919 to_go = size
920 while to_go > 0:
921 chunk = self._chunks[chunk_idx]
922 chunk_pos_offset = self._file_pos - chunk.output_offset
923 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
924
925 if chunk.chunk_type == ImageChunk.TYPE_RAW:
926 self._image.seek(chunk.input_offset + chunk_pos_offset)
927 data.extend(self._image.read(chunk_pos_to_go))
928 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
929 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
930 offset_mod = chunk_pos_offset % len(chunk.fill_data)
931 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
932 else:
933 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
934 data.extend('\0' * chunk_pos_to_go)
935
936 to_go -= chunk_pos_to_go
937 self._file_pos += chunk_pos_to_go
938 chunk_idx += 1
939 # Generate partial read in case of EOF.
940 if chunk_idx >= len(self._chunks):
941 break
942
943 return data
944
945 def tell(self):
946 """Returns the file cursor position for reading from unsparsified file.
947
948 Returns:
949 The file cursor position for reading.
950 """
951 return self._file_pos
952
953 def truncate(self, size):
954 """Truncates the unsparsified file.
955
956 Arguments:
957 size: Desired size of unsparsified file.
958
959 Raises:
960 ValueError: If desired size isn't a multiple of the block size.
961 """
962 if not self.is_sparse:
963 self._image.truncate(size)
964 self._read_header()
965 return
966
967 if size % self.block_size != 0:
968 raise ValueError('Cannot truncate to a size which is not a multiple '
969 'of the block size')
970
971 if size == self.image_size:
972 # Trivial where there's nothing to do.
973 return
974 elif size < self.image_size:
975 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
976 chunk = self._chunks[chunk_idx]
977 if chunk.output_offset != size:
978 # Truncation in the middle of a trunk - need to keep the chunk
979 # and modify it.
980 chunk_idx_for_update = chunk_idx + 1
981 num_to_keep = size - chunk.output_offset
982 assert num_to_keep % self.block_size == 0
983 if chunk.chunk_type == ImageChunk.TYPE_RAW:
984 truncate_at = (chunk.chunk_offset +
985 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
986 data_sz = num_to_keep
987 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
988 truncate_at = (chunk.chunk_offset +
989 struct.calcsize(ImageChunk.FORMAT) + 4)
990 data_sz = 4
991 else:
992 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
993 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
994 data_sz = 0
995 chunk_sz = num_to_keep/self.block_size
996 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
997 self._image.seek(chunk.chunk_offset)
998 self._image.write(struct.pack(ImageChunk.FORMAT,
999 chunk.chunk_type,
1000 0, # Reserved
1001 chunk_sz,
1002 total_sz))
1003 chunk.output_size = num_to_keep
1004 else:
1005 # Truncation at trunk boundary.
1006 truncate_at = chunk.chunk_offset
1007 chunk_idx_for_update = chunk_idx
1008
1009 self._num_total_chunks = chunk_idx_for_update
1010 self._num_total_blocks = 0
1011 for i in range(0, chunk_idx_for_update):
1012 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1013 self._update_chunks_and_blocks()
1014 self._image.truncate(truncate_at)
1015
1016 # We've modified the file so re-read all data.
1017 self._read_header()
1018 else:
1019 # Truncating to grow - just add a DONT_CARE section.
1020 self.append_dont_care(size - self.image_size)
1021
1022
David Zeuthen21e95262016-07-27 17:58:40 -04001023class AvbDescriptor(object):
1024 """Class for AVB descriptor.
1025
1026 See the |AvbDescriptor| C struct for more information.
1027
1028 Attributes:
1029 tag: The tag identifying what kind of descriptor this is.
1030 data: The data in the descriptor.
1031 """
1032
1033 SIZE = 16
1034 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1035
1036 def __init__(self, data):
1037 """Initializes a new property descriptor.
1038
1039 Arguments:
1040 data: If not None, must be a bytearray().
1041
1042 Raises:
1043 LookupError: If the given descriptor is malformed.
1044 """
1045 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1046
1047 if data:
1048 (self.tag, num_bytes_following) = (
1049 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1050 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1051 else:
1052 self.tag = None
1053 self.data = None
1054
1055 def print_desc(self, o):
1056 """Print the descriptor.
1057
1058 Arguments:
1059 o: The object to write the output to.
1060 """
1061 o.write(' Unknown descriptor:\n')
1062 o.write(' Tag: {}\n'.format(self.tag))
1063 if len(self.data) < 256:
1064 o.write(' Data: {} ({} bytes)\n'.format(
1065 repr(str(self.data)), len(self.data)))
1066 else:
1067 o.write(' Data: {} bytes\n'.format(len(self.data)))
1068
1069 def encode(self):
1070 """Serializes the descriptor.
1071
1072 Returns:
1073 A bytearray() with the descriptor data.
1074 """
1075 num_bytes_following = len(self.data)
1076 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1077 padding_size = nbf_with_padding - num_bytes_following
1078 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1079 padding = struct.pack(str(padding_size) + 'x')
1080 ret = desc + self.data + padding
1081 return bytearray(ret)
1082
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001083 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1084 """Verifies contents of the descriptor - used in verify_image sub-command.
1085
1086 Arguments:
1087 image_dir: The directory of the file being verified.
1088 image_ext: The extension of the file being verified (e.g. '.img').
1089 expected_chain_partitions_map: A map from partition name to the
1090 tuple (rollback_index_location, key_blob).
1091
1092 Returns:
1093 True if the descriptor verifies, False otherwise.
1094 """
1095 # Nothing to do.
1096 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001097
1098class AvbPropertyDescriptor(AvbDescriptor):
1099 """A class for property descriptors.
1100
1101 See the |AvbPropertyDescriptor| C struct for more information.
1102
1103 Attributes:
1104 key: The key.
1105 value: The key.
1106 """
1107
1108 TAG = 0
1109 SIZE = 32
1110 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1111 'Q' # key size (bytes)
1112 'Q') # value size (bytes)
1113
1114 def __init__(self, data=None):
1115 """Initializes a new property descriptor.
1116
1117 Arguments:
1118 data: If not None, must be a bytearray of size |SIZE|.
1119
1120 Raises:
1121 LookupError: If the given descriptor is malformed.
1122 """
1123 AvbDescriptor.__init__(self, None)
1124 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1125
1126 if data:
1127 (tag, num_bytes_following, key_size,
1128 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1129 expected_size = round_to_multiple(
1130 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1131 if tag != self.TAG or num_bytes_following != expected_size:
1132 raise LookupError('Given data does not look like a property '
1133 'descriptor.')
1134 self.key = data[self.SIZE:(self.SIZE + key_size)]
1135 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1136 value_size)]
1137 else:
1138 self.key = ''
1139 self.value = ''
1140
1141 def print_desc(self, o):
1142 """Print the descriptor.
1143
1144 Arguments:
1145 o: The object to write the output to.
1146 """
1147 if len(self.value) < 256:
1148 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1149 else:
1150 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1151
1152 def encode(self):
1153 """Serializes the descriptor.
1154
1155 Returns:
1156 A bytearray() with the descriptor data.
1157 """
1158 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1159 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1160 padding_size = nbf_with_padding - num_bytes_following
1161 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1162 len(self.key), len(self.value))
1163 padding = struct.pack(str(padding_size) + 'x')
1164 ret = desc + self.key + '\0' + self.value + '\0' + padding
1165 return bytearray(ret)
1166
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001167 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1168 """Verifies contents of the descriptor - used in verify_image sub-command.
1169
1170 Arguments:
1171 image_dir: The directory of the file being verified.
1172 image_ext: The extension of the file being verified (e.g. '.img').
1173 expected_chain_partitions_map: A map from partition name to the
1174 tuple (rollback_index_location, key_blob).
1175
1176 Returns:
1177 True if the descriptor verifies, False otherwise.
1178 """
1179 # Nothing to do.
1180 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001181
1182class AvbHashtreeDescriptor(AvbDescriptor):
1183 """A class for hashtree descriptors.
1184
1185 See the |AvbHashtreeDescriptor| C struct for more information.
1186
1187 Attributes:
1188 dm_verity_version: dm-verity version used.
1189 image_size: Size of the image, after rounding up to |block_size|.
1190 tree_offset: Offset of the hash tree in the file.
1191 tree_size: Size of the tree.
1192 data_block_size: Data block size
1193 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001194 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1195 fec_offset: Offset of FEC data (0 if FEC is not used).
1196 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001197 hash_algorithm: Hash algorithm used.
1198 partition_name: Partition name.
1199 salt: Salt used.
1200 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001201 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001202 """
1203
1204 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001205 RESERVED = 60
1206 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001207 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1208 'L' # dm-verity version used
1209 'Q' # image size (bytes)
1210 'Q' # tree offset (bytes)
1211 'Q' # tree size (bytes)
1212 'L' # data block size (bytes)
1213 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001214 'L' # FEC number of roots
1215 'Q' # FEC offset (bytes)
1216 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001217 '32s' # hash algorithm used
1218 'L' # partition name (bytes)
1219 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001220 'L' # root digest length (bytes)
1221 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001222 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001223
1224 def __init__(self, data=None):
1225 """Initializes a new hashtree descriptor.
1226
1227 Arguments:
1228 data: If not None, must be a bytearray of size |SIZE|.
1229
1230 Raises:
1231 LookupError: If the given descriptor is malformed.
1232 """
1233 AvbDescriptor.__init__(self, None)
1234 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1235
1236 if data:
1237 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1238 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001239 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1240 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001241 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1242 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001243 expected_size = round_to_multiple(
1244 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1245 if tag != self.TAG or num_bytes_following != expected_size:
1246 raise LookupError('Given data does not look like a hashtree '
1247 'descriptor.')
1248 # Nuke NUL-bytes at the end.
1249 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1250 o = 0
1251 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1252 partition_name_len)])
1253 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1254 self.partition_name.decode('utf-8')
1255 o += partition_name_len
1256 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1257 o += salt_len
1258 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1259 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001260 if root_digest_len != 0:
1261 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001262
1263 else:
1264 self.dm_verity_version = 0
1265 self.image_size = 0
1266 self.tree_offset = 0
1267 self.tree_size = 0
1268 self.data_block_size = 0
1269 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001270 self.fec_num_roots = 0
1271 self.fec_offset = 0
1272 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001273 self.hash_algorithm = ''
1274 self.partition_name = ''
1275 self.salt = bytearray()
1276 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001277 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001278
1279 def print_desc(self, o):
1280 """Print the descriptor.
1281
1282 Arguments:
1283 o: The object to write the output to.
1284 """
1285 o.write(' Hashtree descriptor:\n')
1286 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1287 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1288 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1289 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1290 o.write(' Data Block Size: {} bytes\n'.format(
1291 self.data_block_size))
1292 o.write(' Hash Block Size: {} bytes\n'.format(
1293 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001294 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1295 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1296 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001297 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1298 o.write(' Partition Name: {}\n'.format(self.partition_name))
1299 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1300 'hex')))
1301 o.write(' Root Digest: {}\n'.format(str(
1302 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001303 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001304
1305 def encode(self):
1306 """Serializes the descriptor.
1307
1308 Returns:
1309 A bytearray() with the descriptor data.
1310 """
1311 encoded_name = self.partition_name.encode('utf-8')
1312 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1313 len(self.root_digest) - 16)
1314 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1315 padding_size = nbf_with_padding - num_bytes_following
1316 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1317 self.dm_verity_version, self.image_size,
1318 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001319 self.hash_block_size, self.fec_num_roots,
1320 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001321 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001322 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001323 padding = struct.pack(str(padding_size) + 'x')
1324 ret = desc + encoded_name + self.salt + self.root_digest + padding
1325 return bytearray(ret)
1326
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001327 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1328 """Verifies contents of the descriptor - used in verify_image sub-command.
1329
1330 Arguments:
1331 image_dir: The directory of the file being verified.
1332 image_ext: The extension of the file being verified (e.g. '.img').
1333 expected_chain_partitions_map: A map from partition name to the
1334 tuple (rollback_index_location, key_blob).
1335
1336 Returns:
1337 True if the descriptor verifies, False otherwise.
1338 """
1339 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1340 image = ImageHandler(image_filename)
1341 # Generate the hashtree and checks that it matches what's in the file.
1342 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1343 digest_padding = round_to_pow2(digest_size) - digest_size
1344 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1345 self.image_size, self.data_block_size, digest_size + digest_padding)
1346 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1347 self.data_block_size,
1348 self.hash_algorithm, self.salt,
1349 digest_padding,
1350 hash_level_offsets,
1351 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001352 # The root digest must match unless it is not embedded in the descriptor.
1353 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001354 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1355 format(image_filename))
1356 return False
1357 # ... also check that the on-disk hashtree matches
1358 image.seek(self.tree_offset)
1359 hash_tree_ondisk = image.read(self.tree_size)
1360 if hash_tree != hash_tree_ondisk:
1361 sys.stderr.write('hashtree of {} contains invalid data\n'.
1362 format(image_filename))
1363 return False
1364 # TODO: we could also verify that the FEC stored in the image is
1365 # correct but this a) currently requires the 'fec' binary; and b)
1366 # takes a long time; and c) is not strictly needed for
1367 # verification purposes as we've already verified the root hash.
1368 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1369 .format(self.partition_name, self.hash_algorithm, image_filename,
1370 self.image_size))
1371 return True
1372
David Zeuthen21e95262016-07-27 17:58:40 -04001373
1374class AvbHashDescriptor(AvbDescriptor):
1375 """A class for hash descriptors.
1376
1377 See the |AvbHashDescriptor| C struct for more information.
1378
1379 Attributes:
1380 image_size: Image size, in bytes.
1381 hash_algorithm: Hash algorithm used.
1382 partition_name: Partition name.
1383 salt: Salt used.
1384 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001385 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001386 """
1387
1388 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001389 RESERVED = 60
1390 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001391 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1392 'Q' # image size (bytes)
1393 '32s' # hash algorithm used
1394 'L' # partition name (bytes)
1395 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001396 'L' # digest length (bytes)
1397 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001398 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001399
1400 def __init__(self, data=None):
1401 """Initializes a new hash descriptor.
1402
1403 Arguments:
1404 data: If not None, must be a bytearray of size |SIZE|.
1405
1406 Raises:
1407 LookupError: If the given descriptor is malformed.
1408 """
1409 AvbDescriptor.__init__(self, None)
1410 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1411
1412 if data:
1413 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1414 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001415 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1416 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001417 expected_size = round_to_multiple(
1418 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1419 if tag != self.TAG or num_bytes_following != expected_size:
1420 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1421 # Nuke NUL-bytes at the end.
1422 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1423 o = 0
1424 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1425 partition_name_len)])
1426 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1427 self.partition_name.decode('utf-8')
1428 o += partition_name_len
1429 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1430 o += salt_len
1431 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1432 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001433 if digest_len != 0:
1434 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001435
1436 else:
1437 self.image_size = 0
1438 self.hash_algorithm = ''
1439 self.partition_name = ''
1440 self.salt = bytearray()
1441 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001442 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001443
1444 def print_desc(self, o):
1445 """Print the descriptor.
1446
1447 Arguments:
1448 o: The object to write the output to.
1449 """
1450 o.write(' Hash descriptor:\n')
1451 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1452 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1453 o.write(' Partition Name: {}\n'.format(self.partition_name))
1454 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1455 'hex')))
1456 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1457 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001458 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001459
1460 def encode(self):
1461 """Serializes the descriptor.
1462
1463 Returns:
1464 A bytearray() with the descriptor data.
1465 """
1466 encoded_name = self.partition_name.encode('utf-8')
1467 num_bytes_following = (
1468 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1469 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1470 padding_size = nbf_with_padding - num_bytes_following
1471 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1472 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001473 len(self.salt), len(self.digest), self.flags,
1474 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001475 padding = struct.pack(str(padding_size) + 'x')
1476 ret = desc + encoded_name + self.salt + self.digest + padding
1477 return bytearray(ret)
1478
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001479 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1480 """Verifies contents of the descriptor - used in verify_image sub-command.
1481
1482 Arguments:
1483 image_dir: The directory of the file being verified.
1484 image_ext: The extension of the file being verified (e.g. '.img').
1485 expected_chain_partitions_map: A map from partition name to the
1486 tuple (rollback_index_location, key_blob).
1487
1488 Returns:
1489 True if the descriptor verifies, False otherwise.
1490 """
1491 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1492 image = ImageHandler(image_filename)
1493 data = image.read(self.image_size)
1494 ha = hashlib.new(self.hash_algorithm)
1495 ha.update(self.salt)
1496 ha.update(data)
1497 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001498 # The digest must match unless there is no digest in the descriptor.
1499 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001500 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1501 format(self.hash_algorithm, image_filename))
1502 return False
1503 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
1504 .format(self.partition_name, self.hash_algorithm, image_filename,
1505 self.image_size))
1506 return True
1507
David Zeuthen21e95262016-07-27 17:58:40 -04001508
1509class AvbKernelCmdlineDescriptor(AvbDescriptor):
1510 """A class for kernel command-line descriptors.
1511
1512 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1513
1514 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001515 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001516 kernel_cmdline: The kernel command-line.
1517 """
1518
1519 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001520 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001521 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001522 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001523 'L') # cmdline length (bytes)
1524
David Zeuthenfd41eb92016-11-17 12:24:47 -05001525 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1526 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1527
David Zeuthen21e95262016-07-27 17:58:40 -04001528 def __init__(self, data=None):
1529 """Initializes a new kernel cmdline descriptor.
1530
1531 Arguments:
1532 data: If not None, must be a bytearray of size |SIZE|.
1533
1534 Raises:
1535 LookupError: If the given descriptor is malformed.
1536 """
1537 AvbDescriptor.__init__(self, None)
1538 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1539
1540 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001541 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001542 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1543 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1544 8)
1545 if tag != self.TAG or num_bytes_following != expected_size:
1546 raise LookupError('Given data does not look like a kernel cmdline '
1547 'descriptor.')
1548 # Nuke NUL-bytes at the end.
1549 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1550 kernel_cmdline_length)])
1551 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1552 self.kernel_cmdline.decode('utf-8')
1553 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001554 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001555 self.kernel_cmdline = ''
1556
1557 def print_desc(self, o):
1558 """Print the descriptor.
1559
1560 Arguments:
1561 o: The object to write the output to.
1562 """
1563 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001564 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001565 o.write(' Kernel Cmdline: {}\n'.format(repr(
1566 self.kernel_cmdline)))
1567
1568 def encode(self):
1569 """Serializes the descriptor.
1570
1571 Returns:
1572 A bytearray() with the descriptor data.
1573 """
1574 encoded_str = self.kernel_cmdline.encode('utf-8')
1575 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1576 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1577 padding_size = nbf_with_padding - num_bytes_following
1578 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001579 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001580 padding = struct.pack(str(padding_size) + 'x')
1581 ret = desc + encoded_str + padding
1582 return bytearray(ret)
1583
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001584 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1585 """Verifies contents of the descriptor - used in verify_image sub-command.
1586
1587 Arguments:
1588 image_dir: The directory of the file being verified.
1589 image_ext: The extension of the file being verified (e.g. '.img').
1590 expected_chain_partitions_map: A map from partition name to the
1591 tuple (rollback_index_location, key_blob).
1592
1593 Returns:
1594 True if the descriptor verifies, False otherwise.
1595 """
1596 # Nothing to verify.
1597 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001598
1599class AvbChainPartitionDescriptor(AvbDescriptor):
1600 """A class for chained partition descriptors.
1601
1602 See the |AvbChainPartitionDescriptor| C struct for more information.
1603
1604 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001605 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001606 partition_name: Partition name.
1607 public_key: Bytes for the public key.
1608 """
1609
1610 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001611 RESERVED = 64
1612 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001613 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001614 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001615 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001616 'L' + # public_key_size (bytes)
1617 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001618
1619 def __init__(self, data=None):
1620 """Initializes a new chain partition descriptor.
1621
1622 Arguments:
1623 data: If not None, must be a bytearray of size |SIZE|.
1624
1625 Raises:
1626 LookupError: If the given descriptor is malformed.
1627 """
1628 AvbDescriptor.__init__(self, None)
1629 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1630
1631 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001632 (tag, num_bytes_following, self.rollback_index_location,
1633 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001634 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001635 expected_size = round_to_multiple(
1636 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1637 if tag != self.TAG or num_bytes_following != expected_size:
1638 raise LookupError('Given data does not look like a chain partition '
1639 'descriptor.')
1640 o = 0
1641 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1642 partition_name_len)])
1643 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1644 self.partition_name.decode('utf-8')
1645 o += partition_name_len
1646 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1647
1648 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001649 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001650 self.partition_name = ''
1651 self.public_key = bytearray()
1652
1653 def print_desc(self, o):
1654 """Print the descriptor.
1655
1656 Arguments:
1657 o: The object to write the output to.
1658 """
1659 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001660 o.write(' Partition Name: {}\n'.format(self.partition_name))
1661 o.write(' Rollback Index Location: {}\n'.format(
1662 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001663 # Just show the SHA1 of the key, for size reasons.
1664 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001665 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001666
1667 def encode(self):
1668 """Serializes the descriptor.
1669
1670 Returns:
1671 A bytearray() with the descriptor data.
1672 """
1673 encoded_name = self.partition_name.encode('utf-8')
1674 num_bytes_following = (
1675 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1676 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1677 padding_size = nbf_with_padding - num_bytes_following
1678 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001679 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001680 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001681 padding = struct.pack(str(padding_size) + 'x')
1682 ret = desc + encoded_name + self.public_key + padding
1683 return bytearray(ret)
1684
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001685 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1686 """Verifies contents of the descriptor - used in verify_image sub-command.
1687
1688 Arguments:
1689 image_dir: The directory of the file being verified.
1690 image_ext: The extension of the file being verified (e.g. '.img').
1691 expected_chain_partitions_map: A map from partition name to the
1692 tuple (rollback_index_location, key_blob).
1693
1694 Returns:
1695 True if the descriptor verifies, False otherwise.
1696 """
1697 value = expected_chain_partitions_map.get(self.partition_name)
1698 if not value:
1699 sys.stderr.write('No expected chain partition for partition {}. Use '
1700 '--expected_chain_partition to specify expected '
1701 'contents.\n'.
1702 format(self.partition_name))
1703 return False
1704 rollback_index_location, pk_blob = value
1705
1706 if self.rollback_index_location != rollback_index_location:
1707 sys.stderr.write('Expected rollback_index_location {} does not '
1708 'match {} in descriptor for partition {}\n'.
1709 format(rollback_index_location,
1710 self.rollback_index_location,
1711 self.partition_name))
1712 return False
1713
1714 if self.public_key != pk_blob:
1715 sys.stderr.write('Expected public key blob does not match public '
1716 'key blob in descriptor for partition {}\n'.
1717 format(self.partition_name))
1718 return False
1719
1720 print ('{}: Successfully verified chain partition descriptor matches '
1721 'expected data'.format(self.partition_name))
1722
1723 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001724
1725DESCRIPTOR_CLASSES = [
1726 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1727 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1728]
1729
1730
1731def parse_descriptors(data):
1732 """Parses a blob of data into descriptors.
1733
1734 Arguments:
1735 data: A bytearray() with encoded descriptors.
1736
1737 Returns:
1738 A list of instances of objects derived from AvbDescriptor. For
1739 unknown descriptors, the class AvbDescriptor is used.
1740 """
1741 o = 0
1742 ret = []
1743 while o < len(data):
1744 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1745 if tag < len(DESCRIPTOR_CLASSES):
1746 c = DESCRIPTOR_CLASSES[tag]
1747 else:
1748 c = AvbDescriptor
1749 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1750 o += 16 + nb_following
1751 return ret
1752
1753
1754class AvbFooter(object):
1755 """A class for parsing and writing footers.
1756
1757 Footers are stored at the end of partitions and point to where the
1758 AvbVBMeta blob is located. They also contain the original size of
1759 the image before AVB information was added.
1760
1761 Attributes:
1762 magic: Magic for identifying the footer, see |MAGIC|.
1763 version_major: The major version of avbtool that wrote the footer.
1764 version_minor: The minor version of avbtool that wrote the footer.
1765 original_image_size: Original image size.
1766 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1767 vbmeta_size: Size of the AvbVBMeta blob.
1768 """
1769
1770 MAGIC = 'AVBf'
1771 SIZE = 64
1772 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001773 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1774 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001775 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1776 'Q' # Original image size.
1777 'Q' # Offset of VBMeta blob.
1778 'Q' + # Size of VBMeta blob.
1779 str(RESERVED) + 'x') # padding for reserved bytes
1780
1781 def __init__(self, data=None):
1782 """Initializes a new footer object.
1783
1784 Arguments:
1785 data: If not None, must be a bytearray of size 4096.
1786
1787 Raises:
1788 LookupError: If the given footer is malformed.
1789 struct.error: If the given data has no footer.
1790 """
1791 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1792
1793 if data:
1794 (self.magic, self.version_major, self.version_minor,
1795 self.original_image_size, self.vbmeta_offset,
1796 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1797 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001798 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001799 else:
1800 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001801 self.version_major = self.FOOTER_VERSION_MAJOR
1802 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001803 self.original_image_size = 0
1804 self.vbmeta_offset = 0
1805 self.vbmeta_size = 0
1806
David Zeuthena4fee8b2016-08-22 15:20:43 -04001807 def encode(self):
1808 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001809
David Zeuthena4fee8b2016-08-22 15:20:43 -04001810 Returns:
1811 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001812 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001813 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1814 self.version_minor, self.original_image_size,
1815 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001816
1817
1818class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001819 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001820
1821 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07001822 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1823 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04001824 """
1825
1826 SIZE = 256
1827
David Zeuthene3cadca2017-02-22 21:25:46 -05001828 # Keep in sync with |reserved0| and |reserved| field of
1829 # |AvbVBMetaImageHeader|.
1830 RESERVED0 = 4
1831 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001832
1833 # Keep in sync with |AvbVBMetaImageHeader|.
1834 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1835 '2Q' # 2 x block size
1836 'L' # algorithm type
1837 '2Q' # offset, size (hash)
1838 '2Q' # offset, size (signature)
1839 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001840 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001841 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001842 'Q' # rollback_index
1843 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001844 str(RESERVED0) + 'x' + # padding for reserved bytes
1845 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001846 str(RESERVED) + 'x') # padding for reserved bytes
1847
1848 def __init__(self, data=None):
1849 """Initializes a new header object.
1850
1851 Arguments:
1852 data: If not None, must be a bytearray of size 8192.
1853
1854 Raises:
1855 Exception: If the given data is malformed.
1856 """
1857 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1858
1859 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001860 (self.magic, self.required_libavb_version_major,
1861 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001862 self.authentication_data_block_size, self.auxiliary_data_block_size,
1863 self.algorithm_type, self.hash_offset, self.hash_size,
1864 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001865 self.public_key_size, self.public_key_metadata_offset,
1866 self.public_key_metadata_size, self.descriptors_offset,
1867 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001868 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001869 self.flags,
1870 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001871 # Nuke NUL-bytes at the end of the string.
1872 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001873 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001874 else:
1875 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001876 # Start by just requiring version 1.0. Code that adds features
1877 # in a future version can use bump_required_libavb_version_minor() to
1878 # bump the minor.
1879 self.required_libavb_version_major = AVB_VERSION_MAJOR
1880 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001881 self.authentication_data_block_size = 0
1882 self.auxiliary_data_block_size = 0
1883 self.algorithm_type = 0
1884 self.hash_offset = 0
1885 self.hash_size = 0
1886 self.signature_offset = 0
1887 self.signature_size = 0
1888 self.public_key_offset = 0
1889 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001890 self.public_key_metadata_offset = 0
1891 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001892 self.descriptors_offset = 0
1893 self.descriptors_size = 0
1894 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001895 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001896 self.release_string = get_release_string()
1897
1898 def bump_required_libavb_version_minor(self, minor):
1899 """Function to bump required_libavb_version_minor.
1900
1901 Call this when writing data that requires a specific libavb
1902 version to parse it.
1903
1904 Arguments:
1905 minor: The minor version of libavb that has support for the feature.
1906 """
1907 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001908 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001909
1910 def save(self, output):
1911 """Serializes the header (256 bytes) to disk.
1912
1913 Arguments:
1914 output: The object to write the output to.
1915 """
1916 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001917 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1918 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001919 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1920 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001921 self.public_key_offset, self.public_key_size,
1922 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001923 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001924 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001925
1926 def encode(self):
1927 """Serializes the header (256) to a bytearray().
1928
1929 Returns:
1930 A bytearray() with the encoded header.
1931 """
1932 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001933 self.required_libavb_version_major,
1934 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001935 self.authentication_data_block_size,
1936 self.auxiliary_data_block_size, self.algorithm_type,
1937 self.hash_offset, self.hash_size, self.signature_offset,
1938 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001939 self.public_key_size, self.public_key_metadata_offset,
1940 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001941 self.descriptors_size, self.rollback_index, self.flags,
1942 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001943
1944
1945class Avb(object):
1946 """Business logic for avbtool command-line tool."""
1947
David Zeuthen8b6973b2016-09-20 12:39:49 -04001948 # Keep in sync with avb_ab_flow.h.
1949 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1950 AB_MAGIC = '\0AB0'
1951 AB_MAJOR_VERSION = 1
1952 AB_MINOR_VERSION = 0
1953 AB_MISC_METADATA_OFFSET = 2048
1954
David Zeuthen09692692016-09-30 16:16:40 -04001955 # Constants for maximum metadata size. These are used to give
1956 # meaningful errors if the value passed in via --partition_size is
1957 # too small and when --calc_max_image_size is used. We use
1958 # conservative figures.
1959 MAX_VBMETA_SIZE = 64 * 1024
1960 MAX_FOOTER_SIZE = 4096
1961
David Zeuthena4fee8b2016-08-22 15:20:43 -04001962 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001963 """Implements the 'erase_footer' command.
1964
1965 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001966 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001967 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001968
1969 Raises:
1970 AvbError: If there's no footer in the image.
1971 """
1972
David Zeuthena4fee8b2016-08-22 15:20:43 -04001973 image = ImageHandler(image_filename)
1974
David Zeuthen21e95262016-07-27 17:58:40 -04001975 (footer, _, descriptors, _) = self._parse_image(image)
1976
1977 if not footer:
1978 raise AvbError('Given image does not have a footer.')
1979
1980 new_image_size = None
1981 if not keep_hashtree:
1982 new_image_size = footer.original_image_size
1983 else:
1984 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001985 # descriptor to figure out the location and size of the hashtree
1986 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001987 for desc in descriptors:
1988 if isinstance(desc, AvbHashtreeDescriptor):
1989 # The hashtree is always just following the main data so the
1990 # new size is easily derived.
1991 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001992 # If the image has FEC codes, also keep those.
1993 if desc.fec_offset > 0:
1994 fec_end = desc.fec_offset + desc.fec_size
1995 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001996 break
1997 if not new_image_size:
1998 raise AvbError('Requested to keep hashtree but no hashtree '
1999 'descriptor was found.')
2000
2001 # And cut...
2002 image.truncate(new_image_size)
2003
David Zeuthen2bc232b2017-04-19 14:25:19 -04002004 def resize_image(self, image_filename, partition_size):
2005 """Implements the 'resize_image' command.
2006
2007 Arguments:
2008 image_filename: File with footer to resize.
2009 partition_size: The new size of the image.
2010
2011 Raises:
2012 AvbError: If there's no footer in the image.
2013 """
2014
2015 image = ImageHandler(image_filename)
2016
2017 if partition_size % image.block_size != 0:
2018 raise AvbError('Partition size of {} is not a multiple of the image '
2019 'block size {}.'.format(partition_size,
2020 image.block_size))
2021
2022 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2023
2024 if not footer:
2025 raise AvbError('Given image does not have a footer.')
2026
2027 # The vbmeta blob is always at the end of the data so resizing an
2028 # image amounts to just moving the footer around.
2029
2030 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2031 if vbmeta_end_offset % image.block_size != 0:
2032 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2033
2034 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002035 raise AvbError('Requested size of {} is too small for an image '
2036 'of size {}.'
2037 .format(partition_size,
2038 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002039
2040 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2041 # with enough bytes such that the final Footer block is at the end
2042 # of partition_size.
2043 image.truncate(vbmeta_end_offset)
2044 image.append_dont_care(partition_size - vbmeta_end_offset -
2045 1*image.block_size)
2046
2047 # Just reuse the same footer - only difference is that we're
2048 # writing it in a different place.
2049 footer_blob = footer.encode()
2050 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2051 footer_blob)
2052 image.append_raw(footer_blob_with_padding)
2053
David Zeuthen8b6973b2016-09-20 12:39:49 -04002054 def set_ab_metadata(self, misc_image, slot_data):
2055 """Implements the 'set_ab_metadata' command.
2056
2057 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2058 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2059
2060 Arguments:
2061 misc_image: The misc image to write to.
2062 slot_data: Slot data as a string
2063
2064 Raises:
2065 AvbError: If slot data is malformed.
2066 """
2067 tokens = slot_data.split(':')
2068 if len(tokens) != 6:
2069 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2070 a_priority = int(tokens[0])
2071 a_tries_remaining = int(tokens[1])
2072 a_success = True if int(tokens[2]) != 0 else False
2073 b_priority = int(tokens[3])
2074 b_tries_remaining = int(tokens[4])
2075 b_success = True if int(tokens[5]) != 0 else False
2076
2077 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2078 self.AB_MAGIC,
2079 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2080 a_priority, a_tries_remaining, a_success,
2081 b_priority, b_tries_remaining, b_success)
2082 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2083 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2084 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2085 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2086 misc_image.write(ab_data)
2087
David Zeuthena4fee8b2016-08-22 15:20:43 -04002088 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002089 """Implements the 'info_image' command.
2090
2091 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002092 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002093 output: Output file to write human-readable information to (file object).
2094 """
2095
David Zeuthena4fee8b2016-08-22 15:20:43 -04002096 image = ImageHandler(image_filename)
2097
David Zeuthen21e95262016-07-27 17:58:40 -04002098 o = output
2099
2100 (footer, header, descriptors, image_size) = self._parse_image(image)
2101
2102 if footer:
2103 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2104 footer.version_minor))
2105 o.write('Image size: {} bytes\n'.format(image_size))
2106 o.write('Original image size: {} bytes\n'.format(
2107 footer.original_image_size))
2108 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2109 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2110 o.write('--\n')
2111
2112 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2113
David Zeuthene3cadca2017-02-22 21:25:46 -05002114 o.write('Minimum libavb version: {}.{}{}\n'.format(
2115 header.required_libavb_version_major,
2116 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002117 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002118 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2119 o.write('Authentication Block: {} bytes\n'.format(
2120 header.authentication_data_block_size))
2121 o.write('Auxiliary Block: {} bytes\n'.format(
2122 header.auxiliary_data_block_size))
2123 o.write('Algorithm: {}\n'.format(alg_name))
2124 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002125 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002126 o.write('Release String: \'{}\'\n'.format(
2127 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002128
2129 # Print descriptors.
2130 num_printed = 0
2131 o.write('Descriptors:\n')
2132 for desc in descriptors:
2133 desc.print_desc(o)
2134 num_printed += 1
2135 if num_printed == 0:
2136 o.write(' (none)\n')
2137
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002138 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002139 """Implements the 'verify_image' command.
2140
2141 Arguments:
2142 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002143 key_path: None or check that embedded public key matches key at given path.
2144 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002145 """
2146
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002147 expected_chain_partitions_map = {}
2148 if expected_chain_partitions:
2149 used_locations = {}
2150 for cp in expected_chain_partitions:
2151 cp_tokens = cp.split(':')
2152 if len(cp_tokens) != 3:
2153 raise AvbError('Malformed chained partition "{}".'.format(cp))
2154 partition_name = cp_tokens[0]
2155 rollback_index_location = int(cp_tokens[1])
2156 file_path = cp_tokens[2]
2157 pk_blob = open(file_path).read()
2158 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2159
2160 image_dir = os.path.dirname(image_filename)
2161 image_ext = os.path.splitext(image_filename)[1]
2162
2163 key_blob = None
2164 if key_path:
2165 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2166 key_blob = encode_rsa_key(key_path)
2167 else:
2168 print 'Verifying image {} using embedded public key'.format(image_filename)
2169
David Zeuthenb623d8b2017-04-04 16:05:53 -04002170 image = ImageHandler(image_filename)
2171 (footer, header, descriptors, image_size) = self._parse_image(image)
2172 offset = 0
2173 if footer:
2174 offset = footer.vbmeta_offset
2175 size = (header.SIZE + header.authentication_data_block_size +
2176 header.auxiliary_data_block_size)
2177 image.seek(offset)
2178 vbmeta_blob = image.read(size)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002179 h = AvbVBMetaHeader(vbmeta_blob[0:AvbVBMetaHeader.SIZE])
2180 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002181 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002182 raise AvbError('Signature check failed for {} vbmeta struct {}'
2183 .format(alg_name, image_filename))
2184
2185 if key_blob:
2186 # The embedded public key is in the auxiliary block at an offset.
2187 key_offset = AvbVBMetaHeader.SIZE
2188 key_offset += h.authentication_data_block_size
2189 key_offset += h.public_key_offset
2190 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + h.public_key_size]
2191 if key_blob != key_blob_in_vbmeta:
2192 raise AvbError('Embedded public key does not match given key.')
2193
2194 if footer:
2195 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2196 .format(alg_name, image_filename))
2197 else:
2198 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
2199 .format(alg_name, image_filename))
2200
2201 for desc in descriptors:
2202 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map):
2203 raise AvbError('Error verifying descriptor.')
2204
David Zeuthenb623d8b2017-04-04 16:05:53 -04002205
David Zeuthenb8643c02018-05-17 17:21:18 -04002206 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2207 """Implements the 'calculate_vbmeta_digest' command.
2208
2209 Arguments:
2210 image_filename: Image file to get information from (file object).
2211 hash_algorithm: Hash algorithm used.
2212 output: Output file to write human-readable information to (file object).
2213 """
2214
2215 image_dir = os.path.dirname(image_filename)
2216 image_ext = os.path.splitext(image_filename)[1]
2217
2218 image = ImageHandler(image_filename)
2219 (footer, header, descriptors, image_size) = self._parse_image(image)
2220 offset = 0
2221 if footer:
2222 offset = footer.vbmeta_offset
2223 size = (header.SIZE + header.authentication_data_block_size +
2224 header.auxiliary_data_block_size)
2225 image.seek(offset)
2226 vbmeta_blob = image.read(size)
2227
2228 hasher = hashlib.new(name=hash_algorithm)
2229 hasher.update(vbmeta_blob)
2230
2231 for desc in descriptors:
2232 if isinstance(desc, AvbChainPartitionDescriptor):
2233 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2234 ch_image = ImageHandler(ch_image_filename)
2235 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2236 ch_offset = 0
2237 if ch_footer:
2238 ch_offset = ch_footer.vbmeta_offset
2239 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2240 ch_header.auxiliary_data_block_size)
2241 ch_image.seek(ch_offset)
2242 ch_vbmeta_blob = ch_image.read(ch_size)
2243 hasher.update(ch_vbmeta_blob)
2244
2245 digest = hasher.digest()
2246 output.write('{}\n'.format(digest.encode('hex')))
2247
2248
David Zeuthen21e95262016-07-27 17:58:40 -04002249 def _parse_image(self, image):
2250 """Gets information about an image.
2251
2252 The image can either be a vbmeta or an image with a footer.
2253
2254 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002255 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002256
2257 Returns:
2258 A tuple where the first argument is a AvbFooter (None if there
2259 is no footer on the image), the second argument is a
2260 AvbVBMetaHeader, the third argument is a list of
2261 AvbDescriptor-derived instances, and the fourth argument is the
2262 size of |image|.
2263 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002264 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002265 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002266 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002267 try:
2268 footer = AvbFooter(image.read(AvbFooter.SIZE))
2269 except (LookupError, struct.error):
2270 # Nope, just seek back to the start.
2271 image.seek(0)
2272
2273 vbmeta_offset = 0
2274 if footer:
2275 vbmeta_offset = footer.vbmeta_offset
2276
2277 image.seek(vbmeta_offset)
2278 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2279
2280 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2281 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2282 desc_start_offset = aux_block_offset + h.descriptors_offset
2283 image.seek(desc_start_offset)
2284 descriptors = parse_descriptors(image.read(h.descriptors_size))
2285
David Zeuthen09692692016-09-30 16:16:40 -04002286 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002287
David Zeuthenb1b994d2017-03-06 18:01:31 -05002288 def _load_vbmeta_blob(self, image):
2289 """Gets the vbmeta struct and associated sections.
2290
2291 The image can either be a vbmeta.img or an image with a footer.
2292
2293 Arguments:
2294 image: An ImageHandler (vbmeta or footer).
2295
2296 Returns:
2297 A blob with the vbmeta struct and other sections.
2298 """
2299 assert isinstance(image, ImageHandler)
2300 footer = None
2301 image.seek(image.image_size - AvbFooter.SIZE)
2302 try:
2303 footer = AvbFooter(image.read(AvbFooter.SIZE))
2304 except (LookupError, struct.error):
2305 # Nope, just seek back to the start.
2306 image.seek(0)
2307
2308 vbmeta_offset = 0
2309 if footer:
2310 vbmeta_offset = footer.vbmeta_offset
2311
2312 image.seek(vbmeta_offset)
2313 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2314
2315 image.seek(vbmeta_offset)
2316 data_size = AvbVBMetaHeader.SIZE
2317 data_size += h.authentication_data_block_size
2318 data_size += h.auxiliary_data_block_size
2319 return image.read(data_size)
2320
David Zeuthen73f2afa2017-05-17 16:54:11 -04002321 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002322 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002323
2324 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002325 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002326
2327 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002328 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2329 instructions. There is one for when hashtree is not disabled and one for
2330 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002331
David Zeuthen21e95262016-07-27 17:58:40 -04002332 """
2333
David Zeuthen21e95262016-07-27 17:58:40 -04002334 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002335 c += '0' # start
2336 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2337 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2338 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2339 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2340 c += ' {}'.format(ht.data_block_size) # data_block
2341 c += ' {}'.format(ht.hash_block_size) # hash_block
2342 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2343 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2344 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2345 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2346 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2347 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002348 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002349 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002350 c += ' ignore_zero_blocks'
2351 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2352 c += ' fec_roots {}'.format(ht.fec_num_roots)
2353 # Note that fec_blocks is the size that FEC covers, *not* the
2354 # size of the FEC data. Since we use FEC for everything up until
2355 # the FEC data, it's the same as the offset.
2356 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2357 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2358 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002359 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002360 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002361 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002362 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002363
David Zeuthenfd41eb92016-11-17 12:24:47 -05002364 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002365 desc = AvbKernelCmdlineDescriptor()
2366 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002367 desc.flags = (
2368 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2369
2370 # The descriptor for when hashtree verification is disabled is a lot
2371 # simpler - we just set the root to the partition.
2372 desc_no_ht = AvbKernelCmdlineDescriptor()
2373 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2374 desc_no_ht.flags = (
2375 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2376
2377 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002378
David Zeuthen73f2afa2017-05-17 16:54:11 -04002379 def _get_cmdline_descriptors_for_dm_verity(self, image):
2380 """Generate kernel cmdline descriptors for dm-verity.
2381
2382 Arguments:
2383 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2384
2385 Returns:
2386 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2387 instructions. There is one for when hashtree is not disabled and one for
2388 when it is.
2389
2390 Raises:
2391 AvbError: If |image| doesn't have a hashtree descriptor.
2392
2393 """
2394
2395 (_, _, descriptors, _) = self._parse_image(image)
2396
2397 ht = None
2398 for desc in descriptors:
2399 if isinstance(desc, AvbHashtreeDescriptor):
2400 ht = desc
2401 break
2402
2403 if not ht:
2404 raise AvbError('No hashtree descriptor in given image')
2405
2406 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2407
David Zeuthen21e95262016-07-27 17:58:40 -04002408 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002409 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002410 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002411 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002412 include_descriptors_from_image,
2413 signing_helper,
2414 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002415 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002416 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002417 print_required_libavb_version,
2418 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002419 """Implements the 'make_vbmeta_image' command.
2420
2421 Arguments:
2422 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002423 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002424 algorithm_name: Name of algorithm to use.
2425 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002426 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002427 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002428 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002429 props: Properties to insert (list of strings of the form 'key:value').
2430 props_from_file: Properties to insert (list of strings 'key:<path>').
2431 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002432 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002433 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002434 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002435 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002436 release_string: None or avbtool release string to use instead of default.
2437 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002438 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002439 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002440
2441 Raises:
2442 AvbError: If a chained partition is malformed.
2443 """
2444
David Zeuthen1097a782017-05-31 15:53:17 -04002445 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002446 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002447 if include_descriptors_from_image:
2448 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2449 # version of all included descriptors.
2450 tmp_header = AvbVBMetaHeader()
2451 for image in include_descriptors_from_image:
2452 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2453 tmp_header.bump_required_libavb_version_minor(
2454 image_header.required_libavb_version_minor)
2455 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2456 else:
2457 # Descriptors aside, all vbmeta features are supported in 1.0.
2458 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002459 return
2460
2461 if not output:
2462 raise AvbError('No output file given')
2463
David Zeuthen21e95262016-07-27 17:58:40 -04002464 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002465 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002466 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002467 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002468 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002469 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002470 include_descriptors_from_image, signing_helper,
2471 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002472 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002473
2474 # Write entire vbmeta blob (header, authentication, auxiliary).
2475 output.seek(0)
2476 output.write(vbmeta_blob)
2477
David Zeuthen97cb5802017-06-01 16:14:05 -04002478 if padding_size > 0:
2479 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2480 padding_needed = padded_size - len(vbmeta_blob)
2481 output.write('\0' * padding_needed)
2482
David Zeuthen18666ab2016-11-15 11:18:05 -05002483 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2484 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002485 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002486 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002487 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002488 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002489 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002490 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002491 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002492 release_string, append_to_release_string,
2493 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002494 """Generates a VBMeta blob.
2495
2496 This blob contains the header (struct AvbVBMetaHeader), the
2497 authentication data block (which contains the hash and signature
2498 for the header and auxiliary block), and the auxiliary block
2499 (which contains descriptors, the public key used, and other data).
2500
2501 The |key| parameter can |None| only if the |algorithm_name| is
2502 'NONE'.
2503
2504 Arguments:
2505 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2506 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002507 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002508 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002509 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002510 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002511 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002512 props: Properties to insert (List of strings of the form 'key:value').
2513 props_from_file: Properties to insert (List of strings 'key:<path>').
2514 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002515 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002516 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002517 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2518 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002519 include_descriptors_from_image: List of file objects for which
2520 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002521 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002522 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002523 release_string: None or avbtool release string.
2524 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002525 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002526
2527 Returns:
2528 A bytearray() with the VBMeta blob.
2529
2530 Raises:
2531 Exception: If the |algorithm_name| is not found, if no key has
2532 been given and the given algorithm requires one, or the key is
2533 of the wrong size.
2534
2535 """
2536 try:
2537 alg = ALGORITHMS[algorithm_name]
2538 except KeyError:
2539 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2540
David Zeuthena5fd3a42017-02-27 16:38:54 -05002541 if not descriptors:
2542 descriptors = []
2543
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002544 h = AvbVBMetaHeader()
2545 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2546
David Zeuthena5fd3a42017-02-27 16:38:54 -05002547 # Insert chained partition descriptors, if any
2548 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002549 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002550 for cp in chain_partitions:
2551 cp_tokens = cp.split(':')
2552 if len(cp_tokens) != 3:
2553 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002554 partition_name = cp_tokens[0]
2555 rollback_index_location = int(cp_tokens[1])
2556 file_path = cp_tokens[2]
2557 # Check that the same rollback location isn't being used by
2558 # multiple chained partitions.
2559 if used_locations.get(rollback_index_location):
2560 raise AvbError('Rollback Index Location {} is already in use.'.format(
2561 rollback_index_location))
2562 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002563 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002564 desc.partition_name = partition_name
2565 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002566 if desc.rollback_index_location < 1:
2567 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002568 desc.public_key = open(file_path, 'rb').read()
2569 descriptors.append(desc)
2570
David Zeuthen21e95262016-07-27 17:58:40 -04002571 # Descriptors.
2572 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002573 for desc in descriptors:
2574 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002575
2576 # Add properties.
2577 if props:
2578 for prop in props:
2579 idx = prop.find(':')
2580 if idx == -1:
2581 raise AvbError('Malformed property "{}".'.format(prop))
2582 desc = AvbPropertyDescriptor()
2583 desc.key = prop[0:idx]
2584 desc.value = prop[(idx + 1):]
2585 encoded_descriptors.extend(desc.encode())
2586 if props_from_file:
2587 for prop in props_from_file:
2588 idx = prop.find(':')
2589 if idx == -1:
2590 raise AvbError('Malformed property "{}".'.format(prop))
2591 desc = AvbPropertyDescriptor()
2592 desc.key = prop[0:idx]
2593 desc.value = prop[(idx + 1):]
2594 file_path = prop[(idx + 1):]
2595 desc.value = open(file_path, 'rb').read()
2596 encoded_descriptors.extend(desc.encode())
2597
David Zeuthen73f2afa2017-05-17 16:54:11 -04002598 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002599 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002600 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002601 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002602 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2603 encoded_descriptors.extend(cmdline_desc[0].encode())
2604 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002605
David Zeuthen73f2afa2017-05-17 16:54:11 -04002606 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2607 if ht_desc_to_setup:
2608 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2609 ht_desc_to_setup)
2610 encoded_descriptors.extend(cmdline_desc[0].encode())
2611 encoded_descriptors.extend(cmdline_desc[1].encode())
2612
David Zeuthen21e95262016-07-27 17:58:40 -04002613 # Add kernel command-lines.
2614 if kernel_cmdlines:
2615 for i in kernel_cmdlines:
2616 desc = AvbKernelCmdlineDescriptor()
2617 desc.kernel_cmdline = i
2618 encoded_descriptors.extend(desc.encode())
2619
2620 # Add descriptors from other images.
2621 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002622 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002623 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002624 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002625 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2626 image_handler)
2627 # Bump the required libavb version to support all included descriptors.
2628 h.bump_required_libavb_version_minor(
2629 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002630 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002631 # The --include_descriptors_from_image option is used in some setups
2632 # with images A and B where both A and B contain a descriptor
2633 # for a partition with the same name. Since it's not meaningful
2634 # to include both descriptors, only include the last seen descriptor.
2635 # See bug 76386656 for details.
2636 if hasattr(desc, 'partition_name'):
2637 key = type(desc).__name__ + '_' + desc.partition_name
2638 descriptors_dict[key] = desc.encode()
2639 else:
2640 encoded_descriptors.extend(desc.encode())
2641 for key in sorted(descriptors_dict.keys()):
2642 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002643
David Zeuthen18666ab2016-11-15 11:18:05 -05002644 # Load public key metadata blob, if requested.
2645 pkmd_blob = []
2646 if public_key_metadata_path:
2647 with open(public_key_metadata_path) as f:
2648 pkmd_blob = f.read()
2649
David Zeuthen21e95262016-07-27 17:58:40 -04002650 key = None
2651 encoded_key = bytearray()
2652 if alg.public_key_num_bytes > 0:
2653 if not key_path:
2654 raise AvbError('Key is required for algorithm {}'.format(
2655 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002656 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002657 if len(encoded_key) != alg.public_key_num_bytes:
2658 raise AvbError('Key is wrong size for algorithm {}'.format(
2659 algorithm_name))
2660
David Zeuthene3cadca2017-02-22 21:25:46 -05002661 # Override release string, if requested.
2662 if isinstance(release_string, (str, unicode)):
2663 h.release_string = release_string
2664
2665 # Append to release string, if requested. Also insert a space before.
2666 if isinstance(append_to_release_string, (str, unicode)):
2667 h.release_string += ' ' + append_to_release_string
2668
David Zeuthen18666ab2016-11-15 11:18:05 -05002669 # For the Auxiliary data block, descriptors are stored at offset 0,
2670 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002671 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002672 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002673 h.descriptors_offset = 0
2674 h.descriptors_size = len(encoded_descriptors)
2675 h.public_key_offset = h.descriptors_size
2676 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002677 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2678 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002679
2680 # For the Authentication data block, the hash is first and then
2681 # the signature.
2682 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002683 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002684 h.algorithm_type = alg.algorithm_type
2685 h.hash_offset = 0
2686 h.hash_size = alg.hash_num_bytes
2687 # Signature offset and size - it's stored right after the hash
2688 # (in Authentication data block).
2689 h.signature_offset = alg.hash_num_bytes
2690 h.signature_size = alg.signature_num_bytes
2691
2692 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002693 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002694
2695 # Generate Header data block.
2696 header_data_blob = h.encode()
2697
2698 # Generate Auxiliary data block.
2699 aux_data_blob = bytearray()
2700 aux_data_blob.extend(encoded_descriptors)
2701 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002702 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002703 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2704 aux_data_blob.extend('\0' * padding_bytes)
2705
2706 # Calculate the hash.
2707 binary_hash = bytearray()
2708 binary_signature = bytearray()
2709 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002710 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002711 ha.update(header_data_blob)
2712 ha.update(aux_data_blob)
2713 binary_hash.extend(ha.digest())
2714
2715 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002716 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002717 binary_signature.extend(raw_sign(signing_helper,
2718 signing_helper_with_files,
2719 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002720 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002721 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002722
2723 # Generate Authentication data block.
2724 auth_data_blob = bytearray()
2725 auth_data_blob.extend(binary_hash)
2726 auth_data_blob.extend(binary_signature)
2727 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2728 auth_data_blob.extend('\0' * padding_bytes)
2729
2730 return header_data_blob + auth_data_blob + aux_data_blob
2731
2732 def extract_public_key(self, key_path, output):
2733 """Implements the 'extract_public_key' command.
2734
2735 Arguments:
2736 key_path: The path to a RSA private key file.
2737 output: The file to write to.
2738 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002739 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002740
David Zeuthenb1b994d2017-03-06 18:01:31 -05002741 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2742 partition_size):
2743 """Implementation of the append_vbmeta_image command.
2744
2745 Arguments:
2746 image_filename: File to add the footer to.
2747 vbmeta_image_filename: File to get vbmeta struct from.
2748 partition_size: Size of partition.
2749
2750 Raises:
2751 AvbError: If an argument is incorrect.
2752 """
2753 image = ImageHandler(image_filename)
2754
2755 if partition_size % image.block_size != 0:
2756 raise AvbError('Partition size of {} is not a multiple of the image '
2757 'block size {}.'.format(partition_size,
2758 image.block_size))
2759
2760 # If there's already a footer, truncate the image to its original
2761 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002762 if image.image_size >= AvbFooter.SIZE:
2763 image.seek(image.image_size - AvbFooter.SIZE)
2764 try:
2765 footer = AvbFooter(image.read(AvbFooter.SIZE))
2766 # Existing footer found. Just truncate.
2767 original_image_size = footer.original_image_size
2768 image.truncate(footer.original_image_size)
2769 except (LookupError, struct.error):
2770 original_image_size = image.image_size
2771 else:
2772 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002773 original_image_size = image.image_size
2774
2775 # If anything goes wrong from here-on, restore the image back to
2776 # its original size.
2777 try:
2778 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2779 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2780
2781 # If the image isn't sparse, its size might not be a multiple of
2782 # the block size. This will screw up padding later so just grow it.
2783 if image.image_size % image.block_size != 0:
2784 assert not image.is_sparse
2785 padding_needed = image.block_size - (image.image_size%image.block_size)
2786 image.truncate(image.image_size + padding_needed)
2787
2788 # The append_raw() method requires content with size being a
2789 # multiple of |block_size| so add padding as needed. Also record
2790 # where this is written to since we'll need to put that in the
2791 # footer.
2792 vbmeta_offset = image.image_size
2793 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2794 len(vbmeta_blob))
2795 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2796
2797 # Append vbmeta blob and footer
2798 image.append_raw(vbmeta_blob_with_padding)
2799 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2800
2801 # Now insert a DONT_CARE chunk with enough bytes such that the
2802 # final Footer block is at the end of partition_size..
2803 image.append_dont_care(partition_size - vbmeta_end_offset -
2804 1*image.block_size)
2805
2806 # Generate the Footer that tells where the VBMeta footer
2807 # is. Also put enough padding in the front of the footer since
2808 # we'll write out an entire block.
2809 footer = AvbFooter()
2810 footer.original_image_size = original_image_size
2811 footer.vbmeta_offset = vbmeta_offset
2812 footer.vbmeta_size = len(vbmeta_blob)
2813 footer_blob = footer.encode()
2814 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2815 footer_blob)
2816 image.append_raw(footer_blob_with_padding)
2817
2818 except:
2819 # Truncate back to original size, then re-raise
2820 image.truncate(original_image_size)
2821 raise
2822
David Zeuthena4fee8b2016-08-22 15:20:43 -04002823 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002824 hash_algorithm, salt, chain_partitions, algorithm_name,
2825 key_path,
2826 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002827 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002828 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002829 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002830 signing_helper, signing_helper_with_files,
2831 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002832 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002833 print_required_libavb_version, use_persistent_digest,
2834 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002835 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002836
2837 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002838 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002839 partition_size: Size of partition.
2840 partition_name: Name of partition (without A/B suffix).
2841 hash_algorithm: Hash algorithm to use.
2842 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002843 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002844 algorithm_name: Name of algorithm to use.
2845 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002846 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002847 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002848 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002849 props: Properties to insert (List of strings of the form 'key:value').
2850 props_from_file: Properties to insert (List of strings 'key:<path>').
2851 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002852 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002853 dm-verity kernel cmdline from.
2854 include_descriptors_from_image: List of file objects for which
2855 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002856 calc_max_image_size: Don't store the footer - instead calculate the
2857 maximum image size leaving enough room for metadata with the
2858 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002859 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002860 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002861 release_string: None or avbtool release string.
2862 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002863 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2864 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002865 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002866 use_persistent_digest: Use a persistent digest on device.
2867 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002868
2869 Raises:
2870 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002871 """
David Zeuthen1097a782017-05-31 15:53:17 -04002872
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002873 required_libavb_version_minor = 0
2874 if use_persistent_digest or do_not_use_ab:
2875 required_libavb_version_minor = 1
2876
David Zeuthen1097a782017-05-31 15:53:17 -04002877 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002878 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002879 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04002880 return
2881
David Zeuthenbf562452017-05-17 18:04:43 -04002882 # First, calculate the maximum image size such that an image
2883 # this size + metadata (footer + vbmeta struct) fits in
2884 # |partition_size|.
2885 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002886 if partition_size < max_metadata_size:
2887 raise AvbError('Parition size of {} is too small. '
2888 'Needs to be at least {}'.format(
2889 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04002890 max_image_size = partition_size - max_metadata_size
2891
2892 # If we're asked to only calculate the maximum image size, we're done.
2893 if calc_max_image_size:
2894 print '{}'.format(max_image_size)
2895 return
2896
David Zeuthena4fee8b2016-08-22 15:20:43 -04002897 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
David Zeuthen21e95262016-07-27 17:58:40 -04002904 # If there's already a footer, truncate the image to its original
2905 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2906 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002907 if image.image_size >= AvbFooter.SIZE:
2908 image.seek(image.image_size - AvbFooter.SIZE)
2909 try:
2910 footer = AvbFooter(image.read(AvbFooter.SIZE))
2911 # Existing footer found. Just truncate.
2912 original_image_size = footer.original_image_size
2913 image.truncate(footer.original_image_size)
2914 except (LookupError, struct.error):
2915 original_image_size = image.image_size
2916 else:
2917 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04002918 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002919
2920 # If anything goes wrong from here-on, restore the image back to
2921 # its original size.
2922 try:
David Zeuthen09692692016-09-30 16:16:40 -04002923 # If image size exceeds the maximum image size, fail.
2924 if image.image_size > max_image_size:
2925 raise AvbError('Image size of {} exceeds maximum image '
2926 'size of {} in order to fit in a partition '
2927 'size of {}.'.format(image.image_size, max_image_size,
2928 partition_size))
2929
David Zeuthen21e95262016-07-27 17:58:40 -04002930 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2931 if salt:
2932 salt = salt.decode('hex')
2933 else:
2934 if salt is None:
2935 # If salt is not explicitly specified, choose a hash
2936 # that's the same size as the hash size.
2937 hash_size = digest_size
2938 salt = open('/dev/urandom').read(hash_size)
2939 else:
2940 salt = ''
2941
2942 hasher = hashlib.new(name=hash_algorithm, string=salt)
2943 # TODO(zeuthen): might want to read this in chunks to avoid
2944 # memory pressure, then again, this is only supposed to be used
2945 # on kernel/initramfs partitions. Possible optimization.
2946 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002947 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002948 digest = hasher.digest()
2949
2950 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002951 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002952 h_desc.hash_algorithm = hash_algorithm
2953 h_desc.partition_name = partition_name
2954 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002955 h_desc.flags = 0
2956 if do_not_use_ab:
2957 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
2958 if not use_persistent_digest:
2959 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04002960
2961 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002962 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002963 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002964 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002965 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002966 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002967 include_descriptors_from_image, signing_helper,
2968 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002969 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002970
David Zeuthend247fcb2017-02-16 12:09:27 -05002971 # Write vbmeta blob, if requested.
2972 if output_vbmeta_image:
2973 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002974
David Zeuthend247fcb2017-02-16 12:09:27 -05002975 # Append vbmeta blob and footer, unless requested not to.
2976 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002977 # If the image isn't sparse, its size might not be a multiple of
2978 # the block size. This will screw up padding later so just grow it.
2979 if image.image_size % image.block_size != 0:
2980 assert not image.is_sparse
2981 padding_needed = image.block_size - (
2982 image.image_size % image.block_size)
2983 image.truncate(image.image_size + padding_needed)
2984
2985 # The append_raw() method requires content with size being a
2986 # multiple of |block_size| so add padding as needed. Also record
2987 # where this is written to since we'll need to put that in the
2988 # footer.
2989 vbmeta_offset = image.image_size
2990 padding_needed = (
2991 round_to_multiple(len(vbmeta_blob), image.block_size) -
2992 len(vbmeta_blob))
2993 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
2994
David Zeuthend247fcb2017-02-16 12:09:27 -05002995 image.append_raw(vbmeta_blob_with_padding)
2996 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2997
2998 # Now insert a DONT_CARE chunk with enough bytes such that the
2999 # final Footer block is at the end of partition_size..
3000 image.append_dont_care(partition_size - vbmeta_end_offset -
3001 1*image.block_size)
3002
3003 # Generate the Footer that tells where the VBMeta footer
3004 # is. Also put enough padding in the front of the footer since
3005 # we'll write out an entire block.
3006 footer = AvbFooter()
3007 footer.original_image_size = original_image_size
3008 footer.vbmeta_offset = vbmeta_offset
3009 footer.vbmeta_size = len(vbmeta_blob)
3010 footer_blob = footer.encode()
3011 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3012 footer_blob)
3013 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003014
David Zeuthen21e95262016-07-27 17:58:40 -04003015 except:
3016 # Truncate back to original size, then re-raise
3017 image.truncate(original_image_size)
3018 raise
3019
David Zeuthena4fee8b2016-08-22 15:20:43 -04003020 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003021 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003022 block_size, salt, chain_partitions, algorithm_name,
3023 key_path,
3024 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003025 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003026 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003027 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003028 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003029 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003030 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003031 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003032 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003033 print_required_libavb_version,
3034 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04003035 """Implements the 'add_hashtree_footer' command.
3036
3037 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3038 more information about dm-verity and these hashes.
3039
3040 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003041 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003042 partition_size: Size of partition.
3043 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003044 generate_fec: If True, generate FEC codes.
3045 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003046 hash_algorithm: Hash algorithm to use.
3047 block_size: Block size to use.
3048 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003049 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003050 algorithm_name: Name of algorithm to use.
3051 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003052 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003053 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003054 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003055 props: Properties to insert (List of strings of the form 'key:value').
3056 props_from_file: Properties to insert (List of strings 'key:<path>').
3057 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003058 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003059 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003060 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3061 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003062 include_descriptors_from_image: List of file objects for which
3063 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003064 calc_max_image_size: Don't store the hashtree or footer - instead
3065 calculate the maximum image size leaving enough room for hashtree
3066 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003067 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003068 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003069 release_string: None or avbtool release string.
3070 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003071 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3072 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003073 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003074 use_persistent_root_digest: Use a persistent root digest on device.
3075 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003076
3077 Raises:
3078 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003079 """
David Zeuthen1097a782017-05-31 15:53:17 -04003080
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003081 required_libavb_version_minor = 0
3082 if use_persistent_root_digest or do_not_use_ab:
3083 required_libavb_version_minor = 1
3084
David Zeuthen1097a782017-05-31 15:53:17 -04003085 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003086 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003087 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003088 return
3089
David Zeuthen09692692016-09-30 16:16:40 -04003090 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3091 digest_padding = round_to_pow2(digest_size) - digest_size
3092
3093 # First, calculate the maximum image size such that an image
3094 # this size + the hashtree + metadata (footer + vbmeta struct)
3095 # fits in |partition_size|. We use very conservative figures for
3096 # metadata.
3097 (_, max_tree_size) = calc_hash_level_offsets(
3098 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003099 max_fec_size = 0
3100 if generate_fec:
3101 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3102 max_metadata_size = (max_fec_size + max_tree_size +
3103 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04003104 self.MAX_FOOTER_SIZE)
3105 max_image_size = partition_size - max_metadata_size
3106
3107 # If we're asked to only calculate the maximum image size, we're done.
3108 if calc_max_image_size:
3109 print '{}'.format(max_image_size)
3110 return
3111
David Zeuthena4fee8b2016-08-22 15:20:43 -04003112 image = ImageHandler(image_filename)
3113
3114 if partition_size % image.block_size != 0:
3115 raise AvbError('Partition size of {} is not a multiple of the image '
3116 'block size {}.'.format(partition_size,
3117 image.block_size))
3118
David Zeuthen21e95262016-07-27 17:58:40 -04003119 # If there's already a footer, truncate the image to its original
3120 # size. This way 'avbtool add_hashtree_footer' is idempotent
3121 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003122 if image.image_size >= AvbFooter.SIZE:
3123 image.seek(image.image_size - AvbFooter.SIZE)
3124 try:
3125 footer = AvbFooter(image.read(AvbFooter.SIZE))
3126 # Existing footer found. Just truncate.
3127 original_image_size = footer.original_image_size
3128 image.truncate(footer.original_image_size)
3129 except (LookupError, struct.error):
3130 original_image_size = image.image_size
3131 else:
3132 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003133 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003134
3135 # If anything goes wrong from here-on, restore the image back to
3136 # its original size.
3137 try:
3138 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003139 rounded_image_size = round_to_multiple(image.image_size, block_size)
3140 if rounded_image_size > image.image_size:
3141 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003142
David Zeuthen09692692016-09-30 16:16:40 -04003143 # If image size exceeds the maximum image size, fail.
3144 if image.image_size > max_image_size:
3145 raise AvbError('Image size of {} exceeds maximum image '
3146 'size of {} in order to fit in a partition '
3147 'size of {}.'.format(image.image_size, max_image_size,
3148 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003149
3150 if salt:
3151 salt = salt.decode('hex')
3152 else:
3153 if salt is None:
3154 # If salt is not explicitly specified, choose a hash
3155 # that's the same size as the hash size.
3156 hash_size = digest_size
3157 salt = open('/dev/urandom').read(hash_size)
3158 else:
3159 salt = ''
3160
David Zeuthena4fee8b2016-08-22 15:20:43 -04003161 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003162 # offsets in advance.
3163 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003164 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003165
David Zeuthena4fee8b2016-08-22 15:20:43 -04003166 # If the image isn't sparse, its size might not be a multiple of
3167 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003168 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003169 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003170 padding_needed = image.block_size - (image.image_size%image.block_size)
3171 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003172
David Zeuthena4fee8b2016-08-22 15:20:43 -04003173 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003174 tree_offset = image.image_size
3175 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003176 block_size,
3177 hash_algorithm, salt,
3178 digest_padding,
3179 hash_level_offsets,
3180 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003181
3182 # Generate HashtreeDescriptor with details about the tree we
3183 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003184 ht_desc = AvbHashtreeDescriptor()
3185 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003186 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003187 ht_desc.tree_offset = tree_offset
3188 ht_desc.tree_size = tree_size
3189 ht_desc.data_block_size = block_size
3190 ht_desc.hash_block_size = block_size
3191 ht_desc.hash_algorithm = hash_algorithm
3192 ht_desc.partition_name = partition_name
3193 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003194 if do_not_use_ab:
3195 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3196 if not use_persistent_root_digest:
3197 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003198
David Zeuthen09692692016-09-30 16:16:40 -04003199 # Write the hash tree
3200 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3201 len(hash_tree))
3202 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3203 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003204 len_hashtree_and_fec = len(hash_tree_with_padding)
3205
3206 # Generate FEC codes, if requested.
3207 if generate_fec:
3208 fec_data = generate_fec_data(image_filename, fec_num_roots)
3209 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3210 len(fec_data))
3211 fec_data_with_padding = fec_data + '\0'*padding_needed
3212 fec_offset = image.image_size
3213 image.append_raw(fec_data_with_padding)
3214 len_hashtree_and_fec += len(fec_data_with_padding)
3215 # Update the hashtree descriptor.
3216 ht_desc.fec_num_roots = fec_num_roots
3217 ht_desc.fec_offset = fec_offset
3218 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003219
David Zeuthen73f2afa2017-05-17 16:54:11 -04003220 ht_desc_to_setup = None
3221 if setup_as_rootfs_from_kernel:
3222 ht_desc_to_setup = ht_desc
3223
David Zeuthena4fee8b2016-08-22 15:20:43 -04003224 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003225 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003226 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003227 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003228 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003229 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003230 include_descriptors_from_image, signing_helper,
3231 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003232 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003233 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3234 len(vbmeta_blob))
3235 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003236
David Zeuthend247fcb2017-02-16 12:09:27 -05003237 # Write vbmeta blob, if requested.
3238 if output_vbmeta_image:
3239 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003240
David Zeuthend247fcb2017-02-16 12:09:27 -05003241 # Append vbmeta blob and footer, unless requested not to.
3242 if not do_not_append_vbmeta_image:
3243 image.append_raw(vbmeta_blob_with_padding)
3244
3245 # Now insert a DONT_CARE chunk with enough bytes such that the
3246 # final Footer block is at the end of partition_size..
3247 image.append_dont_care(partition_size - image.image_size -
3248 1*image.block_size)
3249
3250 # Generate the Footer that tells where the VBMeta footer
3251 # is. Also put enough padding in the front of the footer since
3252 # we'll write out an entire block.
3253 footer = AvbFooter()
3254 footer.original_image_size = original_image_size
3255 footer.vbmeta_offset = vbmeta_offset
3256 footer.vbmeta_size = len(vbmeta_blob)
3257 footer_blob = footer.encode()
3258 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3259 footer_blob)
3260 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003261
David Zeuthen21e95262016-07-27 17:58:40 -04003262 except:
David Zeuthen09692692016-09-30 16:16:40 -04003263 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003264 image.truncate(original_image_size)
3265 raise
3266
David Zeuthenc68f0822017-03-31 17:22:35 -04003267 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003268 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003269 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003270 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003271 """Implements the 'make_atx_certificate' command.
3272
3273 Android Things certificates are required for Android Things public key
3274 metadata. They chain the vbmeta signing key for a particular product back to
3275 a fused, permanent root key. These certificates are fixed-length and fixed-
3276 format with the explicit goal of not parsing ASN.1 in bootloader code.
3277
3278 Arguments:
3279 output: Certificate will be written to this file on success.
3280 authority_key_path: A PEM file path with the authority private key.
3281 If None, then a certificate will be created without a
3282 signature. The signature can be created out-of-band
3283 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003284 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003285 subject_key_version: A 64-bit version value. If this is None, the number
3286 of seconds since the epoch is used.
3287 subject: A subject identifier. For Product Signing Key certificates this
3288 should be the same Product ID found in the permanent attributes.
3289 is_intermediate_authority: True if the certificate is for an intermediate
3290 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003291 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003292 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003293 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003294 """
3295 signed_data = bytearray()
3296 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003297 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003298 hasher = hashlib.sha256()
3299 hasher.update(subject)
3300 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003301 if not usage:
3302 usage = 'com.google.android.things.vboot'
3303 if is_intermediate_authority:
3304 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003305 hasher = hashlib.sha256()
3306 hasher.update(usage)
3307 signed_data.extend(hasher.digest())
3308 if not subject_key_version:
3309 subject_key_version = int(time.time())
3310 signed_data.extend(struct.pack('<Q', subject_key_version))
3311 signature = bytearray()
3312 if authority_key_path:
3313 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003314 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003315 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003316 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003317 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003318 hasher.update(signed_data)
3319 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003320 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3321 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003322 alg.signature_num_bytes, authority_key_path,
3323 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003324 output.write(signed_data)
3325 output.write(signature)
3326
David Zeuthenc68f0822017-03-31 17:22:35 -04003327 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003328 product_id):
3329 """Implements the 'make_atx_permanent_attributes' command.
3330
3331 Android Things permanent attributes are designed to be permanent for a
3332 particular product and a hash of these attributes should be fused into
3333 hardware to enforce this.
3334
3335 Arguments:
3336 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003337 root_authority_key_path: Path to a PEM or DER public key for
3338 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003339 product_id: A 16-byte Product ID.
3340
3341 Raises:
3342 AvbError: If an argument is incorrect.
3343 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003344 EXPECTED_PRODUCT_ID_SIZE = 16
3345 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003346 raise AvbError('Invalid Product ID length.')
3347 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003348 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003349 output.write(product_id)
3350
3351 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003352 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003353 """Implements the 'make_atx_metadata' command.
3354
3355 Android Things metadata are included in vbmeta images to facilitate
3356 verification. The output of this command can be used as the
3357 public_key_metadata argument to other commands.
3358
3359 Arguments:
3360 output: Metadata will be written to this file on success.
3361 intermediate_key_certificate: A certificate file as output by
3362 make_atx_certificate with
3363 is_intermediate_authority set to true.
3364 product_key_certificate: A certificate file as output by
3365 make_atx_certificate with
3366 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003367
3368 Raises:
3369 AvbError: If an argument is incorrect.
3370 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003371 EXPECTED_CERTIFICATE_SIZE = 1620
3372 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003373 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003374 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003375 raise AvbError('Invalid product key certificate length.')
3376 output.write(struct.pack('<I', 1)) # Format Version
3377 output.write(intermediate_key_certificate)
3378 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003379
Darren Krahnfccd64e2018-01-16 17:39:35 -08003380 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3381 unlock_key_certificate, challenge_path,
3382 unlock_key_path, signing_helper,
3383 signing_helper_with_files):
3384 """Implements the 'make_atx_unlock_credential' command.
3385
3386 Android Things unlock credentials can be used to authorize the unlock of AVB
3387 on a device. These credentials are presented to an Android Things bootloader
3388 via the fastboot interface in response to a 16-byte challenge. This method
3389 creates all fields of the credential except the challenge signature field
3390 (which is the last field) and can optionally create the challenge signature
3391 field as well if a challenge and the unlock_key_path is provided.
3392
3393 Arguments:
3394 output: The credential will be written to this file on success.
3395 intermediate_key_certificate: A certificate file as output by
3396 make_atx_certificate with
3397 is_intermediate_authority set to true.
3398 unlock_key_certificate: A certificate file as output by
3399 make_atx_certificate with
3400 is_intermediate_authority set to false and the
3401 usage set to
3402 'com.google.android.things.vboot.unlock'.
3403 challenge_path: [optional] A path to the challenge to sign.
3404 unlock_key_path: [optional] A PEM file path with the unlock private key.
3405 signing_helper: Program which signs a hash and returns the signature.
3406 signing_helper_with_files: Same as signing_helper but uses files instead.
3407
3408 Raises:
3409 AvbError: If an argument is incorrect.
3410 """
3411 EXPECTED_CERTIFICATE_SIZE = 1620
3412 EXPECTED_CHALLENGE_SIZE = 16
3413 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3414 raise AvbError('Invalid intermediate key certificate length.')
3415 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3416 raise AvbError('Invalid product key certificate length.')
3417 challenge = bytearray()
3418 if challenge_path:
3419 with open(challenge_path, 'r') as f:
3420 challenge = f.read()
3421 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3422 raise AvbError('Invalid unlock challenge length.')
3423 output.write(struct.pack('<I', 1)) # Format Version
3424 output.write(intermediate_key_certificate)
3425 output.write(unlock_key_certificate)
3426 if challenge_path and unlock_key_path:
3427 signature = bytearray()
3428 padding_and_hash = bytearray()
3429 algorithm_name = 'SHA512_RSA4096'
3430 alg = ALGORITHMS[algorithm_name]
3431 hasher = hashlib.sha512()
3432 padding_and_hash.extend(alg.padding)
3433 hasher.update(challenge)
3434 padding_and_hash.extend(hasher.digest())
3435 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3436 algorithm_name,
3437 alg.signature_num_bytes, unlock_key_path,
3438 padding_and_hash))
3439 output.write(signature)
3440
David Zeuthen21e95262016-07-27 17:58:40 -04003441
3442def calc_hash_level_offsets(image_size, block_size, digest_size):
3443 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3444
3445 Arguments:
3446 image_size: The size of the image to calculate a Merkle-tree for.
3447 block_size: The block size, e.g. 4096.
3448 digest_size: The size of each hash, e.g. 32 for SHA-256.
3449
3450 Returns:
3451 A tuple where the first argument is an array of offsets and the
3452 second is size of the tree, in bytes.
3453 """
3454 level_offsets = []
3455 level_sizes = []
3456 tree_size = 0
3457
3458 num_levels = 0
3459 size = image_size
3460 while size > block_size:
3461 num_blocks = (size + block_size - 1) / block_size
3462 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3463
3464 level_sizes.append(level_size)
3465 tree_size += level_size
3466 num_levels += 1
3467
3468 size = level_size
3469
3470 for n in range(0, num_levels):
3471 offset = 0
3472 for m in range(n + 1, num_levels):
3473 offset += level_sizes[m]
3474 level_offsets.append(offset)
3475
David Zeuthena4fee8b2016-08-22 15:20:43 -04003476 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003477
3478
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003479# See system/extras/libfec/include/fec/io.h for these definitions.
3480FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3481FEC_MAGIC = 0xfecfecfe
3482
3483
3484def calc_fec_data_size(image_size, num_roots):
3485 """Calculates how much space FEC data will take.
3486
3487 Args:
3488 image_size: The size of the image.
3489 num_roots: Number of roots.
3490
3491 Returns:
3492 The number of bytes needed for FEC for an image of the given size
3493 and with the requested number of FEC roots.
3494
3495 Raises:
3496 ValueError: If output from the 'fec' tool is invalid.
3497
3498 """
3499 p = subprocess.Popen(
3500 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3501 stdout=subprocess.PIPE,
3502 stderr=subprocess.PIPE)
3503 (pout, perr) = p.communicate()
3504 retcode = p.wait()
3505 if retcode != 0:
3506 raise ValueError('Error invoking fec: {}'.format(perr))
3507 return int(pout)
3508
3509
3510def generate_fec_data(image_filename, num_roots):
3511 """Generate FEC codes for an image.
3512
3513 Args:
3514 image_filename: The filename of the image.
3515 num_roots: Number of roots.
3516
3517 Returns:
3518 The FEC data blob.
3519
3520 Raises:
3521 ValueError: If output from the 'fec' tool is invalid.
3522 """
3523 fec_tmpfile = tempfile.NamedTemporaryFile()
3524 subprocess.check_call(
3525 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3526 fec_tmpfile.name],
3527 stderr=open(os.devnull))
3528 fec_data = fec_tmpfile.read()
3529 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3530 footer_data = fec_data[-footer_size:]
3531 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3532 footer_data)
3533 if magic != FEC_MAGIC:
3534 raise ValueError('Unexpected magic in FEC footer')
3535 return fec_data[0:fec_size]
3536
3537
David Zeuthen21e95262016-07-27 17:58:40 -04003538def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003539 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003540 """Generates a Merkle-tree for a file.
3541
3542 Args:
3543 image: The image, as a file.
3544 image_size: The size of the image.
3545 block_size: The block size, e.g. 4096.
3546 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3547 salt: The salt to use.
3548 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003549 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003550 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003551
3552 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003553 A tuple where the first element is the top-level hash and the
3554 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003555 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003556 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003557 hash_src_offset = 0
3558 hash_src_size = image_size
3559 level_num = 0
3560 while hash_src_size > block_size:
3561 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003562 remaining = hash_src_size
3563 while remaining > 0:
3564 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003565 # Only read from the file for the first level - for subsequent
3566 # levels, access the array we're building.
3567 if level_num == 0:
3568 image.seek(hash_src_offset + hash_src_size - remaining)
3569 data = image.read(min(remaining, block_size))
3570 else:
3571 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3572 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003573 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003574
3575 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003576 if len(data) < block_size:
3577 hasher.update('\0' * (block_size - len(data)))
3578 level_output += hasher.digest()
3579 if digest_padding > 0:
3580 level_output += '\0' * digest_padding
3581
3582 padding_needed = (round_to_multiple(
3583 len(level_output), block_size) - len(level_output))
3584 level_output += '\0' * padding_needed
3585
David Zeuthena4fee8b2016-08-22 15:20:43 -04003586 # Copy level-output into resulting tree.
3587 offset = hash_level_offsets[level_num]
3588 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003589
David Zeuthena4fee8b2016-08-22 15:20:43 -04003590 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003591 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003592 level_num += 1
3593
3594 hasher = hashlib.new(name=hash_alg_name, string=salt)
3595 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003596 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003597
3598
3599class AvbTool(object):
3600 """Object for avbtool command-line tool."""
3601
3602 def __init__(self):
3603 """Initializer method."""
3604 self.avb = Avb()
3605
3606 def _add_common_args(self, sub_parser):
3607 """Adds arguments used by several sub-commands.
3608
3609 Arguments:
3610 sub_parser: The parser to add arguments to.
3611 """
3612 sub_parser.add_argument('--algorithm',
3613 help='Algorithm to use (default: NONE)',
3614 metavar='ALGORITHM',
3615 default='NONE')
3616 sub_parser.add_argument('--key',
3617 help='Path to RSA private key file',
3618 metavar='KEY',
3619 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003620 sub_parser.add_argument('--signing_helper',
3621 help='Path to helper used for signing',
3622 metavar='APP',
3623 default=None,
3624 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003625 sub_parser.add_argument('--signing_helper_with_files',
3626 help='Path to helper used for signing using files',
3627 metavar='APP',
3628 default=None,
3629 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003630 sub_parser.add_argument('--public_key_metadata',
3631 help='Path to public key metadata file',
3632 metavar='KEY_METADATA',
3633 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003634 sub_parser.add_argument('--rollback_index',
3635 help='Rollback Index',
3636 type=parse_number,
3637 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003638 # This is used internally for unit tests. Do not include in --help output.
3639 sub_parser.add_argument('--internal_release_string',
3640 help=argparse.SUPPRESS)
3641 sub_parser.add_argument('--append_to_release_string',
3642 help='Text to append to release string',
3643 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003644 sub_parser.add_argument('--prop',
3645 help='Add property',
3646 metavar='KEY:VALUE',
3647 action='append')
3648 sub_parser.add_argument('--prop_from_file',
3649 help='Add property from file',
3650 metavar='KEY:PATH',
3651 action='append')
3652 sub_parser.add_argument('--kernel_cmdline',
3653 help='Add kernel cmdline',
3654 metavar='CMDLINE',
3655 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003656 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3657 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3658 # at some future point.
3659 sub_parser.add_argument('--setup_rootfs_from_kernel',
3660 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003661 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003662 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003663 type=argparse.FileType('rb'))
3664 sub_parser.add_argument('--include_descriptors_from_image',
3665 help='Include descriptors from image',
3666 metavar='IMAGE',
3667 action='append',
3668 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003669 sub_parser.add_argument('--print_required_libavb_version',
3670 help=('Don\'t store the footer - '
3671 'instead calculate the required libavb '
3672 'version for the given options.'),
3673 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003674 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3675 sub_parser.add_argument('--chain_partition',
3676 help='Allow signed integrity-data for partition',
3677 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3678 action='append')
3679 sub_parser.add_argument('--flags',
3680 help='VBMeta flags',
3681 type=parse_number,
3682 default=0)
3683 sub_parser.add_argument('--set_hashtree_disabled_flag',
3684 help='Set the HASHTREE_DISABLED flag',
3685 action='store_true')
3686
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003687 def _add_common_footer_args(self, sub_parser):
3688 """Adds arguments used by add_*_footer sub-commands.
3689
3690 Arguments:
3691 sub_parser: The parser to add arguments to.
3692 """
3693 sub_parser.add_argument('--use_persistent_digest',
3694 help='Use a persistent digest on device instead of '
3695 'storing the digest in the descriptor. This '
3696 'cannot be used with A/B so must be combined '
3697 'with --do_not_use_ab when an A/B suffix is '
3698 'expected at runtime.',
3699 action='store_true')
3700 sub_parser.add_argument('--do_not_use_ab',
3701 help='The partition does not use A/B even when an '
3702 'A/B suffix is present. This must not be used '
3703 'for vbmeta or chained partitions.',
3704 action='store_true')
3705
David Zeuthena5fd3a42017-02-27 16:38:54 -05003706 def _fixup_common_args(self, args):
3707 """Common fixups needed by subcommands.
3708
3709 Arguments:
3710 args: Arguments to modify.
3711
3712 Returns:
3713 The modified arguments.
3714 """
3715 if args.set_hashtree_disabled_flag:
3716 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3717 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003718
3719 def run(self, argv):
3720 """Command-line processor.
3721
3722 Arguments:
3723 argv: Pass sys.argv from main.
3724 """
3725 parser = argparse.ArgumentParser()
3726 subparsers = parser.add_subparsers(title='subcommands')
3727
3728 sub_parser = subparsers.add_parser('version',
3729 help='Prints version of avbtool.')
3730 sub_parser.set_defaults(func=self.version)
3731
3732 sub_parser = subparsers.add_parser('extract_public_key',
3733 help='Extract public key.')
3734 sub_parser.add_argument('--key',
3735 help='Path to RSA private key file',
3736 required=True)
3737 sub_parser.add_argument('--output',
3738 help='Output file name',
3739 type=argparse.FileType('wb'),
3740 required=True)
3741 sub_parser.set_defaults(func=self.extract_public_key)
3742
3743 sub_parser = subparsers.add_parser('make_vbmeta_image',
3744 help='Makes a vbmeta image.')
3745 sub_parser.add_argument('--output',
3746 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003747 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003748 sub_parser.add_argument('--padding_size',
3749 metavar='NUMBER',
3750 help='If non-zero, pads output with NUL bytes so '
3751 'its size is a multiple of NUMBER (default: 0)',
3752 type=parse_number,
3753 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003754 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003755 sub_parser.set_defaults(func=self.make_vbmeta_image)
3756
3757 sub_parser = subparsers.add_parser('add_hash_footer',
3758 help='Add hashes and footer to image.')
3759 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003760 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003761 type=argparse.FileType('rab+'))
3762 sub_parser.add_argument('--partition_size',
3763 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003764 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003765 sub_parser.add_argument('--partition_name',
3766 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003767 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003768 sub_parser.add_argument('--hash_algorithm',
3769 help='Hash algorithm to use (default: sha256)',
3770 default='sha256')
3771 sub_parser.add_argument('--salt',
3772 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003773 sub_parser.add_argument('--calc_max_image_size',
3774 help=('Don\'t store the footer - '
3775 'instead calculate the maximum image size '
3776 'leaving enough room for metadata with '
3777 'the given partition size.'),
3778 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003779 sub_parser.add_argument('--output_vbmeta_image',
3780 help='Also write vbmeta struct to file',
3781 type=argparse.FileType('wb'))
3782 sub_parser.add_argument('--do_not_append_vbmeta_image',
3783 help=('Do not append vbmeta struct or footer '
3784 'to the image'),
3785 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003786 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003787 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003788 sub_parser.set_defaults(func=self.add_hash_footer)
3789
David Zeuthenb1b994d2017-03-06 18:01:31 -05003790 sub_parser = subparsers.add_parser('append_vbmeta_image',
3791 help='Append vbmeta image to image.')
3792 sub_parser.add_argument('--image',
3793 help='Image to append vbmeta blob to',
3794 type=argparse.FileType('rab+'))
3795 sub_parser.add_argument('--partition_size',
3796 help='Partition size',
3797 type=parse_number,
3798 required=True)
3799 sub_parser.add_argument('--vbmeta_image',
3800 help='Image with vbmeta blob to append',
3801 type=argparse.FileType('rb'))
3802 sub_parser.set_defaults(func=self.append_vbmeta_image)
3803
David Zeuthen21e95262016-07-27 17:58:40 -04003804 sub_parser = subparsers.add_parser('add_hashtree_footer',
3805 help='Add hashtree and footer to image.')
3806 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003807 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003808 type=argparse.FileType('rab+'))
3809 sub_parser.add_argument('--partition_size',
3810 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003811 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003812 sub_parser.add_argument('--partition_name',
3813 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003814 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003815 sub_parser.add_argument('--hash_algorithm',
3816 help='Hash algorithm to use (default: sha1)',
3817 default='sha1')
3818 sub_parser.add_argument('--salt',
3819 help='Salt in hex (default: /dev/urandom)')
3820 sub_parser.add_argument('--block_size',
3821 help='Block size (default: 4096)',
3822 type=parse_number,
3823 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003824 # TODO(zeuthen): The --generate_fec option was removed when we
3825 # moved to generating FEC by default. To avoid breaking existing
3826 # users needing to transition we simply just print a warning below
3827 # in add_hashtree_footer(). Remove this option and the warning at
3828 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003829 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003830 help=argparse.SUPPRESS,
3831 action='store_true')
3832 sub_parser.add_argument('--do_not_generate_fec',
3833 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003834 action='store_true')
3835 sub_parser.add_argument('--fec_num_roots',
3836 help='Number of roots for FEC (default: 2)',
3837 type=parse_number,
3838 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003839 sub_parser.add_argument('--calc_max_image_size',
3840 help=('Don\'t store the hashtree or footer - '
3841 'instead calculate the maximum image size '
3842 'leaving enough room for hashtree '
3843 'and metadata with the given partition '
3844 'size.'),
3845 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003846 sub_parser.add_argument('--output_vbmeta_image',
3847 help='Also write vbmeta struct to file',
3848 type=argparse.FileType('wb'))
3849 sub_parser.add_argument('--do_not_append_vbmeta_image',
3850 help=('Do not append vbmeta struct or footer '
3851 'to the image'),
3852 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003853 # This is different from --setup_rootfs_from_kernel insofar that
3854 # it doesn't take an IMAGE, the generated cmdline will be for the
3855 # hashtree we're adding.
3856 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3857 action='store_true',
3858 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003859 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003860 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003861 sub_parser.set_defaults(func=self.add_hashtree_footer)
3862
3863 sub_parser = subparsers.add_parser('erase_footer',
3864 help='Erase footer from an image.')
3865 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003866 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003867 type=argparse.FileType('rwb+'),
3868 required=True)
3869 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003870 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003871 action='store_true')
3872 sub_parser.set_defaults(func=self.erase_footer)
3873
David Zeuthen2bc232b2017-04-19 14:25:19 -04003874 sub_parser = subparsers.add_parser('resize_image',
3875 help='Resize image with a footer.')
3876 sub_parser.add_argument('--image',
3877 help='Image with a footer',
3878 type=argparse.FileType('rwb+'),
3879 required=True)
3880 sub_parser.add_argument('--partition_size',
3881 help='New partition size',
3882 type=parse_number)
3883 sub_parser.set_defaults(func=self.resize_image)
3884
David Zeuthen21e95262016-07-27 17:58:40 -04003885 sub_parser = subparsers.add_parser(
3886 'info_image',
3887 help='Show information about vbmeta or footer.')
3888 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003889 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003890 type=argparse.FileType('rb'),
3891 required=True)
3892 sub_parser.add_argument('--output',
3893 help='Write info to file',
3894 type=argparse.FileType('wt'),
3895 default=sys.stdout)
3896 sub_parser.set_defaults(func=self.info_image)
3897
David Zeuthenb623d8b2017-04-04 16:05:53 -04003898 sub_parser = subparsers.add_parser(
3899 'verify_image',
3900 help='Verify an image.')
3901 sub_parser.add_argument('--image',
3902 help='Image to verify',
3903 type=argparse.FileType('rb'),
3904 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003905 sub_parser.add_argument('--key',
3906 help='Check embedded public key matches KEY',
3907 metavar='KEY',
3908 required=False)
3909 sub_parser.add_argument('--expected_chain_partition',
3910 help='Expected chain partition',
3911 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3912 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04003913 sub_parser.set_defaults(func=self.verify_image)
3914
David Zeuthenb8643c02018-05-17 17:21:18 -04003915 sub_parser = subparsers.add_parser(
3916 'calculate_vbmeta_digest',
3917 help='Calculate vbmeta digest.')
3918 sub_parser.add_argument('--image',
3919 help='Image to calculate digest for',
3920 type=argparse.FileType('rb'),
3921 required=True)
3922 sub_parser.add_argument('--hash_algorithm',
3923 help='Hash algorithm to use (default: sha256)',
3924 default='sha256')
3925 sub_parser.add_argument('--output',
3926 help='Write hex digest to file (default: stdout)',
3927 type=argparse.FileType('wt'),
3928 default=sys.stdout)
3929 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
3930
David Zeuthen8b6973b2016-09-20 12:39:49 -04003931 sub_parser = subparsers.add_parser('set_ab_metadata',
3932 help='Set A/B metadata.')
3933 sub_parser.add_argument('--misc_image',
3934 help=('The misc image to modify. If the image does '
3935 'not exist, it will be created.'),
3936 type=argparse.FileType('r+b'),
3937 required=True)
3938 sub_parser.add_argument('--slot_data',
3939 help=('Slot data of the form "priority", '
3940 '"tries_remaining", "sucessful_boot" for '
3941 'slot A followed by the same for slot B, '
3942 'separated by colons. The default value '
3943 'is 15:7:0:14:7:0.'),
3944 default='15:7:0:14:7:0')
3945 sub_parser.set_defaults(func=self.set_ab_metadata)
3946
Darren Krahn147b08d2016-12-20 16:38:29 -08003947 sub_parser = subparsers.add_parser(
3948 'make_atx_certificate',
3949 help='Create an Android Things eXtension (ATX) certificate.')
3950 sub_parser.add_argument('--output',
3951 help='Write certificate to file',
3952 type=argparse.FileType('wb'),
3953 default=sys.stdout)
3954 sub_parser.add_argument('--subject',
3955 help=('Path to subject file'),
3956 type=argparse.FileType('rb'),
3957 required=True)
3958 sub_parser.add_argument('--subject_key',
3959 help=('Path to subject RSA public key file'),
3960 type=argparse.FileType('rb'),
3961 required=True)
3962 sub_parser.add_argument('--subject_key_version',
3963 help=('Version of the subject key'),
3964 type=parse_number,
3965 required=False)
3966 sub_parser.add_argument('--subject_is_intermediate_authority',
3967 help=('Generate an intermediate authority '
3968 'certificate'),
3969 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08003970 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07003971 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08003972 'string'),
3973 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08003974 sub_parser.add_argument('--authority_key',
3975 help='Path to authority RSA private key file',
3976 required=False)
3977 sub_parser.add_argument('--signing_helper',
3978 help='Path to helper used for signing',
3979 metavar='APP',
3980 default=None,
3981 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003982 sub_parser.add_argument('--signing_helper_with_files',
3983 help='Path to helper used for signing using files',
3984 metavar='APP',
3985 default=None,
3986 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08003987 sub_parser.set_defaults(func=self.make_atx_certificate)
3988
3989 sub_parser = subparsers.add_parser(
3990 'make_atx_permanent_attributes',
3991 help='Create Android Things eXtension (ATX) permanent attributes.')
3992 sub_parser.add_argument('--output',
3993 help='Write attributes to file',
3994 type=argparse.FileType('wb'),
3995 default=sys.stdout)
3996 sub_parser.add_argument('--root_authority_key',
3997 help='Path to authority RSA public key file',
3998 type=argparse.FileType('rb'),
3999 required=True)
4000 sub_parser.add_argument('--product_id',
4001 help=('Path to Product ID file'),
4002 type=argparse.FileType('rb'),
4003 required=True)
4004 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4005
4006 sub_parser = subparsers.add_parser(
4007 'make_atx_metadata',
4008 help='Create Android Things eXtension (ATX) metadata.')
4009 sub_parser.add_argument('--output',
4010 help='Write metadata to file',
4011 type=argparse.FileType('wb'),
4012 default=sys.stdout)
4013 sub_parser.add_argument('--intermediate_key_certificate',
4014 help='Path to intermediate key certificate file',
4015 type=argparse.FileType('rb'),
4016 required=True)
4017 sub_parser.add_argument('--product_key_certificate',
4018 help='Path to product key certificate file',
4019 type=argparse.FileType('rb'),
4020 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004021 sub_parser.set_defaults(func=self.make_atx_metadata)
4022
Darren Krahnfccd64e2018-01-16 17:39:35 -08004023 sub_parser = subparsers.add_parser(
4024 'make_atx_unlock_credential',
4025 help='Create an Android Things eXtension (ATX) unlock credential.')
4026 sub_parser.add_argument('--output',
4027 help='Write credential to file',
4028 type=argparse.FileType('wb'),
4029 default=sys.stdout)
4030 sub_parser.add_argument('--intermediate_key_certificate',
4031 help='Path to intermediate key certificate file',
4032 type=argparse.FileType('rb'),
4033 required=True)
4034 sub_parser.add_argument('--unlock_key_certificate',
4035 help='Path to unlock key certificate file',
4036 type=argparse.FileType('rb'),
4037 required=True)
4038 sub_parser.add_argument('--challenge',
4039 help='Path to the challenge to sign (optional). If '
4040 'this is not provided the challenge signature '
4041 'field is omitted and can be concatenated '
4042 'later.',
4043 required=False)
4044 sub_parser.add_argument('--unlock_key',
4045 help='Path to unlock key (optional). Must be '
4046 'provided if using --challenge.',
4047 required=False)
4048 sub_parser.add_argument('--signing_helper',
4049 help='Path to helper used for signing',
4050 metavar='APP',
4051 default=None,
4052 required=False)
4053 sub_parser.add_argument('--signing_helper_with_files',
4054 help='Path to helper used for signing using files',
4055 metavar='APP',
4056 default=None,
4057 required=False)
4058 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4059
David Zeuthen21e95262016-07-27 17:58:40 -04004060 args = parser.parse_args(argv[1:])
4061 try:
4062 args.func(args)
4063 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004064 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004065 sys.exit(1)
4066
4067 def version(self, _):
4068 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004069 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004070
4071 def extract_public_key(self, args):
4072 """Implements the 'extract_public_key' sub-command."""
4073 self.avb.extract_public_key(args.key, args.output)
4074
4075 def make_vbmeta_image(self, args):
4076 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004077 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004078 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004079 args.algorithm, args.key,
4080 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004081 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004082 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004083 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004084 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004085 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004086 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004087 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004088 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004089 args.print_required_libavb_version,
4090 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004091
David Zeuthenb1b994d2017-03-06 18:01:31 -05004092 def append_vbmeta_image(self, args):
4093 """Implements the 'append_vbmeta_image' sub-command."""
4094 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4095 args.partition_size)
4096
David Zeuthen21e95262016-07-27 17:58:40 -04004097 def add_hash_footer(self, args):
4098 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004099 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004100 self.avb.add_hash_footer(args.image.name if args.image else None,
4101 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004102 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004103 args.salt, args.chain_partition, args.algorithm,
4104 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004105 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004106 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004107 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004108 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004109 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004110 args.calc_max_image_size,
4111 args.signing_helper,
4112 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004113 args.internal_release_string,
4114 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004115 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004116 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004117 args.print_required_libavb_version,
4118 args.use_persistent_digest,
4119 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004120
4121 def add_hashtree_footer(self, args):
4122 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004123 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004124 # TODO(zeuthen): Remove when removing support for the
4125 # '--generate_fec' option above.
4126 if args.generate_fec:
4127 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4128 'is now generated by default. Use the option '
4129 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004130 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4131 args.partition_size,
4132 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004133 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004134 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004135 args.salt, args.chain_partition, args.algorithm,
4136 args.key, args.public_key_metadata,
4137 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004138 args.prop_from_file,
4139 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004140 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004141 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004142 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004143 args.calc_max_image_size,
4144 args.signing_helper,
4145 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004146 args.internal_release_string,
4147 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004148 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004149 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004150 args.print_required_libavb_version,
4151 args.use_persistent_digest,
4152 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05004153
David Zeuthen21e95262016-07-27 17:58:40 -04004154 def erase_footer(self, args):
4155 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004156 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004157
David Zeuthen2bc232b2017-04-19 14:25:19 -04004158 def resize_image(self, args):
4159 """Implements the 'resize_image' sub-command."""
4160 self.avb.resize_image(args.image.name, args.partition_size)
4161
David Zeuthen8b6973b2016-09-20 12:39:49 -04004162 def set_ab_metadata(self, args):
4163 """Implements the 'set_ab_metadata' sub-command."""
4164 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4165
David Zeuthen21e95262016-07-27 17:58:40 -04004166 def info_image(self, args):
4167 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004168 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004169
David Zeuthenb623d8b2017-04-04 16:05:53 -04004170 def verify_image(self, args):
4171 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004172 self.avb.verify_image(args.image.name, args.key,
4173 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004174
David Zeuthenb8643c02018-05-17 17:21:18 -04004175 def calculate_vbmeta_digest(self, args):
4176 """Implements the 'calculate_vbmeta_digest' sub-command."""
4177 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4178 args.output)
4179
Darren Krahn147b08d2016-12-20 16:38:29 -08004180 def make_atx_certificate(self, args):
4181 """Implements the 'make_atx_certificate' sub-command."""
4182 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004183 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004184 args.subject_key_version,
4185 args.subject.read(),
4186 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004187 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004188 args.signing_helper,
4189 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004190
4191 def make_atx_permanent_attributes(self, args):
4192 """Implements the 'make_atx_permanent_attributes' sub-command."""
4193 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004194 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004195 args.product_id.read())
4196
4197 def make_atx_metadata(self, args):
4198 """Implements the 'make_atx_metadata' sub-command."""
4199 self.avb.make_atx_metadata(args.output,
4200 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004201 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004202
Darren Krahnfccd64e2018-01-16 17:39:35 -08004203 def make_atx_unlock_credential(self, args):
4204 """Implements the 'make_atx_unlock_credential' sub-command."""
4205 self.avb.make_atx_unlock_credential(
4206 args.output,
4207 args.intermediate_key_certificate.read(),
4208 args.unlock_key_certificate.read(),
4209 args.challenge,
4210 args.unlock_key,
4211 args.signing_helper,
4212 args.signing_helper_with_files)
4213
David Zeuthen21e95262016-07-27 17:58:40 -04004214
4215if __name__ == '__main__':
4216 tool = AvbTool()
4217 tool.run(sys.argv)