blob: 14914ce806b926d968365f7161c1b2459d262aff [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
68 See the avb_vbmeta_header.h file for more details about
69 algorithms.
70
71 The constant |ALGORITHMS| is a dictionary from human-readable
72 names (e.g 'SHA256_RSA2048') to instances of this class.
73
74 Attributes:
75 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040076 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040077 hash_num_bytes: Number of bytes used to store the hash.
78 signature_num_bytes: Number of bytes used to store the signature.
79 public_key_num_bytes: Number of bytes used to store the public key.
80 padding: Padding used for signature, if any.
81 """
82
David Zeuthenb623d8b2017-04-04 16:05:53 -040083 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
84 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040085 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040086 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040087 self.hash_num_bytes = hash_num_bytes
88 self.signature_num_bytes = signature_num_bytes
89 self.public_key_num_bytes = public_key_num_bytes
90 self.padding = padding
91
David Zeuthenb623d8b2017-04-04 16:05:53 -040092
David Zeuthen21e95262016-07-27 17:58:40 -040093# This must be kept in sync with the avb_crypto.h file.
94#
95# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
96# obtained from section 5.2.2 of RFC 4880.
97ALGORITHMS = {
98 'NONE': Algorithm(
99 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400100 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400101 hash_num_bytes=0,
102 signature_num_bytes=0,
103 public_key_num_bytes=0,
104 padding=[]),
105 'SHA256_RSA2048': Algorithm(
106 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400107 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400108 hash_num_bytes=32,
109 signature_num_bytes=256,
110 public_key_num_bytes=8 + 2*2048/8,
111 padding=[
112 # PKCS1-v1_5 padding
113 0x00, 0x01] + [0xff]*202 + [0x00] + [
114 # ASN.1 header
115 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
116 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
117 0x00, 0x04, 0x20,
118 ]),
119 'SHA256_RSA4096': Algorithm(
120 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400121 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400122 hash_num_bytes=32,
123 signature_num_bytes=512,
124 public_key_num_bytes=8 + 2*4096/8,
125 padding=[
126 # PKCS1-v1_5 padding
127 0x00, 0x01] + [0xff]*458 + [0x00] + [
128 # ASN.1 header
129 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
130 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
131 0x00, 0x04, 0x20,
132 ]),
133 'SHA256_RSA8192': Algorithm(
134 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400135 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400136 hash_num_bytes=32,
137 signature_num_bytes=1024,
138 public_key_num_bytes=8 + 2*8192/8,
139 padding=[
140 # PKCS1-v1_5 padding
141 0x00, 0x01] + [0xff]*970 + [0x00] + [
142 # ASN.1 header
143 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
144 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
145 0x00, 0x04, 0x20,
146 ]),
147 'SHA512_RSA2048': Algorithm(
148 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400149 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400150 hash_num_bytes=64,
151 signature_num_bytes=256,
152 public_key_num_bytes=8 + 2*2048/8,
153 padding=[
154 # PKCS1-v1_5 padding
155 0x00, 0x01] + [0xff]*170 + [0x00] + [
156 # ASN.1 header
157 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
158 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
159 0x00, 0x04, 0x40
160 ]),
161 'SHA512_RSA4096': Algorithm(
162 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400163 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400164 hash_num_bytes=64,
165 signature_num_bytes=512,
166 public_key_num_bytes=8 + 2*4096/8,
167 padding=[
168 # PKCS1-v1_5 padding
169 0x00, 0x01] + [0xff]*426 + [0x00] + [
170 # ASN.1 header
171 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
172 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
173 0x00, 0x04, 0x40
174 ]),
175 'SHA512_RSA8192': Algorithm(
176 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400177 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400178 hash_num_bytes=64,
179 signature_num_bytes=1024,
180 public_key_num_bytes=8 + 2*8192/8,
181 padding=[
182 # PKCS1-v1_5 padding
183 0x00, 0x01] + [0xff]*938 + [0x00] + [
184 # ASN.1 header
185 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
186 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
187 0x00, 0x04, 0x40
188 ]),
189}
190
191
David Zeuthene3cadca2017-02-22 21:25:46 -0500192def get_release_string():
193 """Calculates the release string to use in the VBMeta struct."""
194 # Keep in sync with libavb/avb_version.c:avb_version_string().
195 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
196 AVB_VERSION_MINOR,
197 AVB_VERSION_SUB)
198
199
David Zeuthen21e95262016-07-27 17:58:40 -0400200def round_to_multiple(number, size):
201 """Rounds a number up to nearest multiple of another number.
202
203 Args:
204 number: The number to round up.
205 size: The multiple to round up to.
206
207 Returns:
208 If |number| is a multiple of |size|, returns |number|, otherwise
209 returns |number| + |size|.
210 """
211 remainder = number % size
212 if remainder == 0:
213 return number
214 return number + size - remainder
215
216
217def round_to_pow2(number):
218 """Rounds a number up to the next power of 2.
219
220 Args:
221 number: The number to round up.
222
223 Returns:
224 If |number| is already a power of 2 then |number| is
225 returned. Otherwise the smallest power of 2 greater than |number|
226 is returned.
227 """
228 return 2**((number - 1).bit_length())
229
230
David Zeuthen21e95262016-07-27 17:58:40 -0400231def encode_long(num_bits, value):
232 """Encodes a long to a bytearray() using a given amount of bits.
233
234 This number is written big-endian, e.g. with the most significant
235 bit first.
236
David Zeuthenb623d8b2017-04-04 16:05:53 -0400237 This is the reverse of decode_long().
238
David Zeuthen21e95262016-07-27 17:58:40 -0400239 Arguments:
240 num_bits: The number of bits to write, e.g. 2048.
241 value: The value to write.
242
243 Returns:
244 A bytearray() with the encoded long.
245 """
246 ret = bytearray()
247 for bit_pos in range(num_bits, 0, -8):
248 octet = (value >> (bit_pos - 8)) & 0xff
249 ret.extend(struct.pack('!B', octet))
250 return ret
251
252
David Zeuthenb623d8b2017-04-04 16:05:53 -0400253def decode_long(blob):
254 """Decodes a long from a bytearray() using a given amount of bits.
255
256 This number is expected to be in big-endian, e.g. with the most
257 significant bit first.
258
259 This is the reverse of encode_long().
260
261 Arguments:
262 value: A bytearray() with the encoded long.
263
264 Returns:
265 The decoded value.
266 """
267 ret = 0
268 for b in bytearray(blob):
269 ret *= 256
270 ret += b
271 return ret
272
273
David Zeuthen21e95262016-07-27 17:58:40 -0400274def egcd(a, b):
275 """Calculate greatest common divisor of two numbers.
276
277 This implementation uses a recursive version of the extended
278 Euclidian algorithm.
279
280 Arguments:
281 a: First number.
282 b: Second number.
283
284 Returns:
285 A tuple (gcd, x, y) that where |gcd| is the greatest common
286 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
287 """
288 if a == 0:
289 return (b, 0, 1)
290 else:
291 g, y, x = egcd(b % a, a)
292 return (g, x - (b // a) * y, y)
293
294
295def modinv(a, m):
296 """Calculate modular multiplicative inverse of |a| modulo |m|.
297
298 This calculates the number |x| such that |a| * |x| == 1 (modulo
299 |m|). This number only exists if |a| and |m| are co-prime - |None|
300 is returned if this isn't true.
301
302 Arguments:
303 a: The number to calculate a modular inverse of.
304 m: The modulo to use.
305
306 Returns:
307 The modular multiplicative inverse of |a| and |m| or |None| if
308 these numbers are not co-prime.
309 """
310 gcd, x, _ = egcd(a, m)
311 if gcd != 1:
312 return None # modular inverse does not exist
313 else:
314 return x % m
315
316
317def parse_number(string):
318 """Parse a string as a number.
319
320 This is just a short-hand for int(string, 0) suitable for use in the
321 |type| parameter of |ArgumentParser|'s add_argument() function. An
322 improvement to just using type=int is that this function supports
323 numbers in other bases, e.g. "0x1234".
324
325 Arguments:
326 string: The string to parse.
327
328 Returns:
329 The parsed integer.
330
331 Raises:
332 ValueError: If the number could not be parsed.
333 """
334 return int(string, 0)
335
336
David Zeuthenc68f0822017-03-31 17:22:35 -0400337class RSAPublicKey(object):
338 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400339
David Zeuthenc68f0822017-03-31 17:22:35 -0400340 Attributes:
341 exponent: The key exponent.
342 modulus: The key modulus.
343 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400344 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400345
346 MODULUS_PREFIX = 'modulus='
347
348 def __init__(self, key_path):
349 """Loads and parses an RSA key from either a private or public key file.
350
351 Arguments:
352 key_path: The path to a key file.
353 """
354 # We used to have something as simple as this:
355 #
356 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
357 # self.exponent = key.e
358 # self.modulus = key.n
359 # self.num_bits = key.size() + 1
360 #
361 # but unfortunately PyCrypto is not available in the builder. So
362 # instead just parse openssl(1) output to get this
363 # information. It's ugly but...
364 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
365 p = subprocess.Popen(args,
366 stdin=subprocess.PIPE,
367 stdout=subprocess.PIPE,
368 stderr=subprocess.PIPE)
369 (pout, perr) = p.communicate()
370 if p.wait() != 0:
371 # Could be just a public key is passed, try that.
372 args.append('-pubin')
373 p = subprocess.Popen(args,
374 stdin=subprocess.PIPE,
375 stdout=subprocess.PIPE,
376 stderr=subprocess.PIPE)
377 (pout, perr) = p.communicate()
378 if p.wait() != 0:
379 raise AvbError('Error getting public key: {}'.format(perr))
380
381 if not pout.lower().startswith(self.MODULUS_PREFIX):
382 raise AvbError('Unexpected modulus output')
383
384 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
385
386 # The exponent is assumed to always be 65537 and the number of
387 # bits can be derived from the modulus by rounding up to the
388 # nearest power of 2.
389 self.modulus = int(modulus_hexstr, 16)
390 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
391 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400392
393
David Zeuthenc68f0822017-03-31 17:22:35 -0400394def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400395 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
396
397 This creates a |AvbRSAPublicKeyHeader| as well as the two large
398 numbers (|key_num_bits| bits long) following it.
399
400 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400401 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400402
403 Returns:
404 A bytearray() with the |AvbRSAPublicKeyHeader|.
405 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key = RSAPublicKey(key_path)
407 if key.exponent != 65537:
408 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400409 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400410 # Calculate n0inv = -1/n[0] (mod 2^32)
411 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400412 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400413 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 r = 2L**key.modulus.bit_length()
415 rrmodn = r * r % key.modulus
416 ret.extend(struct.pack('!II', key.num_bits, n0inv))
417 ret.extend(encode_long(key.num_bits, key.modulus))
418 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400419 return ret
420
421
422def lookup_algorithm_by_type(alg_type):
423 """Looks up algorithm by type.
424
425 Arguments:
426 alg_type: The integer representing the type.
427
428 Returns:
429 A tuple with the algorithm name and an |Algorithm| instance.
430
431 Raises:
432 Exception: If the algorithm cannot be found
433 """
434 for alg_name in ALGORITHMS:
435 alg_data = ALGORITHMS[alg_name]
436 if alg_data.algorithm_type == alg_type:
437 return (alg_name, alg_data)
438 raise AvbError('Unknown algorithm type {}'.format(alg_type))
439
440
David Zeuthena156d3d2017-06-01 12:08:09 -0400441def raw_sign(signing_helper, signing_helper_with_files,
442 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900443 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800444 """Computes a raw RSA signature using |signing_helper| or openssl.
445
446 Arguments:
447 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400448 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800449 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900450 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800451 key_path: Path to the private key file. Must be PEM format.
452 raw_data_to_sign: Data to sign (bytearray or str expected).
453
454 Returns:
455 A bytearray containing the signature.
456
457 Raises:
458 Exception: If an error occurs.
459 """
460 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400461 if signing_helper_with_files is not None:
462 signing_file = tempfile.NamedTemporaryFile()
463 signing_file.write(str(raw_data_to_sign))
464 signing_file.flush()
David Zeuthene3cadca2017-02-22 21:25:46 -0500465 p = subprocess.Popen(
David Zeuthena156d3d2017-06-01 12:08:09 -0400466 [signing_helper_with_files, algorithm_name, key_path, signing_file.name])
467 retcode = p.wait()
468 if retcode != 0:
469 raise AvbError('Error signing')
470 signing_file.seek(0)
471 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800472 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400473 if signing_helper is not None:
474 p = subprocess.Popen(
475 [signing_helper, algorithm_name, key_path],
476 stdin=subprocess.PIPE,
477 stdout=subprocess.PIPE,
478 stderr=subprocess.PIPE)
479 else:
480 p = subprocess.Popen(
481 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
482 stdin=subprocess.PIPE,
483 stdout=subprocess.PIPE,
484 stderr=subprocess.PIPE)
485 (pout, perr) = p.communicate(str(raw_data_to_sign))
486 retcode = p.wait()
487 if retcode != 0:
488 raise AvbError('Error signing: {}'.format(perr))
489 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900490 if len(signature) != signature_num_bytes:
491 raise AvbError('Error signing: Invalid length of signature')
492 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800493
494
David Zeuthenb623d8b2017-04-04 16:05:53 -0400495def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
496 """Checks that the signature in a vbmeta blob was made by
497 the embedded public key.
498
499 Arguments:
500 vbmeta_header: A AvbVBMetaHeader.
501 vbmeta_blob: The whole vbmeta blob, including the header.
502
503 Returns:
504 True if the signature is valid and corresponds to the embedded
505 public key. Also returns True if the vbmeta blob is not signed.
506 """
507 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
508 if alg.hash_name == '':
509 return True
510 header_blob = vbmeta_blob[0:256]
511 auth_offset = 256
512 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
513 aux_size = vbmeta_header.auxiliary_data_block_size
514 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
515 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
516 pubkey_size = vbmeta_header.public_key_size
517 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
518
519 digest_offset = auth_offset + vbmeta_header.hash_offset
520 digest_size = vbmeta_header.hash_size
521 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
522
523 sig_offset = auth_offset + vbmeta_header.signature_offset
524 sig_size = vbmeta_header.signature_size
525 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
526
527 # Now that we've got the stored digest, public key, and signature
528 # all we need to do is to verify. This is the exactly the same
529 # steps as performed in the avb_vbmeta_image_verify() function in
530 # libavb/avb_vbmeta_image.c.
531
532 ha = hashlib.new(alg.hash_name)
533 ha.update(header_blob)
534 ha.update(aux_blob)
535 computed_digest = ha.digest()
536
537 if computed_digest != digest_blob:
538 return False
539
540 padding_and_digest = bytearray(alg.padding)
541 padding_and_digest.extend(computed_digest)
542
543 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
544 modulus_blob = pubkey_blob[8:8 + num_bits/8]
545 modulus = decode_long(modulus_blob)
546 exponent = 65537
547
548 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
549 # is OK since 'avbtool verify_image' is not expected to run on the
550 # Android builders (see bug #36809096).
551 import Crypto.PublicKey.RSA
552 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
553 if not key.verify(decode_long(padding_and_digest),
554 (decode_long(sig_blob), None)):
555 return False
556 return True
557
558
David Zeuthena4fee8b2016-08-22 15:20:43 -0400559class ImageChunk(object):
560 """Data structure used for representing chunks in Android sparse files.
561
562 Attributes:
563 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
564 chunk_offset: Offset in the sparse file where this chunk begins.
565 output_offset: Offset in de-sparsified file where output begins.
566 output_size: Number of bytes in output.
567 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
568 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
569 """
570
571 FORMAT = '<2H2I'
572 TYPE_RAW = 0xcac1
573 TYPE_FILL = 0xcac2
574 TYPE_DONT_CARE = 0xcac3
575 TYPE_CRC32 = 0xcac4
576
577 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
578 input_offset, fill_data):
579 """Initializes an ImageChunk object.
580
581 Arguments:
582 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
583 chunk_offset: Offset in the sparse file where this chunk begins.
584 output_offset: Offset in de-sparsified file.
585 output_size: Number of bytes in output.
586 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
587 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
588
589 Raises:
590 ValueError: If data is not well-formed.
591 """
592 self.chunk_type = chunk_type
593 self.chunk_offset = chunk_offset
594 self.output_offset = output_offset
595 self.output_size = output_size
596 self.input_offset = input_offset
597 self.fill_data = fill_data
598 # Check invariants.
599 if self.chunk_type == self.TYPE_RAW:
600 if self.fill_data is not None:
601 raise ValueError('RAW chunk cannot have fill_data set.')
602 if not self.input_offset:
603 raise ValueError('RAW chunk must have input_offset set.')
604 elif self.chunk_type == self.TYPE_FILL:
605 if self.fill_data is None:
606 raise ValueError('FILL chunk must have fill_data set.')
607 if self.input_offset:
608 raise ValueError('FILL chunk cannot have input_offset set.')
609 elif self.chunk_type == self.TYPE_DONT_CARE:
610 if self.fill_data is not None:
611 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
612 if self.input_offset:
613 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
614 else:
615 raise ValueError('Invalid chunk type')
616
617
618class ImageHandler(object):
619 """Abstraction for image I/O with support for Android sparse images.
620
621 This class provides an interface for working with image files that
622 may be using the Android Sparse Image format. When an instance is
623 constructed, we test whether it's an Android sparse file. If so,
624 operations will be on the sparse file by interpreting the sparse
625 format, otherwise they will be directly on the file. Either way the
626 operations do the same.
627
628 For reading, this interface mimics a file object - it has seek(),
629 tell(), and read() methods. For writing, only truncation
630 (truncate()) and appending is supported (append_raw() and
631 append_dont_care()). Additionally, data can only be written in units
632 of the block size.
633
634 Attributes:
635 is_sparse: Whether the file being operated on is sparse.
636 block_size: The block size, typically 4096.
637 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400638 """
639 # See system/core/libsparse/sparse_format.h for details.
640 MAGIC = 0xed26ff3a
641 HEADER_FORMAT = '<I4H4I'
642
643 # These are formats and offset of just the |total_chunks| and
644 # |total_blocks| fields.
645 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
646 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
647
648 def __init__(self, image_filename):
649 """Initializes an image handler.
650
651 Arguments:
652 image_filename: The name of the file to operate on.
653
654 Raises:
655 ValueError: If data in the file is invalid.
656 """
657 self._image_filename = image_filename
658 self._read_header()
659
660 def _read_header(self):
661 """Initializes internal data structures used for reading file.
662
663 This may be called multiple times and is typically called after
664 modifying the file (e.g. appending, truncation).
665
666 Raises:
667 ValueError: If data in the file is invalid.
668 """
669 self.is_sparse = False
670 self.block_size = 4096
671 self._file_pos = 0
672 self._image = open(self._image_filename, 'r+b')
673 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400674 self.image_size = self._image.tell()
675
676 self._image.seek(0, os.SEEK_SET)
677 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
678 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
679 block_size, self._num_total_blocks, self._num_total_chunks,
680 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
681 if magic != self.MAGIC:
682 # Not a sparse image, our job here is done.
683 return
684 if not (major_version == 1 and minor_version == 0):
685 raise ValueError('Encountered sparse image format version {}.{} but '
686 'only 1.0 is supported'.format(major_version,
687 minor_version))
688 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
689 raise ValueError('Unexpected file_hdr_sz value {}.'.
690 format(file_hdr_sz))
691 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
692 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
693 format(chunk_hdr_sz))
694
695 self.block_size = block_size
696
697 # Build an list of chunks by parsing the file.
698 self._chunks = []
699
700 # Find the smallest offset where only "Don't care" chunks
701 # follow. This will be the size of the content in the sparse
702 # image.
703 offset = 0
704 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 for _ in xrange(1, self._num_total_chunks + 1):
706 chunk_offset = self._image.tell()
707
708 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
709 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
710 header_bin)
711 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
712
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 if chunk_type == ImageChunk.TYPE_RAW:
714 if data_sz != (chunk_sz * self.block_size):
715 raise ValueError('Raw chunk input size ({}) does not match output '
716 'size ({})'.
717 format(data_sz, chunk_sz*self.block_size))
718 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
719 chunk_offset,
720 output_offset,
721 chunk_sz*self.block_size,
722 self._image.tell(),
723 None))
724 self._image.read(data_sz)
725
726 elif chunk_type == ImageChunk.TYPE_FILL:
727 if data_sz != 4:
728 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
729 'has {}'.format(data_sz))
730 fill_data = self._image.read(4)
731 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
732 chunk_offset,
733 output_offset,
734 chunk_sz*self.block_size,
735 None,
736 fill_data))
737 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
738 if data_sz != 0:
739 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
740 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400741 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
742 chunk_offset,
743 output_offset,
744 chunk_sz*self.block_size,
745 None,
746 None))
747 elif chunk_type == ImageChunk.TYPE_CRC32:
748 if data_sz != 4:
749 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
750 'this has {}'.format(data_sz))
751 self._image.read(4)
752 else:
753 raise ValueError('Unknown chunk type {}'.format(chunk_type))
754
755 offset += chunk_sz
756 output_offset += chunk_sz*self.block_size
757
758 # Record where sparse data end.
759 self._sparse_end = self._image.tell()
760
761 # Now that we've traversed all chunks, sanity check.
762 if self._num_total_blocks != offset:
763 raise ValueError('The header said we should have {} output blocks, '
764 'but we saw {}'.format(self._num_total_blocks, offset))
765 junk_len = len(self._image.read())
766 if junk_len > 0:
767 raise ValueError('There were {} bytes of extra data at the end of the '
768 'file.'.format(junk_len))
769
David Zeuthen09692692016-09-30 16:16:40 -0400770 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400771 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400772
773 # This is used when bisecting in read() to find the initial slice.
774 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
775
776 self.is_sparse = True
777
778 def _update_chunks_and_blocks(self):
779 """Helper function to update the image header.
780
781 The the |total_chunks| and |total_blocks| fields in the header
782 will be set to value of the |_num_total_blocks| and
783 |_num_total_chunks| attributes.
784
785 """
786 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
787 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
788 self._num_total_blocks,
789 self._num_total_chunks))
790
791 def append_dont_care(self, num_bytes):
792 """Appends a DONT_CARE chunk to the sparse file.
793
794 The given number of bytes must be a multiple of the block size.
795
796 Arguments:
797 num_bytes: Size in number of bytes of the DONT_CARE chunk.
798 """
799 assert num_bytes % self.block_size == 0
800
801 if not self.is_sparse:
802 self._image.seek(0, os.SEEK_END)
803 # This is more efficient that writing NUL bytes since it'll add
804 # a hole on file systems that support sparse files (native
805 # sparse, not Android sparse).
806 self._image.truncate(self._image.tell() + num_bytes)
807 self._read_header()
808 return
809
810 self._num_total_chunks += 1
811 self._num_total_blocks += num_bytes / self.block_size
812 self._update_chunks_and_blocks()
813
814 self._image.seek(self._sparse_end, os.SEEK_SET)
815 self._image.write(struct.pack(ImageChunk.FORMAT,
816 ImageChunk.TYPE_DONT_CARE,
817 0, # Reserved
818 num_bytes / self.block_size,
819 struct.calcsize(ImageChunk.FORMAT)))
820 self._read_header()
821
822 def append_raw(self, data):
823 """Appends a RAW chunk to the sparse file.
824
825 The length of the given data must be a multiple of the block size.
826
827 Arguments:
828 data: Data to append.
829 """
830 assert len(data) % self.block_size == 0
831
832 if not self.is_sparse:
833 self._image.seek(0, os.SEEK_END)
834 self._image.write(data)
835 self._read_header()
836 return
837
838 self._num_total_chunks += 1
839 self._num_total_blocks += len(data) / self.block_size
840 self._update_chunks_and_blocks()
841
842 self._image.seek(self._sparse_end, os.SEEK_SET)
843 self._image.write(struct.pack(ImageChunk.FORMAT,
844 ImageChunk.TYPE_RAW,
845 0, # Reserved
846 len(data) / self.block_size,
847 len(data) +
848 struct.calcsize(ImageChunk.FORMAT)))
849 self._image.write(data)
850 self._read_header()
851
852 def append_fill(self, fill_data, size):
853 """Appends a fill chunk to the sparse file.
854
855 The total length of the fill data must be a multiple of the block size.
856
857 Arguments:
858 fill_data: Fill data to append - must be four bytes.
859 size: Number of chunk - must be a multiple of four and the block size.
860 """
861 assert len(fill_data) == 4
862 assert size % 4 == 0
863 assert size % self.block_size == 0
864
865 if not self.is_sparse:
866 self._image.seek(0, os.SEEK_END)
867 self._image.write(fill_data * (size/4))
868 self._read_header()
869 return
870
871 self._num_total_chunks += 1
872 self._num_total_blocks += size / self.block_size
873 self._update_chunks_and_blocks()
874
875 self._image.seek(self._sparse_end, os.SEEK_SET)
876 self._image.write(struct.pack(ImageChunk.FORMAT,
877 ImageChunk.TYPE_FILL,
878 0, # Reserved
879 size / self.block_size,
880 4 + struct.calcsize(ImageChunk.FORMAT)))
881 self._image.write(fill_data)
882 self._read_header()
883
884 def seek(self, offset):
885 """Sets the cursor position for reading from unsparsified file.
886
887 Arguments:
888 offset: Offset to seek to from the beginning of the file.
889 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700890 if offset < 0:
891 raise RuntimeError("Seeking with negative offset: %d" % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400892 self._file_pos = offset
893
894 def read(self, size):
895 """Reads data from the unsparsified file.
896
897 This method may return fewer than |size| bytes of data if the end
898 of the file was encountered.
899
900 The file cursor for reading is advanced by the number of bytes
901 read.
902
903 Arguments:
904 size: Number of bytes to read.
905
906 Returns:
907 The data.
908
909 """
910 if not self.is_sparse:
911 self._image.seek(self._file_pos)
912 data = self._image.read(size)
913 self._file_pos += len(data)
914 return data
915
916 # Iterate over all chunks.
917 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
918 self._file_pos) - 1
919 data = bytearray()
920 to_go = size
921 while to_go > 0:
922 chunk = self._chunks[chunk_idx]
923 chunk_pos_offset = self._file_pos - chunk.output_offset
924 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
925
926 if chunk.chunk_type == ImageChunk.TYPE_RAW:
927 self._image.seek(chunk.input_offset + chunk_pos_offset)
928 data.extend(self._image.read(chunk_pos_to_go))
929 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
930 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
931 offset_mod = chunk_pos_offset % len(chunk.fill_data)
932 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
933 else:
934 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
935 data.extend('\0' * chunk_pos_to_go)
936
937 to_go -= chunk_pos_to_go
938 self._file_pos += chunk_pos_to_go
939 chunk_idx += 1
940 # Generate partial read in case of EOF.
941 if chunk_idx >= len(self._chunks):
942 break
943
944 return data
945
946 def tell(self):
947 """Returns the file cursor position for reading from unsparsified file.
948
949 Returns:
950 The file cursor position for reading.
951 """
952 return self._file_pos
953
954 def truncate(self, size):
955 """Truncates the unsparsified file.
956
957 Arguments:
958 size: Desired size of unsparsified file.
959
960 Raises:
961 ValueError: If desired size isn't a multiple of the block size.
962 """
963 if not self.is_sparse:
964 self._image.truncate(size)
965 self._read_header()
966 return
967
968 if size % self.block_size != 0:
969 raise ValueError('Cannot truncate to a size which is not a multiple '
970 'of the block size')
971
972 if size == self.image_size:
973 # Trivial where there's nothing to do.
974 return
975 elif size < self.image_size:
976 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
977 chunk = self._chunks[chunk_idx]
978 if chunk.output_offset != size:
979 # Truncation in the middle of a trunk - need to keep the chunk
980 # and modify it.
981 chunk_idx_for_update = chunk_idx + 1
982 num_to_keep = size - chunk.output_offset
983 assert num_to_keep % self.block_size == 0
984 if chunk.chunk_type == ImageChunk.TYPE_RAW:
985 truncate_at = (chunk.chunk_offset +
986 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
987 data_sz = num_to_keep
988 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
989 truncate_at = (chunk.chunk_offset +
990 struct.calcsize(ImageChunk.FORMAT) + 4)
991 data_sz = 4
992 else:
993 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
994 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
995 data_sz = 0
996 chunk_sz = num_to_keep/self.block_size
997 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
998 self._image.seek(chunk.chunk_offset)
999 self._image.write(struct.pack(ImageChunk.FORMAT,
1000 chunk.chunk_type,
1001 0, # Reserved
1002 chunk_sz,
1003 total_sz))
1004 chunk.output_size = num_to_keep
1005 else:
1006 # Truncation at trunk boundary.
1007 truncate_at = chunk.chunk_offset
1008 chunk_idx_for_update = chunk_idx
1009
1010 self._num_total_chunks = chunk_idx_for_update
1011 self._num_total_blocks = 0
1012 for i in range(0, chunk_idx_for_update):
1013 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1014 self._update_chunks_and_blocks()
1015 self._image.truncate(truncate_at)
1016
1017 # We've modified the file so re-read all data.
1018 self._read_header()
1019 else:
1020 # Truncating to grow - just add a DONT_CARE section.
1021 self.append_dont_care(size - self.image_size)
1022
1023
David Zeuthen21e95262016-07-27 17:58:40 -04001024class AvbDescriptor(object):
1025 """Class for AVB descriptor.
1026
1027 See the |AvbDescriptor| C struct for more information.
1028
1029 Attributes:
1030 tag: The tag identifying what kind of descriptor this is.
1031 data: The data in the descriptor.
1032 """
1033
1034 SIZE = 16
1035 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1036
1037 def __init__(self, data):
1038 """Initializes a new property descriptor.
1039
1040 Arguments:
1041 data: If not None, must be a bytearray().
1042
1043 Raises:
1044 LookupError: If the given descriptor is malformed.
1045 """
1046 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1047
1048 if data:
1049 (self.tag, num_bytes_following) = (
1050 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1051 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1052 else:
1053 self.tag = None
1054 self.data = None
1055
1056 def print_desc(self, o):
1057 """Print the descriptor.
1058
1059 Arguments:
1060 o: The object to write the output to.
1061 """
1062 o.write(' Unknown descriptor:\n')
1063 o.write(' Tag: {}\n'.format(self.tag))
1064 if len(self.data) < 256:
1065 o.write(' Data: {} ({} bytes)\n'.format(
1066 repr(str(self.data)), len(self.data)))
1067 else:
1068 o.write(' Data: {} bytes\n'.format(len(self.data)))
1069
1070 def encode(self):
1071 """Serializes the descriptor.
1072
1073 Returns:
1074 A bytearray() with the descriptor data.
1075 """
1076 num_bytes_following = len(self.data)
1077 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1078 padding_size = nbf_with_padding - num_bytes_following
1079 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1080 padding = struct.pack(str(padding_size) + 'x')
1081 ret = desc + self.data + padding
1082 return bytearray(ret)
1083
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001084 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1085 """Verifies contents of the descriptor - used in verify_image sub-command.
1086
1087 Arguments:
1088 image_dir: The directory of the file being verified.
1089 image_ext: The extension of the file being verified (e.g. '.img').
1090 expected_chain_partitions_map: A map from partition name to the
1091 tuple (rollback_index_location, key_blob).
1092
1093 Returns:
1094 True if the descriptor verifies, False otherwise.
1095 """
1096 # Nothing to do.
1097 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001098
1099class AvbPropertyDescriptor(AvbDescriptor):
1100 """A class for property descriptors.
1101
1102 See the |AvbPropertyDescriptor| C struct for more information.
1103
1104 Attributes:
1105 key: The key.
1106 value: The key.
1107 """
1108
1109 TAG = 0
1110 SIZE = 32
1111 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1112 'Q' # key size (bytes)
1113 'Q') # value size (bytes)
1114
1115 def __init__(self, data=None):
1116 """Initializes a new property descriptor.
1117
1118 Arguments:
1119 data: If not None, must be a bytearray of size |SIZE|.
1120
1121 Raises:
1122 LookupError: If the given descriptor is malformed.
1123 """
1124 AvbDescriptor.__init__(self, None)
1125 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1126
1127 if data:
1128 (tag, num_bytes_following, key_size,
1129 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1130 expected_size = round_to_multiple(
1131 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1132 if tag != self.TAG or num_bytes_following != expected_size:
1133 raise LookupError('Given data does not look like a property '
1134 'descriptor.')
1135 self.key = data[self.SIZE:(self.SIZE + key_size)]
1136 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1137 value_size)]
1138 else:
1139 self.key = ''
1140 self.value = ''
1141
1142 def print_desc(self, o):
1143 """Print the descriptor.
1144
1145 Arguments:
1146 o: The object to write the output to.
1147 """
1148 if len(self.value) < 256:
1149 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1150 else:
1151 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1152
1153 def encode(self):
1154 """Serializes the descriptor.
1155
1156 Returns:
1157 A bytearray() with the descriptor data.
1158 """
1159 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1160 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1161 padding_size = nbf_with_padding - num_bytes_following
1162 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1163 len(self.key), len(self.value))
1164 padding = struct.pack(str(padding_size) + 'x')
1165 ret = desc + self.key + '\0' + self.value + '\0' + padding
1166 return bytearray(ret)
1167
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001168 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1169 """Verifies contents of the descriptor - used in verify_image sub-command.
1170
1171 Arguments:
1172 image_dir: The directory of the file being verified.
1173 image_ext: The extension of the file being verified (e.g. '.img').
1174 expected_chain_partitions_map: A map from partition name to the
1175 tuple (rollback_index_location, key_blob).
1176
1177 Returns:
1178 True if the descriptor verifies, False otherwise.
1179 """
1180 # Nothing to do.
1181 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001182
1183class AvbHashtreeDescriptor(AvbDescriptor):
1184 """A class for hashtree descriptors.
1185
1186 See the |AvbHashtreeDescriptor| C struct for more information.
1187
1188 Attributes:
1189 dm_verity_version: dm-verity version used.
1190 image_size: Size of the image, after rounding up to |block_size|.
1191 tree_offset: Offset of the hash tree in the file.
1192 tree_size: Size of the tree.
1193 data_block_size: Data block size
1194 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001195 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1196 fec_offset: Offset of FEC data (0 if FEC is not used).
1197 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001198 hash_algorithm: Hash algorithm used.
1199 partition_name: Partition name.
1200 salt: Salt used.
1201 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001202 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001203 """
1204
1205 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001206 RESERVED = 60
1207 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001208 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1209 'L' # dm-verity version used
1210 'Q' # image size (bytes)
1211 'Q' # tree offset (bytes)
1212 'Q' # tree size (bytes)
1213 'L' # data block size (bytes)
1214 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001215 'L' # FEC number of roots
1216 'Q' # FEC offset (bytes)
1217 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001218 '32s' # hash algorithm used
1219 'L' # partition name (bytes)
1220 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001221 'L' # root digest length (bytes)
1222 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001223 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001224
1225 def __init__(self, data=None):
1226 """Initializes a new hashtree descriptor.
1227
1228 Arguments:
1229 data: If not None, must be a bytearray of size |SIZE|.
1230
1231 Raises:
1232 LookupError: If the given descriptor is malformed.
1233 """
1234 AvbDescriptor.__init__(self, None)
1235 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1236
1237 if data:
1238 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1239 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001240 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1241 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001242 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1243 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001244 expected_size = round_to_multiple(
1245 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1246 if tag != self.TAG or num_bytes_following != expected_size:
1247 raise LookupError('Given data does not look like a hashtree '
1248 'descriptor.')
1249 # Nuke NUL-bytes at the end.
1250 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1251 o = 0
1252 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1253 partition_name_len)])
1254 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1255 self.partition_name.decode('utf-8')
1256 o += partition_name_len
1257 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1258 o += salt_len
1259 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1260 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001261 if root_digest_len != 0:
1262 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001263
1264 else:
1265 self.dm_verity_version = 0
1266 self.image_size = 0
1267 self.tree_offset = 0
1268 self.tree_size = 0
1269 self.data_block_size = 0
1270 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001271 self.fec_num_roots = 0
1272 self.fec_offset = 0
1273 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001274 self.hash_algorithm = ''
1275 self.partition_name = ''
1276 self.salt = bytearray()
1277 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001278 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001279
1280 def print_desc(self, o):
1281 """Print the descriptor.
1282
1283 Arguments:
1284 o: The object to write the output to.
1285 """
1286 o.write(' Hashtree descriptor:\n')
1287 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1288 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1289 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1290 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1291 o.write(' Data Block Size: {} bytes\n'.format(
1292 self.data_block_size))
1293 o.write(' Hash Block Size: {} bytes\n'.format(
1294 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001295 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1296 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1297 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001298 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1299 o.write(' Partition Name: {}\n'.format(self.partition_name))
1300 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1301 'hex')))
1302 o.write(' Root Digest: {}\n'.format(str(
1303 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001304 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001305
1306 def encode(self):
1307 """Serializes the descriptor.
1308
1309 Returns:
1310 A bytearray() with the descriptor data.
1311 """
1312 encoded_name = self.partition_name.encode('utf-8')
1313 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1314 len(self.root_digest) - 16)
1315 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1316 padding_size = nbf_with_padding - num_bytes_following
1317 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1318 self.dm_verity_version, self.image_size,
1319 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001320 self.hash_block_size, self.fec_num_roots,
1321 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001322 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001323 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001324 padding = struct.pack(str(padding_size) + 'x')
1325 ret = desc + encoded_name + self.salt + self.root_digest + padding
1326 return bytearray(ret)
1327
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001328 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1329 """Verifies contents of the descriptor - used in verify_image sub-command.
1330
1331 Arguments:
1332 image_dir: The directory of the file being verified.
1333 image_ext: The extension of the file being verified (e.g. '.img').
1334 expected_chain_partitions_map: A map from partition name to the
1335 tuple (rollback_index_location, key_blob).
1336
1337 Returns:
1338 True if the descriptor verifies, False otherwise.
1339 """
1340 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1341 image = ImageHandler(image_filename)
1342 # Generate the hashtree and checks that it matches what's in the file.
1343 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1344 digest_padding = round_to_pow2(digest_size) - digest_size
1345 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1346 self.image_size, self.data_block_size, digest_size + digest_padding)
1347 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1348 self.data_block_size,
1349 self.hash_algorithm, self.salt,
1350 digest_padding,
1351 hash_level_offsets,
1352 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001353 # The root digest must match unless it is not embedded in the descriptor.
1354 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001355 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1356 format(image_filename))
1357 return False
1358 # ... also check that the on-disk hashtree matches
1359 image.seek(self.tree_offset)
1360 hash_tree_ondisk = image.read(self.tree_size)
1361 if hash_tree != hash_tree_ondisk:
1362 sys.stderr.write('hashtree of {} contains invalid data\n'.
1363 format(image_filename))
1364 return False
1365 # TODO: we could also verify that the FEC stored in the image is
1366 # correct but this a) currently requires the 'fec' binary; and b)
1367 # takes a long time; and c) is not strictly needed for
1368 # verification purposes as we've already verified the root hash.
1369 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1370 .format(self.partition_name, self.hash_algorithm, image_filename,
1371 self.image_size))
1372 return True
1373
David Zeuthen21e95262016-07-27 17:58:40 -04001374
1375class AvbHashDescriptor(AvbDescriptor):
1376 """A class for hash descriptors.
1377
1378 See the |AvbHashDescriptor| C struct for more information.
1379
1380 Attributes:
1381 image_size: Image size, in bytes.
1382 hash_algorithm: Hash algorithm used.
1383 partition_name: Partition name.
1384 salt: Salt used.
1385 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001386 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001387 """
1388
1389 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001390 RESERVED = 60
1391 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001392 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1393 'Q' # image size (bytes)
1394 '32s' # hash algorithm used
1395 'L' # partition name (bytes)
1396 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001397 'L' # digest length (bytes)
1398 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001399 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001400
1401 def __init__(self, data=None):
1402 """Initializes a new hash descriptor.
1403
1404 Arguments:
1405 data: If not None, must be a bytearray of size |SIZE|.
1406
1407 Raises:
1408 LookupError: If the given descriptor is malformed.
1409 """
1410 AvbDescriptor.__init__(self, None)
1411 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1412
1413 if data:
1414 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1415 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001416 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1417 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001418 expected_size = round_to_multiple(
1419 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1420 if tag != self.TAG or num_bytes_following != expected_size:
1421 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1422 # Nuke NUL-bytes at the end.
1423 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1424 o = 0
1425 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1426 partition_name_len)])
1427 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1428 self.partition_name.decode('utf-8')
1429 o += partition_name_len
1430 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1431 o += salt_len
1432 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1433 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001434 if digest_len != 0:
1435 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001436
1437 else:
1438 self.image_size = 0
1439 self.hash_algorithm = ''
1440 self.partition_name = ''
1441 self.salt = bytearray()
1442 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001443 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001444
1445 def print_desc(self, o):
1446 """Print the descriptor.
1447
1448 Arguments:
1449 o: The object to write the output to.
1450 """
1451 o.write(' Hash descriptor:\n')
1452 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1453 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1454 o.write(' Partition Name: {}\n'.format(self.partition_name))
1455 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1456 'hex')))
1457 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1458 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001459 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001460
1461 def encode(self):
1462 """Serializes the descriptor.
1463
1464 Returns:
1465 A bytearray() with the descriptor data.
1466 """
1467 encoded_name = self.partition_name.encode('utf-8')
1468 num_bytes_following = (
1469 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1470 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1471 padding_size = nbf_with_padding - num_bytes_following
1472 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1473 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001474 len(self.salt), len(self.digest), self.flags,
1475 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001476 padding = struct.pack(str(padding_size) + 'x')
1477 ret = desc + encoded_name + self.salt + self.digest + padding
1478 return bytearray(ret)
1479
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001480 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1481 """Verifies contents of the descriptor - used in verify_image sub-command.
1482
1483 Arguments:
1484 image_dir: The directory of the file being verified.
1485 image_ext: The extension of the file being verified (e.g. '.img').
1486 expected_chain_partitions_map: A map from partition name to the
1487 tuple (rollback_index_location, key_blob).
1488
1489 Returns:
1490 True if the descriptor verifies, False otherwise.
1491 """
1492 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1493 image = ImageHandler(image_filename)
1494 data = image.read(self.image_size)
1495 ha = hashlib.new(self.hash_algorithm)
1496 ha.update(self.salt)
1497 ha.update(data)
1498 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001499 # The digest must match unless there is no digest in the descriptor.
1500 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001501 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1502 format(self.hash_algorithm, image_filename))
1503 return False
1504 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
1505 .format(self.partition_name, self.hash_algorithm, image_filename,
1506 self.image_size))
1507 return True
1508
David Zeuthen21e95262016-07-27 17:58:40 -04001509
1510class AvbKernelCmdlineDescriptor(AvbDescriptor):
1511 """A class for kernel command-line descriptors.
1512
1513 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1514
1515 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001516 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001517 kernel_cmdline: The kernel command-line.
1518 """
1519
1520 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001521 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001522 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001523 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001524 'L') # cmdline length (bytes)
1525
David Zeuthenfd41eb92016-11-17 12:24:47 -05001526 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1527 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1528
David Zeuthen21e95262016-07-27 17:58:40 -04001529 def __init__(self, data=None):
1530 """Initializes a new kernel cmdline descriptor.
1531
1532 Arguments:
1533 data: If not None, must be a bytearray of size |SIZE|.
1534
1535 Raises:
1536 LookupError: If the given descriptor is malformed.
1537 """
1538 AvbDescriptor.__init__(self, None)
1539 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1540
1541 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001542 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001543 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1544 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1545 8)
1546 if tag != self.TAG or num_bytes_following != expected_size:
1547 raise LookupError('Given data does not look like a kernel cmdline '
1548 'descriptor.')
1549 # Nuke NUL-bytes at the end.
1550 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1551 kernel_cmdline_length)])
1552 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1553 self.kernel_cmdline.decode('utf-8')
1554 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001555 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001556 self.kernel_cmdline = ''
1557
1558 def print_desc(self, o):
1559 """Print the descriptor.
1560
1561 Arguments:
1562 o: The object to write the output to.
1563 """
1564 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001565 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001566 o.write(' Kernel Cmdline: {}\n'.format(repr(
1567 self.kernel_cmdline)))
1568
1569 def encode(self):
1570 """Serializes the descriptor.
1571
1572 Returns:
1573 A bytearray() with the descriptor data.
1574 """
1575 encoded_str = self.kernel_cmdline.encode('utf-8')
1576 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1577 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1578 padding_size = nbf_with_padding - num_bytes_following
1579 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001580 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001581 padding = struct.pack(str(padding_size) + 'x')
1582 ret = desc + encoded_str + padding
1583 return bytearray(ret)
1584
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001585 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1586 """Verifies contents of the descriptor - used in verify_image sub-command.
1587
1588 Arguments:
1589 image_dir: The directory of the file being verified.
1590 image_ext: The extension of the file being verified (e.g. '.img').
1591 expected_chain_partitions_map: A map from partition name to the
1592 tuple (rollback_index_location, key_blob).
1593
1594 Returns:
1595 True if the descriptor verifies, False otherwise.
1596 """
1597 # Nothing to verify.
1598 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001599
1600class AvbChainPartitionDescriptor(AvbDescriptor):
1601 """A class for chained partition descriptors.
1602
1603 See the |AvbChainPartitionDescriptor| C struct for more information.
1604
1605 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001606 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001607 partition_name: Partition name.
1608 public_key: Bytes for the public key.
1609 """
1610
1611 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001612 RESERVED = 64
1613 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001614 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001615 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001616 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001617 'L' + # public_key_size (bytes)
1618 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001619
1620 def __init__(self, data=None):
1621 """Initializes a new chain partition descriptor.
1622
1623 Arguments:
1624 data: If not None, must be a bytearray of size |SIZE|.
1625
1626 Raises:
1627 LookupError: If the given descriptor is malformed.
1628 """
1629 AvbDescriptor.__init__(self, None)
1630 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1631
1632 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001633 (tag, num_bytes_following, self.rollback_index_location,
1634 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001635 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001636 expected_size = round_to_multiple(
1637 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1638 if tag != self.TAG or num_bytes_following != expected_size:
1639 raise LookupError('Given data does not look like a chain partition '
1640 'descriptor.')
1641 o = 0
1642 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1643 partition_name_len)])
1644 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1645 self.partition_name.decode('utf-8')
1646 o += partition_name_len
1647 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1648
1649 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001650 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001651 self.partition_name = ''
1652 self.public_key = bytearray()
1653
1654 def print_desc(self, o):
1655 """Print the descriptor.
1656
1657 Arguments:
1658 o: The object to write the output to.
1659 """
1660 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001661 o.write(' Partition Name: {}\n'.format(self.partition_name))
1662 o.write(' Rollback Index Location: {}\n'.format(
1663 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001664 # Just show the SHA1 of the key, for size reasons.
1665 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001666 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001667
1668 def encode(self):
1669 """Serializes the descriptor.
1670
1671 Returns:
1672 A bytearray() with the descriptor data.
1673 """
1674 encoded_name = self.partition_name.encode('utf-8')
1675 num_bytes_following = (
1676 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1677 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1678 padding_size = nbf_with_padding - num_bytes_following
1679 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001680 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001681 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001682 padding = struct.pack(str(padding_size) + 'x')
1683 ret = desc + encoded_name + self.public_key + padding
1684 return bytearray(ret)
1685
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001686 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1687 """Verifies contents of the descriptor - used in verify_image sub-command.
1688
1689 Arguments:
1690 image_dir: The directory of the file being verified.
1691 image_ext: The extension of the file being verified (e.g. '.img').
1692 expected_chain_partitions_map: A map from partition name to the
1693 tuple (rollback_index_location, key_blob).
1694
1695 Returns:
1696 True if the descriptor verifies, False otherwise.
1697 """
1698 value = expected_chain_partitions_map.get(self.partition_name)
1699 if not value:
1700 sys.stderr.write('No expected chain partition for partition {}. Use '
1701 '--expected_chain_partition to specify expected '
1702 'contents.\n'.
1703 format(self.partition_name))
1704 return False
1705 rollback_index_location, pk_blob = value
1706
1707 if self.rollback_index_location != rollback_index_location:
1708 sys.stderr.write('Expected rollback_index_location {} does not '
1709 'match {} in descriptor for partition {}\n'.
1710 format(rollback_index_location,
1711 self.rollback_index_location,
1712 self.partition_name))
1713 return False
1714
1715 if self.public_key != pk_blob:
1716 sys.stderr.write('Expected public key blob does not match public '
1717 'key blob in descriptor for partition {}\n'.
1718 format(self.partition_name))
1719 return False
1720
1721 print ('{}: Successfully verified chain partition descriptor matches '
1722 'expected data'.format(self.partition_name))
1723
1724 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001725
1726DESCRIPTOR_CLASSES = [
1727 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1728 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1729]
1730
1731
1732def parse_descriptors(data):
1733 """Parses a blob of data into descriptors.
1734
1735 Arguments:
1736 data: A bytearray() with encoded descriptors.
1737
1738 Returns:
1739 A list of instances of objects derived from AvbDescriptor. For
1740 unknown descriptors, the class AvbDescriptor is used.
1741 """
1742 o = 0
1743 ret = []
1744 while o < len(data):
1745 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1746 if tag < len(DESCRIPTOR_CLASSES):
1747 c = DESCRIPTOR_CLASSES[tag]
1748 else:
1749 c = AvbDescriptor
1750 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1751 o += 16 + nb_following
1752 return ret
1753
1754
1755class AvbFooter(object):
1756 """A class for parsing and writing footers.
1757
1758 Footers are stored at the end of partitions and point to where the
1759 AvbVBMeta blob is located. They also contain the original size of
1760 the image before AVB information was added.
1761
1762 Attributes:
1763 magic: Magic for identifying the footer, see |MAGIC|.
1764 version_major: The major version of avbtool that wrote the footer.
1765 version_minor: The minor version of avbtool that wrote the footer.
1766 original_image_size: Original image size.
1767 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1768 vbmeta_size: Size of the AvbVBMeta blob.
1769 """
1770
1771 MAGIC = 'AVBf'
1772 SIZE = 64
1773 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001774 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1775 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001776 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1777 'Q' # Original image size.
1778 'Q' # Offset of VBMeta blob.
1779 'Q' + # Size of VBMeta blob.
1780 str(RESERVED) + 'x') # padding for reserved bytes
1781
1782 def __init__(self, data=None):
1783 """Initializes a new footer object.
1784
1785 Arguments:
1786 data: If not None, must be a bytearray of size 4096.
1787
1788 Raises:
1789 LookupError: If the given footer is malformed.
1790 struct.error: If the given data has no footer.
1791 """
1792 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1793
1794 if data:
1795 (self.magic, self.version_major, self.version_minor,
1796 self.original_image_size, self.vbmeta_offset,
1797 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1798 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001799 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001800 else:
1801 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001802 self.version_major = self.FOOTER_VERSION_MAJOR
1803 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001804 self.original_image_size = 0
1805 self.vbmeta_offset = 0
1806 self.vbmeta_size = 0
1807
David Zeuthena4fee8b2016-08-22 15:20:43 -04001808 def encode(self):
1809 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001810
David Zeuthena4fee8b2016-08-22 15:20:43 -04001811 Returns:
1812 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001813 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001814 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1815 self.version_minor, self.original_image_size,
1816 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001817
1818
1819class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001820 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001821
1822 Attributes:
1823 The attributes correspond to the |AvbVBMetaHeader| struct
1824 defined in avb_vbmeta_header.h.
1825 """
1826
1827 SIZE = 256
1828
David Zeuthene3cadca2017-02-22 21:25:46 -05001829 # Keep in sync with |reserved0| and |reserved| field of
1830 # |AvbVBMetaImageHeader|.
1831 RESERVED0 = 4
1832 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001833
1834 # Keep in sync with |AvbVBMetaImageHeader|.
1835 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1836 '2Q' # 2 x block size
1837 'L' # algorithm type
1838 '2Q' # offset, size (hash)
1839 '2Q' # offset, size (signature)
1840 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001841 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001842 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001843 'Q' # rollback_index
1844 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001845 str(RESERVED0) + 'x' + # padding for reserved bytes
1846 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001847 str(RESERVED) + 'x') # padding for reserved bytes
1848
1849 def __init__(self, data=None):
1850 """Initializes a new header object.
1851
1852 Arguments:
1853 data: If not None, must be a bytearray of size 8192.
1854
1855 Raises:
1856 Exception: If the given data is malformed.
1857 """
1858 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1859
1860 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001861 (self.magic, self.required_libavb_version_major,
1862 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001863 self.authentication_data_block_size, self.auxiliary_data_block_size,
1864 self.algorithm_type, self.hash_offset, self.hash_size,
1865 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001866 self.public_key_size, self.public_key_metadata_offset,
1867 self.public_key_metadata_size, self.descriptors_offset,
1868 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001869 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001870 self.flags,
1871 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001872 # Nuke NUL-bytes at the end of the string.
1873 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001874 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001875 else:
1876 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001877 # Start by just requiring version 1.0. Code that adds features
1878 # in a future version can use bump_required_libavb_version_minor() to
1879 # bump the minor.
1880 self.required_libavb_version_major = AVB_VERSION_MAJOR
1881 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001882 self.authentication_data_block_size = 0
1883 self.auxiliary_data_block_size = 0
1884 self.algorithm_type = 0
1885 self.hash_offset = 0
1886 self.hash_size = 0
1887 self.signature_offset = 0
1888 self.signature_size = 0
1889 self.public_key_offset = 0
1890 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001891 self.public_key_metadata_offset = 0
1892 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001893 self.descriptors_offset = 0
1894 self.descriptors_size = 0
1895 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001896 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001897 self.release_string = get_release_string()
1898
1899 def bump_required_libavb_version_minor(self, minor):
1900 """Function to bump required_libavb_version_minor.
1901
1902 Call this when writing data that requires a specific libavb
1903 version to parse it.
1904
1905 Arguments:
1906 minor: The minor version of libavb that has support for the feature.
1907 """
1908 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001909 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001910
1911 def save(self, output):
1912 """Serializes the header (256 bytes) to disk.
1913
1914 Arguments:
1915 output: The object to write the output to.
1916 """
1917 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001918 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1919 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001920 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1921 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001922 self.public_key_offset, self.public_key_size,
1923 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001924 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001925 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001926
1927 def encode(self):
1928 """Serializes the header (256) to a bytearray().
1929
1930 Returns:
1931 A bytearray() with the encoded header.
1932 """
1933 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001934 self.required_libavb_version_major,
1935 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001936 self.authentication_data_block_size,
1937 self.auxiliary_data_block_size, self.algorithm_type,
1938 self.hash_offset, self.hash_size, self.signature_offset,
1939 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001940 self.public_key_size, self.public_key_metadata_offset,
1941 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001942 self.descriptors_size, self.rollback_index, self.flags,
1943 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001944
1945
1946class Avb(object):
1947 """Business logic for avbtool command-line tool."""
1948
David Zeuthen8b6973b2016-09-20 12:39:49 -04001949 # Keep in sync with avb_ab_flow.h.
1950 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1951 AB_MAGIC = '\0AB0'
1952 AB_MAJOR_VERSION = 1
1953 AB_MINOR_VERSION = 0
1954 AB_MISC_METADATA_OFFSET = 2048
1955
David Zeuthen09692692016-09-30 16:16:40 -04001956 # Constants for maximum metadata size. These are used to give
1957 # meaningful errors if the value passed in via --partition_size is
1958 # too small and when --calc_max_image_size is used. We use
1959 # conservative figures.
1960 MAX_VBMETA_SIZE = 64 * 1024
1961 MAX_FOOTER_SIZE = 4096
1962
David Zeuthena4fee8b2016-08-22 15:20:43 -04001963 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001964 """Implements the 'erase_footer' command.
1965
1966 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001967 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001968 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001969
1970 Raises:
1971 AvbError: If there's no footer in the image.
1972 """
1973
David Zeuthena4fee8b2016-08-22 15:20:43 -04001974 image = ImageHandler(image_filename)
1975
David Zeuthen21e95262016-07-27 17:58:40 -04001976 (footer, _, descriptors, _) = self._parse_image(image)
1977
1978 if not footer:
1979 raise AvbError('Given image does not have a footer.')
1980
1981 new_image_size = None
1982 if not keep_hashtree:
1983 new_image_size = footer.original_image_size
1984 else:
1985 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001986 # descriptor to figure out the location and size of the hashtree
1987 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001988 for desc in descriptors:
1989 if isinstance(desc, AvbHashtreeDescriptor):
1990 # The hashtree is always just following the main data so the
1991 # new size is easily derived.
1992 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001993 # If the image has FEC codes, also keep those.
1994 if desc.fec_offset > 0:
1995 fec_end = desc.fec_offset + desc.fec_size
1996 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001997 break
1998 if not new_image_size:
1999 raise AvbError('Requested to keep hashtree but no hashtree '
2000 'descriptor was found.')
2001
2002 # And cut...
2003 image.truncate(new_image_size)
2004
David Zeuthen2bc232b2017-04-19 14:25:19 -04002005 def resize_image(self, image_filename, partition_size):
2006 """Implements the 'resize_image' command.
2007
2008 Arguments:
2009 image_filename: File with footer to resize.
2010 partition_size: The new size of the image.
2011
2012 Raises:
2013 AvbError: If there's no footer in the image.
2014 """
2015
2016 image = ImageHandler(image_filename)
2017
2018 if partition_size % image.block_size != 0:
2019 raise AvbError('Partition size of {} is not a multiple of the image '
2020 'block size {}.'.format(partition_size,
2021 image.block_size))
2022
2023 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2024
2025 if not footer:
2026 raise AvbError('Given image does not have a footer.')
2027
2028 # The vbmeta blob is always at the end of the data so resizing an
2029 # image amounts to just moving the footer around.
2030
2031 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2032 if vbmeta_end_offset % image.block_size != 0:
2033 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2034
2035 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002036 raise AvbError('Requested size of {} is too small for an image '
2037 'of size {}.'
2038 .format(partition_size,
2039 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002040
2041 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2042 # with enough bytes such that the final Footer block is at the end
2043 # of partition_size.
2044 image.truncate(vbmeta_end_offset)
2045 image.append_dont_care(partition_size - vbmeta_end_offset -
2046 1*image.block_size)
2047
2048 # Just reuse the same footer - only difference is that we're
2049 # writing it in a different place.
2050 footer_blob = footer.encode()
2051 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2052 footer_blob)
2053 image.append_raw(footer_blob_with_padding)
2054
David Zeuthen8b6973b2016-09-20 12:39:49 -04002055 def set_ab_metadata(self, misc_image, slot_data):
2056 """Implements the 'set_ab_metadata' command.
2057
2058 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2059 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2060
2061 Arguments:
2062 misc_image: The misc image to write to.
2063 slot_data: Slot data as a string
2064
2065 Raises:
2066 AvbError: If slot data is malformed.
2067 """
2068 tokens = slot_data.split(':')
2069 if len(tokens) != 6:
2070 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2071 a_priority = int(tokens[0])
2072 a_tries_remaining = int(tokens[1])
2073 a_success = True if int(tokens[2]) != 0 else False
2074 b_priority = int(tokens[3])
2075 b_tries_remaining = int(tokens[4])
2076 b_success = True if int(tokens[5]) != 0 else False
2077
2078 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2079 self.AB_MAGIC,
2080 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2081 a_priority, a_tries_remaining, a_success,
2082 b_priority, b_tries_remaining, b_success)
2083 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2084 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2085 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2086 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2087 misc_image.write(ab_data)
2088
David Zeuthena4fee8b2016-08-22 15:20:43 -04002089 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002090 """Implements the 'info_image' command.
2091
2092 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002093 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002094 output: Output file to write human-readable information to (file object).
2095 """
2096
David Zeuthena4fee8b2016-08-22 15:20:43 -04002097 image = ImageHandler(image_filename)
2098
David Zeuthen21e95262016-07-27 17:58:40 -04002099 o = output
2100
2101 (footer, header, descriptors, image_size) = self._parse_image(image)
2102
2103 if footer:
2104 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2105 footer.version_minor))
2106 o.write('Image size: {} bytes\n'.format(image_size))
2107 o.write('Original image size: {} bytes\n'.format(
2108 footer.original_image_size))
2109 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2110 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2111 o.write('--\n')
2112
2113 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2114
David Zeuthene3cadca2017-02-22 21:25:46 -05002115 o.write('Minimum libavb version: {}.{}{}\n'.format(
2116 header.required_libavb_version_major,
2117 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002118 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002119 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2120 o.write('Authentication Block: {} bytes\n'.format(
2121 header.authentication_data_block_size))
2122 o.write('Auxiliary Block: {} bytes\n'.format(
2123 header.auxiliary_data_block_size))
2124 o.write('Algorithm: {}\n'.format(alg_name))
2125 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002126 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002127 o.write('Release String: \'{}\'\n'.format(
2128 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002129
2130 # Print descriptors.
2131 num_printed = 0
2132 o.write('Descriptors:\n')
2133 for desc in descriptors:
2134 desc.print_desc(o)
2135 num_printed += 1
2136 if num_printed == 0:
2137 o.write(' (none)\n')
2138
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002139 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002140 """Implements the 'verify_image' command.
2141
2142 Arguments:
2143 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002144 key_path: None or check that embedded public key matches key at given path.
2145 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002146 """
2147
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002148 expected_chain_partitions_map = {}
2149 if expected_chain_partitions:
2150 used_locations = {}
2151 for cp in expected_chain_partitions:
2152 cp_tokens = cp.split(':')
2153 if len(cp_tokens) != 3:
2154 raise AvbError('Malformed chained partition "{}".'.format(cp))
2155 partition_name = cp_tokens[0]
2156 rollback_index_location = int(cp_tokens[1])
2157 file_path = cp_tokens[2]
2158 pk_blob = open(file_path).read()
2159 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2160
2161 image_dir = os.path.dirname(image_filename)
2162 image_ext = os.path.splitext(image_filename)[1]
2163
2164 key_blob = None
2165 if key_path:
2166 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2167 key_blob = encode_rsa_key(key_path)
2168 else:
2169 print 'Verifying image {} using embedded public key'.format(image_filename)
2170
David Zeuthenb623d8b2017-04-04 16:05:53 -04002171 image = ImageHandler(image_filename)
2172 (footer, header, descriptors, image_size) = self._parse_image(image)
2173 offset = 0
2174 if footer:
2175 offset = footer.vbmeta_offset
2176 size = (header.SIZE + header.authentication_data_block_size +
2177 header.auxiliary_data_block_size)
2178 image.seek(offset)
2179 vbmeta_blob = image.read(size)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002180 h = AvbVBMetaHeader(vbmeta_blob[0:AvbVBMetaHeader.SIZE])
2181 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002182 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002183 raise AvbError('Signature check failed for {} vbmeta struct {}'
2184 .format(alg_name, image_filename))
2185
2186 if key_blob:
2187 # The embedded public key is in the auxiliary block at an offset.
2188 key_offset = AvbVBMetaHeader.SIZE
2189 key_offset += h.authentication_data_block_size
2190 key_offset += h.public_key_offset
2191 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + h.public_key_size]
2192 if key_blob != key_blob_in_vbmeta:
2193 raise AvbError('Embedded public key does not match given key.')
2194
2195 if footer:
2196 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2197 .format(alg_name, image_filename))
2198 else:
2199 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
2200 .format(alg_name, image_filename))
2201
2202 for desc in descriptors:
2203 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map):
2204 raise AvbError('Error verifying descriptor.')
2205
David Zeuthenb623d8b2017-04-04 16:05:53 -04002206
David Zeuthen21e95262016-07-27 17:58:40 -04002207 def _parse_image(self, image):
2208 """Gets information about an image.
2209
2210 The image can either be a vbmeta or an image with a footer.
2211
2212 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002213 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002214
2215 Returns:
2216 A tuple where the first argument is a AvbFooter (None if there
2217 is no footer on the image), the second argument is a
2218 AvbVBMetaHeader, the third argument is a list of
2219 AvbDescriptor-derived instances, and the fourth argument is the
2220 size of |image|.
2221 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002222 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002223 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002224 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002225 try:
2226 footer = AvbFooter(image.read(AvbFooter.SIZE))
2227 except (LookupError, struct.error):
2228 # Nope, just seek back to the start.
2229 image.seek(0)
2230
2231 vbmeta_offset = 0
2232 if footer:
2233 vbmeta_offset = footer.vbmeta_offset
2234
2235 image.seek(vbmeta_offset)
2236 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2237
2238 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2239 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2240 desc_start_offset = aux_block_offset + h.descriptors_offset
2241 image.seek(desc_start_offset)
2242 descriptors = parse_descriptors(image.read(h.descriptors_size))
2243
David Zeuthen09692692016-09-30 16:16:40 -04002244 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002245
David Zeuthenb1b994d2017-03-06 18:01:31 -05002246 def _load_vbmeta_blob(self, image):
2247 """Gets the vbmeta struct and associated sections.
2248
2249 The image can either be a vbmeta.img or an image with a footer.
2250
2251 Arguments:
2252 image: An ImageHandler (vbmeta or footer).
2253
2254 Returns:
2255 A blob with the vbmeta struct and other sections.
2256 """
2257 assert isinstance(image, ImageHandler)
2258 footer = None
2259 image.seek(image.image_size - AvbFooter.SIZE)
2260 try:
2261 footer = AvbFooter(image.read(AvbFooter.SIZE))
2262 except (LookupError, struct.error):
2263 # Nope, just seek back to the start.
2264 image.seek(0)
2265
2266 vbmeta_offset = 0
2267 if footer:
2268 vbmeta_offset = footer.vbmeta_offset
2269
2270 image.seek(vbmeta_offset)
2271 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2272
2273 image.seek(vbmeta_offset)
2274 data_size = AvbVBMetaHeader.SIZE
2275 data_size += h.authentication_data_block_size
2276 data_size += h.auxiliary_data_block_size
2277 return image.read(data_size)
2278
David Zeuthen73f2afa2017-05-17 16:54:11 -04002279 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002280 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002281
2282 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002283 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002284
2285 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002286 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2287 instructions. There is one for when hashtree is not disabled and one for
2288 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002289
David Zeuthen21e95262016-07-27 17:58:40 -04002290 """
2291
David Zeuthen21e95262016-07-27 17:58:40 -04002292 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002293 c += '0' # start
2294 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2295 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2296 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2297 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2298 c += ' {}'.format(ht.data_block_size) # data_block
2299 c += ' {}'.format(ht.hash_block_size) # hash_block
2300 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2301 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2302 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2303 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2304 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2305 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002306 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002307 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002308 c += ' ignore_zero_blocks'
2309 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2310 c += ' fec_roots {}'.format(ht.fec_num_roots)
2311 # Note that fec_blocks is the size that FEC covers, *not* the
2312 # size of the FEC data. Since we use FEC for everything up until
2313 # the FEC data, it's the same as the offset.
2314 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2315 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2316 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002317 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002318 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002319 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002320 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002321
David Zeuthenfd41eb92016-11-17 12:24:47 -05002322 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002323 desc = AvbKernelCmdlineDescriptor()
2324 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002325 desc.flags = (
2326 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2327
2328 # The descriptor for when hashtree verification is disabled is a lot
2329 # simpler - we just set the root to the partition.
2330 desc_no_ht = AvbKernelCmdlineDescriptor()
2331 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2332 desc_no_ht.flags = (
2333 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2334
2335 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002336
David Zeuthen73f2afa2017-05-17 16:54:11 -04002337 def _get_cmdline_descriptors_for_dm_verity(self, image):
2338 """Generate kernel cmdline descriptors for dm-verity.
2339
2340 Arguments:
2341 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2342
2343 Returns:
2344 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2345 instructions. There is one for when hashtree is not disabled and one for
2346 when it is.
2347
2348 Raises:
2349 AvbError: If |image| doesn't have a hashtree descriptor.
2350
2351 """
2352
2353 (_, _, descriptors, _) = self._parse_image(image)
2354
2355 ht = None
2356 for desc in descriptors:
2357 if isinstance(desc, AvbHashtreeDescriptor):
2358 ht = desc
2359 break
2360
2361 if not ht:
2362 raise AvbError('No hashtree descriptor in given image')
2363
2364 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2365
David Zeuthen21e95262016-07-27 17:58:40 -04002366 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002367 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002368 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002369 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002370 include_descriptors_from_image,
2371 signing_helper,
2372 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002373 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002374 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002375 print_required_libavb_version,
2376 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002377 """Implements the 'make_vbmeta_image' command.
2378
2379 Arguments:
2380 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002381 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002382 algorithm_name: Name of algorithm to use.
2383 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002384 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002385 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002386 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002387 props: Properties to insert (list of strings of the form 'key:value').
2388 props_from_file: Properties to insert (list of strings 'key:<path>').
2389 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002390 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002391 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002392 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002393 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002394 release_string: None or avbtool release string to use instead of default.
2395 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002396 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002397 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002398
2399 Raises:
2400 AvbError: If a chained partition is malformed.
2401 """
2402
David Zeuthen1097a782017-05-31 15:53:17 -04002403 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002404 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002405 if include_descriptors_from_image:
2406 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2407 # version of all included descriptors.
2408 tmp_header = AvbVBMetaHeader()
2409 for image in include_descriptors_from_image:
2410 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2411 tmp_header.bump_required_libavb_version_minor(
2412 image_header.required_libavb_version_minor)
2413 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2414 else:
2415 # Descriptors aside, all vbmeta features are supported in 1.0.
2416 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002417 return
2418
2419 if not output:
2420 raise AvbError('No output file given')
2421
David Zeuthen21e95262016-07-27 17:58:40 -04002422 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002423 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002424 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002425 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002426 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002427 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002428 include_descriptors_from_image, signing_helper,
2429 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002430 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002431
2432 # Write entire vbmeta blob (header, authentication, auxiliary).
2433 output.seek(0)
2434 output.write(vbmeta_blob)
2435
David Zeuthen97cb5802017-06-01 16:14:05 -04002436 if padding_size > 0:
2437 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2438 padding_needed = padded_size - len(vbmeta_blob)
2439 output.write('\0' * padding_needed)
2440
David Zeuthen18666ab2016-11-15 11:18:05 -05002441 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2442 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002443 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002444 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002445 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002446 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002447 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002448 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002449 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002450 release_string, append_to_release_string,
2451 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002452 """Generates a VBMeta blob.
2453
2454 This blob contains the header (struct AvbVBMetaHeader), the
2455 authentication data block (which contains the hash and signature
2456 for the header and auxiliary block), and the auxiliary block
2457 (which contains descriptors, the public key used, and other data).
2458
2459 The |key| parameter can |None| only if the |algorithm_name| is
2460 'NONE'.
2461
2462 Arguments:
2463 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2464 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002465 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002466 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002467 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002468 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002469 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002470 props: Properties to insert (List of strings of the form 'key:value').
2471 props_from_file: Properties to insert (List of strings 'key:<path>').
2472 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002473 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002474 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002475 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2476 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002477 include_descriptors_from_image: List of file objects for which
2478 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002479 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002480 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002481 release_string: None or avbtool release string.
2482 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002483 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002484
2485 Returns:
2486 A bytearray() with the VBMeta blob.
2487
2488 Raises:
2489 Exception: If the |algorithm_name| is not found, if no key has
2490 been given and the given algorithm requires one, or the key is
2491 of the wrong size.
2492
2493 """
2494 try:
2495 alg = ALGORITHMS[algorithm_name]
2496 except KeyError:
2497 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2498
David Zeuthena5fd3a42017-02-27 16:38:54 -05002499 if not descriptors:
2500 descriptors = []
2501
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002502 h = AvbVBMetaHeader()
2503 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2504
David Zeuthena5fd3a42017-02-27 16:38:54 -05002505 # Insert chained partition descriptors, if any
2506 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002507 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002508 for cp in chain_partitions:
2509 cp_tokens = cp.split(':')
2510 if len(cp_tokens) != 3:
2511 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002512 partition_name = cp_tokens[0]
2513 rollback_index_location = int(cp_tokens[1])
2514 file_path = cp_tokens[2]
2515 # Check that the same rollback location isn't being used by
2516 # multiple chained partitions.
2517 if used_locations.get(rollback_index_location):
2518 raise AvbError('Rollback Index Location {} is already in use.'.format(
2519 rollback_index_location))
2520 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002521 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002522 desc.partition_name = partition_name
2523 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002524 if desc.rollback_index_location < 1:
2525 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002526 desc.public_key = open(file_path, 'rb').read()
2527 descriptors.append(desc)
2528
David Zeuthen21e95262016-07-27 17:58:40 -04002529 # Descriptors.
2530 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002531 for desc in descriptors:
2532 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002533
2534 # Add properties.
2535 if props:
2536 for prop in props:
2537 idx = prop.find(':')
2538 if idx == -1:
2539 raise AvbError('Malformed property "{}".'.format(prop))
2540 desc = AvbPropertyDescriptor()
2541 desc.key = prop[0:idx]
2542 desc.value = prop[(idx + 1):]
2543 encoded_descriptors.extend(desc.encode())
2544 if props_from_file:
2545 for prop in props_from_file:
2546 idx = prop.find(':')
2547 if idx == -1:
2548 raise AvbError('Malformed property "{}".'.format(prop))
2549 desc = AvbPropertyDescriptor()
2550 desc.key = prop[0:idx]
2551 desc.value = prop[(idx + 1):]
2552 file_path = prop[(idx + 1):]
2553 desc.value = open(file_path, 'rb').read()
2554 encoded_descriptors.extend(desc.encode())
2555
David Zeuthen73f2afa2017-05-17 16:54:11 -04002556 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002557 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002558 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002559 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002560 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2561 encoded_descriptors.extend(cmdline_desc[0].encode())
2562 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002563
David Zeuthen73f2afa2017-05-17 16:54:11 -04002564 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2565 if ht_desc_to_setup:
2566 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2567 ht_desc_to_setup)
2568 encoded_descriptors.extend(cmdline_desc[0].encode())
2569 encoded_descriptors.extend(cmdline_desc[1].encode())
2570
David Zeuthen21e95262016-07-27 17:58:40 -04002571 # Add kernel command-lines.
2572 if kernel_cmdlines:
2573 for i in kernel_cmdlines:
2574 desc = AvbKernelCmdlineDescriptor()
2575 desc.kernel_cmdline = i
2576 encoded_descriptors.extend(desc.encode())
2577
2578 # Add descriptors from other images.
2579 if include_descriptors_from_image:
2580 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002581 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002582 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2583 image_handler)
2584 # Bump the required libavb version to support all included descriptors.
2585 h.bump_required_libavb_version_minor(
2586 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002587 for desc in image_descriptors:
2588 encoded_descriptors.extend(desc.encode())
2589
David Zeuthen18666ab2016-11-15 11:18:05 -05002590 # Load public key metadata blob, if requested.
2591 pkmd_blob = []
2592 if public_key_metadata_path:
2593 with open(public_key_metadata_path) as f:
2594 pkmd_blob = f.read()
2595
David Zeuthen21e95262016-07-27 17:58:40 -04002596 key = None
2597 encoded_key = bytearray()
2598 if alg.public_key_num_bytes > 0:
2599 if not key_path:
2600 raise AvbError('Key is required for algorithm {}'.format(
2601 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002602 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002603 if len(encoded_key) != alg.public_key_num_bytes:
2604 raise AvbError('Key is wrong size for algorithm {}'.format(
2605 algorithm_name))
2606
David Zeuthene3cadca2017-02-22 21:25:46 -05002607 # Override release string, if requested.
2608 if isinstance(release_string, (str, unicode)):
2609 h.release_string = release_string
2610
2611 # Append to release string, if requested. Also insert a space before.
2612 if isinstance(append_to_release_string, (str, unicode)):
2613 h.release_string += ' ' + append_to_release_string
2614
David Zeuthen18666ab2016-11-15 11:18:05 -05002615 # For the Auxiliary data block, descriptors are stored at offset 0,
2616 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002617 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002618 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002619 h.descriptors_offset = 0
2620 h.descriptors_size = len(encoded_descriptors)
2621 h.public_key_offset = h.descriptors_size
2622 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002623 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2624 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002625
2626 # For the Authentication data block, the hash is first and then
2627 # the signature.
2628 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002629 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002630 h.algorithm_type = alg.algorithm_type
2631 h.hash_offset = 0
2632 h.hash_size = alg.hash_num_bytes
2633 # Signature offset and size - it's stored right after the hash
2634 # (in Authentication data block).
2635 h.signature_offset = alg.hash_num_bytes
2636 h.signature_size = alg.signature_num_bytes
2637
2638 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002639 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002640
2641 # Generate Header data block.
2642 header_data_blob = h.encode()
2643
2644 # Generate Auxiliary data block.
2645 aux_data_blob = bytearray()
2646 aux_data_blob.extend(encoded_descriptors)
2647 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002648 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002649 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2650 aux_data_blob.extend('\0' * padding_bytes)
2651
2652 # Calculate the hash.
2653 binary_hash = bytearray()
2654 binary_signature = bytearray()
2655 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002656 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002657 ha.update(header_data_blob)
2658 ha.update(aux_data_blob)
2659 binary_hash.extend(ha.digest())
2660
2661 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002662 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002663 binary_signature.extend(raw_sign(signing_helper,
2664 signing_helper_with_files,
2665 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002666 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002667 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002668
2669 # Generate Authentication data block.
2670 auth_data_blob = bytearray()
2671 auth_data_blob.extend(binary_hash)
2672 auth_data_blob.extend(binary_signature)
2673 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2674 auth_data_blob.extend('\0' * padding_bytes)
2675
2676 return header_data_blob + auth_data_blob + aux_data_blob
2677
2678 def extract_public_key(self, key_path, output):
2679 """Implements the 'extract_public_key' command.
2680
2681 Arguments:
2682 key_path: The path to a RSA private key file.
2683 output: The file to write to.
2684 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002685 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002686
David Zeuthenb1b994d2017-03-06 18:01:31 -05002687 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2688 partition_size):
2689 """Implementation of the append_vbmeta_image command.
2690
2691 Arguments:
2692 image_filename: File to add the footer to.
2693 vbmeta_image_filename: File to get vbmeta struct from.
2694 partition_size: Size of partition.
2695
2696 Raises:
2697 AvbError: If an argument is incorrect.
2698 """
2699 image = ImageHandler(image_filename)
2700
2701 if partition_size % image.block_size != 0:
2702 raise AvbError('Partition size of {} is not a multiple of the image '
2703 'block size {}.'.format(partition_size,
2704 image.block_size))
2705
2706 # If there's already a footer, truncate the image to its original
2707 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002708 if image.image_size >= AvbFooter.SIZE:
2709 image.seek(image.image_size - AvbFooter.SIZE)
2710 try:
2711 footer = AvbFooter(image.read(AvbFooter.SIZE))
2712 # Existing footer found. Just truncate.
2713 original_image_size = footer.original_image_size
2714 image.truncate(footer.original_image_size)
2715 except (LookupError, struct.error):
2716 original_image_size = image.image_size
2717 else:
2718 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002719 original_image_size = image.image_size
2720
2721 # If anything goes wrong from here-on, restore the image back to
2722 # its original size.
2723 try:
2724 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2725 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2726
2727 # If the image isn't sparse, its size might not be a multiple of
2728 # the block size. This will screw up padding later so just grow it.
2729 if image.image_size % image.block_size != 0:
2730 assert not image.is_sparse
2731 padding_needed = image.block_size - (image.image_size%image.block_size)
2732 image.truncate(image.image_size + padding_needed)
2733
2734 # The append_raw() method requires content with size being a
2735 # multiple of |block_size| so add padding as needed. Also record
2736 # where this is written to since we'll need to put that in the
2737 # footer.
2738 vbmeta_offset = image.image_size
2739 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2740 len(vbmeta_blob))
2741 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2742
2743 # Append vbmeta blob and footer
2744 image.append_raw(vbmeta_blob_with_padding)
2745 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2746
2747 # Now insert a DONT_CARE chunk with enough bytes such that the
2748 # final Footer block is at the end of partition_size..
2749 image.append_dont_care(partition_size - vbmeta_end_offset -
2750 1*image.block_size)
2751
2752 # Generate the Footer that tells where the VBMeta footer
2753 # is. Also put enough padding in the front of the footer since
2754 # we'll write out an entire block.
2755 footer = AvbFooter()
2756 footer.original_image_size = original_image_size
2757 footer.vbmeta_offset = vbmeta_offset
2758 footer.vbmeta_size = len(vbmeta_blob)
2759 footer_blob = footer.encode()
2760 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2761 footer_blob)
2762 image.append_raw(footer_blob_with_padding)
2763
2764 except:
2765 # Truncate back to original size, then re-raise
2766 image.truncate(original_image_size)
2767 raise
2768
David Zeuthena4fee8b2016-08-22 15:20:43 -04002769 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002770 hash_algorithm, salt, chain_partitions, algorithm_name,
2771 key_path,
2772 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002773 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002774 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002775 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002776 signing_helper, signing_helper_with_files,
2777 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002778 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002779 print_required_libavb_version, use_persistent_digest,
2780 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002781 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002782
2783 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002784 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002785 partition_size: Size of partition.
2786 partition_name: Name of partition (without A/B suffix).
2787 hash_algorithm: Hash algorithm to use.
2788 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002789 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002790 algorithm_name: Name of algorithm to use.
2791 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002792 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002793 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002794 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002795 props: Properties to insert (List of strings of the form 'key:value').
2796 props_from_file: Properties to insert (List of strings 'key:<path>').
2797 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002798 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002799 dm-verity kernel cmdline from.
2800 include_descriptors_from_image: List of file objects for which
2801 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002802 calc_max_image_size: Don't store the footer - instead calculate the
2803 maximum image size leaving enough room for metadata with the
2804 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002805 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002806 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002807 release_string: None or avbtool release string.
2808 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002809 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2810 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002811 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002812 use_persistent_digest: Use a persistent digest on device.
2813 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002814
2815 Raises:
2816 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002817 """
David Zeuthen1097a782017-05-31 15:53:17 -04002818
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002819 required_libavb_version_minor = 0
2820 if use_persistent_digest or do_not_use_ab:
2821 required_libavb_version_minor = 1
2822
David Zeuthen1097a782017-05-31 15:53:17 -04002823 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002824 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002825 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04002826 return
2827
David Zeuthenbf562452017-05-17 18:04:43 -04002828 # First, calculate the maximum image size such that an image
2829 # this size + metadata (footer + vbmeta struct) fits in
2830 # |partition_size|.
2831 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002832 if partition_size < max_metadata_size:
2833 raise AvbError('Parition size of {} is too small. '
2834 'Needs to be at least {}'.format(
2835 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04002836 max_image_size = partition_size - max_metadata_size
2837
2838 # If we're asked to only calculate the maximum image size, we're done.
2839 if calc_max_image_size:
2840 print '{}'.format(max_image_size)
2841 return
2842
David Zeuthena4fee8b2016-08-22 15:20:43 -04002843 image = ImageHandler(image_filename)
2844
2845 if partition_size % image.block_size != 0:
2846 raise AvbError('Partition size of {} is not a multiple of the image '
2847 'block size {}.'.format(partition_size,
2848 image.block_size))
2849
David Zeuthen21e95262016-07-27 17:58:40 -04002850 # If there's already a footer, truncate the image to its original
2851 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2852 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002853 if image.image_size >= AvbFooter.SIZE:
2854 image.seek(image.image_size - AvbFooter.SIZE)
2855 try:
2856 footer = AvbFooter(image.read(AvbFooter.SIZE))
2857 # Existing footer found. Just truncate.
2858 original_image_size = footer.original_image_size
2859 image.truncate(footer.original_image_size)
2860 except (LookupError, struct.error):
2861 original_image_size = image.image_size
2862 else:
2863 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04002864 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002865
2866 # If anything goes wrong from here-on, restore the image back to
2867 # its original size.
2868 try:
David Zeuthen09692692016-09-30 16:16:40 -04002869 # If image size exceeds the maximum image size, fail.
2870 if image.image_size > max_image_size:
2871 raise AvbError('Image size of {} exceeds maximum image '
2872 'size of {} in order to fit in a partition '
2873 'size of {}.'.format(image.image_size, max_image_size,
2874 partition_size))
2875
David Zeuthen21e95262016-07-27 17:58:40 -04002876 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2877 if salt:
2878 salt = salt.decode('hex')
2879 else:
2880 if salt is None:
2881 # If salt is not explicitly specified, choose a hash
2882 # that's the same size as the hash size.
2883 hash_size = digest_size
2884 salt = open('/dev/urandom').read(hash_size)
2885 else:
2886 salt = ''
2887
2888 hasher = hashlib.new(name=hash_algorithm, string=salt)
2889 # TODO(zeuthen): might want to read this in chunks to avoid
2890 # memory pressure, then again, this is only supposed to be used
2891 # on kernel/initramfs partitions. Possible optimization.
2892 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002893 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002894 digest = hasher.digest()
2895
2896 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002897 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002898 h_desc.hash_algorithm = hash_algorithm
2899 h_desc.partition_name = partition_name
2900 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002901 h_desc.flags = 0
2902 if do_not_use_ab:
2903 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
2904 if not use_persistent_digest:
2905 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04002906
2907 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002908 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002909 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002910 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002911 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002912 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002913 include_descriptors_from_image, signing_helper,
2914 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002915 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002916
David Zeuthend247fcb2017-02-16 12:09:27 -05002917 # Write vbmeta blob, if requested.
2918 if output_vbmeta_image:
2919 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002920
David Zeuthend247fcb2017-02-16 12:09:27 -05002921 # Append vbmeta blob and footer, unless requested not to.
2922 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002923 # If the image isn't sparse, its size might not be a multiple of
2924 # the block size. This will screw up padding later so just grow it.
2925 if image.image_size % image.block_size != 0:
2926 assert not image.is_sparse
2927 padding_needed = image.block_size - (
2928 image.image_size % image.block_size)
2929 image.truncate(image.image_size + padding_needed)
2930
2931 # The append_raw() method requires content with size being a
2932 # multiple of |block_size| so add padding as needed. Also record
2933 # where this is written to since we'll need to put that in the
2934 # footer.
2935 vbmeta_offset = image.image_size
2936 padding_needed = (
2937 round_to_multiple(len(vbmeta_blob), image.block_size) -
2938 len(vbmeta_blob))
2939 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
2940
David Zeuthend247fcb2017-02-16 12:09:27 -05002941 image.append_raw(vbmeta_blob_with_padding)
2942 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2943
2944 # Now insert a DONT_CARE chunk with enough bytes such that the
2945 # final Footer block is at the end of partition_size..
2946 image.append_dont_care(partition_size - vbmeta_end_offset -
2947 1*image.block_size)
2948
2949 # Generate the Footer that tells where the VBMeta footer
2950 # is. Also put enough padding in the front of the footer since
2951 # we'll write out an entire block.
2952 footer = AvbFooter()
2953 footer.original_image_size = original_image_size
2954 footer.vbmeta_offset = vbmeta_offset
2955 footer.vbmeta_size = len(vbmeta_blob)
2956 footer_blob = footer.encode()
2957 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2958 footer_blob)
2959 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002960
David Zeuthen21e95262016-07-27 17:58:40 -04002961 except:
2962 # Truncate back to original size, then re-raise
2963 image.truncate(original_image_size)
2964 raise
2965
David Zeuthena4fee8b2016-08-22 15:20:43 -04002966 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002967 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002968 block_size, salt, chain_partitions, algorithm_name,
2969 key_path,
2970 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002971 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002972 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002973 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002974 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002975 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002976 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002977 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002978 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002979 print_required_libavb_version,
2980 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04002981 """Implements the 'add_hashtree_footer' command.
2982
2983 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2984 more information about dm-verity and these hashes.
2985
2986 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002987 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002988 partition_size: Size of partition.
2989 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002990 generate_fec: If True, generate FEC codes.
2991 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002992 hash_algorithm: Hash algorithm to use.
2993 block_size: Block size to use.
2994 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002995 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002996 algorithm_name: Name of algorithm to use.
2997 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002998 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002999 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003000 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003001 props: Properties to insert (List of strings of the form 'key:value').
3002 props_from_file: Properties to insert (List of strings 'key:<path>').
3003 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003004 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003005 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003006 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3007 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003008 include_descriptors_from_image: List of file objects for which
3009 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003010 calc_max_image_size: Don't store the hashtree or footer - instead
3011 calculate the maximum image size leaving enough room for hashtree
3012 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003013 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003014 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003015 release_string: None or avbtool release string.
3016 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003017 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3018 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003019 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003020 use_persistent_root_digest: Use a persistent root digest on device.
3021 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003022
3023 Raises:
3024 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003025 """
David Zeuthen1097a782017-05-31 15:53:17 -04003026
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003027 required_libavb_version_minor = 0
3028 if use_persistent_root_digest or do_not_use_ab:
3029 required_libavb_version_minor = 1
3030
David Zeuthen1097a782017-05-31 15:53:17 -04003031 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003032 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003033 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003034 return
3035
David Zeuthen09692692016-09-30 16:16:40 -04003036 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3037 digest_padding = round_to_pow2(digest_size) - digest_size
3038
3039 # First, calculate the maximum image size such that an image
3040 # this size + the hashtree + metadata (footer + vbmeta struct)
3041 # fits in |partition_size|. We use very conservative figures for
3042 # metadata.
3043 (_, max_tree_size) = calc_hash_level_offsets(
3044 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003045 max_fec_size = 0
3046 if generate_fec:
3047 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3048 max_metadata_size = (max_fec_size + max_tree_size +
3049 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04003050 self.MAX_FOOTER_SIZE)
3051 max_image_size = partition_size - max_metadata_size
3052
3053 # If we're asked to only calculate the maximum image size, we're done.
3054 if calc_max_image_size:
3055 print '{}'.format(max_image_size)
3056 return
3057
David Zeuthena4fee8b2016-08-22 15:20:43 -04003058 image = ImageHandler(image_filename)
3059
3060 if partition_size % image.block_size != 0:
3061 raise AvbError('Partition size of {} is not a multiple of the image '
3062 'block size {}.'.format(partition_size,
3063 image.block_size))
3064
David Zeuthen21e95262016-07-27 17:58:40 -04003065 # If there's already a footer, truncate the image to its original
3066 # size. This way 'avbtool add_hashtree_footer' is idempotent
3067 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003068 if image.image_size >= AvbFooter.SIZE:
3069 image.seek(image.image_size - AvbFooter.SIZE)
3070 try:
3071 footer = AvbFooter(image.read(AvbFooter.SIZE))
3072 # Existing footer found. Just truncate.
3073 original_image_size = footer.original_image_size
3074 image.truncate(footer.original_image_size)
3075 except (LookupError, struct.error):
3076 original_image_size = image.image_size
3077 else:
3078 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003079 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003080
3081 # If anything goes wrong from here-on, restore the image back to
3082 # its original size.
3083 try:
3084 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003085 rounded_image_size = round_to_multiple(image.image_size, block_size)
3086 if rounded_image_size > image.image_size:
3087 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003088
David Zeuthen09692692016-09-30 16:16:40 -04003089 # If image size exceeds the maximum image size, fail.
3090 if image.image_size > max_image_size:
3091 raise AvbError('Image size of {} exceeds maximum image '
3092 'size of {} in order to fit in a partition '
3093 'size of {}.'.format(image.image_size, max_image_size,
3094 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003095
3096 if salt:
3097 salt = salt.decode('hex')
3098 else:
3099 if salt is None:
3100 # If salt is not explicitly specified, choose a hash
3101 # that's the same size as the hash size.
3102 hash_size = digest_size
3103 salt = open('/dev/urandom').read(hash_size)
3104 else:
3105 salt = ''
3106
David Zeuthena4fee8b2016-08-22 15:20:43 -04003107 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003108 # offsets in advance.
3109 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003110 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003111
David Zeuthena4fee8b2016-08-22 15:20:43 -04003112 # If the image isn't sparse, its size might not be a multiple of
3113 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003114 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003115 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003116 padding_needed = image.block_size - (image.image_size%image.block_size)
3117 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003118
David Zeuthena4fee8b2016-08-22 15:20:43 -04003119 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003120 tree_offset = image.image_size
3121 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003122 block_size,
3123 hash_algorithm, salt,
3124 digest_padding,
3125 hash_level_offsets,
3126 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003127
3128 # Generate HashtreeDescriptor with details about the tree we
3129 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003130 ht_desc = AvbHashtreeDescriptor()
3131 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003132 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003133 ht_desc.tree_offset = tree_offset
3134 ht_desc.tree_size = tree_size
3135 ht_desc.data_block_size = block_size
3136 ht_desc.hash_block_size = block_size
3137 ht_desc.hash_algorithm = hash_algorithm
3138 ht_desc.partition_name = partition_name
3139 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003140 if do_not_use_ab:
3141 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3142 if not use_persistent_root_digest:
3143 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003144
David Zeuthen09692692016-09-30 16:16:40 -04003145 # Write the hash tree
3146 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3147 len(hash_tree))
3148 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3149 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003150 len_hashtree_and_fec = len(hash_tree_with_padding)
3151
3152 # Generate FEC codes, if requested.
3153 if generate_fec:
3154 fec_data = generate_fec_data(image_filename, fec_num_roots)
3155 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3156 len(fec_data))
3157 fec_data_with_padding = fec_data + '\0'*padding_needed
3158 fec_offset = image.image_size
3159 image.append_raw(fec_data_with_padding)
3160 len_hashtree_and_fec += len(fec_data_with_padding)
3161 # Update the hashtree descriptor.
3162 ht_desc.fec_num_roots = fec_num_roots
3163 ht_desc.fec_offset = fec_offset
3164 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003165
David Zeuthen73f2afa2017-05-17 16:54:11 -04003166 ht_desc_to_setup = None
3167 if setup_as_rootfs_from_kernel:
3168 ht_desc_to_setup = ht_desc
3169
David Zeuthena4fee8b2016-08-22 15:20:43 -04003170 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003171 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003172 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003173 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003174 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003175 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003176 include_descriptors_from_image, signing_helper,
3177 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003178 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003179 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3180 len(vbmeta_blob))
3181 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003182
David Zeuthend247fcb2017-02-16 12:09:27 -05003183 # Write vbmeta blob, if requested.
3184 if output_vbmeta_image:
3185 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003186
David Zeuthend247fcb2017-02-16 12:09:27 -05003187 # Append vbmeta blob and footer, unless requested not to.
3188 if not do_not_append_vbmeta_image:
3189 image.append_raw(vbmeta_blob_with_padding)
3190
3191 # Now insert a DONT_CARE chunk with enough bytes such that the
3192 # final Footer block is at the end of partition_size..
3193 image.append_dont_care(partition_size - image.image_size -
3194 1*image.block_size)
3195
3196 # Generate the Footer that tells where the VBMeta footer
3197 # is. Also put enough padding in the front of the footer since
3198 # we'll write out an entire block.
3199 footer = AvbFooter()
3200 footer.original_image_size = original_image_size
3201 footer.vbmeta_offset = vbmeta_offset
3202 footer.vbmeta_size = len(vbmeta_blob)
3203 footer_blob = footer.encode()
3204 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3205 footer_blob)
3206 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003207
David Zeuthen21e95262016-07-27 17:58:40 -04003208 except:
David Zeuthen09692692016-09-30 16:16:40 -04003209 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003210 image.truncate(original_image_size)
3211 raise
3212
David Zeuthenc68f0822017-03-31 17:22:35 -04003213 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003214 subject_key_version, subject,
David Zeuthena156d3d2017-06-01 12:08:09 -04003215 is_intermediate_authority, signing_helper,
3216 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003217 """Implements the 'make_atx_certificate' command.
3218
3219 Android Things certificates are required for Android Things public key
3220 metadata. They chain the vbmeta signing key for a particular product back to
3221 a fused, permanent root key. These certificates are fixed-length and fixed-
3222 format with the explicit goal of not parsing ASN.1 in bootloader code.
3223
3224 Arguments:
3225 output: Certificate will be written to this file on success.
3226 authority_key_path: A PEM file path with the authority private key.
3227 If None, then a certificate will be created without a
3228 signature. The signature can be created out-of-band
3229 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003230 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003231 subject_key_version: A 64-bit version value. If this is None, the number
3232 of seconds since the epoch is used.
3233 subject: A subject identifier. For Product Signing Key certificates this
3234 should be the same Product ID found in the permanent attributes.
3235 is_intermediate_authority: True if the certificate is for an intermediate
3236 authority.
3237 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003238 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003239 """
3240 signed_data = bytearray()
3241 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003242 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003243 hasher = hashlib.sha256()
3244 hasher.update(subject)
3245 signed_data.extend(hasher.digest())
3246 usage = 'com.google.android.things.vboot'
3247 if is_intermediate_authority:
3248 usage += '.ca'
3249 hasher = hashlib.sha256()
3250 hasher.update(usage)
3251 signed_data.extend(hasher.digest())
3252 if not subject_key_version:
3253 subject_key_version = int(time.time())
3254 signed_data.extend(struct.pack('<Q', subject_key_version))
3255 signature = bytearray()
3256 if authority_key_path:
3257 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003258 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003259 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003260 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003261 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003262 hasher.update(signed_data)
3263 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003264 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3265 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003266 alg.signature_num_bytes, authority_key_path,
3267 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003268 output.write(signed_data)
3269 output.write(signature)
3270
David Zeuthenc68f0822017-03-31 17:22:35 -04003271 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003272 product_id):
3273 """Implements the 'make_atx_permanent_attributes' command.
3274
3275 Android Things permanent attributes are designed to be permanent for a
3276 particular product and a hash of these attributes should be fused into
3277 hardware to enforce this.
3278
3279 Arguments:
3280 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003281 root_authority_key_path: Path to a PEM or DER public key for
3282 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003283 product_id: A 16-byte Product ID.
3284
3285 Raises:
3286 AvbError: If an argument is incorrect.
3287 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003288 EXPECTED_PRODUCT_ID_SIZE = 16
3289 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003290 raise AvbError('Invalid Product ID length.')
3291 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003292 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003293 output.write(product_id)
3294
3295 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003296 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003297 """Implements the 'make_atx_metadata' command.
3298
3299 Android Things metadata are included in vbmeta images to facilitate
3300 verification. The output of this command can be used as the
3301 public_key_metadata argument to other commands.
3302
3303 Arguments:
3304 output: Metadata will be written to this file on success.
3305 intermediate_key_certificate: A certificate file as output by
3306 make_atx_certificate with
3307 is_intermediate_authority set to true.
3308 product_key_certificate: A certificate file as output by
3309 make_atx_certificate with
3310 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003311
3312 Raises:
3313 AvbError: If an argument is incorrect.
3314 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003315 EXPECTED_CERTIFICATE_SIZE = 1620
3316 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003317 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003318 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003319 raise AvbError('Invalid product key certificate length.')
3320 output.write(struct.pack('<I', 1)) # Format Version
3321 output.write(intermediate_key_certificate)
3322 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003323
David Zeuthen21e95262016-07-27 17:58:40 -04003324
3325def calc_hash_level_offsets(image_size, block_size, digest_size):
3326 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3327
3328 Arguments:
3329 image_size: The size of the image to calculate a Merkle-tree for.
3330 block_size: The block size, e.g. 4096.
3331 digest_size: The size of each hash, e.g. 32 for SHA-256.
3332
3333 Returns:
3334 A tuple where the first argument is an array of offsets and the
3335 second is size of the tree, in bytes.
3336 """
3337 level_offsets = []
3338 level_sizes = []
3339 tree_size = 0
3340
3341 num_levels = 0
3342 size = image_size
3343 while size > block_size:
3344 num_blocks = (size + block_size - 1) / block_size
3345 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3346
3347 level_sizes.append(level_size)
3348 tree_size += level_size
3349 num_levels += 1
3350
3351 size = level_size
3352
3353 for n in range(0, num_levels):
3354 offset = 0
3355 for m in range(n + 1, num_levels):
3356 offset += level_sizes[m]
3357 level_offsets.append(offset)
3358
David Zeuthena4fee8b2016-08-22 15:20:43 -04003359 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003360
3361
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003362# See system/extras/libfec/include/fec/io.h for these definitions.
3363FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3364FEC_MAGIC = 0xfecfecfe
3365
3366
3367def calc_fec_data_size(image_size, num_roots):
3368 """Calculates how much space FEC data will take.
3369
3370 Args:
3371 image_size: The size of the image.
3372 num_roots: Number of roots.
3373
3374 Returns:
3375 The number of bytes needed for FEC for an image of the given size
3376 and with the requested number of FEC roots.
3377
3378 Raises:
3379 ValueError: If output from the 'fec' tool is invalid.
3380
3381 """
3382 p = subprocess.Popen(
3383 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3384 stdout=subprocess.PIPE,
3385 stderr=subprocess.PIPE)
3386 (pout, perr) = p.communicate()
3387 retcode = p.wait()
3388 if retcode != 0:
3389 raise ValueError('Error invoking fec: {}'.format(perr))
3390 return int(pout)
3391
3392
3393def generate_fec_data(image_filename, num_roots):
3394 """Generate FEC codes for an image.
3395
3396 Args:
3397 image_filename: The filename of the image.
3398 num_roots: Number of roots.
3399
3400 Returns:
3401 The FEC data blob.
3402
3403 Raises:
3404 ValueError: If output from the 'fec' tool is invalid.
3405 """
3406 fec_tmpfile = tempfile.NamedTemporaryFile()
3407 subprocess.check_call(
3408 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3409 fec_tmpfile.name],
3410 stderr=open(os.devnull))
3411 fec_data = fec_tmpfile.read()
3412 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3413 footer_data = fec_data[-footer_size:]
3414 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3415 footer_data)
3416 if magic != FEC_MAGIC:
3417 raise ValueError('Unexpected magic in FEC footer')
3418 return fec_data[0:fec_size]
3419
3420
David Zeuthen21e95262016-07-27 17:58:40 -04003421def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003422 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003423 """Generates a Merkle-tree for a file.
3424
3425 Args:
3426 image: The image, as a file.
3427 image_size: The size of the image.
3428 block_size: The block size, e.g. 4096.
3429 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3430 salt: The salt to use.
3431 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003432 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003433 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003434
3435 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003436 A tuple where the first element is the top-level hash and the
3437 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003438 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003439 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003440 hash_src_offset = 0
3441 hash_src_size = image_size
3442 level_num = 0
3443 while hash_src_size > block_size:
3444 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003445 remaining = hash_src_size
3446 while remaining > 0:
3447 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003448 # Only read from the file for the first level - for subsequent
3449 # levels, access the array we're building.
3450 if level_num == 0:
3451 image.seek(hash_src_offset + hash_src_size - remaining)
3452 data = image.read(min(remaining, block_size))
3453 else:
3454 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3455 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003456 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003457
3458 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003459 if len(data) < block_size:
3460 hasher.update('\0' * (block_size - len(data)))
3461 level_output += hasher.digest()
3462 if digest_padding > 0:
3463 level_output += '\0' * digest_padding
3464
3465 padding_needed = (round_to_multiple(
3466 len(level_output), block_size) - len(level_output))
3467 level_output += '\0' * padding_needed
3468
David Zeuthena4fee8b2016-08-22 15:20:43 -04003469 # Copy level-output into resulting tree.
3470 offset = hash_level_offsets[level_num]
3471 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003472
David Zeuthena4fee8b2016-08-22 15:20:43 -04003473 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003474 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003475 level_num += 1
3476
3477 hasher = hashlib.new(name=hash_alg_name, string=salt)
3478 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003479 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003480
3481
3482class AvbTool(object):
3483 """Object for avbtool command-line tool."""
3484
3485 def __init__(self):
3486 """Initializer method."""
3487 self.avb = Avb()
3488
3489 def _add_common_args(self, sub_parser):
3490 """Adds arguments used by several sub-commands.
3491
3492 Arguments:
3493 sub_parser: The parser to add arguments to.
3494 """
3495 sub_parser.add_argument('--algorithm',
3496 help='Algorithm to use (default: NONE)',
3497 metavar='ALGORITHM',
3498 default='NONE')
3499 sub_parser.add_argument('--key',
3500 help='Path to RSA private key file',
3501 metavar='KEY',
3502 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003503 sub_parser.add_argument('--signing_helper',
3504 help='Path to helper used for signing',
3505 metavar='APP',
3506 default=None,
3507 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003508 sub_parser.add_argument('--signing_helper_with_files',
3509 help='Path to helper used for signing using files',
3510 metavar='APP',
3511 default=None,
3512 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003513 sub_parser.add_argument('--public_key_metadata',
3514 help='Path to public key metadata file',
3515 metavar='KEY_METADATA',
3516 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003517 sub_parser.add_argument('--rollback_index',
3518 help='Rollback Index',
3519 type=parse_number,
3520 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003521 # This is used internally for unit tests. Do not include in --help output.
3522 sub_parser.add_argument('--internal_release_string',
3523 help=argparse.SUPPRESS)
3524 sub_parser.add_argument('--append_to_release_string',
3525 help='Text to append to release string',
3526 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003527 sub_parser.add_argument('--prop',
3528 help='Add property',
3529 metavar='KEY:VALUE',
3530 action='append')
3531 sub_parser.add_argument('--prop_from_file',
3532 help='Add property from file',
3533 metavar='KEY:PATH',
3534 action='append')
3535 sub_parser.add_argument('--kernel_cmdline',
3536 help='Add kernel cmdline',
3537 metavar='CMDLINE',
3538 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003539 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3540 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3541 # at some future point.
3542 sub_parser.add_argument('--setup_rootfs_from_kernel',
3543 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003544 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003545 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003546 type=argparse.FileType('rb'))
3547 sub_parser.add_argument('--include_descriptors_from_image',
3548 help='Include descriptors from image',
3549 metavar='IMAGE',
3550 action='append',
3551 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003552 sub_parser.add_argument('--print_required_libavb_version',
3553 help=('Don\'t store the footer - '
3554 'instead calculate the required libavb '
3555 'version for the given options.'),
3556 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003557 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3558 sub_parser.add_argument('--chain_partition',
3559 help='Allow signed integrity-data for partition',
3560 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3561 action='append')
3562 sub_parser.add_argument('--flags',
3563 help='VBMeta flags',
3564 type=parse_number,
3565 default=0)
3566 sub_parser.add_argument('--set_hashtree_disabled_flag',
3567 help='Set the HASHTREE_DISABLED flag',
3568 action='store_true')
3569
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003570 def _add_common_footer_args(self, sub_parser):
3571 """Adds arguments used by add_*_footer sub-commands.
3572
3573 Arguments:
3574 sub_parser: The parser to add arguments to.
3575 """
3576 sub_parser.add_argument('--use_persistent_digest',
3577 help='Use a persistent digest on device instead of '
3578 'storing the digest in the descriptor. This '
3579 'cannot be used with A/B so must be combined '
3580 'with --do_not_use_ab when an A/B suffix is '
3581 'expected at runtime.',
3582 action='store_true')
3583 sub_parser.add_argument('--do_not_use_ab',
3584 help='The partition does not use A/B even when an '
3585 'A/B suffix is present. This must not be used '
3586 'for vbmeta or chained partitions.',
3587 action='store_true')
3588
David Zeuthena5fd3a42017-02-27 16:38:54 -05003589 def _fixup_common_args(self, args):
3590 """Common fixups needed by subcommands.
3591
3592 Arguments:
3593 args: Arguments to modify.
3594
3595 Returns:
3596 The modified arguments.
3597 """
3598 if args.set_hashtree_disabled_flag:
3599 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3600 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003601
3602 def run(self, argv):
3603 """Command-line processor.
3604
3605 Arguments:
3606 argv: Pass sys.argv from main.
3607 """
3608 parser = argparse.ArgumentParser()
3609 subparsers = parser.add_subparsers(title='subcommands')
3610
3611 sub_parser = subparsers.add_parser('version',
3612 help='Prints version of avbtool.')
3613 sub_parser.set_defaults(func=self.version)
3614
3615 sub_parser = subparsers.add_parser('extract_public_key',
3616 help='Extract public key.')
3617 sub_parser.add_argument('--key',
3618 help='Path to RSA private key file',
3619 required=True)
3620 sub_parser.add_argument('--output',
3621 help='Output file name',
3622 type=argparse.FileType('wb'),
3623 required=True)
3624 sub_parser.set_defaults(func=self.extract_public_key)
3625
3626 sub_parser = subparsers.add_parser('make_vbmeta_image',
3627 help='Makes a vbmeta image.')
3628 sub_parser.add_argument('--output',
3629 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003630 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003631 sub_parser.add_argument('--padding_size',
3632 metavar='NUMBER',
3633 help='If non-zero, pads output with NUL bytes so '
3634 'its size is a multiple of NUMBER (default: 0)',
3635 type=parse_number,
3636 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003637 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003638 sub_parser.set_defaults(func=self.make_vbmeta_image)
3639
3640 sub_parser = subparsers.add_parser('add_hash_footer',
3641 help='Add hashes and footer to image.')
3642 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003643 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003644 type=argparse.FileType('rab+'))
3645 sub_parser.add_argument('--partition_size',
3646 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003647 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003648 sub_parser.add_argument('--partition_name',
3649 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003650 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003651 sub_parser.add_argument('--hash_algorithm',
3652 help='Hash algorithm to use (default: sha256)',
3653 default='sha256')
3654 sub_parser.add_argument('--salt',
3655 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003656 sub_parser.add_argument('--calc_max_image_size',
3657 help=('Don\'t store the footer - '
3658 'instead calculate the maximum image size '
3659 'leaving enough room for metadata with '
3660 'the given partition size.'),
3661 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003662 sub_parser.add_argument('--output_vbmeta_image',
3663 help='Also write vbmeta struct to file',
3664 type=argparse.FileType('wb'))
3665 sub_parser.add_argument('--do_not_append_vbmeta_image',
3666 help=('Do not append vbmeta struct or footer '
3667 'to the image'),
3668 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003669 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003670 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003671 sub_parser.set_defaults(func=self.add_hash_footer)
3672
David Zeuthenb1b994d2017-03-06 18:01:31 -05003673 sub_parser = subparsers.add_parser('append_vbmeta_image',
3674 help='Append vbmeta image to image.')
3675 sub_parser.add_argument('--image',
3676 help='Image to append vbmeta blob to',
3677 type=argparse.FileType('rab+'))
3678 sub_parser.add_argument('--partition_size',
3679 help='Partition size',
3680 type=parse_number,
3681 required=True)
3682 sub_parser.add_argument('--vbmeta_image',
3683 help='Image with vbmeta blob to append',
3684 type=argparse.FileType('rb'))
3685 sub_parser.set_defaults(func=self.append_vbmeta_image)
3686
David Zeuthen21e95262016-07-27 17:58:40 -04003687 sub_parser = subparsers.add_parser('add_hashtree_footer',
3688 help='Add hashtree and footer to image.')
3689 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003690 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003691 type=argparse.FileType('rab+'))
3692 sub_parser.add_argument('--partition_size',
3693 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003694 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003695 sub_parser.add_argument('--partition_name',
3696 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003697 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003698 sub_parser.add_argument('--hash_algorithm',
3699 help='Hash algorithm to use (default: sha1)',
3700 default='sha1')
3701 sub_parser.add_argument('--salt',
3702 help='Salt in hex (default: /dev/urandom)')
3703 sub_parser.add_argument('--block_size',
3704 help='Block size (default: 4096)',
3705 type=parse_number,
3706 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003707 # TODO(zeuthen): The --generate_fec option was removed when we
3708 # moved to generating FEC by default. To avoid breaking existing
3709 # users needing to transition we simply just print a warning below
3710 # in add_hashtree_footer(). Remove this option and the warning at
3711 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003712 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003713 help=argparse.SUPPRESS,
3714 action='store_true')
3715 sub_parser.add_argument('--do_not_generate_fec',
3716 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003717 action='store_true')
3718 sub_parser.add_argument('--fec_num_roots',
3719 help='Number of roots for FEC (default: 2)',
3720 type=parse_number,
3721 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003722 sub_parser.add_argument('--calc_max_image_size',
3723 help=('Don\'t store the hashtree or footer - '
3724 'instead calculate the maximum image size '
3725 'leaving enough room for hashtree '
3726 'and metadata with the given partition '
3727 'size.'),
3728 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003729 sub_parser.add_argument('--output_vbmeta_image',
3730 help='Also write vbmeta struct to file',
3731 type=argparse.FileType('wb'))
3732 sub_parser.add_argument('--do_not_append_vbmeta_image',
3733 help=('Do not append vbmeta struct or footer '
3734 'to the image'),
3735 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003736 # This is different from --setup_rootfs_from_kernel insofar that
3737 # it doesn't take an IMAGE, the generated cmdline will be for the
3738 # hashtree we're adding.
3739 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3740 action='store_true',
3741 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003742 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003743 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003744 sub_parser.set_defaults(func=self.add_hashtree_footer)
3745
3746 sub_parser = subparsers.add_parser('erase_footer',
3747 help='Erase footer from an image.')
3748 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003749 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003750 type=argparse.FileType('rwb+'),
3751 required=True)
3752 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003753 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003754 action='store_true')
3755 sub_parser.set_defaults(func=self.erase_footer)
3756
David Zeuthen2bc232b2017-04-19 14:25:19 -04003757 sub_parser = subparsers.add_parser('resize_image',
3758 help='Resize image with a footer.')
3759 sub_parser.add_argument('--image',
3760 help='Image with a footer',
3761 type=argparse.FileType('rwb+'),
3762 required=True)
3763 sub_parser.add_argument('--partition_size',
3764 help='New partition size',
3765 type=parse_number)
3766 sub_parser.set_defaults(func=self.resize_image)
3767
David Zeuthen21e95262016-07-27 17:58:40 -04003768 sub_parser = subparsers.add_parser(
3769 'info_image',
3770 help='Show information about vbmeta or footer.')
3771 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003772 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003773 type=argparse.FileType('rb'),
3774 required=True)
3775 sub_parser.add_argument('--output',
3776 help='Write info to file',
3777 type=argparse.FileType('wt'),
3778 default=sys.stdout)
3779 sub_parser.set_defaults(func=self.info_image)
3780
David Zeuthenb623d8b2017-04-04 16:05:53 -04003781 sub_parser = subparsers.add_parser(
3782 'verify_image',
3783 help='Verify an image.')
3784 sub_parser.add_argument('--image',
3785 help='Image to verify',
3786 type=argparse.FileType('rb'),
3787 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003788 sub_parser.add_argument('--key',
3789 help='Check embedded public key matches KEY',
3790 metavar='KEY',
3791 required=False)
3792 sub_parser.add_argument('--expected_chain_partition',
3793 help='Expected chain partition',
3794 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3795 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04003796 sub_parser.set_defaults(func=self.verify_image)
3797
David Zeuthen8b6973b2016-09-20 12:39:49 -04003798 sub_parser = subparsers.add_parser('set_ab_metadata',
3799 help='Set A/B metadata.')
3800 sub_parser.add_argument('--misc_image',
3801 help=('The misc image to modify. If the image does '
3802 'not exist, it will be created.'),
3803 type=argparse.FileType('r+b'),
3804 required=True)
3805 sub_parser.add_argument('--slot_data',
3806 help=('Slot data of the form "priority", '
3807 '"tries_remaining", "sucessful_boot" for '
3808 'slot A followed by the same for slot B, '
3809 'separated by colons. The default value '
3810 'is 15:7:0:14:7:0.'),
3811 default='15:7:0:14:7:0')
3812 sub_parser.set_defaults(func=self.set_ab_metadata)
3813
Darren Krahn147b08d2016-12-20 16:38:29 -08003814 sub_parser = subparsers.add_parser(
3815 'make_atx_certificate',
3816 help='Create an Android Things eXtension (ATX) certificate.')
3817 sub_parser.add_argument('--output',
3818 help='Write certificate to file',
3819 type=argparse.FileType('wb'),
3820 default=sys.stdout)
3821 sub_parser.add_argument('--subject',
3822 help=('Path to subject file'),
3823 type=argparse.FileType('rb'),
3824 required=True)
3825 sub_parser.add_argument('--subject_key',
3826 help=('Path to subject RSA public key file'),
3827 type=argparse.FileType('rb'),
3828 required=True)
3829 sub_parser.add_argument('--subject_key_version',
3830 help=('Version of the subject key'),
3831 type=parse_number,
3832 required=False)
3833 sub_parser.add_argument('--subject_is_intermediate_authority',
3834 help=('Generate an intermediate authority '
3835 'certificate'),
3836 action='store_true')
3837 sub_parser.add_argument('--authority_key',
3838 help='Path to authority RSA private key file',
3839 required=False)
3840 sub_parser.add_argument('--signing_helper',
3841 help='Path to helper used for signing',
3842 metavar='APP',
3843 default=None,
3844 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003845 sub_parser.add_argument('--signing_helper_with_files',
3846 help='Path to helper used for signing using files',
3847 metavar='APP',
3848 default=None,
3849 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08003850 sub_parser.set_defaults(func=self.make_atx_certificate)
3851
3852 sub_parser = subparsers.add_parser(
3853 'make_atx_permanent_attributes',
3854 help='Create Android Things eXtension (ATX) permanent attributes.')
3855 sub_parser.add_argument('--output',
3856 help='Write attributes to file',
3857 type=argparse.FileType('wb'),
3858 default=sys.stdout)
3859 sub_parser.add_argument('--root_authority_key',
3860 help='Path to authority RSA public key file',
3861 type=argparse.FileType('rb'),
3862 required=True)
3863 sub_parser.add_argument('--product_id',
3864 help=('Path to Product ID file'),
3865 type=argparse.FileType('rb'),
3866 required=True)
3867 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3868
3869 sub_parser = subparsers.add_parser(
3870 'make_atx_metadata',
3871 help='Create Android Things eXtension (ATX) metadata.')
3872 sub_parser.add_argument('--output',
3873 help='Write metadata to file',
3874 type=argparse.FileType('wb'),
3875 default=sys.stdout)
3876 sub_parser.add_argument('--intermediate_key_certificate',
3877 help='Path to intermediate key certificate file',
3878 type=argparse.FileType('rb'),
3879 required=True)
3880 sub_parser.add_argument('--product_key_certificate',
3881 help='Path to product key certificate file',
3882 type=argparse.FileType('rb'),
3883 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003884 sub_parser.set_defaults(func=self.make_atx_metadata)
3885
David Zeuthen21e95262016-07-27 17:58:40 -04003886 args = parser.parse_args(argv[1:])
3887 try:
3888 args.func(args)
3889 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003890 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003891 sys.exit(1)
3892
3893 def version(self, _):
3894 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003895 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003896
3897 def extract_public_key(self, args):
3898 """Implements the 'extract_public_key' sub-command."""
3899 self.avb.extract_public_key(args.key, args.output)
3900
3901 def make_vbmeta_image(self, args):
3902 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003903 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003904 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003905 args.algorithm, args.key,
3906 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003907 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003908 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003909 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003910 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003911 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003912 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003913 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003914 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003915 args.print_required_libavb_version,
3916 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003917
David Zeuthenb1b994d2017-03-06 18:01:31 -05003918 def append_vbmeta_image(self, args):
3919 """Implements the 'append_vbmeta_image' sub-command."""
3920 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3921 args.partition_size)
3922
David Zeuthen21e95262016-07-27 17:58:40 -04003923 def add_hash_footer(self, args):
3924 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003925 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04003926 self.avb.add_hash_footer(args.image.name if args.image else None,
3927 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003928 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003929 args.salt, args.chain_partition, args.algorithm,
3930 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003931 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003932 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003933 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003934 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003935 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04003936 args.calc_max_image_size,
3937 args.signing_helper,
3938 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003939 args.internal_release_string,
3940 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003941 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04003942 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003943 args.print_required_libavb_version,
3944 args.use_persistent_digest,
3945 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04003946
3947 def add_hashtree_footer(self, args):
3948 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003949 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04003950 # TODO(zeuthen): Remove when removing support for the
3951 # '--generate_fec' option above.
3952 if args.generate_fec:
3953 sys.stderr.write('The --generate_fec option is deprecated since FEC '
3954 'is now generated by default. Use the option '
3955 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04003956 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3957 args.partition_size,
3958 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04003959 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003960 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003961 args.salt, args.chain_partition, args.algorithm,
3962 args.key, args.public_key_metadata,
3963 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003964 args.prop_from_file,
3965 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003966 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003967 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003968 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04003969 args.calc_max_image_size,
3970 args.signing_helper,
3971 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003972 args.internal_release_string,
3973 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003974 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04003975 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003976 args.print_required_libavb_version,
3977 args.use_persistent_digest,
3978 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05003979
David Zeuthen21e95262016-07-27 17:58:40 -04003980 def erase_footer(self, args):
3981 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003982 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003983
David Zeuthen2bc232b2017-04-19 14:25:19 -04003984 def resize_image(self, args):
3985 """Implements the 'resize_image' sub-command."""
3986 self.avb.resize_image(args.image.name, args.partition_size)
3987
David Zeuthen8b6973b2016-09-20 12:39:49 -04003988 def set_ab_metadata(self, args):
3989 """Implements the 'set_ab_metadata' sub-command."""
3990 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3991
David Zeuthen21e95262016-07-27 17:58:40 -04003992 def info_image(self, args):
3993 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003994 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003995
David Zeuthenb623d8b2017-04-04 16:05:53 -04003996 def verify_image(self, args):
3997 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003998 self.avb.verify_image(args.image.name, args.key,
3999 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004000
Darren Krahn147b08d2016-12-20 16:38:29 -08004001 def make_atx_certificate(self, args):
4002 """Implements the 'make_atx_certificate' sub-command."""
4003 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004004 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004005 args.subject_key_version,
4006 args.subject.read(),
4007 args.subject_is_intermediate_authority,
David Zeuthena156d3d2017-06-01 12:08:09 -04004008 args.signing_helper,
4009 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004010
4011 def make_atx_permanent_attributes(self, args):
4012 """Implements the 'make_atx_permanent_attributes' sub-command."""
4013 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004014 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004015 args.product_id.read())
4016
4017 def make_atx_metadata(self, args):
4018 """Implements the 'make_atx_metadata' sub-command."""
4019 self.avb.make_atx_metadata(args.output,
4020 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004021 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004022
David Zeuthen21e95262016-07-27 17:58:40 -04004023
4024if __name__ == '__main__':
4025 tool = AvbTool()
4026 tool.run(sys.argv)