blob: 75bae27f25e43ba2fda6440be9fbd3185eddbbf5 [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
41AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
David Zeuthen58305522017-01-11 17:42:47 -050044AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040045
David Zeuthene3cadca2017-02-22 21:25:46 -050046
David Zeuthen21e95262016-07-27 17:58:40 -040047class AvbError(Exception):
48 """Application-specific errors.
49
50 These errors represent issues for which a stack-trace should not be
51 presented.
52
53 Attributes:
54 message: Error message.
55 """
56
57 def __init__(self, message):
58 Exception.__init__(self, message)
59
60
61class Algorithm(object):
62 """Contains details about an algorithm.
63
64 See the avb_vbmeta_header.h file for more details about
65 algorithms.
66
67 The constant |ALGORITHMS| is a dictionary from human-readable
68 names (e.g 'SHA256_RSA2048') to instances of this class.
69
70 Attributes:
71 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040072 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040073 hash_num_bytes: Number of bytes used to store the hash.
74 signature_num_bytes: Number of bytes used to store the signature.
75 public_key_num_bytes: Number of bytes used to store the public key.
76 padding: Padding used for signature, if any.
77 """
78
David Zeuthenb623d8b2017-04-04 16:05:53 -040079 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
80 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040081 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040083 self.hash_num_bytes = hash_num_bytes
84 self.signature_num_bytes = signature_num_bytes
85 self.public_key_num_bytes = public_key_num_bytes
86 self.padding = padding
87
David Zeuthenb623d8b2017-04-04 16:05:53 -040088
David Zeuthen21e95262016-07-27 17:58:40 -040089# This must be kept in sync with the avb_crypto.h file.
90#
91# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
92# obtained from section 5.2.2 of RFC 4880.
93ALGORITHMS = {
94 'NONE': Algorithm(
95 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040096 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -040097 hash_num_bytes=0,
98 signature_num_bytes=0,
99 public_key_num_bytes=0,
100 padding=[]),
101 'SHA256_RSA2048': Algorithm(
102 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400103 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400104 hash_num_bytes=32,
105 signature_num_bytes=256,
106 public_key_num_bytes=8 + 2*2048/8,
107 padding=[
108 # PKCS1-v1_5 padding
109 0x00, 0x01] + [0xff]*202 + [0x00] + [
110 # ASN.1 header
111 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
112 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
113 0x00, 0x04, 0x20,
114 ]),
115 'SHA256_RSA4096': Algorithm(
116 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400117 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400118 hash_num_bytes=32,
119 signature_num_bytes=512,
120 public_key_num_bytes=8 + 2*4096/8,
121 padding=[
122 # PKCS1-v1_5 padding
123 0x00, 0x01] + [0xff]*458 + [0x00] + [
124 # ASN.1 header
125 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
126 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
127 0x00, 0x04, 0x20,
128 ]),
129 'SHA256_RSA8192': Algorithm(
130 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400131 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400132 hash_num_bytes=32,
133 signature_num_bytes=1024,
134 public_key_num_bytes=8 + 2*8192/8,
135 padding=[
136 # PKCS1-v1_5 padding
137 0x00, 0x01] + [0xff]*970 + [0x00] + [
138 # ASN.1 header
139 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
140 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
141 0x00, 0x04, 0x20,
142 ]),
143 'SHA512_RSA2048': Algorithm(
144 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400145 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400146 hash_num_bytes=64,
147 signature_num_bytes=256,
148 public_key_num_bytes=8 + 2*2048/8,
149 padding=[
150 # PKCS1-v1_5 padding
151 0x00, 0x01] + [0xff]*170 + [0x00] + [
152 # ASN.1 header
153 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
154 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
155 0x00, 0x04, 0x40
156 ]),
157 'SHA512_RSA4096': Algorithm(
158 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400159 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400160 hash_num_bytes=64,
161 signature_num_bytes=512,
162 public_key_num_bytes=8 + 2*4096/8,
163 padding=[
164 # PKCS1-v1_5 padding
165 0x00, 0x01] + [0xff]*426 + [0x00] + [
166 # ASN.1 header
167 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
168 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
169 0x00, 0x04, 0x40
170 ]),
171 'SHA512_RSA8192': Algorithm(
172 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400173 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400174 hash_num_bytes=64,
175 signature_num_bytes=1024,
176 public_key_num_bytes=8 + 2*8192/8,
177 padding=[
178 # PKCS1-v1_5 padding
179 0x00, 0x01] + [0xff]*938 + [0x00] + [
180 # ASN.1 header
181 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
182 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
183 0x00, 0x04, 0x40
184 ]),
185}
186
187
David Zeuthene3cadca2017-02-22 21:25:46 -0500188def get_release_string():
189 """Calculates the release string to use in the VBMeta struct."""
190 # Keep in sync with libavb/avb_version.c:avb_version_string().
191 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
192 AVB_VERSION_MINOR,
193 AVB_VERSION_SUB)
194
195
David Zeuthen21e95262016-07-27 17:58:40 -0400196def round_to_multiple(number, size):
197 """Rounds a number up to nearest multiple of another number.
198
199 Args:
200 number: The number to round up.
201 size: The multiple to round up to.
202
203 Returns:
204 If |number| is a multiple of |size|, returns |number|, otherwise
205 returns |number| + |size|.
206 """
207 remainder = number % size
208 if remainder == 0:
209 return number
210 return number + size - remainder
211
212
213def round_to_pow2(number):
214 """Rounds a number up to the next power of 2.
215
216 Args:
217 number: The number to round up.
218
219 Returns:
220 If |number| is already a power of 2 then |number| is
221 returned. Otherwise the smallest power of 2 greater than |number|
222 is returned.
223 """
224 return 2**((number - 1).bit_length())
225
226
David Zeuthen21e95262016-07-27 17:58:40 -0400227def encode_long(num_bits, value):
228 """Encodes a long to a bytearray() using a given amount of bits.
229
230 This number is written big-endian, e.g. with the most significant
231 bit first.
232
David Zeuthenb623d8b2017-04-04 16:05:53 -0400233 This is the reverse of decode_long().
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235 Arguments:
236 num_bits: The number of bits to write, e.g. 2048.
237 value: The value to write.
238
239 Returns:
240 A bytearray() with the encoded long.
241 """
242 ret = bytearray()
243 for bit_pos in range(num_bits, 0, -8):
244 octet = (value >> (bit_pos - 8)) & 0xff
245 ret.extend(struct.pack('!B', octet))
246 return ret
247
248
David Zeuthenb623d8b2017-04-04 16:05:53 -0400249def decode_long(blob):
250 """Decodes a long from a bytearray() using a given amount of bits.
251
252 This number is expected to be in big-endian, e.g. with the most
253 significant bit first.
254
255 This is the reverse of encode_long().
256
257 Arguments:
258 value: A bytearray() with the encoded long.
259
260 Returns:
261 The decoded value.
262 """
263 ret = 0
264 for b in bytearray(blob):
265 ret *= 256
266 ret += b
267 return ret
268
269
David Zeuthen21e95262016-07-27 17:58:40 -0400270def egcd(a, b):
271 """Calculate greatest common divisor of two numbers.
272
273 This implementation uses a recursive version of the extended
274 Euclidian algorithm.
275
276 Arguments:
277 a: First number.
278 b: Second number.
279
280 Returns:
281 A tuple (gcd, x, y) that where |gcd| is the greatest common
282 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
283 """
284 if a == 0:
285 return (b, 0, 1)
286 else:
287 g, y, x = egcd(b % a, a)
288 return (g, x - (b // a) * y, y)
289
290
291def modinv(a, m):
292 """Calculate modular multiplicative inverse of |a| modulo |m|.
293
294 This calculates the number |x| such that |a| * |x| == 1 (modulo
295 |m|). This number only exists if |a| and |m| are co-prime - |None|
296 is returned if this isn't true.
297
298 Arguments:
299 a: The number to calculate a modular inverse of.
300 m: The modulo to use.
301
302 Returns:
303 The modular multiplicative inverse of |a| and |m| or |None| if
304 these numbers are not co-prime.
305 """
306 gcd, x, _ = egcd(a, m)
307 if gcd != 1:
308 return None # modular inverse does not exist
309 else:
310 return x % m
311
312
313def parse_number(string):
314 """Parse a string as a number.
315
316 This is just a short-hand for int(string, 0) suitable for use in the
317 |type| parameter of |ArgumentParser|'s add_argument() function. An
318 improvement to just using type=int is that this function supports
319 numbers in other bases, e.g. "0x1234".
320
321 Arguments:
322 string: The string to parse.
323
324 Returns:
325 The parsed integer.
326
327 Raises:
328 ValueError: If the number could not be parsed.
329 """
330 return int(string, 0)
331
332
David Zeuthenc68f0822017-03-31 17:22:35 -0400333class RSAPublicKey(object):
334 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336 Attributes:
337 exponent: The key exponent.
338 modulus: The key modulus.
339 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400340 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400341
342 MODULUS_PREFIX = 'modulus='
343
344 def __init__(self, key_path):
345 """Loads and parses an RSA key from either a private or public key file.
346
347 Arguments:
348 key_path: The path to a key file.
349 """
350 # We used to have something as simple as this:
351 #
352 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
353 # self.exponent = key.e
354 # self.modulus = key.n
355 # self.num_bits = key.size() + 1
356 #
357 # but unfortunately PyCrypto is not available in the builder. So
358 # instead just parse openssl(1) output to get this
359 # information. It's ugly but...
360 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
361 p = subprocess.Popen(args,
362 stdin=subprocess.PIPE,
363 stdout=subprocess.PIPE,
364 stderr=subprocess.PIPE)
365 (pout, perr) = p.communicate()
366 if p.wait() != 0:
367 # Could be just a public key is passed, try that.
368 args.append('-pubin')
369 p = subprocess.Popen(args,
370 stdin=subprocess.PIPE,
371 stdout=subprocess.PIPE,
372 stderr=subprocess.PIPE)
373 (pout, perr) = p.communicate()
374 if p.wait() != 0:
375 raise AvbError('Error getting public key: {}'.format(perr))
376
377 if not pout.lower().startswith(self.MODULUS_PREFIX):
378 raise AvbError('Unexpected modulus output')
379
380 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
381
382 # The exponent is assumed to always be 65537 and the number of
383 # bits can be derived from the modulus by rounding up to the
384 # nearest power of 2.
385 self.modulus = int(modulus_hexstr, 16)
386 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
387 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400388
389
David Zeuthenc68f0822017-03-31 17:22:35 -0400390def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400391 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
392
393 This creates a |AvbRSAPublicKeyHeader| as well as the two large
394 numbers (|key_num_bits| bits long) following it.
395
396 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400397 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400398
399 Returns:
400 A bytearray() with the |AvbRSAPublicKeyHeader|.
401 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400402 key = RSAPublicKey(key_path)
403 if key.exponent != 65537:
404 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400405 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400406 # Calculate n0inv = -1/n[0] (mod 2^32)
407 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400408 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400409 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400410 r = 2L**key.modulus.bit_length()
411 rrmodn = r * r % key.modulus
412 ret.extend(struct.pack('!II', key.num_bits, n0inv))
413 ret.extend(encode_long(key.num_bits, key.modulus))
414 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400415 return ret
416
417
418def lookup_algorithm_by_type(alg_type):
419 """Looks up algorithm by type.
420
421 Arguments:
422 alg_type: The integer representing the type.
423
424 Returns:
425 A tuple with the algorithm name and an |Algorithm| instance.
426
427 Raises:
428 Exception: If the algorithm cannot be found
429 """
430 for alg_name in ALGORITHMS:
431 alg_data = ALGORITHMS[alg_name]
432 if alg_data.algorithm_type == alg_type:
433 return (alg_name, alg_data)
434 raise AvbError('Unknown algorithm type {}'.format(alg_type))
435
436
Esun Kimff44f232017-03-30 10:34:54 +0900437def raw_sign(signing_helper, algorithm_name, signature_num_bytes, key_path,
438 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800439 """Computes a raw RSA signature using |signing_helper| or openssl.
440
441 Arguments:
442 signing_helper: Program which signs a hash and returns the signature.
443 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900444 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800445 key_path: Path to the private key file. Must be PEM format.
446 raw_data_to_sign: Data to sign (bytearray or str expected).
447
448 Returns:
449 A bytearray containing the signature.
450
451 Raises:
452 Exception: If an error occurs.
453 """
454 p = None
455 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500456 p = subprocess.Popen(
457 [signing_helper, algorithm_name, key_path],
458 stdin=subprocess.PIPE,
459 stdout=subprocess.PIPE,
460 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800461 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500462 p = subprocess.Popen(
463 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
464 stdin=subprocess.PIPE,
465 stdout=subprocess.PIPE,
466 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800467 (pout, perr) = p.communicate(str(raw_data_to_sign))
468 retcode = p.wait()
469 if retcode != 0:
470 raise AvbError('Error signing: {}'.format(perr))
Esun Kimff44f232017-03-30 10:34:54 +0900471 signature = bytearray(pout)
472 if len(signature) != signature_num_bytes:
473 raise AvbError('Error signing: Invalid length of signature')
474 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800475
476
David Zeuthenb623d8b2017-04-04 16:05:53 -0400477def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
478 """Checks that the signature in a vbmeta blob was made by
479 the embedded public key.
480
481 Arguments:
482 vbmeta_header: A AvbVBMetaHeader.
483 vbmeta_blob: The whole vbmeta blob, including the header.
484
485 Returns:
486 True if the signature is valid and corresponds to the embedded
487 public key. Also returns True if the vbmeta blob is not signed.
488 """
489 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
490 if alg.hash_name == '':
491 return True
492 header_blob = vbmeta_blob[0:256]
493 auth_offset = 256
494 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
495 aux_size = vbmeta_header.auxiliary_data_block_size
496 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
497 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
498 pubkey_size = vbmeta_header.public_key_size
499 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
500
501 digest_offset = auth_offset + vbmeta_header.hash_offset
502 digest_size = vbmeta_header.hash_size
503 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
504
505 sig_offset = auth_offset + vbmeta_header.signature_offset
506 sig_size = vbmeta_header.signature_size
507 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
508
509 # Now that we've got the stored digest, public key, and signature
510 # all we need to do is to verify. This is the exactly the same
511 # steps as performed in the avb_vbmeta_image_verify() function in
512 # libavb/avb_vbmeta_image.c.
513
514 ha = hashlib.new(alg.hash_name)
515 ha.update(header_blob)
516 ha.update(aux_blob)
517 computed_digest = ha.digest()
518
519 if computed_digest != digest_blob:
520 return False
521
522 padding_and_digest = bytearray(alg.padding)
523 padding_and_digest.extend(computed_digest)
524
525 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
526 modulus_blob = pubkey_blob[8:8 + num_bits/8]
527 modulus = decode_long(modulus_blob)
528 exponent = 65537
529
530 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
531 # is OK since 'avbtool verify_image' is not expected to run on the
532 # Android builders (see bug #36809096).
533 import Crypto.PublicKey.RSA
534 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
535 if not key.verify(decode_long(padding_and_digest),
536 (decode_long(sig_blob), None)):
537 return False
538 return True
539
540
David Zeuthena4fee8b2016-08-22 15:20:43 -0400541class ImageChunk(object):
542 """Data structure used for representing chunks in Android sparse files.
543
544 Attributes:
545 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
546 chunk_offset: Offset in the sparse file where this chunk begins.
547 output_offset: Offset in de-sparsified file where output begins.
548 output_size: Number of bytes in output.
549 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
550 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
551 """
552
553 FORMAT = '<2H2I'
554 TYPE_RAW = 0xcac1
555 TYPE_FILL = 0xcac2
556 TYPE_DONT_CARE = 0xcac3
557 TYPE_CRC32 = 0xcac4
558
559 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
560 input_offset, fill_data):
561 """Initializes an ImageChunk object.
562
563 Arguments:
564 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
565 chunk_offset: Offset in the sparse file where this chunk begins.
566 output_offset: Offset in de-sparsified file.
567 output_size: Number of bytes in output.
568 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
569 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
570
571 Raises:
572 ValueError: If data is not well-formed.
573 """
574 self.chunk_type = chunk_type
575 self.chunk_offset = chunk_offset
576 self.output_offset = output_offset
577 self.output_size = output_size
578 self.input_offset = input_offset
579 self.fill_data = fill_data
580 # Check invariants.
581 if self.chunk_type == self.TYPE_RAW:
582 if self.fill_data is not None:
583 raise ValueError('RAW chunk cannot have fill_data set.')
584 if not self.input_offset:
585 raise ValueError('RAW chunk must have input_offset set.')
586 elif self.chunk_type == self.TYPE_FILL:
587 if self.fill_data is None:
588 raise ValueError('FILL chunk must have fill_data set.')
589 if self.input_offset:
590 raise ValueError('FILL chunk cannot have input_offset set.')
591 elif self.chunk_type == self.TYPE_DONT_CARE:
592 if self.fill_data is not None:
593 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
594 if self.input_offset:
595 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
596 else:
597 raise ValueError('Invalid chunk type')
598
599
600class ImageHandler(object):
601 """Abstraction for image I/O with support for Android sparse images.
602
603 This class provides an interface for working with image files that
604 may be using the Android Sparse Image format. When an instance is
605 constructed, we test whether it's an Android sparse file. If so,
606 operations will be on the sparse file by interpreting the sparse
607 format, otherwise they will be directly on the file. Either way the
608 operations do the same.
609
610 For reading, this interface mimics a file object - it has seek(),
611 tell(), and read() methods. For writing, only truncation
612 (truncate()) and appending is supported (append_raw() and
613 append_dont_care()). Additionally, data can only be written in units
614 of the block size.
615
616 Attributes:
617 is_sparse: Whether the file being operated on is sparse.
618 block_size: The block size, typically 4096.
619 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400620 """
621 # See system/core/libsparse/sparse_format.h for details.
622 MAGIC = 0xed26ff3a
623 HEADER_FORMAT = '<I4H4I'
624
625 # These are formats and offset of just the |total_chunks| and
626 # |total_blocks| fields.
627 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
628 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
629
630 def __init__(self, image_filename):
631 """Initializes an image handler.
632
633 Arguments:
634 image_filename: The name of the file to operate on.
635
636 Raises:
637 ValueError: If data in the file is invalid.
638 """
639 self._image_filename = image_filename
640 self._read_header()
641
642 def _read_header(self):
643 """Initializes internal data structures used for reading file.
644
645 This may be called multiple times and is typically called after
646 modifying the file (e.g. appending, truncation).
647
648 Raises:
649 ValueError: If data in the file is invalid.
650 """
651 self.is_sparse = False
652 self.block_size = 4096
653 self._file_pos = 0
654 self._image = open(self._image_filename, 'r+b')
655 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400656 self.image_size = self._image.tell()
657
658 self._image.seek(0, os.SEEK_SET)
659 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
660 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
661 block_size, self._num_total_blocks, self._num_total_chunks,
662 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
663 if magic != self.MAGIC:
664 # Not a sparse image, our job here is done.
665 return
666 if not (major_version == 1 and minor_version == 0):
667 raise ValueError('Encountered sparse image format version {}.{} but '
668 'only 1.0 is supported'.format(major_version,
669 minor_version))
670 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
671 raise ValueError('Unexpected file_hdr_sz value {}.'.
672 format(file_hdr_sz))
673 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
674 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
675 format(chunk_hdr_sz))
676
677 self.block_size = block_size
678
679 # Build an list of chunks by parsing the file.
680 self._chunks = []
681
682 # Find the smallest offset where only "Don't care" chunks
683 # follow. This will be the size of the content in the sparse
684 # image.
685 offset = 0
686 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400687 for _ in xrange(1, self._num_total_chunks + 1):
688 chunk_offset = self._image.tell()
689
690 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
691 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
692 header_bin)
693 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
694
David Zeuthena4fee8b2016-08-22 15:20:43 -0400695 if chunk_type == ImageChunk.TYPE_RAW:
696 if data_sz != (chunk_sz * self.block_size):
697 raise ValueError('Raw chunk input size ({}) does not match output '
698 'size ({})'.
699 format(data_sz, chunk_sz*self.block_size))
700 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
701 chunk_offset,
702 output_offset,
703 chunk_sz*self.block_size,
704 self._image.tell(),
705 None))
706 self._image.read(data_sz)
707
708 elif chunk_type == ImageChunk.TYPE_FILL:
709 if data_sz != 4:
710 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
711 'has {}'.format(data_sz))
712 fill_data = self._image.read(4)
713 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
714 chunk_offset,
715 output_offset,
716 chunk_sz*self.block_size,
717 None,
718 fill_data))
719 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
720 if data_sz != 0:
721 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
722 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400723 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
724 chunk_offset,
725 output_offset,
726 chunk_sz*self.block_size,
727 None,
728 None))
729 elif chunk_type == ImageChunk.TYPE_CRC32:
730 if data_sz != 4:
731 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
732 'this has {}'.format(data_sz))
733 self._image.read(4)
734 else:
735 raise ValueError('Unknown chunk type {}'.format(chunk_type))
736
737 offset += chunk_sz
738 output_offset += chunk_sz*self.block_size
739
740 # Record where sparse data end.
741 self._sparse_end = self._image.tell()
742
743 # Now that we've traversed all chunks, sanity check.
744 if self._num_total_blocks != offset:
745 raise ValueError('The header said we should have {} output blocks, '
746 'but we saw {}'.format(self._num_total_blocks, offset))
747 junk_len = len(self._image.read())
748 if junk_len > 0:
749 raise ValueError('There were {} bytes of extra data at the end of the '
750 'file.'.format(junk_len))
751
David Zeuthen09692692016-09-30 16:16:40 -0400752 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400753 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400754
755 # This is used when bisecting in read() to find the initial slice.
756 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
757
758 self.is_sparse = True
759
760 def _update_chunks_and_blocks(self):
761 """Helper function to update the image header.
762
763 The the |total_chunks| and |total_blocks| fields in the header
764 will be set to value of the |_num_total_blocks| and
765 |_num_total_chunks| attributes.
766
767 """
768 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
769 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
770 self._num_total_blocks,
771 self._num_total_chunks))
772
773 def append_dont_care(self, num_bytes):
774 """Appends a DONT_CARE chunk to the sparse file.
775
776 The given number of bytes must be a multiple of the block size.
777
778 Arguments:
779 num_bytes: Size in number of bytes of the DONT_CARE chunk.
780 """
781 assert num_bytes % self.block_size == 0
782
783 if not self.is_sparse:
784 self._image.seek(0, os.SEEK_END)
785 # This is more efficient that writing NUL bytes since it'll add
786 # a hole on file systems that support sparse files (native
787 # sparse, not Android sparse).
788 self._image.truncate(self._image.tell() + num_bytes)
789 self._read_header()
790 return
791
792 self._num_total_chunks += 1
793 self._num_total_blocks += num_bytes / self.block_size
794 self._update_chunks_and_blocks()
795
796 self._image.seek(self._sparse_end, os.SEEK_SET)
797 self._image.write(struct.pack(ImageChunk.FORMAT,
798 ImageChunk.TYPE_DONT_CARE,
799 0, # Reserved
800 num_bytes / self.block_size,
801 struct.calcsize(ImageChunk.FORMAT)))
802 self._read_header()
803
804 def append_raw(self, data):
805 """Appends a RAW chunk to the sparse file.
806
807 The length of the given data must be a multiple of the block size.
808
809 Arguments:
810 data: Data to append.
811 """
812 assert len(data) % self.block_size == 0
813
814 if not self.is_sparse:
815 self._image.seek(0, os.SEEK_END)
816 self._image.write(data)
817 self._read_header()
818 return
819
820 self._num_total_chunks += 1
821 self._num_total_blocks += len(data) / self.block_size
822 self._update_chunks_and_blocks()
823
824 self._image.seek(self._sparse_end, os.SEEK_SET)
825 self._image.write(struct.pack(ImageChunk.FORMAT,
826 ImageChunk.TYPE_RAW,
827 0, # Reserved
828 len(data) / self.block_size,
829 len(data) +
830 struct.calcsize(ImageChunk.FORMAT)))
831 self._image.write(data)
832 self._read_header()
833
834 def append_fill(self, fill_data, size):
835 """Appends a fill chunk to the sparse file.
836
837 The total length of the fill data must be a multiple of the block size.
838
839 Arguments:
840 fill_data: Fill data to append - must be four bytes.
841 size: Number of chunk - must be a multiple of four and the block size.
842 """
843 assert len(fill_data) == 4
844 assert size % 4 == 0
845 assert size % self.block_size == 0
846
847 if not self.is_sparse:
848 self._image.seek(0, os.SEEK_END)
849 self._image.write(fill_data * (size/4))
850 self._read_header()
851 return
852
853 self._num_total_chunks += 1
854 self._num_total_blocks += size / self.block_size
855 self._update_chunks_and_blocks()
856
857 self._image.seek(self._sparse_end, os.SEEK_SET)
858 self._image.write(struct.pack(ImageChunk.FORMAT,
859 ImageChunk.TYPE_FILL,
860 0, # Reserved
861 size / self.block_size,
862 4 + struct.calcsize(ImageChunk.FORMAT)))
863 self._image.write(fill_data)
864 self._read_header()
865
866 def seek(self, offset):
867 """Sets the cursor position for reading from unsparsified file.
868
869 Arguments:
870 offset: Offset to seek to from the beginning of the file.
871 """
872 self._file_pos = offset
873
874 def read(self, size):
875 """Reads data from the unsparsified file.
876
877 This method may return fewer than |size| bytes of data if the end
878 of the file was encountered.
879
880 The file cursor for reading is advanced by the number of bytes
881 read.
882
883 Arguments:
884 size: Number of bytes to read.
885
886 Returns:
887 The data.
888
889 """
890 if not self.is_sparse:
891 self._image.seek(self._file_pos)
892 data = self._image.read(size)
893 self._file_pos += len(data)
894 return data
895
896 # Iterate over all chunks.
897 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
898 self._file_pos) - 1
899 data = bytearray()
900 to_go = size
901 while to_go > 0:
902 chunk = self._chunks[chunk_idx]
903 chunk_pos_offset = self._file_pos - chunk.output_offset
904 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
905
906 if chunk.chunk_type == ImageChunk.TYPE_RAW:
907 self._image.seek(chunk.input_offset + chunk_pos_offset)
908 data.extend(self._image.read(chunk_pos_to_go))
909 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
910 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
911 offset_mod = chunk_pos_offset % len(chunk.fill_data)
912 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
913 else:
914 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
915 data.extend('\0' * chunk_pos_to_go)
916
917 to_go -= chunk_pos_to_go
918 self._file_pos += chunk_pos_to_go
919 chunk_idx += 1
920 # Generate partial read in case of EOF.
921 if chunk_idx >= len(self._chunks):
922 break
923
924 return data
925
926 def tell(self):
927 """Returns the file cursor position for reading from unsparsified file.
928
929 Returns:
930 The file cursor position for reading.
931 """
932 return self._file_pos
933
934 def truncate(self, size):
935 """Truncates the unsparsified file.
936
937 Arguments:
938 size: Desired size of unsparsified file.
939
940 Raises:
941 ValueError: If desired size isn't a multiple of the block size.
942 """
943 if not self.is_sparse:
944 self._image.truncate(size)
945 self._read_header()
946 return
947
948 if size % self.block_size != 0:
949 raise ValueError('Cannot truncate to a size which is not a multiple '
950 'of the block size')
951
952 if size == self.image_size:
953 # Trivial where there's nothing to do.
954 return
955 elif size < self.image_size:
956 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
957 chunk = self._chunks[chunk_idx]
958 if chunk.output_offset != size:
959 # Truncation in the middle of a trunk - need to keep the chunk
960 # and modify it.
961 chunk_idx_for_update = chunk_idx + 1
962 num_to_keep = size - chunk.output_offset
963 assert num_to_keep % self.block_size == 0
964 if chunk.chunk_type == ImageChunk.TYPE_RAW:
965 truncate_at = (chunk.chunk_offset +
966 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
967 data_sz = num_to_keep
968 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
969 truncate_at = (chunk.chunk_offset +
970 struct.calcsize(ImageChunk.FORMAT) + 4)
971 data_sz = 4
972 else:
973 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
974 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
975 data_sz = 0
976 chunk_sz = num_to_keep/self.block_size
977 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
978 self._image.seek(chunk.chunk_offset)
979 self._image.write(struct.pack(ImageChunk.FORMAT,
980 chunk.chunk_type,
981 0, # Reserved
982 chunk_sz,
983 total_sz))
984 chunk.output_size = num_to_keep
985 else:
986 # Truncation at trunk boundary.
987 truncate_at = chunk.chunk_offset
988 chunk_idx_for_update = chunk_idx
989
990 self._num_total_chunks = chunk_idx_for_update
991 self._num_total_blocks = 0
992 for i in range(0, chunk_idx_for_update):
993 self._num_total_blocks += self._chunks[i].output_size / self.block_size
994 self._update_chunks_and_blocks()
995 self._image.truncate(truncate_at)
996
997 # We've modified the file so re-read all data.
998 self._read_header()
999 else:
1000 # Truncating to grow - just add a DONT_CARE section.
1001 self.append_dont_care(size - self.image_size)
1002
1003
David Zeuthen21e95262016-07-27 17:58:40 -04001004class AvbDescriptor(object):
1005 """Class for AVB descriptor.
1006
1007 See the |AvbDescriptor| C struct for more information.
1008
1009 Attributes:
1010 tag: The tag identifying what kind of descriptor this is.
1011 data: The data in the descriptor.
1012 """
1013
1014 SIZE = 16
1015 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1016
1017 def __init__(self, data):
1018 """Initializes a new property descriptor.
1019
1020 Arguments:
1021 data: If not None, must be a bytearray().
1022
1023 Raises:
1024 LookupError: If the given descriptor is malformed.
1025 """
1026 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1027
1028 if data:
1029 (self.tag, num_bytes_following) = (
1030 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1031 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1032 else:
1033 self.tag = None
1034 self.data = None
1035
1036 def print_desc(self, o):
1037 """Print the descriptor.
1038
1039 Arguments:
1040 o: The object to write the output to.
1041 """
1042 o.write(' Unknown descriptor:\n')
1043 o.write(' Tag: {}\n'.format(self.tag))
1044 if len(self.data) < 256:
1045 o.write(' Data: {} ({} bytes)\n'.format(
1046 repr(str(self.data)), len(self.data)))
1047 else:
1048 o.write(' Data: {} bytes\n'.format(len(self.data)))
1049
1050 def encode(self):
1051 """Serializes the descriptor.
1052
1053 Returns:
1054 A bytearray() with the descriptor data.
1055 """
1056 num_bytes_following = len(self.data)
1057 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1058 padding_size = nbf_with_padding - num_bytes_following
1059 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1060 padding = struct.pack(str(padding_size) + 'x')
1061 ret = desc + self.data + padding
1062 return bytearray(ret)
1063
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001064 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1065 """Verifies contents of the descriptor - used in verify_image sub-command.
1066
1067 Arguments:
1068 image_dir: The directory of the file being verified.
1069 image_ext: The extension of the file being verified (e.g. '.img').
1070 expected_chain_partitions_map: A map from partition name to the
1071 tuple (rollback_index_location, key_blob).
1072
1073 Returns:
1074 True if the descriptor verifies, False otherwise.
1075 """
1076 # Nothing to do.
1077 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001078
1079class AvbPropertyDescriptor(AvbDescriptor):
1080 """A class for property descriptors.
1081
1082 See the |AvbPropertyDescriptor| C struct for more information.
1083
1084 Attributes:
1085 key: The key.
1086 value: The key.
1087 """
1088
1089 TAG = 0
1090 SIZE = 32
1091 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1092 'Q' # key size (bytes)
1093 'Q') # value size (bytes)
1094
1095 def __init__(self, data=None):
1096 """Initializes a new property descriptor.
1097
1098 Arguments:
1099 data: If not None, must be a bytearray of size |SIZE|.
1100
1101 Raises:
1102 LookupError: If the given descriptor is malformed.
1103 """
1104 AvbDescriptor.__init__(self, None)
1105 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1106
1107 if data:
1108 (tag, num_bytes_following, key_size,
1109 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1110 expected_size = round_to_multiple(
1111 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1112 if tag != self.TAG or num_bytes_following != expected_size:
1113 raise LookupError('Given data does not look like a property '
1114 'descriptor.')
1115 self.key = data[self.SIZE:(self.SIZE + key_size)]
1116 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1117 value_size)]
1118 else:
1119 self.key = ''
1120 self.value = ''
1121
1122 def print_desc(self, o):
1123 """Print the descriptor.
1124
1125 Arguments:
1126 o: The object to write the output to.
1127 """
1128 if len(self.value) < 256:
1129 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1130 else:
1131 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1132
1133 def encode(self):
1134 """Serializes the descriptor.
1135
1136 Returns:
1137 A bytearray() with the descriptor data.
1138 """
1139 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1140 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1141 padding_size = nbf_with_padding - num_bytes_following
1142 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1143 len(self.key), len(self.value))
1144 padding = struct.pack(str(padding_size) + 'x')
1145 ret = desc + self.key + '\0' + self.value + '\0' + padding
1146 return bytearray(ret)
1147
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001148 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1149 """Verifies contents of the descriptor - used in verify_image sub-command.
1150
1151 Arguments:
1152 image_dir: The directory of the file being verified.
1153 image_ext: The extension of the file being verified (e.g. '.img').
1154 expected_chain_partitions_map: A map from partition name to the
1155 tuple (rollback_index_location, key_blob).
1156
1157 Returns:
1158 True if the descriptor verifies, False otherwise.
1159 """
1160 # Nothing to do.
1161 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001162
1163class AvbHashtreeDescriptor(AvbDescriptor):
1164 """A class for hashtree descriptors.
1165
1166 See the |AvbHashtreeDescriptor| C struct for more information.
1167
1168 Attributes:
1169 dm_verity_version: dm-verity version used.
1170 image_size: Size of the image, after rounding up to |block_size|.
1171 tree_offset: Offset of the hash tree in the file.
1172 tree_size: Size of the tree.
1173 data_block_size: Data block size
1174 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001175 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1176 fec_offset: Offset of FEC data (0 if FEC is not used).
1177 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001178 hash_algorithm: Hash algorithm used.
1179 partition_name: Partition name.
1180 salt: Salt used.
1181 root_digest: Root digest.
1182 """
1183
1184 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001185 RESERVED = 64
1186 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001187 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1188 'L' # dm-verity version used
1189 'Q' # image size (bytes)
1190 'Q' # tree offset (bytes)
1191 'Q' # tree size (bytes)
1192 'L' # data block size (bytes)
1193 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001194 'L' # FEC number of roots
1195 'Q' # FEC offset (bytes)
1196 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001197 '32s' # hash algorithm used
1198 'L' # partition name (bytes)
1199 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001200 'L' + # root digest length (bytes)
1201 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001202
1203 def __init__(self, data=None):
1204 """Initializes a new hashtree descriptor.
1205
1206 Arguments:
1207 data: If not None, must be a bytearray of size |SIZE|.
1208
1209 Raises:
1210 LookupError: If the given descriptor is malformed.
1211 """
1212 AvbDescriptor.__init__(self, None)
1213 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1214
1215 if data:
1216 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1217 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001218 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1219 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001220 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1221 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001222 expected_size = round_to_multiple(
1223 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1224 if tag != self.TAG or num_bytes_following != expected_size:
1225 raise LookupError('Given data does not look like a hashtree '
1226 'descriptor.')
1227 # Nuke NUL-bytes at the end.
1228 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1229 o = 0
1230 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1231 partition_name_len)])
1232 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1233 self.partition_name.decode('utf-8')
1234 o += partition_name_len
1235 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1236 o += salt_len
1237 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1238 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1239 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1240
1241 else:
1242 self.dm_verity_version = 0
1243 self.image_size = 0
1244 self.tree_offset = 0
1245 self.tree_size = 0
1246 self.data_block_size = 0
1247 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001248 self.fec_num_roots = 0
1249 self.fec_offset = 0
1250 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001251 self.hash_algorithm = ''
1252 self.partition_name = ''
1253 self.salt = bytearray()
1254 self.root_digest = bytearray()
1255
1256 def print_desc(self, o):
1257 """Print the descriptor.
1258
1259 Arguments:
1260 o: The object to write the output to.
1261 """
1262 o.write(' Hashtree descriptor:\n')
1263 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1264 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1265 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1266 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1267 o.write(' Data Block Size: {} bytes\n'.format(
1268 self.data_block_size))
1269 o.write(' Hash Block Size: {} bytes\n'.format(
1270 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001271 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1272 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1273 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001274 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1275 o.write(' Partition Name: {}\n'.format(self.partition_name))
1276 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1277 'hex')))
1278 o.write(' Root Digest: {}\n'.format(str(
1279 self.root_digest).encode('hex')))
1280
1281 def encode(self):
1282 """Serializes the descriptor.
1283
1284 Returns:
1285 A bytearray() with the descriptor data.
1286 """
1287 encoded_name = self.partition_name.encode('utf-8')
1288 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1289 len(self.root_digest) - 16)
1290 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1291 padding_size = nbf_with_padding - num_bytes_following
1292 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1293 self.dm_verity_version, self.image_size,
1294 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001295 self.hash_block_size, self.fec_num_roots,
1296 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001297 len(encoded_name), len(self.salt), len(self.root_digest),
1298 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001299 padding = struct.pack(str(padding_size) + 'x')
1300 ret = desc + encoded_name + self.salt + self.root_digest + padding
1301 return bytearray(ret)
1302
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001303 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1304 """Verifies contents of the descriptor - used in verify_image sub-command.
1305
1306 Arguments:
1307 image_dir: The directory of the file being verified.
1308 image_ext: The extension of the file being verified (e.g. '.img').
1309 expected_chain_partitions_map: A map from partition name to the
1310 tuple (rollback_index_location, key_blob).
1311
1312 Returns:
1313 True if the descriptor verifies, False otherwise.
1314 """
1315 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1316 image = ImageHandler(image_filename)
1317 # Generate the hashtree and checks that it matches what's in the file.
1318 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1319 digest_padding = round_to_pow2(digest_size) - digest_size
1320 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1321 self.image_size, self.data_block_size, digest_size + digest_padding)
1322 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1323 self.data_block_size,
1324 self.hash_algorithm, self.salt,
1325 digest_padding,
1326 hash_level_offsets,
1327 tree_size)
1328 # The root digest must match...
1329 if root_digest != self.root_digest:
1330 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1331 format(image_filename))
1332 return False
1333 # ... also check that the on-disk hashtree matches
1334 image.seek(self.tree_offset)
1335 hash_tree_ondisk = image.read(self.tree_size)
1336 if hash_tree != hash_tree_ondisk:
1337 sys.stderr.write('hashtree of {} contains invalid data\n'.
1338 format(image_filename))
1339 return False
1340 # TODO: we could also verify that the FEC stored in the image is
1341 # correct but this a) currently requires the 'fec' binary; and b)
1342 # takes a long time; and c) is not strictly needed for
1343 # verification purposes as we've already verified the root hash.
1344 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1345 .format(self.partition_name, self.hash_algorithm, image_filename,
1346 self.image_size))
1347 return True
1348
David Zeuthen21e95262016-07-27 17:58:40 -04001349
1350class AvbHashDescriptor(AvbDescriptor):
1351 """A class for hash descriptors.
1352
1353 See the |AvbHashDescriptor| C struct for more information.
1354
1355 Attributes:
1356 image_size: Image size, in bytes.
1357 hash_algorithm: Hash algorithm used.
1358 partition_name: Partition name.
1359 salt: Salt used.
1360 digest: The hash value of salt and data combined.
1361 """
1362
1363 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001364 RESERVED = 64
1365 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001366 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1367 'Q' # image size (bytes)
1368 '32s' # hash algorithm used
1369 'L' # partition name (bytes)
1370 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001371 'L' + # digest length (bytes)
1372 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001373
1374 def __init__(self, data=None):
1375 """Initializes a new hash descriptor.
1376
1377 Arguments:
1378 data: If not None, must be a bytearray of size |SIZE|.
1379
1380 Raises:
1381 LookupError: If the given descriptor is malformed.
1382 """
1383 AvbDescriptor.__init__(self, None)
1384 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1385
1386 if data:
1387 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1388 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001389 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001390 expected_size = round_to_multiple(
1391 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1392 if tag != self.TAG or num_bytes_following != expected_size:
1393 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1394 # Nuke NUL-bytes at the end.
1395 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1396 o = 0
1397 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1398 partition_name_len)])
1399 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1400 self.partition_name.decode('utf-8')
1401 o += partition_name_len
1402 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1403 o += salt_len
1404 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1405 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1406 raise LookupError('digest_len doesn\'t match hash algorithm')
1407
1408 else:
1409 self.image_size = 0
1410 self.hash_algorithm = ''
1411 self.partition_name = ''
1412 self.salt = bytearray()
1413 self.digest = bytearray()
1414
1415 def print_desc(self, o):
1416 """Print the descriptor.
1417
1418 Arguments:
1419 o: The object to write the output to.
1420 """
1421 o.write(' Hash descriptor:\n')
1422 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1423 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1424 o.write(' Partition Name: {}\n'.format(self.partition_name))
1425 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1426 'hex')))
1427 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1428 'hex')))
1429
1430 def encode(self):
1431 """Serializes the descriptor.
1432
1433 Returns:
1434 A bytearray() with the descriptor data.
1435 """
1436 encoded_name = self.partition_name.encode('utf-8')
1437 num_bytes_following = (
1438 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1439 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1440 padding_size = nbf_with_padding - num_bytes_following
1441 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1442 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001443 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001444 padding = struct.pack(str(padding_size) + 'x')
1445 ret = desc + encoded_name + self.salt + self.digest + padding
1446 return bytearray(ret)
1447
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001448 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1449 """Verifies contents of the descriptor - used in verify_image sub-command.
1450
1451 Arguments:
1452 image_dir: The directory of the file being verified.
1453 image_ext: The extension of the file being verified (e.g. '.img').
1454 expected_chain_partitions_map: A map from partition name to the
1455 tuple (rollback_index_location, key_blob).
1456
1457 Returns:
1458 True if the descriptor verifies, False otherwise.
1459 """
1460 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1461 image = ImageHandler(image_filename)
1462 data = image.read(self.image_size)
1463 ha = hashlib.new(self.hash_algorithm)
1464 ha.update(self.salt)
1465 ha.update(data)
1466 digest = ha.digest()
1467 if digest != self.digest:
1468 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1469 format(self.hash_algorithm, image_filename))
1470 return False
1471 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
1472 .format(self.partition_name, self.hash_algorithm, image_filename,
1473 self.image_size))
1474 return True
1475
David Zeuthen21e95262016-07-27 17:58:40 -04001476
1477class AvbKernelCmdlineDescriptor(AvbDescriptor):
1478 """A class for kernel command-line descriptors.
1479
1480 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1481
1482 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001483 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001484 kernel_cmdline: The kernel command-line.
1485 """
1486
1487 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001488 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001489 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001490 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001491 'L') # cmdline length (bytes)
1492
David Zeuthenfd41eb92016-11-17 12:24:47 -05001493 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1494 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1495
David Zeuthen21e95262016-07-27 17:58:40 -04001496 def __init__(self, data=None):
1497 """Initializes a new kernel cmdline descriptor.
1498
1499 Arguments:
1500 data: If not None, must be a bytearray of size |SIZE|.
1501
1502 Raises:
1503 LookupError: If the given descriptor is malformed.
1504 """
1505 AvbDescriptor.__init__(self, None)
1506 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1507
1508 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001509 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001510 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1511 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1512 8)
1513 if tag != self.TAG or num_bytes_following != expected_size:
1514 raise LookupError('Given data does not look like a kernel cmdline '
1515 'descriptor.')
1516 # Nuke NUL-bytes at the end.
1517 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1518 kernel_cmdline_length)])
1519 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1520 self.kernel_cmdline.decode('utf-8')
1521 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001522 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001523 self.kernel_cmdline = ''
1524
1525 def print_desc(self, o):
1526 """Print the descriptor.
1527
1528 Arguments:
1529 o: The object to write the output to.
1530 """
1531 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001532 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001533 o.write(' Kernel Cmdline: {}\n'.format(repr(
1534 self.kernel_cmdline)))
1535
1536 def encode(self):
1537 """Serializes the descriptor.
1538
1539 Returns:
1540 A bytearray() with the descriptor data.
1541 """
1542 encoded_str = self.kernel_cmdline.encode('utf-8')
1543 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1544 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1545 padding_size = nbf_with_padding - num_bytes_following
1546 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001547 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001548 padding = struct.pack(str(padding_size) + 'x')
1549 ret = desc + encoded_str + padding
1550 return bytearray(ret)
1551
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001552 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1553 """Verifies contents of the descriptor - used in verify_image sub-command.
1554
1555 Arguments:
1556 image_dir: The directory of the file being verified.
1557 image_ext: The extension of the file being verified (e.g. '.img').
1558 expected_chain_partitions_map: A map from partition name to the
1559 tuple (rollback_index_location, key_blob).
1560
1561 Returns:
1562 True if the descriptor verifies, False otherwise.
1563 """
1564 # Nothing to verify.
1565 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001566
1567class AvbChainPartitionDescriptor(AvbDescriptor):
1568 """A class for chained partition descriptors.
1569
1570 See the |AvbChainPartitionDescriptor| C struct for more information.
1571
1572 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001573 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001574 partition_name: Partition name.
1575 public_key: Bytes for the public key.
1576 """
1577
1578 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001579 RESERVED = 64
1580 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001581 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001582 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001583 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001584 'L' + # public_key_size (bytes)
1585 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001586
1587 def __init__(self, data=None):
1588 """Initializes a new chain partition descriptor.
1589
1590 Arguments:
1591 data: If not None, must be a bytearray of size |SIZE|.
1592
1593 Raises:
1594 LookupError: If the given descriptor is malformed.
1595 """
1596 AvbDescriptor.__init__(self, None)
1597 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1598
1599 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001600 (tag, num_bytes_following, self.rollback_index_location,
1601 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001602 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001603 expected_size = round_to_multiple(
1604 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1605 if tag != self.TAG or num_bytes_following != expected_size:
1606 raise LookupError('Given data does not look like a chain partition '
1607 'descriptor.')
1608 o = 0
1609 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1610 partition_name_len)])
1611 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1612 self.partition_name.decode('utf-8')
1613 o += partition_name_len
1614 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1615
1616 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001617 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001618 self.partition_name = ''
1619 self.public_key = bytearray()
1620
1621 def print_desc(self, o):
1622 """Print the descriptor.
1623
1624 Arguments:
1625 o: The object to write the output to.
1626 """
1627 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001628 o.write(' Partition Name: {}\n'.format(self.partition_name))
1629 o.write(' Rollback Index Location: {}\n'.format(
1630 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001631 # Just show the SHA1 of the key, for size reasons.
1632 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001633 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001634
1635 def encode(self):
1636 """Serializes the descriptor.
1637
1638 Returns:
1639 A bytearray() with the descriptor data.
1640 """
1641 encoded_name = self.partition_name.encode('utf-8')
1642 num_bytes_following = (
1643 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1644 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1645 padding_size = nbf_with_padding - num_bytes_following
1646 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001647 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001648 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001649 padding = struct.pack(str(padding_size) + 'x')
1650 ret = desc + encoded_name + self.public_key + padding
1651 return bytearray(ret)
1652
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001653 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1654 """Verifies contents of the descriptor - used in verify_image sub-command.
1655
1656 Arguments:
1657 image_dir: The directory of the file being verified.
1658 image_ext: The extension of the file being verified (e.g. '.img').
1659 expected_chain_partitions_map: A map from partition name to the
1660 tuple (rollback_index_location, key_blob).
1661
1662 Returns:
1663 True if the descriptor verifies, False otherwise.
1664 """
1665 value = expected_chain_partitions_map.get(self.partition_name)
1666 if not value:
1667 sys.stderr.write('No expected chain partition for partition {}. Use '
1668 '--expected_chain_partition to specify expected '
1669 'contents.\n'.
1670 format(self.partition_name))
1671 return False
1672 rollback_index_location, pk_blob = value
1673
1674 if self.rollback_index_location != rollback_index_location:
1675 sys.stderr.write('Expected rollback_index_location {} does not '
1676 'match {} in descriptor for partition {}\n'.
1677 format(rollback_index_location,
1678 self.rollback_index_location,
1679 self.partition_name))
1680 return False
1681
1682 if self.public_key != pk_blob:
1683 sys.stderr.write('Expected public key blob does not match public '
1684 'key blob in descriptor for partition {}\n'.
1685 format(self.partition_name))
1686 return False
1687
1688 print ('{}: Successfully verified chain partition descriptor matches '
1689 'expected data'.format(self.partition_name))
1690
1691 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001692
1693DESCRIPTOR_CLASSES = [
1694 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1695 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1696]
1697
1698
1699def parse_descriptors(data):
1700 """Parses a blob of data into descriptors.
1701
1702 Arguments:
1703 data: A bytearray() with encoded descriptors.
1704
1705 Returns:
1706 A list of instances of objects derived from AvbDescriptor. For
1707 unknown descriptors, the class AvbDescriptor is used.
1708 """
1709 o = 0
1710 ret = []
1711 while o < len(data):
1712 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1713 if tag < len(DESCRIPTOR_CLASSES):
1714 c = DESCRIPTOR_CLASSES[tag]
1715 else:
1716 c = AvbDescriptor
1717 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1718 o += 16 + nb_following
1719 return ret
1720
1721
1722class AvbFooter(object):
1723 """A class for parsing and writing footers.
1724
1725 Footers are stored at the end of partitions and point to where the
1726 AvbVBMeta blob is located. They also contain the original size of
1727 the image before AVB information was added.
1728
1729 Attributes:
1730 magic: Magic for identifying the footer, see |MAGIC|.
1731 version_major: The major version of avbtool that wrote the footer.
1732 version_minor: The minor version of avbtool that wrote the footer.
1733 original_image_size: Original image size.
1734 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1735 vbmeta_size: Size of the AvbVBMeta blob.
1736 """
1737
1738 MAGIC = 'AVBf'
1739 SIZE = 64
1740 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001741 FOOTER_VERSION_MAJOR = 1
1742 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001743 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1744 'Q' # Original image size.
1745 'Q' # Offset of VBMeta blob.
1746 'Q' + # Size of VBMeta blob.
1747 str(RESERVED) + 'x') # padding for reserved bytes
1748
1749 def __init__(self, data=None):
1750 """Initializes a new footer object.
1751
1752 Arguments:
1753 data: If not None, must be a bytearray of size 4096.
1754
1755 Raises:
1756 LookupError: If the given footer is malformed.
1757 struct.error: If the given data has no footer.
1758 """
1759 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1760
1761 if data:
1762 (self.magic, self.version_major, self.version_minor,
1763 self.original_image_size, self.vbmeta_offset,
1764 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1765 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001766 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001767 else:
1768 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001769 self.version_major = self.FOOTER_VERSION_MAJOR
1770 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001771 self.original_image_size = 0
1772 self.vbmeta_offset = 0
1773 self.vbmeta_size = 0
1774
David Zeuthena4fee8b2016-08-22 15:20:43 -04001775 def encode(self):
1776 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001777
David Zeuthena4fee8b2016-08-22 15:20:43 -04001778 Returns:
1779 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001780 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001781 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1782 self.version_minor, self.original_image_size,
1783 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001784
1785
1786class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001787 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001788
1789 Attributes:
1790 The attributes correspond to the |AvbVBMetaHeader| struct
1791 defined in avb_vbmeta_header.h.
1792 """
1793
1794 SIZE = 256
1795
David Zeuthene3cadca2017-02-22 21:25:46 -05001796 # Keep in sync with |reserved0| and |reserved| field of
1797 # |AvbVBMetaImageHeader|.
1798 RESERVED0 = 4
1799 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001800
1801 # Keep in sync with |AvbVBMetaImageHeader|.
1802 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1803 '2Q' # 2 x block size
1804 'L' # algorithm type
1805 '2Q' # offset, size (hash)
1806 '2Q' # offset, size (signature)
1807 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001808 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001809 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001810 'Q' # rollback_index
1811 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001812 str(RESERVED0) + 'x' + # padding for reserved bytes
1813 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001814 str(RESERVED) + 'x') # padding for reserved bytes
1815
1816 def __init__(self, data=None):
1817 """Initializes a new header object.
1818
1819 Arguments:
1820 data: If not None, must be a bytearray of size 8192.
1821
1822 Raises:
1823 Exception: If the given data is malformed.
1824 """
1825 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1826
1827 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001828 (self.magic, self.required_libavb_version_major,
1829 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001830 self.authentication_data_block_size, self.auxiliary_data_block_size,
1831 self.algorithm_type, self.hash_offset, self.hash_size,
1832 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001833 self.public_key_size, self.public_key_metadata_offset,
1834 self.public_key_metadata_size, self.descriptors_offset,
1835 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001836 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001837 self.flags,
1838 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001839 # Nuke NUL-bytes at the end of the string.
1840 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001841 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001842 else:
1843 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001844 # Start by just requiring version 1.0. Code that adds features
1845 # in a future version can use bump_required_libavb_version_minor() to
1846 # bump the minor.
1847 self.required_libavb_version_major = AVB_VERSION_MAJOR
1848 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001849 self.authentication_data_block_size = 0
1850 self.auxiliary_data_block_size = 0
1851 self.algorithm_type = 0
1852 self.hash_offset = 0
1853 self.hash_size = 0
1854 self.signature_offset = 0
1855 self.signature_size = 0
1856 self.public_key_offset = 0
1857 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001858 self.public_key_metadata_offset = 0
1859 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001860 self.descriptors_offset = 0
1861 self.descriptors_size = 0
1862 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001863 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001864 self.release_string = get_release_string()
1865
1866 def bump_required_libavb_version_minor(self, minor):
1867 """Function to bump required_libavb_version_minor.
1868
1869 Call this when writing data that requires a specific libavb
1870 version to parse it.
1871
1872 Arguments:
1873 minor: The minor version of libavb that has support for the feature.
1874 """
1875 self.required_libavb_version_minor = (
1876 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001877
1878 def save(self, output):
1879 """Serializes the header (256 bytes) to disk.
1880
1881 Arguments:
1882 output: The object to write the output to.
1883 """
1884 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001885 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1886 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001887 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1888 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001889 self.public_key_offset, self.public_key_size,
1890 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001891 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001892 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001893
1894 def encode(self):
1895 """Serializes the header (256) to a bytearray().
1896
1897 Returns:
1898 A bytearray() with the encoded header.
1899 """
1900 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001901 self.required_libavb_version_major,
1902 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001903 self.authentication_data_block_size,
1904 self.auxiliary_data_block_size, self.algorithm_type,
1905 self.hash_offset, self.hash_size, self.signature_offset,
1906 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001907 self.public_key_size, self.public_key_metadata_offset,
1908 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001909 self.descriptors_size, self.rollback_index, self.flags,
1910 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001911
1912
1913class Avb(object):
1914 """Business logic for avbtool command-line tool."""
1915
David Zeuthen8b6973b2016-09-20 12:39:49 -04001916 # Keep in sync with avb_ab_flow.h.
1917 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1918 AB_MAGIC = '\0AB0'
1919 AB_MAJOR_VERSION = 1
1920 AB_MINOR_VERSION = 0
1921 AB_MISC_METADATA_OFFSET = 2048
1922
David Zeuthen09692692016-09-30 16:16:40 -04001923 # Constants for maximum metadata size. These are used to give
1924 # meaningful errors if the value passed in via --partition_size is
1925 # too small and when --calc_max_image_size is used. We use
1926 # conservative figures.
1927 MAX_VBMETA_SIZE = 64 * 1024
1928 MAX_FOOTER_SIZE = 4096
1929
David Zeuthena4fee8b2016-08-22 15:20:43 -04001930 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001931 """Implements the 'erase_footer' command.
1932
1933 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001934 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001935 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001936
1937 Raises:
1938 AvbError: If there's no footer in the image.
1939 """
1940
David Zeuthena4fee8b2016-08-22 15:20:43 -04001941 image = ImageHandler(image_filename)
1942
David Zeuthen21e95262016-07-27 17:58:40 -04001943 (footer, _, descriptors, _) = self._parse_image(image)
1944
1945 if not footer:
1946 raise AvbError('Given image does not have a footer.')
1947
1948 new_image_size = None
1949 if not keep_hashtree:
1950 new_image_size = footer.original_image_size
1951 else:
1952 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001953 # descriptor to figure out the location and size of the hashtree
1954 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001955 for desc in descriptors:
1956 if isinstance(desc, AvbHashtreeDescriptor):
1957 # The hashtree is always just following the main data so the
1958 # new size is easily derived.
1959 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001960 # If the image has FEC codes, also keep those.
1961 if desc.fec_offset > 0:
1962 fec_end = desc.fec_offset + desc.fec_size
1963 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001964 break
1965 if not new_image_size:
1966 raise AvbError('Requested to keep hashtree but no hashtree '
1967 'descriptor was found.')
1968
1969 # And cut...
1970 image.truncate(new_image_size)
1971
David Zeuthen2bc232b2017-04-19 14:25:19 -04001972 def resize_image(self, image_filename, partition_size):
1973 """Implements the 'resize_image' command.
1974
1975 Arguments:
1976 image_filename: File with footer to resize.
1977 partition_size: The new size of the image.
1978
1979 Raises:
1980 AvbError: If there's no footer in the image.
1981 """
1982
1983 image = ImageHandler(image_filename)
1984
1985 if partition_size % image.block_size != 0:
1986 raise AvbError('Partition size of {} is not a multiple of the image '
1987 'block size {}.'.format(partition_size,
1988 image.block_size))
1989
1990 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
1991
1992 if not footer:
1993 raise AvbError('Given image does not have a footer.')
1994
1995 # The vbmeta blob is always at the end of the data so resizing an
1996 # image amounts to just moving the footer around.
1997
1998 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
1999 if vbmeta_end_offset % image.block_size != 0:
2000 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2001
2002 if partition_size < vbmeta_end_offset + 1*image.block_size:
2003 raise AvbError('Requested size of {} is too small for an image '
2004 'of size {}.'
2005 .format(partition_size,
2006 vbmeta_end_offset + 1*image.block_size))
2007
2008 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2009 # with enough bytes such that the final Footer block is at the end
2010 # of partition_size.
2011 image.truncate(vbmeta_end_offset)
2012 image.append_dont_care(partition_size - vbmeta_end_offset -
2013 1*image.block_size)
2014
2015 # Just reuse the same footer - only difference is that we're
2016 # writing it in a different place.
2017 footer_blob = footer.encode()
2018 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2019 footer_blob)
2020 image.append_raw(footer_blob_with_padding)
2021
David Zeuthen8b6973b2016-09-20 12:39:49 -04002022 def set_ab_metadata(self, misc_image, slot_data):
2023 """Implements the 'set_ab_metadata' command.
2024
2025 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2026 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2027
2028 Arguments:
2029 misc_image: The misc image to write to.
2030 slot_data: Slot data as a string
2031
2032 Raises:
2033 AvbError: If slot data is malformed.
2034 """
2035 tokens = slot_data.split(':')
2036 if len(tokens) != 6:
2037 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2038 a_priority = int(tokens[0])
2039 a_tries_remaining = int(tokens[1])
2040 a_success = True if int(tokens[2]) != 0 else False
2041 b_priority = int(tokens[3])
2042 b_tries_remaining = int(tokens[4])
2043 b_success = True if int(tokens[5]) != 0 else False
2044
2045 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2046 self.AB_MAGIC,
2047 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2048 a_priority, a_tries_remaining, a_success,
2049 b_priority, b_tries_remaining, b_success)
2050 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2051 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2052 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2053 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2054 misc_image.write(ab_data)
2055
David Zeuthena4fee8b2016-08-22 15:20:43 -04002056 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002057 """Implements the 'info_image' command.
2058
2059 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002060 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002061 output: Output file to write human-readable information to (file object).
2062 """
2063
David Zeuthena4fee8b2016-08-22 15:20:43 -04002064 image = ImageHandler(image_filename)
2065
David Zeuthen21e95262016-07-27 17:58:40 -04002066 o = output
2067
2068 (footer, header, descriptors, image_size) = self._parse_image(image)
2069
2070 if footer:
2071 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2072 footer.version_minor))
2073 o.write('Image size: {} bytes\n'.format(image_size))
2074 o.write('Original image size: {} bytes\n'.format(
2075 footer.original_image_size))
2076 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2077 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2078 o.write('--\n')
2079
2080 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2081
David Zeuthene3cadca2017-02-22 21:25:46 -05002082 o.write('Minimum libavb version: {}.{}{}\n'.format(
2083 header.required_libavb_version_major,
2084 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002085 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002086 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2087 o.write('Authentication Block: {} bytes\n'.format(
2088 header.authentication_data_block_size))
2089 o.write('Auxiliary Block: {} bytes\n'.format(
2090 header.auxiliary_data_block_size))
2091 o.write('Algorithm: {}\n'.format(alg_name))
2092 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002093 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002094 o.write('Release String: \'{}\'\n'.format(
2095 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002096
2097 # Print descriptors.
2098 num_printed = 0
2099 o.write('Descriptors:\n')
2100 for desc in descriptors:
2101 desc.print_desc(o)
2102 num_printed += 1
2103 if num_printed == 0:
2104 o.write(' (none)\n')
2105
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002106 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002107 """Implements the 'verify_image' command.
2108
2109 Arguments:
2110 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002111 key_path: None or check that embedded public key matches key at given path.
2112 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002113 """
2114
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002115 expected_chain_partitions_map = {}
2116 if expected_chain_partitions:
2117 used_locations = {}
2118 for cp in expected_chain_partitions:
2119 cp_tokens = cp.split(':')
2120 if len(cp_tokens) != 3:
2121 raise AvbError('Malformed chained partition "{}".'.format(cp))
2122 partition_name = cp_tokens[0]
2123 rollback_index_location = int(cp_tokens[1])
2124 file_path = cp_tokens[2]
2125 pk_blob = open(file_path).read()
2126 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2127
2128 image_dir = os.path.dirname(image_filename)
2129 image_ext = os.path.splitext(image_filename)[1]
2130
2131 key_blob = None
2132 if key_path:
2133 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2134 key_blob = encode_rsa_key(key_path)
2135 else:
2136 print 'Verifying image {} using embedded public key'.format(image_filename)
2137
David Zeuthenb623d8b2017-04-04 16:05:53 -04002138 image = ImageHandler(image_filename)
2139 (footer, header, descriptors, image_size) = self._parse_image(image)
2140 offset = 0
2141 if footer:
2142 offset = footer.vbmeta_offset
2143 size = (header.SIZE + header.authentication_data_block_size +
2144 header.auxiliary_data_block_size)
2145 image.seek(offset)
2146 vbmeta_blob = image.read(size)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002147 h = AvbVBMetaHeader(vbmeta_blob[0:AvbVBMetaHeader.SIZE])
2148 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002149 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002150 raise AvbError('Signature check failed for {} vbmeta struct {}'
2151 .format(alg_name, image_filename))
2152
2153 if key_blob:
2154 # The embedded public key is in the auxiliary block at an offset.
2155 key_offset = AvbVBMetaHeader.SIZE
2156 key_offset += h.authentication_data_block_size
2157 key_offset += h.public_key_offset
2158 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + h.public_key_size]
2159 if key_blob != key_blob_in_vbmeta:
2160 raise AvbError('Embedded public key does not match given key.')
2161
2162 if footer:
2163 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2164 .format(alg_name, image_filename))
2165 else:
2166 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
2167 .format(alg_name, image_filename))
2168
2169 for desc in descriptors:
2170 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map):
2171 raise AvbError('Error verifying descriptor.')
2172
David Zeuthenb623d8b2017-04-04 16:05:53 -04002173
David Zeuthen21e95262016-07-27 17:58:40 -04002174 def _parse_image(self, image):
2175 """Gets information about an image.
2176
2177 The image can either be a vbmeta or an image with a footer.
2178
2179 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002180 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002181
2182 Returns:
2183 A tuple where the first argument is a AvbFooter (None if there
2184 is no footer on the image), the second argument is a
2185 AvbVBMetaHeader, the third argument is a list of
2186 AvbDescriptor-derived instances, and the fourth argument is the
2187 size of |image|.
2188 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002189 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002190 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002191 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002192 try:
2193 footer = AvbFooter(image.read(AvbFooter.SIZE))
2194 except (LookupError, struct.error):
2195 # Nope, just seek back to the start.
2196 image.seek(0)
2197
2198 vbmeta_offset = 0
2199 if footer:
2200 vbmeta_offset = footer.vbmeta_offset
2201
2202 image.seek(vbmeta_offset)
2203 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2204
2205 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2206 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2207 desc_start_offset = aux_block_offset + h.descriptors_offset
2208 image.seek(desc_start_offset)
2209 descriptors = parse_descriptors(image.read(h.descriptors_size))
2210
David Zeuthen09692692016-09-30 16:16:40 -04002211 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002212
David Zeuthenb1b994d2017-03-06 18:01:31 -05002213 def _load_vbmeta_blob(self, image):
2214 """Gets the vbmeta struct and associated sections.
2215
2216 The image can either be a vbmeta.img or an image with a footer.
2217
2218 Arguments:
2219 image: An ImageHandler (vbmeta or footer).
2220
2221 Returns:
2222 A blob with the vbmeta struct and other sections.
2223 """
2224 assert isinstance(image, ImageHandler)
2225 footer = None
2226 image.seek(image.image_size - AvbFooter.SIZE)
2227 try:
2228 footer = AvbFooter(image.read(AvbFooter.SIZE))
2229 except (LookupError, struct.error):
2230 # Nope, just seek back to the start.
2231 image.seek(0)
2232
2233 vbmeta_offset = 0
2234 if footer:
2235 vbmeta_offset = footer.vbmeta_offset
2236
2237 image.seek(vbmeta_offset)
2238 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2239
2240 image.seek(vbmeta_offset)
2241 data_size = AvbVBMetaHeader.SIZE
2242 data_size += h.authentication_data_block_size
2243 data_size += h.auxiliary_data_block_size
2244 return image.read(data_size)
2245
David Zeuthen73f2afa2017-05-17 16:54:11 -04002246 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002247 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002248
2249 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002250 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002251
2252 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002253 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2254 instructions. There is one for when hashtree is not disabled and one for
2255 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002256
David Zeuthen21e95262016-07-27 17:58:40 -04002257 """
2258
David Zeuthen21e95262016-07-27 17:58:40 -04002259 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002260 c += '0' # start
2261 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2262 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2263 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2264 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2265 c += ' {}'.format(ht.data_block_size) # data_block
2266 c += ' {}'.format(ht.hash_block_size) # hash_block
2267 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2268 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2269 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2270 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2271 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2272 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002273 c += ' 10' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002274 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002275 c += ' ignore_zero_blocks'
2276 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2277 c += ' fec_roots {}'.format(ht.fec_num_roots)
2278 # Note that fec_blocks is the size that FEC covers, *not* the
2279 # size of the FEC data. Since we use FEC for everything up until
2280 # the FEC data, it's the same as the offset.
2281 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2282 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2283 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002284 c += ' 2' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002285 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002286 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002287 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002288
David Zeuthenfd41eb92016-11-17 12:24:47 -05002289 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002290 desc = AvbKernelCmdlineDescriptor()
2291 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002292 desc.flags = (
2293 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2294
2295 # The descriptor for when hashtree verification is disabled is a lot
2296 # simpler - we just set the root to the partition.
2297 desc_no_ht = AvbKernelCmdlineDescriptor()
2298 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2299 desc_no_ht.flags = (
2300 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2301
2302 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002303
David Zeuthen73f2afa2017-05-17 16:54:11 -04002304 def _get_cmdline_descriptors_for_dm_verity(self, image):
2305 """Generate kernel cmdline descriptors for dm-verity.
2306
2307 Arguments:
2308 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2309
2310 Returns:
2311 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2312 instructions. There is one for when hashtree is not disabled and one for
2313 when it is.
2314
2315 Raises:
2316 AvbError: If |image| doesn't have a hashtree descriptor.
2317
2318 """
2319
2320 (_, _, descriptors, _) = self._parse_image(image)
2321
2322 ht = None
2323 for desc in descriptors:
2324 if isinstance(desc, AvbHashtreeDescriptor):
2325 ht = desc
2326 break
2327
2328 if not ht:
2329 raise AvbError('No hashtree descriptor in given image')
2330
2331 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2332
David Zeuthen21e95262016-07-27 17:58:40 -04002333 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002334 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002335 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002336 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002337 include_descriptors_from_image, signing_helper,
2338 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002339 append_to_release_string,
2340 print_required_libavb_version):
David Zeuthen21e95262016-07-27 17:58:40 -04002341 """Implements the 'make_vbmeta_image' command.
2342
2343 Arguments:
2344 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002345 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002346 algorithm_name: Name of algorithm to use.
2347 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002348 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002349 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002350 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002351 props: Properties to insert (list of strings of the form 'key:value').
2352 props_from_file: Properties to insert (list of strings 'key:<path>').
2353 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002354 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002355 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002356 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002357 release_string: None or avbtool release string to use instead of default.
2358 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002359 print_required_libavb_version: True to only print required libavb version.
David Zeuthen21e95262016-07-27 17:58:40 -04002360
2361 Raises:
2362 AvbError: If a chained partition is malformed.
2363 """
2364
David Zeuthen1097a782017-05-31 15:53:17 -04002365 # If we're asked to calculate minimum required libavb version, we're done.
2366 #
2367 # NOTE: When we get to 1.1 and later this will get more complicated.
2368 if print_required_libavb_version:
2369 print '1.0'
2370 return
2371
2372 if not output:
2373 raise AvbError('No output file given')
2374
David Zeuthen21e95262016-07-27 17:58:40 -04002375 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002376 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002377 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002378 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002379 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002380 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002381 include_descriptors_from_image, signing_helper, release_string,
2382 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002383
2384 # Write entire vbmeta blob (header, authentication, auxiliary).
2385 output.seek(0)
2386 output.write(vbmeta_blob)
2387
David Zeuthen18666ab2016-11-15 11:18:05 -05002388 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2389 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002390 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002391 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002392 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002393 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002394 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002395 include_descriptors_from_image, signing_helper,
2396 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002397 """Generates a VBMeta blob.
2398
2399 This blob contains the header (struct AvbVBMetaHeader), the
2400 authentication data block (which contains the hash and signature
2401 for the header and auxiliary block), and the auxiliary block
2402 (which contains descriptors, the public key used, and other data).
2403
2404 The |key| parameter can |None| only if the |algorithm_name| is
2405 'NONE'.
2406
2407 Arguments:
2408 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2409 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002410 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002411 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002412 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002413 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002414 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002415 props: Properties to insert (List of strings of the form 'key:value').
2416 props_from_file: Properties to insert (List of strings 'key:<path>').
2417 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002418 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002419 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002420 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2421 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002422 include_descriptors_from_image: List of file objects for which
2423 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002424 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002425 release_string: None or avbtool release string.
2426 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002427
2428 Returns:
2429 A bytearray() with the VBMeta blob.
2430
2431 Raises:
2432 Exception: If the |algorithm_name| is not found, if no key has
2433 been given and the given algorithm requires one, or the key is
2434 of the wrong size.
2435
2436 """
2437 try:
2438 alg = ALGORITHMS[algorithm_name]
2439 except KeyError:
2440 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2441
David Zeuthena5fd3a42017-02-27 16:38:54 -05002442 if not descriptors:
2443 descriptors = []
2444
2445 # Insert chained partition descriptors, if any
2446 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002447 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002448 for cp in chain_partitions:
2449 cp_tokens = cp.split(':')
2450 if len(cp_tokens) != 3:
2451 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002452 partition_name = cp_tokens[0]
2453 rollback_index_location = int(cp_tokens[1])
2454 file_path = cp_tokens[2]
2455 # Check that the same rollback location isn't being used by
2456 # multiple chained partitions.
2457 if used_locations.get(rollback_index_location):
2458 raise AvbError('Rollback Index Location {} is already in use.'.format(
2459 rollback_index_location))
2460 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002461 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002462 desc.partition_name = partition_name
2463 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002464 if desc.rollback_index_location < 1:
2465 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002466 desc.public_key = open(file_path, 'rb').read()
2467 descriptors.append(desc)
2468
David Zeuthen21e95262016-07-27 17:58:40 -04002469 # Descriptors.
2470 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002471 for desc in descriptors:
2472 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002473
2474 # Add properties.
2475 if props:
2476 for prop in props:
2477 idx = prop.find(':')
2478 if idx == -1:
2479 raise AvbError('Malformed property "{}".'.format(prop))
2480 desc = AvbPropertyDescriptor()
2481 desc.key = prop[0:idx]
2482 desc.value = prop[(idx + 1):]
2483 encoded_descriptors.extend(desc.encode())
2484 if props_from_file:
2485 for prop in props_from_file:
2486 idx = prop.find(':')
2487 if idx == -1:
2488 raise AvbError('Malformed property "{}".'.format(prop))
2489 desc = AvbPropertyDescriptor()
2490 desc.key = prop[0:idx]
2491 desc.value = prop[(idx + 1):]
2492 file_path = prop[(idx + 1):]
2493 desc.value = open(file_path, 'rb').read()
2494 encoded_descriptors.extend(desc.encode())
2495
David Zeuthen73f2afa2017-05-17 16:54:11 -04002496 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002497 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002498 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002499 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002500 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2501 encoded_descriptors.extend(cmdline_desc[0].encode())
2502 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002503
David Zeuthen73f2afa2017-05-17 16:54:11 -04002504 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2505 if ht_desc_to_setup:
2506 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2507 ht_desc_to_setup)
2508 encoded_descriptors.extend(cmdline_desc[0].encode())
2509 encoded_descriptors.extend(cmdline_desc[1].encode())
2510
David Zeuthen21e95262016-07-27 17:58:40 -04002511 # Add kernel command-lines.
2512 if kernel_cmdlines:
2513 for i in kernel_cmdlines:
2514 desc = AvbKernelCmdlineDescriptor()
2515 desc.kernel_cmdline = i
2516 encoded_descriptors.extend(desc.encode())
2517
2518 # Add descriptors from other images.
2519 if include_descriptors_from_image:
2520 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002521 image_handler = ImageHandler(image.name)
2522 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002523 for desc in image_descriptors:
2524 encoded_descriptors.extend(desc.encode())
2525
David Zeuthen18666ab2016-11-15 11:18:05 -05002526 # Load public key metadata blob, if requested.
2527 pkmd_blob = []
2528 if public_key_metadata_path:
2529 with open(public_key_metadata_path) as f:
2530 pkmd_blob = f.read()
2531
David Zeuthen21e95262016-07-27 17:58:40 -04002532 key = None
2533 encoded_key = bytearray()
2534 if alg.public_key_num_bytes > 0:
2535 if not key_path:
2536 raise AvbError('Key is required for algorithm {}'.format(
2537 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002538 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002539 if len(encoded_key) != alg.public_key_num_bytes:
2540 raise AvbError('Key is wrong size for algorithm {}'.format(
2541 algorithm_name))
2542
2543 h = AvbVBMetaHeader()
2544
David Zeuthene3cadca2017-02-22 21:25:46 -05002545 # Override release string, if requested.
2546 if isinstance(release_string, (str, unicode)):
2547 h.release_string = release_string
2548
2549 # Append to release string, if requested. Also insert a space before.
2550 if isinstance(append_to_release_string, (str, unicode)):
2551 h.release_string += ' ' + append_to_release_string
2552
David Zeuthen18666ab2016-11-15 11:18:05 -05002553 # For the Auxiliary data block, descriptors are stored at offset 0,
2554 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002555 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002556 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002557 h.descriptors_offset = 0
2558 h.descriptors_size = len(encoded_descriptors)
2559 h.public_key_offset = h.descriptors_size
2560 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002561 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2562 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002563
2564 # For the Authentication data block, the hash is first and then
2565 # the signature.
2566 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002567 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002568 h.algorithm_type = alg.algorithm_type
2569 h.hash_offset = 0
2570 h.hash_size = alg.hash_num_bytes
2571 # Signature offset and size - it's stored right after the hash
2572 # (in Authentication data block).
2573 h.signature_offset = alg.hash_num_bytes
2574 h.signature_size = alg.signature_num_bytes
2575
2576 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002577 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002578
2579 # Generate Header data block.
2580 header_data_blob = h.encode()
2581
2582 # Generate Auxiliary data block.
2583 aux_data_blob = bytearray()
2584 aux_data_blob.extend(encoded_descriptors)
2585 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002586 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002587 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2588 aux_data_blob.extend('\0' * padding_bytes)
2589
2590 # Calculate the hash.
2591 binary_hash = bytearray()
2592 binary_signature = bytearray()
2593 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002594 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002595 ha.update(header_data_blob)
2596 ha.update(aux_data_blob)
2597 binary_hash.extend(ha.digest())
2598
2599 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002600 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002601 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2602 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002603 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002604
2605 # Generate Authentication data block.
2606 auth_data_blob = bytearray()
2607 auth_data_blob.extend(binary_hash)
2608 auth_data_blob.extend(binary_signature)
2609 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2610 auth_data_blob.extend('\0' * padding_bytes)
2611
2612 return header_data_blob + auth_data_blob + aux_data_blob
2613
2614 def extract_public_key(self, key_path, output):
2615 """Implements the 'extract_public_key' command.
2616
2617 Arguments:
2618 key_path: The path to a RSA private key file.
2619 output: The file to write to.
2620 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002621 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002622
David Zeuthenb1b994d2017-03-06 18:01:31 -05002623 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2624 partition_size):
2625 """Implementation of the append_vbmeta_image command.
2626
2627 Arguments:
2628 image_filename: File to add the footer to.
2629 vbmeta_image_filename: File to get vbmeta struct from.
2630 partition_size: Size of partition.
2631
2632 Raises:
2633 AvbError: If an argument is incorrect.
2634 """
2635 image = ImageHandler(image_filename)
2636
2637 if partition_size % image.block_size != 0:
2638 raise AvbError('Partition size of {} is not a multiple of the image '
2639 'block size {}.'.format(partition_size,
2640 image.block_size))
2641
2642 # If there's already a footer, truncate the image to its original
2643 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2644 image.seek(image.image_size - AvbFooter.SIZE)
2645 try:
2646 footer = AvbFooter(image.read(AvbFooter.SIZE))
2647 # Existing footer found. Just truncate.
2648 original_image_size = footer.original_image_size
2649 image.truncate(footer.original_image_size)
2650 except (LookupError, struct.error):
2651 original_image_size = image.image_size
2652
2653 # If anything goes wrong from here-on, restore the image back to
2654 # its original size.
2655 try:
2656 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2657 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2658
2659 # If the image isn't sparse, its size might not be a multiple of
2660 # the block size. This will screw up padding later so just grow it.
2661 if image.image_size % image.block_size != 0:
2662 assert not image.is_sparse
2663 padding_needed = image.block_size - (image.image_size%image.block_size)
2664 image.truncate(image.image_size + padding_needed)
2665
2666 # The append_raw() method requires content with size being a
2667 # multiple of |block_size| so add padding as needed. Also record
2668 # where this is written to since we'll need to put that in the
2669 # footer.
2670 vbmeta_offset = image.image_size
2671 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2672 len(vbmeta_blob))
2673 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2674
2675 # Append vbmeta blob and footer
2676 image.append_raw(vbmeta_blob_with_padding)
2677 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2678
2679 # Now insert a DONT_CARE chunk with enough bytes such that the
2680 # final Footer block is at the end of partition_size..
2681 image.append_dont_care(partition_size - vbmeta_end_offset -
2682 1*image.block_size)
2683
2684 # Generate the Footer that tells where the VBMeta footer
2685 # is. Also put enough padding in the front of the footer since
2686 # we'll write out an entire block.
2687 footer = AvbFooter()
2688 footer.original_image_size = original_image_size
2689 footer.vbmeta_offset = vbmeta_offset
2690 footer.vbmeta_size = len(vbmeta_blob)
2691 footer_blob = footer.encode()
2692 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2693 footer_blob)
2694 image.append_raw(footer_blob_with_padding)
2695
2696 except:
2697 # Truncate back to original size, then re-raise
2698 image.truncate(original_image_size)
2699 raise
2700
David Zeuthena4fee8b2016-08-22 15:20:43 -04002701 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002702 hash_algorithm, salt, chain_partitions, algorithm_name,
2703 key_path,
2704 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002705 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002706 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002707 include_descriptors_from_image, calc_max_image_size,
2708 signing_helper, release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002709 output_vbmeta_image, do_not_append_vbmeta_image,
2710 print_required_libavb_version):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002711 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002712
2713 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002714 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002715 partition_size: Size of partition.
2716 partition_name: Name of partition (without A/B suffix).
2717 hash_algorithm: Hash algorithm to use.
2718 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002719 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002720 algorithm_name: Name of algorithm to use.
2721 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002722 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002723 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002724 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002725 props: Properties to insert (List of strings of the form 'key:value').
2726 props_from_file: Properties to insert (List of strings 'key:<path>').
2727 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002728 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002729 dm-verity kernel cmdline from.
2730 include_descriptors_from_image: List of file objects for which
2731 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002732 calc_max_image_size: Don't store the footer - instead calculate the
2733 maximum image size leaving enough room for metadata with the
2734 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002735 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002736 release_string: None or avbtool release string.
2737 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002738 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2739 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002740 print_required_libavb_version: True to only print required libavb version.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002741
2742 Raises:
2743 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002744 """
David Zeuthen1097a782017-05-31 15:53:17 -04002745
2746 # If we're asked to calculate minimum required libavb version, we're done.
2747 #
2748 # NOTE: When we get to 1.1 and later this will get more complicated.
2749 if print_required_libavb_version:
2750 print '1.0'
2751 return
2752
David Zeuthenbf562452017-05-17 18:04:43 -04002753 # First, calculate the maximum image size such that an image
2754 # this size + metadata (footer + vbmeta struct) fits in
2755 # |partition_size|.
2756 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2757 max_image_size = partition_size - max_metadata_size
2758
2759 # If we're asked to only calculate the maximum image size, we're done.
2760 if calc_max_image_size:
2761 print '{}'.format(max_image_size)
2762 return
2763
David Zeuthena4fee8b2016-08-22 15:20:43 -04002764 image = ImageHandler(image_filename)
2765
2766 if partition_size % image.block_size != 0:
2767 raise AvbError('Partition size of {} is not a multiple of the image '
2768 'block size {}.'.format(partition_size,
2769 image.block_size))
2770
David Zeuthen21e95262016-07-27 17:58:40 -04002771 # If there's already a footer, truncate the image to its original
2772 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2773 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002774 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002775 try:
2776 footer = AvbFooter(image.read(AvbFooter.SIZE))
2777 # Existing footer found. Just truncate.
2778 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002779 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002780 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002781 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002782
2783 # If anything goes wrong from here-on, restore the image back to
2784 # its original size.
2785 try:
David Zeuthen09692692016-09-30 16:16:40 -04002786 # If image size exceeds the maximum image size, fail.
2787 if image.image_size > max_image_size:
2788 raise AvbError('Image size of {} exceeds maximum image '
2789 'size of {} in order to fit in a partition '
2790 'size of {}.'.format(image.image_size, max_image_size,
2791 partition_size))
2792
David Zeuthen21e95262016-07-27 17:58:40 -04002793 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2794 if salt:
2795 salt = salt.decode('hex')
2796 else:
2797 if salt is None:
2798 # If salt is not explicitly specified, choose a hash
2799 # that's the same size as the hash size.
2800 hash_size = digest_size
2801 salt = open('/dev/urandom').read(hash_size)
2802 else:
2803 salt = ''
2804
2805 hasher = hashlib.new(name=hash_algorithm, string=salt)
2806 # TODO(zeuthen): might want to read this in chunks to avoid
2807 # memory pressure, then again, this is only supposed to be used
2808 # on kernel/initramfs partitions. Possible optimization.
2809 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002810 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002811 digest = hasher.digest()
2812
2813 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002814 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002815 h_desc.hash_algorithm = hash_algorithm
2816 h_desc.partition_name = partition_name
2817 h_desc.salt = salt
2818 h_desc.digest = digest
2819
2820 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002821 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002822 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002823 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002824 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002825 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002826 include_descriptors_from_image, signing_helper, release_string,
2827 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002828
David Zeuthena4fee8b2016-08-22 15:20:43 -04002829 # If the image isn't sparse, its size might not be a multiple of
2830 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002831 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002832 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002833 padding_needed = image.block_size - (image.image_size%image.block_size)
2834 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002835
David Zeuthena4fee8b2016-08-22 15:20:43 -04002836 # The append_raw() method requires content with size being a
2837 # multiple of |block_size| so add padding as needed. Also record
2838 # where this is written to since we'll need to put that in the
2839 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002840 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002841 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2842 len(vbmeta_blob))
2843 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002844
David Zeuthend247fcb2017-02-16 12:09:27 -05002845 # Write vbmeta blob, if requested.
2846 if output_vbmeta_image:
2847 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002848
David Zeuthend247fcb2017-02-16 12:09:27 -05002849 # Append vbmeta blob and footer, unless requested not to.
2850 if not do_not_append_vbmeta_image:
2851 image.append_raw(vbmeta_blob_with_padding)
2852 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2853
2854 # Now insert a DONT_CARE chunk with enough bytes such that the
2855 # final Footer block is at the end of partition_size..
2856 image.append_dont_care(partition_size - vbmeta_end_offset -
2857 1*image.block_size)
2858
2859 # Generate the Footer that tells where the VBMeta footer
2860 # is. Also put enough padding in the front of the footer since
2861 # we'll write out an entire block.
2862 footer = AvbFooter()
2863 footer.original_image_size = original_image_size
2864 footer.vbmeta_offset = vbmeta_offset
2865 footer.vbmeta_size = len(vbmeta_blob)
2866 footer_blob = footer.encode()
2867 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2868 footer_blob)
2869 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002870
David Zeuthen21e95262016-07-27 17:58:40 -04002871 except:
2872 # Truncate back to original size, then re-raise
2873 image.truncate(original_image_size)
2874 raise
2875
David Zeuthena4fee8b2016-08-22 15:20:43 -04002876 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002877 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002878 block_size, salt, chain_partitions, algorithm_name,
2879 key_path,
2880 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002881 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002882 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002883 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002884 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002885 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002886 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002887 output_vbmeta_image, do_not_append_vbmeta_image,
2888 print_required_libavb_version):
David Zeuthen21e95262016-07-27 17:58:40 -04002889 """Implements the 'add_hashtree_footer' command.
2890
2891 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2892 more information about dm-verity and these hashes.
2893
2894 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002895 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002896 partition_size: Size of partition.
2897 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002898 generate_fec: If True, generate FEC codes.
2899 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002900 hash_algorithm: Hash algorithm to use.
2901 block_size: Block size to use.
2902 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002903 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002904 algorithm_name: Name of algorithm to use.
2905 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002906 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002907 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002908 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002909 props: Properties to insert (List of strings of the form 'key:value').
2910 props_from_file: Properties to insert (List of strings 'key:<path>').
2911 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002912 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002913 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002914 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
2915 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04002916 include_descriptors_from_image: List of file objects for which
2917 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002918 calc_max_image_size: Don't store the hashtree or footer - instead
2919 calculate the maximum image size leaving enough room for hashtree
2920 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002921 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002922 release_string: None or avbtool release string.
2923 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002924 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2925 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002926 print_required_libavb_version: True to only print required libavb version.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002927
2928 Raises:
2929 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002930 """
David Zeuthen1097a782017-05-31 15:53:17 -04002931
2932 # If we're asked to calculate minimum required libavb version, we're done.
2933 #
2934 # NOTE: When we get to 1.1 and later this will get more complicated.
2935 if print_required_libavb_version:
2936 print '1.0'
2937 return
2938
David Zeuthen09692692016-09-30 16:16:40 -04002939 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2940 digest_padding = round_to_pow2(digest_size) - digest_size
2941
2942 # First, calculate the maximum image size such that an image
2943 # this size + the hashtree + metadata (footer + vbmeta struct)
2944 # fits in |partition_size|. We use very conservative figures for
2945 # metadata.
2946 (_, max_tree_size) = calc_hash_level_offsets(
2947 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002948 max_fec_size = 0
2949 if generate_fec:
2950 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2951 max_metadata_size = (max_fec_size + max_tree_size +
2952 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002953 self.MAX_FOOTER_SIZE)
2954 max_image_size = partition_size - max_metadata_size
2955
2956 # If we're asked to only calculate the maximum image size, we're done.
2957 if calc_max_image_size:
2958 print '{}'.format(max_image_size)
2959 return
2960
David Zeuthena4fee8b2016-08-22 15:20:43 -04002961 image = ImageHandler(image_filename)
2962
2963 if partition_size % image.block_size != 0:
2964 raise AvbError('Partition size of {} is not a multiple of the image '
2965 'block size {}.'.format(partition_size,
2966 image.block_size))
2967
David Zeuthen21e95262016-07-27 17:58:40 -04002968 # If there's already a footer, truncate the image to its original
2969 # size. This way 'avbtool add_hashtree_footer' is idempotent
2970 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002971 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002972 try:
2973 footer = AvbFooter(image.read(AvbFooter.SIZE))
2974 # Existing footer found. Just truncate.
2975 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002976 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002977 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002978 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002979
2980 # If anything goes wrong from here-on, restore the image back to
2981 # its original size.
2982 try:
2983 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002984 rounded_image_size = round_to_multiple(image.image_size, block_size)
2985 if rounded_image_size > image.image_size:
2986 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002987
David Zeuthen09692692016-09-30 16:16:40 -04002988 # If image size exceeds the maximum image size, fail.
2989 if image.image_size > max_image_size:
2990 raise AvbError('Image size of {} exceeds maximum image '
2991 'size of {} in order to fit in a partition '
2992 'size of {}.'.format(image.image_size, max_image_size,
2993 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002994
2995 if salt:
2996 salt = salt.decode('hex')
2997 else:
2998 if salt is None:
2999 # If salt is not explicitly specified, choose a hash
3000 # that's the same size as the hash size.
3001 hash_size = digest_size
3002 salt = open('/dev/urandom').read(hash_size)
3003 else:
3004 salt = ''
3005
David Zeuthena4fee8b2016-08-22 15:20:43 -04003006 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003007 # offsets in advance.
3008 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003009 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003010
David Zeuthena4fee8b2016-08-22 15:20:43 -04003011 # If the image isn't sparse, its size might not be a multiple of
3012 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003013 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003014 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003015 padding_needed = image.block_size - (image.image_size%image.block_size)
3016 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003017
David Zeuthena4fee8b2016-08-22 15:20:43 -04003018 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003019 tree_offset = image.image_size
3020 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003021 block_size,
3022 hash_algorithm, salt,
3023 digest_padding,
3024 hash_level_offsets,
3025 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003026
3027 # Generate HashtreeDescriptor with details about the tree we
3028 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003029 ht_desc = AvbHashtreeDescriptor()
3030 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003031 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003032 ht_desc.tree_offset = tree_offset
3033 ht_desc.tree_size = tree_size
3034 ht_desc.data_block_size = block_size
3035 ht_desc.hash_block_size = block_size
3036 ht_desc.hash_algorithm = hash_algorithm
3037 ht_desc.partition_name = partition_name
3038 ht_desc.salt = salt
3039 ht_desc.root_digest = root_digest
3040
David Zeuthen09692692016-09-30 16:16:40 -04003041 # Write the hash tree
3042 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3043 len(hash_tree))
3044 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3045 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003046 len_hashtree_and_fec = len(hash_tree_with_padding)
3047
3048 # Generate FEC codes, if requested.
3049 if generate_fec:
3050 fec_data = generate_fec_data(image_filename, fec_num_roots)
3051 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3052 len(fec_data))
3053 fec_data_with_padding = fec_data + '\0'*padding_needed
3054 fec_offset = image.image_size
3055 image.append_raw(fec_data_with_padding)
3056 len_hashtree_and_fec += len(fec_data_with_padding)
3057 # Update the hashtree descriptor.
3058 ht_desc.fec_num_roots = fec_num_roots
3059 ht_desc.fec_offset = fec_offset
3060 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003061
David Zeuthen73f2afa2017-05-17 16:54:11 -04003062 ht_desc_to_setup = None
3063 if setup_as_rootfs_from_kernel:
3064 ht_desc_to_setup = ht_desc
3065
David Zeuthena4fee8b2016-08-22 15:20:43 -04003066 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003067 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003068 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003069 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003070 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003071 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003072 include_descriptors_from_image, signing_helper, release_string,
3073 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003074 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3075 len(vbmeta_blob))
3076 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003077
David Zeuthend247fcb2017-02-16 12:09:27 -05003078 # Write vbmeta blob, if requested.
3079 if output_vbmeta_image:
3080 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003081
David Zeuthend247fcb2017-02-16 12:09:27 -05003082 # Append vbmeta blob and footer, unless requested not to.
3083 if not do_not_append_vbmeta_image:
3084 image.append_raw(vbmeta_blob_with_padding)
3085
3086 # Now insert a DONT_CARE chunk with enough bytes such that the
3087 # final Footer block is at the end of partition_size..
3088 image.append_dont_care(partition_size - image.image_size -
3089 1*image.block_size)
3090
3091 # Generate the Footer that tells where the VBMeta footer
3092 # is. Also put enough padding in the front of the footer since
3093 # we'll write out an entire block.
3094 footer = AvbFooter()
3095 footer.original_image_size = original_image_size
3096 footer.vbmeta_offset = vbmeta_offset
3097 footer.vbmeta_size = len(vbmeta_blob)
3098 footer_blob = footer.encode()
3099 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3100 footer_blob)
3101 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003102
David Zeuthen21e95262016-07-27 17:58:40 -04003103 except:
David Zeuthen09692692016-09-30 16:16:40 -04003104 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003105 image.truncate(original_image_size)
3106 raise
3107
David Zeuthenc68f0822017-03-31 17:22:35 -04003108 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003109 subject_key_version, subject,
3110 is_intermediate_authority, signing_helper):
3111 """Implements the 'make_atx_certificate' command.
3112
3113 Android Things certificates are required for Android Things public key
3114 metadata. They chain the vbmeta signing key for a particular product back to
3115 a fused, permanent root key. These certificates are fixed-length and fixed-
3116 format with the explicit goal of not parsing ASN.1 in bootloader code.
3117
3118 Arguments:
3119 output: Certificate will be written to this file on success.
3120 authority_key_path: A PEM file path with the authority private key.
3121 If None, then a certificate will be created without a
3122 signature. The signature can be created out-of-band
3123 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003124 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003125 subject_key_version: A 64-bit version value. If this is None, the number
3126 of seconds since the epoch is used.
3127 subject: A subject identifier. For Product Signing Key certificates this
3128 should be the same Product ID found in the permanent attributes.
3129 is_intermediate_authority: True if the certificate is for an intermediate
3130 authority.
3131 signing_helper: Program which signs a hash and returns the signature.
3132 """
3133 signed_data = bytearray()
3134 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003135 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003136 hasher = hashlib.sha256()
3137 hasher.update(subject)
3138 signed_data.extend(hasher.digest())
3139 usage = 'com.google.android.things.vboot'
3140 if is_intermediate_authority:
3141 usage += '.ca'
3142 hasher = hashlib.sha256()
3143 hasher.update(usage)
3144 signed_data.extend(hasher.digest())
3145 if not subject_key_version:
3146 subject_key_version = int(time.time())
3147 signed_data.extend(struct.pack('<Q', subject_key_version))
3148 signature = bytearray()
3149 if authority_key_path:
3150 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003151 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003152 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003153 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003154 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003155 hasher.update(signed_data)
3156 padding_and_hash.extend(hasher.digest())
3157 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003158 alg.signature_num_bytes, authority_key_path,
3159 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003160 output.write(signed_data)
3161 output.write(signature)
3162
David Zeuthenc68f0822017-03-31 17:22:35 -04003163 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003164 product_id):
3165 """Implements the 'make_atx_permanent_attributes' command.
3166
3167 Android Things permanent attributes are designed to be permanent for a
3168 particular product and a hash of these attributes should be fused into
3169 hardware to enforce this.
3170
3171 Arguments:
3172 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003173 root_authority_key_path: Path to a PEM or DER public key for
3174 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003175 product_id: A 16-byte Product ID.
3176
3177 Raises:
3178 AvbError: If an argument is incorrect.
3179 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003180 EXPECTED_PRODUCT_ID_SIZE = 16
3181 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003182 raise AvbError('Invalid Product ID length.')
3183 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003184 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003185 output.write(product_id)
3186
3187 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003188 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003189 """Implements the 'make_atx_metadata' command.
3190
3191 Android Things metadata are included in vbmeta images to facilitate
3192 verification. The output of this command can be used as the
3193 public_key_metadata argument to other commands.
3194
3195 Arguments:
3196 output: Metadata will be written to this file on success.
3197 intermediate_key_certificate: A certificate file as output by
3198 make_atx_certificate with
3199 is_intermediate_authority set to true.
3200 product_key_certificate: A certificate file as output by
3201 make_atx_certificate with
3202 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003203
3204 Raises:
3205 AvbError: If an argument is incorrect.
3206 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003207 EXPECTED_CERTIFICATE_SIZE = 1620
3208 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003209 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003210 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003211 raise AvbError('Invalid product key certificate length.')
3212 output.write(struct.pack('<I', 1)) # Format Version
3213 output.write(intermediate_key_certificate)
3214 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003215
David Zeuthen21e95262016-07-27 17:58:40 -04003216
3217def calc_hash_level_offsets(image_size, block_size, digest_size):
3218 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3219
3220 Arguments:
3221 image_size: The size of the image to calculate a Merkle-tree for.
3222 block_size: The block size, e.g. 4096.
3223 digest_size: The size of each hash, e.g. 32 for SHA-256.
3224
3225 Returns:
3226 A tuple where the first argument is an array of offsets and the
3227 second is size of the tree, in bytes.
3228 """
3229 level_offsets = []
3230 level_sizes = []
3231 tree_size = 0
3232
3233 num_levels = 0
3234 size = image_size
3235 while size > block_size:
3236 num_blocks = (size + block_size - 1) / block_size
3237 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3238
3239 level_sizes.append(level_size)
3240 tree_size += level_size
3241 num_levels += 1
3242
3243 size = level_size
3244
3245 for n in range(0, num_levels):
3246 offset = 0
3247 for m in range(n + 1, num_levels):
3248 offset += level_sizes[m]
3249 level_offsets.append(offset)
3250
David Zeuthena4fee8b2016-08-22 15:20:43 -04003251 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003252
3253
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003254# See system/extras/libfec/include/fec/io.h for these definitions.
3255FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3256FEC_MAGIC = 0xfecfecfe
3257
3258
3259def calc_fec_data_size(image_size, num_roots):
3260 """Calculates how much space FEC data will take.
3261
3262 Args:
3263 image_size: The size of the image.
3264 num_roots: Number of roots.
3265
3266 Returns:
3267 The number of bytes needed for FEC for an image of the given size
3268 and with the requested number of FEC roots.
3269
3270 Raises:
3271 ValueError: If output from the 'fec' tool is invalid.
3272
3273 """
3274 p = subprocess.Popen(
3275 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3276 stdout=subprocess.PIPE,
3277 stderr=subprocess.PIPE)
3278 (pout, perr) = p.communicate()
3279 retcode = p.wait()
3280 if retcode != 0:
3281 raise ValueError('Error invoking fec: {}'.format(perr))
3282 return int(pout)
3283
3284
3285def generate_fec_data(image_filename, num_roots):
3286 """Generate FEC codes for an image.
3287
3288 Args:
3289 image_filename: The filename of the image.
3290 num_roots: Number of roots.
3291
3292 Returns:
3293 The FEC data blob.
3294
3295 Raises:
3296 ValueError: If output from the 'fec' tool is invalid.
3297 """
3298 fec_tmpfile = tempfile.NamedTemporaryFile()
3299 subprocess.check_call(
3300 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3301 fec_tmpfile.name],
3302 stderr=open(os.devnull))
3303 fec_data = fec_tmpfile.read()
3304 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3305 footer_data = fec_data[-footer_size:]
3306 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3307 footer_data)
3308 if magic != FEC_MAGIC:
3309 raise ValueError('Unexpected magic in FEC footer')
3310 return fec_data[0:fec_size]
3311
3312
David Zeuthen21e95262016-07-27 17:58:40 -04003313def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003314 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003315 """Generates a Merkle-tree for a file.
3316
3317 Args:
3318 image: The image, as a file.
3319 image_size: The size of the image.
3320 block_size: The block size, e.g. 4096.
3321 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3322 salt: The salt to use.
3323 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003324 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003325 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003326
3327 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003328 A tuple where the first element is the top-level hash and the
3329 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003330 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003331 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003332 hash_src_offset = 0
3333 hash_src_size = image_size
3334 level_num = 0
3335 while hash_src_size > block_size:
3336 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003337 remaining = hash_src_size
3338 while remaining > 0:
3339 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003340 # Only read from the file for the first level - for subsequent
3341 # levels, access the array we're building.
3342 if level_num == 0:
3343 image.seek(hash_src_offset + hash_src_size - remaining)
3344 data = image.read(min(remaining, block_size))
3345 else:
3346 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3347 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003348 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003349
3350 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003351 if len(data) < block_size:
3352 hasher.update('\0' * (block_size - len(data)))
3353 level_output += hasher.digest()
3354 if digest_padding > 0:
3355 level_output += '\0' * digest_padding
3356
3357 padding_needed = (round_to_multiple(
3358 len(level_output), block_size) - len(level_output))
3359 level_output += '\0' * padding_needed
3360
David Zeuthena4fee8b2016-08-22 15:20:43 -04003361 # Copy level-output into resulting tree.
3362 offset = hash_level_offsets[level_num]
3363 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003364
David Zeuthena4fee8b2016-08-22 15:20:43 -04003365 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003366 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003367 level_num += 1
3368
3369 hasher = hashlib.new(name=hash_alg_name, string=salt)
3370 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003371 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003372
3373
3374class AvbTool(object):
3375 """Object for avbtool command-line tool."""
3376
3377 def __init__(self):
3378 """Initializer method."""
3379 self.avb = Avb()
3380
3381 def _add_common_args(self, sub_parser):
3382 """Adds arguments used by several sub-commands.
3383
3384 Arguments:
3385 sub_parser: The parser to add arguments to.
3386 """
3387 sub_parser.add_argument('--algorithm',
3388 help='Algorithm to use (default: NONE)',
3389 metavar='ALGORITHM',
3390 default='NONE')
3391 sub_parser.add_argument('--key',
3392 help='Path to RSA private key file',
3393 metavar='KEY',
3394 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003395 sub_parser.add_argument('--signing_helper',
3396 help='Path to helper used for signing',
3397 metavar='APP',
3398 default=None,
3399 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003400 sub_parser.add_argument('--public_key_metadata',
3401 help='Path to public key metadata file',
3402 metavar='KEY_METADATA',
3403 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003404 sub_parser.add_argument('--rollback_index',
3405 help='Rollback Index',
3406 type=parse_number,
3407 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003408 # This is used internally for unit tests. Do not include in --help output.
3409 sub_parser.add_argument('--internal_release_string',
3410 help=argparse.SUPPRESS)
3411 sub_parser.add_argument('--append_to_release_string',
3412 help='Text to append to release string',
3413 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003414 sub_parser.add_argument('--prop',
3415 help='Add property',
3416 metavar='KEY:VALUE',
3417 action='append')
3418 sub_parser.add_argument('--prop_from_file',
3419 help='Add property from file',
3420 metavar='KEY:PATH',
3421 action='append')
3422 sub_parser.add_argument('--kernel_cmdline',
3423 help='Add kernel cmdline',
3424 metavar='CMDLINE',
3425 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003426 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3427 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3428 # at some future point.
3429 sub_parser.add_argument('--setup_rootfs_from_kernel',
3430 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003431 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003432 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003433 type=argparse.FileType('rb'))
3434 sub_parser.add_argument('--include_descriptors_from_image',
3435 help='Include descriptors from image',
3436 metavar='IMAGE',
3437 action='append',
3438 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003439 sub_parser.add_argument('--print_required_libavb_version',
3440 help=('Don\'t store the footer - '
3441 'instead calculate the required libavb '
3442 'version for the given options.'),
3443 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003444 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3445 sub_parser.add_argument('--chain_partition',
3446 help='Allow signed integrity-data for partition',
3447 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3448 action='append')
3449 sub_parser.add_argument('--flags',
3450 help='VBMeta flags',
3451 type=parse_number,
3452 default=0)
3453 sub_parser.add_argument('--set_hashtree_disabled_flag',
3454 help='Set the HASHTREE_DISABLED flag',
3455 action='store_true')
3456
3457 def _fixup_common_args(self, args):
3458 """Common fixups needed by subcommands.
3459
3460 Arguments:
3461 args: Arguments to modify.
3462
3463 Returns:
3464 The modified arguments.
3465 """
3466 if args.set_hashtree_disabled_flag:
3467 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3468 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003469
3470 def run(self, argv):
3471 """Command-line processor.
3472
3473 Arguments:
3474 argv: Pass sys.argv from main.
3475 """
3476 parser = argparse.ArgumentParser()
3477 subparsers = parser.add_subparsers(title='subcommands')
3478
3479 sub_parser = subparsers.add_parser('version',
3480 help='Prints version of avbtool.')
3481 sub_parser.set_defaults(func=self.version)
3482
3483 sub_parser = subparsers.add_parser('extract_public_key',
3484 help='Extract public key.')
3485 sub_parser.add_argument('--key',
3486 help='Path to RSA private key file',
3487 required=True)
3488 sub_parser.add_argument('--output',
3489 help='Output file name',
3490 type=argparse.FileType('wb'),
3491 required=True)
3492 sub_parser.set_defaults(func=self.extract_public_key)
3493
3494 sub_parser = subparsers.add_parser('make_vbmeta_image',
3495 help='Makes a vbmeta image.')
3496 sub_parser.add_argument('--output',
3497 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003498 type=argparse.FileType('wb'))
David Zeuthen21e95262016-07-27 17:58:40 -04003499 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003500 sub_parser.set_defaults(func=self.make_vbmeta_image)
3501
3502 sub_parser = subparsers.add_parser('add_hash_footer',
3503 help='Add hashes and footer to image.')
3504 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003505 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003506 type=argparse.FileType('rab+'))
3507 sub_parser.add_argument('--partition_size',
3508 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003509 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003510 sub_parser.add_argument('--partition_name',
3511 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003512 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003513 sub_parser.add_argument('--hash_algorithm',
3514 help='Hash algorithm to use (default: sha256)',
3515 default='sha256')
3516 sub_parser.add_argument('--salt',
3517 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003518 sub_parser.add_argument('--calc_max_image_size',
3519 help=('Don\'t store the footer - '
3520 'instead calculate the maximum image size '
3521 'leaving enough room for metadata with '
3522 'the given partition size.'),
3523 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003524 sub_parser.add_argument('--output_vbmeta_image',
3525 help='Also write vbmeta struct to file',
3526 type=argparse.FileType('wb'))
3527 sub_parser.add_argument('--do_not_append_vbmeta_image',
3528 help=('Do not append vbmeta struct or footer '
3529 'to the image'),
3530 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003531 self._add_common_args(sub_parser)
3532 sub_parser.set_defaults(func=self.add_hash_footer)
3533
David Zeuthenb1b994d2017-03-06 18:01:31 -05003534 sub_parser = subparsers.add_parser('append_vbmeta_image',
3535 help='Append vbmeta image to image.')
3536 sub_parser.add_argument('--image',
3537 help='Image to append vbmeta blob to',
3538 type=argparse.FileType('rab+'))
3539 sub_parser.add_argument('--partition_size',
3540 help='Partition size',
3541 type=parse_number,
3542 required=True)
3543 sub_parser.add_argument('--vbmeta_image',
3544 help='Image with vbmeta blob to append',
3545 type=argparse.FileType('rb'))
3546 sub_parser.set_defaults(func=self.append_vbmeta_image)
3547
David Zeuthen21e95262016-07-27 17:58:40 -04003548 sub_parser = subparsers.add_parser('add_hashtree_footer',
3549 help='Add hashtree and footer to image.')
3550 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003551 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003552 type=argparse.FileType('rab+'))
3553 sub_parser.add_argument('--partition_size',
3554 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003555 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003556 sub_parser.add_argument('--partition_name',
3557 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003558 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003559 sub_parser.add_argument('--hash_algorithm',
3560 help='Hash algorithm to use (default: sha1)',
3561 default='sha1')
3562 sub_parser.add_argument('--salt',
3563 help='Salt in hex (default: /dev/urandom)')
3564 sub_parser.add_argument('--block_size',
3565 help='Block size (default: 4096)',
3566 type=parse_number,
3567 default=4096)
David Zeuthen02c550f2017-05-10 17:18:04 -04003568 # TODO(zeuthen): The --generate_fec option was removed when we
3569 # moved to generating FEC by default. To avoid breaking existing
3570 # users needing to transition we simply just print a warning below
3571 # in add_hashtree_footer(). Remove this option and the warning at
3572 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003573 sub_parser.add_argument('--generate_fec',
David Zeuthen02c550f2017-05-10 17:18:04 -04003574 help=argparse.SUPPRESS,
3575 action='store_true')
3576 sub_parser.add_argument('--do_not_generate_fec',
3577 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003578 action='store_true')
3579 sub_parser.add_argument('--fec_num_roots',
3580 help='Number of roots for FEC (default: 2)',
3581 type=parse_number,
3582 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003583 sub_parser.add_argument('--calc_max_image_size',
3584 help=('Don\'t store the hashtree or footer - '
3585 'instead calculate the maximum image size '
3586 'leaving enough room for hashtree '
3587 'and metadata with the given partition '
3588 'size.'),
3589 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003590 sub_parser.add_argument('--output_vbmeta_image',
3591 help='Also write vbmeta struct to file',
3592 type=argparse.FileType('wb'))
3593 sub_parser.add_argument('--do_not_append_vbmeta_image',
3594 help=('Do not append vbmeta struct or footer '
3595 'to the image'),
3596 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003597 # This is different from --setup_rootfs_from_kernel insofar that
3598 # it doesn't take an IMAGE, the generated cmdline will be for the
3599 # hashtree we're adding.
3600 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3601 action='store_true',
3602 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003603 self._add_common_args(sub_parser)
3604 sub_parser.set_defaults(func=self.add_hashtree_footer)
3605
3606 sub_parser = subparsers.add_parser('erase_footer',
3607 help='Erase footer from an image.')
3608 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003609 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003610 type=argparse.FileType('rwb+'),
3611 required=True)
3612 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003613 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003614 action='store_true')
3615 sub_parser.set_defaults(func=self.erase_footer)
3616
David Zeuthen2bc232b2017-04-19 14:25:19 -04003617 sub_parser = subparsers.add_parser('resize_image',
3618 help='Resize image with a footer.')
3619 sub_parser.add_argument('--image',
3620 help='Image with a footer',
3621 type=argparse.FileType('rwb+'),
3622 required=True)
3623 sub_parser.add_argument('--partition_size',
3624 help='New partition size',
3625 type=parse_number)
3626 sub_parser.set_defaults(func=self.resize_image)
3627
David Zeuthen21e95262016-07-27 17:58:40 -04003628 sub_parser = subparsers.add_parser(
3629 'info_image',
3630 help='Show information about vbmeta or footer.')
3631 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003632 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003633 type=argparse.FileType('rb'),
3634 required=True)
3635 sub_parser.add_argument('--output',
3636 help='Write info to file',
3637 type=argparse.FileType('wt'),
3638 default=sys.stdout)
3639 sub_parser.set_defaults(func=self.info_image)
3640
David Zeuthenb623d8b2017-04-04 16:05:53 -04003641 sub_parser = subparsers.add_parser(
3642 'verify_image',
3643 help='Verify an image.')
3644 sub_parser.add_argument('--image',
3645 help='Image to verify',
3646 type=argparse.FileType('rb'),
3647 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003648 sub_parser.add_argument('--key',
3649 help='Check embedded public key matches KEY',
3650 metavar='KEY',
3651 required=False)
3652 sub_parser.add_argument('--expected_chain_partition',
3653 help='Expected chain partition',
3654 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3655 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04003656 sub_parser.set_defaults(func=self.verify_image)
3657
David Zeuthen8b6973b2016-09-20 12:39:49 -04003658 sub_parser = subparsers.add_parser('set_ab_metadata',
3659 help='Set A/B metadata.')
3660 sub_parser.add_argument('--misc_image',
3661 help=('The misc image to modify. If the image does '
3662 'not exist, it will be created.'),
3663 type=argparse.FileType('r+b'),
3664 required=True)
3665 sub_parser.add_argument('--slot_data',
3666 help=('Slot data of the form "priority", '
3667 '"tries_remaining", "sucessful_boot" for '
3668 'slot A followed by the same for slot B, '
3669 'separated by colons. The default value '
3670 'is 15:7:0:14:7:0.'),
3671 default='15:7:0:14:7:0')
3672 sub_parser.set_defaults(func=self.set_ab_metadata)
3673
Darren Krahn147b08d2016-12-20 16:38:29 -08003674 sub_parser = subparsers.add_parser(
3675 'make_atx_certificate',
3676 help='Create an Android Things eXtension (ATX) certificate.')
3677 sub_parser.add_argument('--output',
3678 help='Write certificate to file',
3679 type=argparse.FileType('wb'),
3680 default=sys.stdout)
3681 sub_parser.add_argument('--subject',
3682 help=('Path to subject file'),
3683 type=argparse.FileType('rb'),
3684 required=True)
3685 sub_parser.add_argument('--subject_key',
3686 help=('Path to subject RSA public key file'),
3687 type=argparse.FileType('rb'),
3688 required=True)
3689 sub_parser.add_argument('--subject_key_version',
3690 help=('Version of the subject key'),
3691 type=parse_number,
3692 required=False)
3693 sub_parser.add_argument('--subject_is_intermediate_authority',
3694 help=('Generate an intermediate authority '
3695 'certificate'),
3696 action='store_true')
3697 sub_parser.add_argument('--authority_key',
3698 help='Path to authority RSA private key file',
3699 required=False)
3700 sub_parser.add_argument('--signing_helper',
3701 help='Path to helper used for signing',
3702 metavar='APP',
3703 default=None,
3704 required=False)
3705 sub_parser.set_defaults(func=self.make_atx_certificate)
3706
3707 sub_parser = subparsers.add_parser(
3708 'make_atx_permanent_attributes',
3709 help='Create Android Things eXtension (ATX) permanent attributes.')
3710 sub_parser.add_argument('--output',
3711 help='Write attributes to file',
3712 type=argparse.FileType('wb'),
3713 default=sys.stdout)
3714 sub_parser.add_argument('--root_authority_key',
3715 help='Path to authority RSA public key file',
3716 type=argparse.FileType('rb'),
3717 required=True)
3718 sub_parser.add_argument('--product_id',
3719 help=('Path to Product ID file'),
3720 type=argparse.FileType('rb'),
3721 required=True)
3722 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3723
3724 sub_parser = subparsers.add_parser(
3725 'make_atx_metadata',
3726 help='Create Android Things eXtension (ATX) metadata.')
3727 sub_parser.add_argument('--output',
3728 help='Write metadata to file',
3729 type=argparse.FileType('wb'),
3730 default=sys.stdout)
3731 sub_parser.add_argument('--intermediate_key_certificate',
3732 help='Path to intermediate key certificate file',
3733 type=argparse.FileType('rb'),
3734 required=True)
3735 sub_parser.add_argument('--product_key_certificate',
3736 help='Path to product key certificate file',
3737 type=argparse.FileType('rb'),
3738 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003739 sub_parser.set_defaults(func=self.make_atx_metadata)
3740
David Zeuthen21e95262016-07-27 17:58:40 -04003741 args = parser.parse_args(argv[1:])
3742 try:
3743 args.func(args)
3744 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003745 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003746 sys.exit(1)
3747
3748 def version(self, _):
3749 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003750 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003751
3752 def extract_public_key(self, args):
3753 """Implements the 'extract_public_key' sub-command."""
3754 self.avb.extract_public_key(args.key, args.output)
3755
3756 def make_vbmeta_image(self, args):
3757 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003758 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003759 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003760 args.algorithm, args.key,
3761 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003762 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003763 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003764 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003765 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003766 args.signing_helper,
3767 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003768 args.append_to_release_string,
3769 args.print_required_libavb_version)
David Zeuthen21e95262016-07-27 17:58:40 -04003770
David Zeuthenb1b994d2017-03-06 18:01:31 -05003771 def append_vbmeta_image(self, args):
3772 """Implements the 'append_vbmeta_image' sub-command."""
3773 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3774 args.partition_size)
3775
David Zeuthen21e95262016-07-27 17:58:40 -04003776 def add_hash_footer(self, args):
3777 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003778 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04003779 self.avb.add_hash_footer(args.image.name if args.image else None,
3780 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003781 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003782 args.salt, args.chain_partition, args.algorithm,
3783 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003784 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003785 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003786 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003787 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003788 args.include_descriptors_from_image,
David Zeuthenbf562452017-05-17 18:04:43 -04003789 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003790 args.internal_release_string,
3791 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003792 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04003793 args.do_not_append_vbmeta_image,
3794 args.print_required_libavb_version)
David Zeuthen21e95262016-07-27 17:58:40 -04003795
3796 def add_hashtree_footer(self, args):
3797 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003798 args = self._fixup_common_args(args)
David Zeuthen02c550f2017-05-10 17:18:04 -04003799 # TODO(zeuthen): Remove when removing support for the
3800 # '--generate_fec' option above.
3801 if args.generate_fec:
3802 sys.stderr.write('The --generate_fec option is deprecated since FEC '
3803 'is now generated by default. Use the option '
3804 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04003805 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3806 args.partition_size,
3807 args.partition_name,
David Zeuthen02c550f2017-05-10 17:18:04 -04003808 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003809 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003810 args.salt, args.chain_partition, args.algorithm,
3811 args.key, args.public_key_metadata,
3812 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003813 args.prop_from_file,
3814 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003815 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003816 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003817 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003818 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003819 args.internal_release_string,
3820 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003821 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04003822 args.do_not_append_vbmeta_image,
3823 args.print_required_libavb_version)
David Zeuthend247fcb2017-02-16 12:09:27 -05003824
David Zeuthen21e95262016-07-27 17:58:40 -04003825 def erase_footer(self, args):
3826 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003827 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003828
David Zeuthen2bc232b2017-04-19 14:25:19 -04003829 def resize_image(self, args):
3830 """Implements the 'resize_image' sub-command."""
3831 self.avb.resize_image(args.image.name, args.partition_size)
3832
David Zeuthen8b6973b2016-09-20 12:39:49 -04003833 def set_ab_metadata(self, args):
3834 """Implements the 'set_ab_metadata' sub-command."""
3835 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3836
David Zeuthen21e95262016-07-27 17:58:40 -04003837 def info_image(self, args):
3838 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003839 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003840
David Zeuthenb623d8b2017-04-04 16:05:53 -04003841 def verify_image(self, args):
3842 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003843 self.avb.verify_image(args.image.name, args.key,
3844 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04003845
Darren Krahn147b08d2016-12-20 16:38:29 -08003846 def make_atx_certificate(self, args):
3847 """Implements the 'make_atx_certificate' sub-command."""
3848 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003849 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003850 args.subject_key_version,
3851 args.subject.read(),
3852 args.subject_is_intermediate_authority,
3853 args.signing_helper)
3854
3855 def make_atx_permanent_attributes(self, args):
3856 """Implements the 'make_atx_permanent_attributes' sub-command."""
3857 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003858 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003859 args.product_id.read())
3860
3861 def make_atx_metadata(self, args):
3862 """Implements the 'make_atx_metadata' sub-command."""
3863 self.avb.make_atx_metadata(args.output,
3864 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003865 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003866
David Zeuthen21e95262016-07-27 17:58:40 -04003867
3868if __name__ == '__main__':
3869 tool = AvbTool()
3870 tool.run(sys.argv)