blob: bc121bc7f76a97c7ec53ed8f95773ff234abc857 [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
1064
1065class AvbPropertyDescriptor(AvbDescriptor):
1066 """A class for property descriptors.
1067
1068 See the |AvbPropertyDescriptor| C struct for more information.
1069
1070 Attributes:
1071 key: The key.
1072 value: The key.
1073 """
1074
1075 TAG = 0
1076 SIZE = 32
1077 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1078 'Q' # key size (bytes)
1079 'Q') # value size (bytes)
1080
1081 def __init__(self, data=None):
1082 """Initializes a new property descriptor.
1083
1084 Arguments:
1085 data: If not None, must be a bytearray of size |SIZE|.
1086
1087 Raises:
1088 LookupError: If the given descriptor is malformed.
1089 """
1090 AvbDescriptor.__init__(self, None)
1091 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1092
1093 if data:
1094 (tag, num_bytes_following, key_size,
1095 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1096 expected_size = round_to_multiple(
1097 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1098 if tag != self.TAG or num_bytes_following != expected_size:
1099 raise LookupError('Given data does not look like a property '
1100 'descriptor.')
1101 self.key = data[self.SIZE:(self.SIZE + key_size)]
1102 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1103 value_size)]
1104 else:
1105 self.key = ''
1106 self.value = ''
1107
1108 def print_desc(self, o):
1109 """Print the descriptor.
1110
1111 Arguments:
1112 o: The object to write the output to.
1113 """
1114 if len(self.value) < 256:
1115 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1116 else:
1117 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1118
1119 def encode(self):
1120 """Serializes the descriptor.
1121
1122 Returns:
1123 A bytearray() with the descriptor data.
1124 """
1125 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1126 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1127 padding_size = nbf_with_padding - num_bytes_following
1128 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1129 len(self.key), len(self.value))
1130 padding = struct.pack(str(padding_size) + 'x')
1131 ret = desc + self.key + '\0' + self.value + '\0' + padding
1132 return bytearray(ret)
1133
1134
1135class AvbHashtreeDescriptor(AvbDescriptor):
1136 """A class for hashtree descriptors.
1137
1138 See the |AvbHashtreeDescriptor| C struct for more information.
1139
1140 Attributes:
1141 dm_verity_version: dm-verity version used.
1142 image_size: Size of the image, after rounding up to |block_size|.
1143 tree_offset: Offset of the hash tree in the file.
1144 tree_size: Size of the tree.
1145 data_block_size: Data block size
1146 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001147 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1148 fec_offset: Offset of FEC data (0 if FEC is not used).
1149 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001150 hash_algorithm: Hash algorithm used.
1151 partition_name: Partition name.
1152 salt: Salt used.
1153 root_digest: Root digest.
1154 """
1155
1156 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001157 RESERVED = 64
1158 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001159 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1160 'L' # dm-verity version used
1161 'Q' # image size (bytes)
1162 'Q' # tree offset (bytes)
1163 'Q' # tree size (bytes)
1164 'L' # data block size (bytes)
1165 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001166 'L' # FEC number of roots
1167 'Q' # FEC offset (bytes)
1168 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001169 '32s' # hash algorithm used
1170 'L' # partition name (bytes)
1171 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 'L' + # root digest length (bytes)
1173 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001174
1175 def __init__(self, data=None):
1176 """Initializes a new hashtree descriptor.
1177
1178 Arguments:
1179 data: If not None, must be a bytearray of size |SIZE|.
1180
1181 Raises:
1182 LookupError: If the given descriptor is malformed.
1183 """
1184 AvbDescriptor.__init__(self, None)
1185 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1186
1187 if data:
1188 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1189 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001190 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1191 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001192 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1193 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001194 expected_size = round_to_multiple(
1195 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1196 if tag != self.TAG or num_bytes_following != expected_size:
1197 raise LookupError('Given data does not look like a hashtree '
1198 'descriptor.')
1199 # Nuke NUL-bytes at the end.
1200 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1201 o = 0
1202 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1203 partition_name_len)])
1204 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1205 self.partition_name.decode('utf-8')
1206 o += partition_name_len
1207 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1208 o += salt_len
1209 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1210 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1211 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1212
1213 else:
1214 self.dm_verity_version = 0
1215 self.image_size = 0
1216 self.tree_offset = 0
1217 self.tree_size = 0
1218 self.data_block_size = 0
1219 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001220 self.fec_num_roots = 0
1221 self.fec_offset = 0
1222 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001223 self.hash_algorithm = ''
1224 self.partition_name = ''
1225 self.salt = bytearray()
1226 self.root_digest = bytearray()
1227
1228 def print_desc(self, o):
1229 """Print the descriptor.
1230
1231 Arguments:
1232 o: The object to write the output to.
1233 """
1234 o.write(' Hashtree descriptor:\n')
1235 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1236 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1237 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1238 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1239 o.write(' Data Block Size: {} bytes\n'.format(
1240 self.data_block_size))
1241 o.write(' Hash Block Size: {} bytes\n'.format(
1242 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001243 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1244 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1245 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001246 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1247 o.write(' Partition Name: {}\n'.format(self.partition_name))
1248 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1249 'hex')))
1250 o.write(' Root Digest: {}\n'.format(str(
1251 self.root_digest).encode('hex')))
1252
1253 def encode(self):
1254 """Serializes the descriptor.
1255
1256 Returns:
1257 A bytearray() with the descriptor data.
1258 """
1259 encoded_name = self.partition_name.encode('utf-8')
1260 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1261 len(self.root_digest) - 16)
1262 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1263 padding_size = nbf_with_padding - num_bytes_following
1264 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1265 self.dm_verity_version, self.image_size,
1266 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001267 self.hash_block_size, self.fec_num_roots,
1268 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001269 len(encoded_name), len(self.salt), len(self.root_digest),
1270 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001271 padding = struct.pack(str(padding_size) + 'x')
1272 ret = desc + encoded_name + self.salt + self.root_digest + padding
1273 return bytearray(ret)
1274
1275
1276class AvbHashDescriptor(AvbDescriptor):
1277 """A class for hash descriptors.
1278
1279 See the |AvbHashDescriptor| C struct for more information.
1280
1281 Attributes:
1282 image_size: Image size, in bytes.
1283 hash_algorithm: Hash algorithm used.
1284 partition_name: Partition name.
1285 salt: Salt used.
1286 digest: The hash value of salt and data combined.
1287 """
1288
1289 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001290 RESERVED = 64
1291 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001292 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1293 'Q' # image size (bytes)
1294 '32s' # hash algorithm used
1295 'L' # partition name (bytes)
1296 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001297 'L' + # digest length (bytes)
1298 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001299
1300 def __init__(self, data=None):
1301 """Initializes a new hash descriptor.
1302
1303 Arguments:
1304 data: If not None, must be a bytearray of size |SIZE|.
1305
1306 Raises:
1307 LookupError: If the given descriptor is malformed.
1308 """
1309 AvbDescriptor.__init__(self, None)
1310 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1311
1312 if data:
1313 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1314 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001315 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001316 expected_size = round_to_multiple(
1317 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1318 if tag != self.TAG or num_bytes_following != expected_size:
1319 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1320 # Nuke NUL-bytes at the end.
1321 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1322 o = 0
1323 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1324 partition_name_len)])
1325 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1326 self.partition_name.decode('utf-8')
1327 o += partition_name_len
1328 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1329 o += salt_len
1330 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1331 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1332 raise LookupError('digest_len doesn\'t match hash algorithm')
1333
1334 else:
1335 self.image_size = 0
1336 self.hash_algorithm = ''
1337 self.partition_name = ''
1338 self.salt = bytearray()
1339 self.digest = bytearray()
1340
1341 def print_desc(self, o):
1342 """Print the descriptor.
1343
1344 Arguments:
1345 o: The object to write the output to.
1346 """
1347 o.write(' Hash descriptor:\n')
1348 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1349 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1350 o.write(' Partition Name: {}\n'.format(self.partition_name))
1351 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1352 'hex')))
1353 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1354 'hex')))
1355
1356 def encode(self):
1357 """Serializes the descriptor.
1358
1359 Returns:
1360 A bytearray() with the descriptor data.
1361 """
1362 encoded_name = self.partition_name.encode('utf-8')
1363 num_bytes_following = (
1364 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1365 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1366 padding_size = nbf_with_padding - num_bytes_following
1367 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1368 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001369 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001370 padding = struct.pack(str(padding_size) + 'x')
1371 ret = desc + encoded_name + self.salt + self.digest + padding
1372 return bytearray(ret)
1373
1374
1375class AvbKernelCmdlineDescriptor(AvbDescriptor):
1376 """A class for kernel command-line descriptors.
1377
1378 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1379
1380 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001381 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001382 kernel_cmdline: The kernel command-line.
1383 """
1384
1385 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001386 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001387 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001388 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001389 'L') # cmdline length (bytes)
1390
David Zeuthenfd41eb92016-11-17 12:24:47 -05001391 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1392 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1393
David Zeuthen21e95262016-07-27 17:58:40 -04001394 def __init__(self, data=None):
1395 """Initializes a new kernel cmdline descriptor.
1396
1397 Arguments:
1398 data: If not None, must be a bytearray of size |SIZE|.
1399
1400 Raises:
1401 LookupError: If the given descriptor is malformed.
1402 """
1403 AvbDescriptor.__init__(self, None)
1404 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1405
1406 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001407 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001408 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1409 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1410 8)
1411 if tag != self.TAG or num_bytes_following != expected_size:
1412 raise LookupError('Given data does not look like a kernel cmdline '
1413 'descriptor.')
1414 # Nuke NUL-bytes at the end.
1415 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1416 kernel_cmdline_length)])
1417 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1418 self.kernel_cmdline.decode('utf-8')
1419 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001420 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001421 self.kernel_cmdline = ''
1422
1423 def print_desc(self, o):
1424 """Print the descriptor.
1425
1426 Arguments:
1427 o: The object to write the output to.
1428 """
1429 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001430 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001431 o.write(' Kernel Cmdline: {}\n'.format(repr(
1432 self.kernel_cmdline)))
1433
1434 def encode(self):
1435 """Serializes the descriptor.
1436
1437 Returns:
1438 A bytearray() with the descriptor data.
1439 """
1440 encoded_str = self.kernel_cmdline.encode('utf-8')
1441 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1442 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1443 padding_size = nbf_with_padding - num_bytes_following
1444 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001445 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001446 padding = struct.pack(str(padding_size) + 'x')
1447 ret = desc + encoded_str + padding
1448 return bytearray(ret)
1449
1450
1451class AvbChainPartitionDescriptor(AvbDescriptor):
1452 """A class for chained partition descriptors.
1453
1454 See the |AvbChainPartitionDescriptor| C struct for more information.
1455
1456 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001457 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001458 partition_name: Partition name.
1459 public_key: Bytes for the public key.
1460 """
1461
1462 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001463 RESERVED = 64
1464 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001465 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001466 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001467 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001468 'L' + # public_key_size (bytes)
1469 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001470
1471 def __init__(self, data=None):
1472 """Initializes a new chain partition descriptor.
1473
1474 Arguments:
1475 data: If not None, must be a bytearray of size |SIZE|.
1476
1477 Raises:
1478 LookupError: If the given descriptor is malformed.
1479 """
1480 AvbDescriptor.__init__(self, None)
1481 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1482
1483 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001484 (tag, num_bytes_following, self.rollback_index_location,
1485 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001486 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001487 expected_size = round_to_multiple(
1488 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1489 if tag != self.TAG or num_bytes_following != expected_size:
1490 raise LookupError('Given data does not look like a chain partition '
1491 'descriptor.')
1492 o = 0
1493 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1494 partition_name_len)])
1495 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1496 self.partition_name.decode('utf-8')
1497 o += partition_name_len
1498 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1499
1500 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001501 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001502 self.partition_name = ''
1503 self.public_key = bytearray()
1504
1505 def print_desc(self, o):
1506 """Print the descriptor.
1507
1508 Arguments:
1509 o: The object to write the output to.
1510 """
1511 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001512 o.write(' Partition Name: {}\n'.format(self.partition_name))
1513 o.write(' Rollback Index Location: {}\n'.format(
1514 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001515 # Just show the SHA1 of the key, for size reasons.
1516 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001517 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001518
1519 def encode(self):
1520 """Serializes the descriptor.
1521
1522 Returns:
1523 A bytearray() with the descriptor data.
1524 """
1525 encoded_name = self.partition_name.encode('utf-8')
1526 num_bytes_following = (
1527 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1528 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1529 padding_size = nbf_with_padding - num_bytes_following
1530 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001531 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001532 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001533 padding = struct.pack(str(padding_size) + 'x')
1534 ret = desc + encoded_name + self.public_key + padding
1535 return bytearray(ret)
1536
1537
1538DESCRIPTOR_CLASSES = [
1539 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1540 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1541]
1542
1543
1544def parse_descriptors(data):
1545 """Parses a blob of data into descriptors.
1546
1547 Arguments:
1548 data: A bytearray() with encoded descriptors.
1549
1550 Returns:
1551 A list of instances of objects derived from AvbDescriptor. For
1552 unknown descriptors, the class AvbDescriptor is used.
1553 """
1554 o = 0
1555 ret = []
1556 while o < len(data):
1557 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1558 if tag < len(DESCRIPTOR_CLASSES):
1559 c = DESCRIPTOR_CLASSES[tag]
1560 else:
1561 c = AvbDescriptor
1562 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1563 o += 16 + nb_following
1564 return ret
1565
1566
1567class AvbFooter(object):
1568 """A class for parsing and writing footers.
1569
1570 Footers are stored at the end of partitions and point to where the
1571 AvbVBMeta blob is located. They also contain the original size of
1572 the image before AVB information was added.
1573
1574 Attributes:
1575 magic: Magic for identifying the footer, see |MAGIC|.
1576 version_major: The major version of avbtool that wrote the footer.
1577 version_minor: The minor version of avbtool that wrote the footer.
1578 original_image_size: Original image size.
1579 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1580 vbmeta_size: Size of the AvbVBMeta blob.
1581 """
1582
1583 MAGIC = 'AVBf'
1584 SIZE = 64
1585 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001586 FOOTER_VERSION_MAJOR = 1
1587 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001588 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1589 'Q' # Original image size.
1590 'Q' # Offset of VBMeta blob.
1591 'Q' + # Size of VBMeta blob.
1592 str(RESERVED) + 'x') # padding for reserved bytes
1593
1594 def __init__(self, data=None):
1595 """Initializes a new footer object.
1596
1597 Arguments:
1598 data: If not None, must be a bytearray of size 4096.
1599
1600 Raises:
1601 LookupError: If the given footer is malformed.
1602 struct.error: If the given data has no footer.
1603 """
1604 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1605
1606 if data:
1607 (self.magic, self.version_major, self.version_minor,
1608 self.original_image_size, self.vbmeta_offset,
1609 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1610 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001611 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001612 else:
1613 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001614 self.version_major = self.FOOTER_VERSION_MAJOR
1615 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001616 self.original_image_size = 0
1617 self.vbmeta_offset = 0
1618 self.vbmeta_size = 0
1619
David Zeuthena4fee8b2016-08-22 15:20:43 -04001620 def encode(self):
1621 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001622
David Zeuthena4fee8b2016-08-22 15:20:43 -04001623 Returns:
1624 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001625 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001626 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1627 self.version_minor, self.original_image_size,
1628 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001629
1630
1631class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001632 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001633
1634 Attributes:
1635 The attributes correspond to the |AvbVBMetaHeader| struct
1636 defined in avb_vbmeta_header.h.
1637 """
1638
1639 SIZE = 256
1640
David Zeuthene3cadca2017-02-22 21:25:46 -05001641 # Keep in sync with |reserved0| and |reserved| field of
1642 # |AvbVBMetaImageHeader|.
1643 RESERVED0 = 4
1644 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646 # Keep in sync with |AvbVBMetaImageHeader|.
1647 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1648 '2Q' # 2 x block size
1649 'L' # algorithm type
1650 '2Q' # offset, size (hash)
1651 '2Q' # offset, size (signature)
1652 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001653 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001654 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001655 'Q' # rollback_index
1656 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001657 str(RESERVED0) + 'x' + # padding for reserved bytes
1658 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001659 str(RESERVED) + 'x') # padding for reserved bytes
1660
1661 def __init__(self, data=None):
1662 """Initializes a new header object.
1663
1664 Arguments:
1665 data: If not None, must be a bytearray of size 8192.
1666
1667 Raises:
1668 Exception: If the given data is malformed.
1669 """
1670 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1671
1672 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001673 (self.magic, self.required_libavb_version_major,
1674 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001675 self.authentication_data_block_size, self.auxiliary_data_block_size,
1676 self.algorithm_type, self.hash_offset, self.hash_size,
1677 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001678 self.public_key_size, self.public_key_metadata_offset,
1679 self.public_key_metadata_size, self.descriptors_offset,
1680 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001681 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001682 self.flags,
1683 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001684 # Nuke NUL-bytes at the end of the string.
1685 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001686 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001687 else:
1688 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001689 # Start by just requiring version 1.0. Code that adds features
1690 # in a future version can use bump_required_libavb_version_minor() to
1691 # bump the minor.
1692 self.required_libavb_version_major = AVB_VERSION_MAJOR
1693 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.authentication_data_block_size = 0
1695 self.auxiliary_data_block_size = 0
1696 self.algorithm_type = 0
1697 self.hash_offset = 0
1698 self.hash_size = 0
1699 self.signature_offset = 0
1700 self.signature_size = 0
1701 self.public_key_offset = 0
1702 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001703 self.public_key_metadata_offset = 0
1704 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001705 self.descriptors_offset = 0
1706 self.descriptors_size = 0
1707 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001708 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001709 self.release_string = get_release_string()
1710
1711 def bump_required_libavb_version_minor(self, minor):
1712 """Function to bump required_libavb_version_minor.
1713
1714 Call this when writing data that requires a specific libavb
1715 version to parse it.
1716
1717 Arguments:
1718 minor: The minor version of libavb that has support for the feature.
1719 """
1720 self.required_libavb_version_minor = (
1721 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001722
1723 def save(self, output):
1724 """Serializes the header (256 bytes) to disk.
1725
1726 Arguments:
1727 output: The object to write the output to.
1728 """
1729 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001730 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1731 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001732 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1733 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001734 self.public_key_offset, self.public_key_size,
1735 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001736 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001737 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001738
1739 def encode(self):
1740 """Serializes the header (256) to a bytearray().
1741
1742 Returns:
1743 A bytearray() with the encoded header.
1744 """
1745 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001746 self.required_libavb_version_major,
1747 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001748 self.authentication_data_block_size,
1749 self.auxiliary_data_block_size, self.algorithm_type,
1750 self.hash_offset, self.hash_size, self.signature_offset,
1751 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001752 self.public_key_size, self.public_key_metadata_offset,
1753 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001754 self.descriptors_size, self.rollback_index, self.flags,
1755 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001756
1757
1758class Avb(object):
1759 """Business logic for avbtool command-line tool."""
1760
David Zeuthen8b6973b2016-09-20 12:39:49 -04001761 # Keep in sync with avb_ab_flow.h.
1762 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1763 AB_MAGIC = '\0AB0'
1764 AB_MAJOR_VERSION = 1
1765 AB_MINOR_VERSION = 0
1766 AB_MISC_METADATA_OFFSET = 2048
1767
David Zeuthen09692692016-09-30 16:16:40 -04001768 # Constants for maximum metadata size. These are used to give
1769 # meaningful errors if the value passed in via --partition_size is
1770 # too small and when --calc_max_image_size is used. We use
1771 # conservative figures.
1772 MAX_VBMETA_SIZE = 64 * 1024
1773 MAX_FOOTER_SIZE = 4096
1774
David Zeuthena4fee8b2016-08-22 15:20:43 -04001775 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001776 """Implements the 'erase_footer' command.
1777
1778 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001779 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001780 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001781
1782 Raises:
1783 AvbError: If there's no footer in the image.
1784 """
1785
David Zeuthena4fee8b2016-08-22 15:20:43 -04001786 image = ImageHandler(image_filename)
1787
David Zeuthen21e95262016-07-27 17:58:40 -04001788 (footer, _, descriptors, _) = self._parse_image(image)
1789
1790 if not footer:
1791 raise AvbError('Given image does not have a footer.')
1792
1793 new_image_size = None
1794 if not keep_hashtree:
1795 new_image_size = footer.original_image_size
1796 else:
1797 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001798 # descriptor to figure out the location and size of the hashtree
1799 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001800 for desc in descriptors:
1801 if isinstance(desc, AvbHashtreeDescriptor):
1802 # The hashtree is always just following the main data so the
1803 # new size is easily derived.
1804 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001805 # If the image has FEC codes, also keep those.
1806 if desc.fec_offset > 0:
1807 fec_end = desc.fec_offset + desc.fec_size
1808 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001809 break
1810 if not new_image_size:
1811 raise AvbError('Requested to keep hashtree but no hashtree '
1812 'descriptor was found.')
1813
1814 # And cut...
1815 image.truncate(new_image_size)
1816
David Zeuthen2bc232b2017-04-19 14:25:19 -04001817 def resize_image(self, image_filename, partition_size):
1818 """Implements the 'resize_image' command.
1819
1820 Arguments:
1821 image_filename: File with footer to resize.
1822 partition_size: The new size of the image.
1823
1824 Raises:
1825 AvbError: If there's no footer in the image.
1826 """
1827
1828 image = ImageHandler(image_filename)
1829
1830 if partition_size % image.block_size != 0:
1831 raise AvbError('Partition size of {} is not a multiple of the image '
1832 'block size {}.'.format(partition_size,
1833 image.block_size))
1834
1835 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
1836
1837 if not footer:
1838 raise AvbError('Given image does not have a footer.')
1839
1840 # The vbmeta blob is always at the end of the data so resizing an
1841 # image amounts to just moving the footer around.
1842
1843 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
1844 if vbmeta_end_offset % image.block_size != 0:
1845 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
1846
1847 if partition_size < vbmeta_end_offset + 1*image.block_size:
1848 raise AvbError('Requested size of {} is too small for an image '
1849 'of size {}.'
1850 .format(partition_size,
1851 vbmeta_end_offset + 1*image.block_size))
1852
1853 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
1854 # with enough bytes such that the final Footer block is at the end
1855 # of partition_size.
1856 image.truncate(vbmeta_end_offset)
1857 image.append_dont_care(partition_size - vbmeta_end_offset -
1858 1*image.block_size)
1859
1860 # Just reuse the same footer - only difference is that we're
1861 # writing it in a different place.
1862 footer_blob = footer.encode()
1863 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
1864 footer_blob)
1865 image.append_raw(footer_blob_with_padding)
1866
David Zeuthen8b6973b2016-09-20 12:39:49 -04001867 def set_ab_metadata(self, misc_image, slot_data):
1868 """Implements the 'set_ab_metadata' command.
1869
1870 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1871 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1872
1873 Arguments:
1874 misc_image: The misc image to write to.
1875 slot_data: Slot data as a string
1876
1877 Raises:
1878 AvbError: If slot data is malformed.
1879 """
1880 tokens = slot_data.split(':')
1881 if len(tokens) != 6:
1882 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1883 a_priority = int(tokens[0])
1884 a_tries_remaining = int(tokens[1])
1885 a_success = True if int(tokens[2]) != 0 else False
1886 b_priority = int(tokens[3])
1887 b_tries_remaining = int(tokens[4])
1888 b_success = True if int(tokens[5]) != 0 else False
1889
1890 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1891 self.AB_MAGIC,
1892 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1893 a_priority, a_tries_remaining, a_success,
1894 b_priority, b_tries_remaining, b_success)
1895 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1896 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1897 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1898 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1899 misc_image.write(ab_data)
1900
David Zeuthena4fee8b2016-08-22 15:20:43 -04001901 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001902 """Implements the 'info_image' command.
1903
1904 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001905 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001906 output: Output file to write human-readable information to (file object).
1907 """
1908
David Zeuthena4fee8b2016-08-22 15:20:43 -04001909 image = ImageHandler(image_filename)
1910
David Zeuthen21e95262016-07-27 17:58:40 -04001911 o = output
1912
1913 (footer, header, descriptors, image_size) = self._parse_image(image)
1914
1915 if footer:
1916 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1917 footer.version_minor))
1918 o.write('Image size: {} bytes\n'.format(image_size))
1919 o.write('Original image size: {} bytes\n'.format(
1920 footer.original_image_size))
1921 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1922 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1923 o.write('--\n')
1924
1925 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1926
David Zeuthene3cadca2017-02-22 21:25:46 -05001927 o.write('Minimum libavb version: {}.{}{}\n'.format(
1928 header.required_libavb_version_major,
1929 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001930 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001931 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1932 o.write('Authentication Block: {} bytes\n'.format(
1933 header.authentication_data_block_size))
1934 o.write('Auxiliary Block: {} bytes\n'.format(
1935 header.auxiliary_data_block_size))
1936 o.write('Algorithm: {}\n'.format(alg_name))
1937 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001938 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001939 o.write('Release String: \'{}\'\n'.format(
1940 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001941
1942 # Print descriptors.
1943 num_printed = 0
1944 o.write('Descriptors:\n')
1945 for desc in descriptors:
1946 desc.print_desc(o)
1947 num_printed += 1
1948 if num_printed == 0:
1949 o.write(' (none)\n')
1950
David Zeuthenb623d8b2017-04-04 16:05:53 -04001951 def verify_image(self, image_filename):
1952 """Implements the 'verify_image' command.
1953
1954 Arguments:
1955 image_filename: Image file to get information from (file object).
1956 """
1957
1958 image = ImageHandler(image_filename)
1959 (footer, header, descriptors, image_size) = self._parse_image(image)
1960 offset = 0
1961 if footer:
1962 offset = footer.vbmeta_offset
1963 size = (header.SIZE + header.authentication_data_block_size +
1964 header.auxiliary_data_block_size)
1965 image.seek(offset)
1966 vbmeta_blob = image.read(size)
1967 if not verify_vbmeta_signature(header, vbmeta_blob):
1968 raise AvbError('Signature check failed.')
1969
David Zeuthen21e95262016-07-27 17:58:40 -04001970 def _parse_image(self, image):
1971 """Gets information about an image.
1972
1973 The image can either be a vbmeta or an image with a footer.
1974
1975 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001976 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001977
1978 Returns:
1979 A tuple where the first argument is a AvbFooter (None if there
1980 is no footer on the image), the second argument is a
1981 AvbVBMetaHeader, the third argument is a list of
1982 AvbDescriptor-derived instances, and the fourth argument is the
1983 size of |image|.
1984 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001985 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001986 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001987 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001988 try:
1989 footer = AvbFooter(image.read(AvbFooter.SIZE))
1990 except (LookupError, struct.error):
1991 # Nope, just seek back to the start.
1992 image.seek(0)
1993
1994 vbmeta_offset = 0
1995 if footer:
1996 vbmeta_offset = footer.vbmeta_offset
1997
1998 image.seek(vbmeta_offset)
1999 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2000
2001 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2002 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2003 desc_start_offset = aux_block_offset + h.descriptors_offset
2004 image.seek(desc_start_offset)
2005 descriptors = parse_descriptors(image.read(h.descriptors_size))
2006
David Zeuthen09692692016-09-30 16:16:40 -04002007 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002008
David Zeuthenb1b994d2017-03-06 18:01:31 -05002009 def _load_vbmeta_blob(self, image):
2010 """Gets the vbmeta struct and associated sections.
2011
2012 The image can either be a vbmeta.img or an image with a footer.
2013
2014 Arguments:
2015 image: An ImageHandler (vbmeta or footer).
2016
2017 Returns:
2018 A blob with the vbmeta struct and other sections.
2019 """
2020 assert isinstance(image, ImageHandler)
2021 footer = None
2022 image.seek(image.image_size - AvbFooter.SIZE)
2023 try:
2024 footer = AvbFooter(image.read(AvbFooter.SIZE))
2025 except (LookupError, struct.error):
2026 # Nope, just seek back to the start.
2027 image.seek(0)
2028
2029 vbmeta_offset = 0
2030 if footer:
2031 vbmeta_offset = footer.vbmeta_offset
2032
2033 image.seek(vbmeta_offset)
2034 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2035
2036 image.seek(vbmeta_offset)
2037 data_size = AvbVBMetaHeader.SIZE
2038 data_size += h.authentication_data_block_size
2039 data_size += h.auxiliary_data_block_size
2040 return image.read(data_size)
2041
David Zeuthenfd41eb92016-11-17 12:24:47 -05002042 def _get_cmdline_descriptors_for_dm_verity(self, image):
2043 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002044
2045 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002046 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002047
2048 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002049 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2050 instructions. There is one for when hashtree is not disabled and one for
2051 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002052
2053 Raises:
2054 AvbError: If |image| doesn't have a hashtree descriptor.
2055
2056 """
2057
2058 (_, _, descriptors, _) = self._parse_image(image)
2059
2060 ht = None
2061 for desc in descriptors:
2062 if isinstance(desc, AvbHashtreeDescriptor):
2063 ht = desc
2064 break
2065
2066 if not ht:
2067 raise AvbError('No hashtree descriptor in given image')
2068
2069 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002070 c += '0' # start
2071 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2072 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2073 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2074 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2075 c += ' {}'.format(ht.data_block_size) # data_block
2076 c += ' {}'.format(ht.hash_block_size) # hash_block
2077 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2078 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2079 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2080 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2081 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2082 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002083 c += ' 10' # number of optional args
2084 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002085 c += ' ignore_zero_blocks'
2086 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2087 c += ' fec_roots {}'.format(ht.fec_num_roots)
2088 # Note that fec_blocks is the size that FEC covers, *not* the
2089 # size of the FEC data. Since we use FEC for everything up until
2090 # the FEC data, it's the same as the offset.
2091 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2092 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2093 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002094 c += ' 2' # number of optional args
2095 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002096 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002097 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002098
David Zeuthenfd41eb92016-11-17 12:24:47 -05002099 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002100 desc = AvbKernelCmdlineDescriptor()
2101 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002102 desc.flags = (
2103 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2104
2105 # The descriptor for when hashtree verification is disabled is a lot
2106 # simpler - we just set the root to the partition.
2107 desc_no_ht = AvbKernelCmdlineDescriptor()
2108 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2109 desc_no_ht.flags = (
2110 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2111
2112 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002113
2114 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002115 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002116 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002117 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002118 include_descriptors_from_image, signing_helper,
2119 release_string,
2120 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002121 """Implements the 'make_vbmeta_image' command.
2122
2123 Arguments:
2124 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002125 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002126 algorithm_name: Name of algorithm to use.
2127 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002128 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002129 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002130 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002131 props: Properties to insert (list of strings of the form 'key:value').
2132 props_from_file: Properties to insert (list of strings 'key:<path>').
2133 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002134 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002135 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002136 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002137 release_string: None or avbtool release string to use instead of default.
2138 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002139
2140 Raises:
2141 AvbError: If a chained partition is malformed.
2142 """
2143
2144 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04002145 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002146 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002147 chain_partitions, rollback_index, flags, props, props_from_file,
2148 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002149 include_descriptors_from_image, signing_helper, release_string,
2150 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002151
2152 # Write entire vbmeta blob (header, authentication, auxiliary).
2153 output.seek(0)
2154 output.write(vbmeta_blob)
2155
David Zeuthen18666ab2016-11-15 11:18:05 -05002156 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2157 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002158 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002159 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002160 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002161 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002162 include_descriptors_from_image, signing_helper,
2163 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002164 """Generates a VBMeta blob.
2165
2166 This blob contains the header (struct AvbVBMetaHeader), the
2167 authentication data block (which contains the hash and signature
2168 for the header and auxiliary block), and the auxiliary block
2169 (which contains descriptors, the public key used, and other data).
2170
2171 The |key| parameter can |None| only if the |algorithm_name| is
2172 'NONE'.
2173
2174 Arguments:
2175 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2176 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002177 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002178 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002179 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002180 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002181 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002182 props: Properties to insert (List of strings of the form 'key:value').
2183 props_from_file: Properties to insert (List of strings 'key:<path>').
2184 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002185 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002186 dm-verity kernel cmdline from.
2187 include_descriptors_from_image: List of file objects for which
2188 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002189 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002190 release_string: None or avbtool release string.
2191 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002192
2193 Returns:
2194 A bytearray() with the VBMeta blob.
2195
2196 Raises:
2197 Exception: If the |algorithm_name| is not found, if no key has
2198 been given and the given algorithm requires one, or the key is
2199 of the wrong size.
2200
2201 """
2202 try:
2203 alg = ALGORITHMS[algorithm_name]
2204 except KeyError:
2205 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2206
David Zeuthena5fd3a42017-02-27 16:38:54 -05002207 if not descriptors:
2208 descriptors = []
2209
2210 # Insert chained partition descriptors, if any
2211 if chain_partitions:
2212 for cp in chain_partitions:
2213 cp_tokens = cp.split(':')
2214 if len(cp_tokens) != 3:
2215 raise AvbError('Malformed chained partition "{}".'.format(cp))
2216 desc = AvbChainPartitionDescriptor()
2217 desc.partition_name = cp_tokens[0]
2218 desc.rollback_index_location = int(cp_tokens[1])
2219 if desc.rollback_index_location < 1:
2220 raise AvbError('Rollback index location must be 1 or larger.')
2221 file_path = cp_tokens[2]
2222 desc.public_key = open(file_path, 'rb').read()
2223 descriptors.append(desc)
2224
David Zeuthen21e95262016-07-27 17:58:40 -04002225 # Descriptors.
2226 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002227 for desc in descriptors:
2228 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002229
2230 # Add properties.
2231 if props:
2232 for prop in props:
2233 idx = prop.find(':')
2234 if idx == -1:
2235 raise AvbError('Malformed property "{}".'.format(prop))
2236 desc = AvbPropertyDescriptor()
2237 desc.key = prop[0:idx]
2238 desc.value = prop[(idx + 1):]
2239 encoded_descriptors.extend(desc.encode())
2240 if props_from_file:
2241 for prop in props_from_file:
2242 idx = prop.find(':')
2243 if idx == -1:
2244 raise AvbError('Malformed property "{}".'.format(prop))
2245 desc = AvbPropertyDescriptor()
2246 desc.key = prop[0:idx]
2247 desc.value = prop[(idx + 1):]
2248 file_path = prop[(idx + 1):]
2249 desc.value = open(file_path, 'rb').read()
2250 encoded_descriptors.extend(desc.encode())
2251
2252 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002253 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002254 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002255 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002256 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2257 encoded_descriptors.extend(cmdline_desc[0].encode())
2258 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002259
2260 # Add kernel command-lines.
2261 if kernel_cmdlines:
2262 for i in kernel_cmdlines:
2263 desc = AvbKernelCmdlineDescriptor()
2264 desc.kernel_cmdline = i
2265 encoded_descriptors.extend(desc.encode())
2266
2267 # Add descriptors from other images.
2268 if include_descriptors_from_image:
2269 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002270 image_handler = ImageHandler(image.name)
2271 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002272 for desc in image_descriptors:
2273 encoded_descriptors.extend(desc.encode())
2274
David Zeuthen18666ab2016-11-15 11:18:05 -05002275 # Load public key metadata blob, if requested.
2276 pkmd_blob = []
2277 if public_key_metadata_path:
2278 with open(public_key_metadata_path) as f:
2279 pkmd_blob = f.read()
2280
David Zeuthen21e95262016-07-27 17:58:40 -04002281 key = None
2282 encoded_key = bytearray()
2283 if alg.public_key_num_bytes > 0:
2284 if not key_path:
2285 raise AvbError('Key is required for algorithm {}'.format(
2286 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002287 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002288 if len(encoded_key) != alg.public_key_num_bytes:
2289 raise AvbError('Key is wrong size for algorithm {}'.format(
2290 algorithm_name))
2291
2292 h = AvbVBMetaHeader()
2293
David Zeuthene3cadca2017-02-22 21:25:46 -05002294 # Override release string, if requested.
2295 if isinstance(release_string, (str, unicode)):
2296 h.release_string = release_string
2297
2298 # Append to release string, if requested. Also insert a space before.
2299 if isinstance(append_to_release_string, (str, unicode)):
2300 h.release_string += ' ' + append_to_release_string
2301
David Zeuthen18666ab2016-11-15 11:18:05 -05002302 # For the Auxiliary data block, descriptors are stored at offset 0,
2303 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002304 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002305 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002306 h.descriptors_offset = 0
2307 h.descriptors_size = len(encoded_descriptors)
2308 h.public_key_offset = h.descriptors_size
2309 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002310 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2311 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002312
2313 # For the Authentication data block, the hash is first and then
2314 # the signature.
2315 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002316 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002317 h.algorithm_type = alg.algorithm_type
2318 h.hash_offset = 0
2319 h.hash_size = alg.hash_num_bytes
2320 # Signature offset and size - it's stored right after the hash
2321 # (in Authentication data block).
2322 h.signature_offset = alg.hash_num_bytes
2323 h.signature_size = alg.signature_num_bytes
2324
2325 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002326 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002327
2328 # Generate Header data block.
2329 header_data_blob = h.encode()
2330
2331 # Generate Auxiliary data block.
2332 aux_data_blob = bytearray()
2333 aux_data_blob.extend(encoded_descriptors)
2334 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002335 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002336 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2337 aux_data_blob.extend('\0' * padding_bytes)
2338
2339 # Calculate the hash.
2340 binary_hash = bytearray()
2341 binary_signature = bytearray()
2342 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002343 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002344 ha.update(header_data_blob)
2345 ha.update(aux_data_blob)
2346 binary_hash.extend(ha.digest())
2347
2348 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002349 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002350 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2351 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002352 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002353
2354 # Generate Authentication data block.
2355 auth_data_blob = bytearray()
2356 auth_data_blob.extend(binary_hash)
2357 auth_data_blob.extend(binary_signature)
2358 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2359 auth_data_blob.extend('\0' * padding_bytes)
2360
2361 return header_data_blob + auth_data_blob + aux_data_blob
2362
2363 def extract_public_key(self, key_path, output):
2364 """Implements the 'extract_public_key' command.
2365
2366 Arguments:
2367 key_path: The path to a RSA private key file.
2368 output: The file to write to.
2369 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002370 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002371
David Zeuthenb1b994d2017-03-06 18:01:31 -05002372 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2373 partition_size):
2374 """Implementation of the append_vbmeta_image command.
2375
2376 Arguments:
2377 image_filename: File to add the footer to.
2378 vbmeta_image_filename: File to get vbmeta struct from.
2379 partition_size: Size of partition.
2380
2381 Raises:
2382 AvbError: If an argument is incorrect.
2383 """
2384 image = ImageHandler(image_filename)
2385
2386 if partition_size % image.block_size != 0:
2387 raise AvbError('Partition size of {} is not a multiple of the image '
2388 'block size {}.'.format(partition_size,
2389 image.block_size))
2390
2391 # If there's already a footer, truncate the image to its original
2392 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2393 image.seek(image.image_size - AvbFooter.SIZE)
2394 try:
2395 footer = AvbFooter(image.read(AvbFooter.SIZE))
2396 # Existing footer found. Just truncate.
2397 original_image_size = footer.original_image_size
2398 image.truncate(footer.original_image_size)
2399 except (LookupError, struct.error):
2400 original_image_size = image.image_size
2401
2402 # If anything goes wrong from here-on, restore the image back to
2403 # its original size.
2404 try:
2405 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2406 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2407
2408 # If the image isn't sparse, its size might not be a multiple of
2409 # the block size. This will screw up padding later so just grow it.
2410 if image.image_size % image.block_size != 0:
2411 assert not image.is_sparse
2412 padding_needed = image.block_size - (image.image_size%image.block_size)
2413 image.truncate(image.image_size + padding_needed)
2414
2415 # The append_raw() method requires content with size being a
2416 # multiple of |block_size| so add padding as needed. Also record
2417 # where this is written to since we'll need to put that in the
2418 # footer.
2419 vbmeta_offset = image.image_size
2420 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2421 len(vbmeta_blob))
2422 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2423
2424 # Append vbmeta blob and footer
2425 image.append_raw(vbmeta_blob_with_padding)
2426 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2427
2428 # Now insert a DONT_CARE chunk with enough bytes such that the
2429 # final Footer block is at the end of partition_size..
2430 image.append_dont_care(partition_size - vbmeta_end_offset -
2431 1*image.block_size)
2432
2433 # Generate the Footer that tells where the VBMeta footer
2434 # is. Also put enough padding in the front of the footer since
2435 # we'll write out an entire block.
2436 footer = AvbFooter()
2437 footer.original_image_size = original_image_size
2438 footer.vbmeta_offset = vbmeta_offset
2439 footer.vbmeta_size = len(vbmeta_blob)
2440 footer_blob = footer.encode()
2441 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2442 footer_blob)
2443 image.append_raw(footer_blob_with_padding)
2444
2445 except:
2446 # Truncate back to original size, then re-raise
2447 image.truncate(original_image_size)
2448 raise
2449
David Zeuthena4fee8b2016-08-22 15:20:43 -04002450 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002451 hash_algorithm, salt, chain_partitions, algorithm_name,
2452 key_path,
2453 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002454 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002455 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002456 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002457 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002458 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002459 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002460
2461 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002462 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002463 partition_size: Size of partition.
2464 partition_name: Name of partition (without A/B suffix).
2465 hash_algorithm: Hash algorithm to use.
2466 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002467 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002468 algorithm_name: Name of algorithm to use.
2469 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002470 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002471 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002472 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002473 props: Properties to insert (List of strings of the form 'key:value').
2474 props_from_file: Properties to insert (List of strings 'key:<path>').
2475 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002476 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002477 dm-verity kernel cmdline from.
2478 include_descriptors_from_image: List of file objects for which
2479 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002480 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002481 release_string: None or avbtool release string.
2482 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002483 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2484 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002485
2486 Raises:
2487 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002488 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002489 image = ImageHandler(image_filename)
2490
2491 if partition_size % image.block_size != 0:
2492 raise AvbError('Partition size of {} is not a multiple of the image '
2493 'block size {}.'.format(partition_size,
2494 image.block_size))
2495
David Zeuthen21e95262016-07-27 17:58:40 -04002496 # If there's already a footer, truncate the image to its original
2497 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2498 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002499 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002500 try:
2501 footer = AvbFooter(image.read(AvbFooter.SIZE))
2502 # Existing footer found. Just truncate.
2503 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002504 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002505 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002506 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002507
2508 # If anything goes wrong from here-on, restore the image back to
2509 # its original size.
2510 try:
David Zeuthen09692692016-09-30 16:16:40 -04002511 # First, calculate the maximum image size such that an image
2512 # this size + metadata (footer + vbmeta struct) fits in
2513 # |partition_size|.
2514 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2515 max_image_size = partition_size - max_metadata_size
2516
2517 # If image size exceeds the maximum image size, fail.
2518 if image.image_size > max_image_size:
2519 raise AvbError('Image size of {} exceeds maximum image '
2520 'size of {} in order to fit in a partition '
2521 'size of {}.'.format(image.image_size, max_image_size,
2522 partition_size))
2523
David Zeuthen21e95262016-07-27 17:58:40 -04002524 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2525 if salt:
2526 salt = salt.decode('hex')
2527 else:
2528 if salt is None:
2529 # If salt is not explicitly specified, choose a hash
2530 # that's the same size as the hash size.
2531 hash_size = digest_size
2532 salt = open('/dev/urandom').read(hash_size)
2533 else:
2534 salt = ''
2535
2536 hasher = hashlib.new(name=hash_algorithm, string=salt)
2537 # TODO(zeuthen): might want to read this in chunks to avoid
2538 # memory pressure, then again, this is only supposed to be used
2539 # on kernel/initramfs partitions. Possible optimization.
2540 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002541 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002542 digest = hasher.digest()
2543
2544 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002545 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002546 h_desc.hash_algorithm = hash_algorithm
2547 h_desc.partition_name = partition_name
2548 h_desc.salt = salt
2549 h_desc.digest = digest
2550
2551 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002552 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002553 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002554 chain_partitions, rollback_index, flags, props, props_from_file,
2555 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002556 include_descriptors_from_image, signing_helper, release_string,
2557 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002558
David Zeuthena4fee8b2016-08-22 15:20:43 -04002559 # If the image isn't sparse, its size might not be a multiple of
2560 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002561 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002562 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002563 padding_needed = image.block_size - (image.image_size%image.block_size)
2564 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002565
David Zeuthena4fee8b2016-08-22 15:20:43 -04002566 # The append_raw() method requires content with size being a
2567 # multiple of |block_size| so add padding as needed. Also record
2568 # where this is written to since we'll need to put that in the
2569 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002570 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002571 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2572 len(vbmeta_blob))
2573 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002574
David Zeuthend247fcb2017-02-16 12:09:27 -05002575 # Write vbmeta blob, if requested.
2576 if output_vbmeta_image:
2577 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002578
David Zeuthend247fcb2017-02-16 12:09:27 -05002579 # Append vbmeta blob and footer, unless requested not to.
2580 if not do_not_append_vbmeta_image:
2581 image.append_raw(vbmeta_blob_with_padding)
2582 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2583
2584 # Now insert a DONT_CARE chunk with enough bytes such that the
2585 # final Footer block is at the end of partition_size..
2586 image.append_dont_care(partition_size - vbmeta_end_offset -
2587 1*image.block_size)
2588
2589 # Generate the Footer that tells where the VBMeta footer
2590 # is. Also put enough padding in the front of the footer since
2591 # we'll write out an entire block.
2592 footer = AvbFooter()
2593 footer.original_image_size = original_image_size
2594 footer.vbmeta_offset = vbmeta_offset
2595 footer.vbmeta_size = len(vbmeta_blob)
2596 footer_blob = footer.encode()
2597 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2598 footer_blob)
2599 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002600
David Zeuthen21e95262016-07-27 17:58:40 -04002601 except:
2602 # Truncate back to original size, then re-raise
2603 image.truncate(original_image_size)
2604 raise
2605
David Zeuthena4fee8b2016-08-22 15:20:43 -04002606 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002607 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002608 block_size, salt, chain_partitions, algorithm_name,
2609 key_path,
2610 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002611 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002612 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002613 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002614 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002615 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002616 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002617 """Implements the 'add_hashtree_footer' command.
2618
2619 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2620 more information about dm-verity and these hashes.
2621
2622 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002623 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002624 partition_size: Size of partition.
2625 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002626 generate_fec: If True, generate FEC codes.
2627 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002628 hash_algorithm: Hash algorithm to use.
2629 block_size: Block size to use.
2630 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002631 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002632 algorithm_name: Name of algorithm to use.
2633 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002634 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002635 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002636 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002637 props: Properties to insert (List of strings of the form 'key:value').
2638 props_from_file: Properties to insert (List of strings 'key:<path>').
2639 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002640 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002641 dm-verity kernel cmdline from.
2642 include_descriptors_from_image: List of file objects for which
2643 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002644 calc_max_image_size: Don't store the hashtree or footer - instead
2645 calculate the maximum image size leaving enough room for hashtree
2646 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002647 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002648 release_string: None or avbtool release string.
2649 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002650 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2651 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002652
2653 Raises:
2654 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002655 """
David Zeuthen09692692016-09-30 16:16:40 -04002656 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2657 digest_padding = round_to_pow2(digest_size) - digest_size
2658
2659 # First, calculate the maximum image size such that an image
2660 # this size + the hashtree + metadata (footer + vbmeta struct)
2661 # fits in |partition_size|. We use very conservative figures for
2662 # metadata.
2663 (_, max_tree_size) = calc_hash_level_offsets(
2664 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002665 max_fec_size = 0
2666 if generate_fec:
2667 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2668 max_metadata_size = (max_fec_size + max_tree_size +
2669 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002670 self.MAX_FOOTER_SIZE)
2671 max_image_size = partition_size - max_metadata_size
2672
2673 # If we're asked to only calculate the maximum image size, we're done.
2674 if calc_max_image_size:
2675 print '{}'.format(max_image_size)
2676 return
2677
David Zeuthena4fee8b2016-08-22 15:20:43 -04002678 image = ImageHandler(image_filename)
2679
2680 if partition_size % image.block_size != 0:
2681 raise AvbError('Partition size of {} is not a multiple of the image '
2682 'block size {}.'.format(partition_size,
2683 image.block_size))
2684
David Zeuthen21e95262016-07-27 17:58:40 -04002685 # If there's already a footer, truncate the image to its original
2686 # size. This way 'avbtool add_hashtree_footer' is idempotent
2687 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002688 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002689 try:
2690 footer = AvbFooter(image.read(AvbFooter.SIZE))
2691 # Existing footer found. Just truncate.
2692 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002693 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002694 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002695 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002696
2697 # If anything goes wrong from here-on, restore the image back to
2698 # its original size.
2699 try:
2700 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002701 rounded_image_size = round_to_multiple(image.image_size, block_size)
2702 if rounded_image_size > image.image_size:
2703 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002704
David Zeuthen09692692016-09-30 16:16:40 -04002705 # If image size exceeds the maximum image size, fail.
2706 if image.image_size > max_image_size:
2707 raise AvbError('Image size of {} exceeds maximum image '
2708 'size of {} in order to fit in a partition '
2709 'size of {}.'.format(image.image_size, max_image_size,
2710 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002711
2712 if salt:
2713 salt = salt.decode('hex')
2714 else:
2715 if salt is None:
2716 # If salt is not explicitly specified, choose a hash
2717 # that's the same size as the hash size.
2718 hash_size = digest_size
2719 salt = open('/dev/urandom').read(hash_size)
2720 else:
2721 salt = ''
2722
David Zeuthena4fee8b2016-08-22 15:20:43 -04002723 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002724 # offsets in advance.
2725 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002726 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002727
David Zeuthena4fee8b2016-08-22 15:20:43 -04002728 # If the image isn't sparse, its size might not be a multiple of
2729 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002730 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002731 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002732 padding_needed = image.block_size - (image.image_size%image.block_size)
2733 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002734
David Zeuthena4fee8b2016-08-22 15:20:43 -04002735 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002736 tree_offset = image.image_size
2737 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002738 block_size,
2739 hash_algorithm, salt,
2740 digest_padding,
2741 hash_level_offsets,
2742 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002743
2744 # Generate HashtreeDescriptor with details about the tree we
2745 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002746 ht_desc = AvbHashtreeDescriptor()
2747 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002748 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002749 ht_desc.tree_offset = tree_offset
2750 ht_desc.tree_size = tree_size
2751 ht_desc.data_block_size = block_size
2752 ht_desc.hash_block_size = block_size
2753 ht_desc.hash_algorithm = hash_algorithm
2754 ht_desc.partition_name = partition_name
2755 ht_desc.salt = salt
2756 ht_desc.root_digest = root_digest
2757
David Zeuthen09692692016-09-30 16:16:40 -04002758 # Write the hash tree
2759 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2760 len(hash_tree))
2761 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2762 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002763 len_hashtree_and_fec = len(hash_tree_with_padding)
2764
2765 # Generate FEC codes, if requested.
2766 if generate_fec:
2767 fec_data = generate_fec_data(image_filename, fec_num_roots)
2768 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2769 len(fec_data))
2770 fec_data_with_padding = fec_data + '\0'*padding_needed
2771 fec_offset = image.image_size
2772 image.append_raw(fec_data_with_padding)
2773 len_hashtree_and_fec += len(fec_data_with_padding)
2774 # Update the hashtree descriptor.
2775 ht_desc.fec_num_roots = fec_num_roots
2776 ht_desc.fec_offset = fec_offset
2777 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002778
David Zeuthena4fee8b2016-08-22 15:20:43 -04002779 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002780 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002781 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002782 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002783 chain_partitions, rollback_index, flags, props, props_from_file,
2784 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002785 include_descriptors_from_image, signing_helper, release_string,
2786 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002787 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2788 len(vbmeta_blob))
2789 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002790
David Zeuthend247fcb2017-02-16 12:09:27 -05002791 # Write vbmeta blob, if requested.
2792 if output_vbmeta_image:
2793 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002794
David Zeuthend247fcb2017-02-16 12:09:27 -05002795 # Append vbmeta blob and footer, unless requested not to.
2796 if not do_not_append_vbmeta_image:
2797 image.append_raw(vbmeta_blob_with_padding)
2798
2799 # Now insert a DONT_CARE chunk with enough bytes such that the
2800 # final Footer block is at the end of partition_size..
2801 image.append_dont_care(partition_size - image.image_size -
2802 1*image.block_size)
2803
2804 # Generate the Footer that tells where the VBMeta footer
2805 # is. Also put enough padding in the front of the footer since
2806 # we'll write out an entire block.
2807 footer = AvbFooter()
2808 footer.original_image_size = original_image_size
2809 footer.vbmeta_offset = vbmeta_offset
2810 footer.vbmeta_size = len(vbmeta_blob)
2811 footer_blob = footer.encode()
2812 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2813 footer_blob)
2814 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002815
David Zeuthen21e95262016-07-27 17:58:40 -04002816 except:
David Zeuthen09692692016-09-30 16:16:40 -04002817 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002818 image.truncate(original_image_size)
2819 raise
2820
David Zeuthenc68f0822017-03-31 17:22:35 -04002821 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002822 subject_key_version, subject,
2823 is_intermediate_authority, signing_helper):
2824 """Implements the 'make_atx_certificate' command.
2825
2826 Android Things certificates are required for Android Things public key
2827 metadata. They chain the vbmeta signing key for a particular product back to
2828 a fused, permanent root key. These certificates are fixed-length and fixed-
2829 format with the explicit goal of not parsing ASN.1 in bootloader code.
2830
2831 Arguments:
2832 output: Certificate will be written to this file on success.
2833 authority_key_path: A PEM file path with the authority private key.
2834 If None, then a certificate will be created without a
2835 signature. The signature can be created out-of-band
2836 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04002837 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08002838 subject_key_version: A 64-bit version value. If this is None, the number
2839 of seconds since the epoch is used.
2840 subject: A subject identifier. For Product Signing Key certificates this
2841 should be the same Product ID found in the permanent attributes.
2842 is_intermediate_authority: True if the certificate is for an intermediate
2843 authority.
2844 signing_helper: Program which signs a hash and returns the signature.
2845 """
2846 signed_data = bytearray()
2847 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002848 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002849 hasher = hashlib.sha256()
2850 hasher.update(subject)
2851 signed_data.extend(hasher.digest())
2852 usage = 'com.google.android.things.vboot'
2853 if is_intermediate_authority:
2854 usage += '.ca'
2855 hasher = hashlib.sha256()
2856 hasher.update(usage)
2857 signed_data.extend(hasher.digest())
2858 if not subject_key_version:
2859 subject_key_version = int(time.time())
2860 signed_data.extend(struct.pack('<Q', subject_key_version))
2861 signature = bytearray()
2862 if authority_key_path:
2863 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002864 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002865 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002866 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002867 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002868 hasher.update(signed_data)
2869 padding_and_hash.extend(hasher.digest())
2870 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002871 alg.signature_num_bytes, authority_key_path,
2872 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002873 output.write(signed_data)
2874 output.write(signature)
2875
David Zeuthenc68f0822017-03-31 17:22:35 -04002876 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002877 product_id):
2878 """Implements the 'make_atx_permanent_attributes' command.
2879
2880 Android Things permanent attributes are designed to be permanent for a
2881 particular product and a hash of these attributes should be fused into
2882 hardware to enforce this.
2883
2884 Arguments:
2885 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04002886 root_authority_key_path: Path to a PEM or DER public key for
2887 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08002888 product_id: A 16-byte Product ID.
2889
2890 Raises:
2891 AvbError: If an argument is incorrect.
2892 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002893 EXPECTED_PRODUCT_ID_SIZE = 16
2894 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002895 raise AvbError('Invalid Product ID length.')
2896 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002897 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002898 output.write(product_id)
2899
2900 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002901 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002902 """Implements the 'make_atx_metadata' command.
2903
2904 Android Things metadata are included in vbmeta images to facilitate
2905 verification. The output of this command can be used as the
2906 public_key_metadata argument to other commands.
2907
2908 Arguments:
2909 output: Metadata will be written to this file on success.
2910 intermediate_key_certificate: A certificate file as output by
2911 make_atx_certificate with
2912 is_intermediate_authority set to true.
2913 product_key_certificate: A certificate file as output by
2914 make_atx_certificate with
2915 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002916
2917 Raises:
2918 AvbError: If an argument is incorrect.
2919 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002920 EXPECTED_CERTIFICATE_SIZE = 1620
2921 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002922 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002923 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002924 raise AvbError('Invalid product key certificate length.')
2925 output.write(struct.pack('<I', 1)) # Format Version
2926 output.write(intermediate_key_certificate)
2927 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002928
David Zeuthen21e95262016-07-27 17:58:40 -04002929
2930def calc_hash_level_offsets(image_size, block_size, digest_size):
2931 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2932
2933 Arguments:
2934 image_size: The size of the image to calculate a Merkle-tree for.
2935 block_size: The block size, e.g. 4096.
2936 digest_size: The size of each hash, e.g. 32 for SHA-256.
2937
2938 Returns:
2939 A tuple where the first argument is an array of offsets and the
2940 second is size of the tree, in bytes.
2941 """
2942 level_offsets = []
2943 level_sizes = []
2944 tree_size = 0
2945
2946 num_levels = 0
2947 size = image_size
2948 while size > block_size:
2949 num_blocks = (size + block_size - 1) / block_size
2950 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2951
2952 level_sizes.append(level_size)
2953 tree_size += level_size
2954 num_levels += 1
2955
2956 size = level_size
2957
2958 for n in range(0, num_levels):
2959 offset = 0
2960 for m in range(n + 1, num_levels):
2961 offset += level_sizes[m]
2962 level_offsets.append(offset)
2963
David Zeuthena4fee8b2016-08-22 15:20:43 -04002964 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002965
2966
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002967# See system/extras/libfec/include/fec/io.h for these definitions.
2968FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2969FEC_MAGIC = 0xfecfecfe
2970
2971
2972def calc_fec_data_size(image_size, num_roots):
2973 """Calculates how much space FEC data will take.
2974
2975 Args:
2976 image_size: The size of the image.
2977 num_roots: Number of roots.
2978
2979 Returns:
2980 The number of bytes needed for FEC for an image of the given size
2981 and with the requested number of FEC roots.
2982
2983 Raises:
2984 ValueError: If output from the 'fec' tool is invalid.
2985
2986 """
2987 p = subprocess.Popen(
2988 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2989 stdout=subprocess.PIPE,
2990 stderr=subprocess.PIPE)
2991 (pout, perr) = p.communicate()
2992 retcode = p.wait()
2993 if retcode != 0:
2994 raise ValueError('Error invoking fec: {}'.format(perr))
2995 return int(pout)
2996
2997
2998def generate_fec_data(image_filename, num_roots):
2999 """Generate FEC codes for an image.
3000
3001 Args:
3002 image_filename: The filename of the image.
3003 num_roots: Number of roots.
3004
3005 Returns:
3006 The FEC data blob.
3007
3008 Raises:
3009 ValueError: If output from the 'fec' tool is invalid.
3010 """
3011 fec_tmpfile = tempfile.NamedTemporaryFile()
3012 subprocess.check_call(
3013 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3014 fec_tmpfile.name],
3015 stderr=open(os.devnull))
3016 fec_data = fec_tmpfile.read()
3017 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3018 footer_data = fec_data[-footer_size:]
3019 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3020 footer_data)
3021 if magic != FEC_MAGIC:
3022 raise ValueError('Unexpected magic in FEC footer')
3023 return fec_data[0:fec_size]
3024
3025
David Zeuthen21e95262016-07-27 17:58:40 -04003026def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003027 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003028 """Generates a Merkle-tree for a file.
3029
3030 Args:
3031 image: The image, as a file.
3032 image_size: The size of the image.
3033 block_size: The block size, e.g. 4096.
3034 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3035 salt: The salt to use.
3036 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003037 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003038 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003039
3040 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003041 A tuple where the first element is the top-level hash and the
3042 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003043 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003044 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003045 hash_src_offset = 0
3046 hash_src_size = image_size
3047 level_num = 0
3048 while hash_src_size > block_size:
3049 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003050 remaining = hash_src_size
3051 while remaining > 0:
3052 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003053 # Only read from the file for the first level - for subsequent
3054 # levels, access the array we're building.
3055 if level_num == 0:
3056 image.seek(hash_src_offset + hash_src_size - remaining)
3057 data = image.read(min(remaining, block_size))
3058 else:
3059 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3060 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003061 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003062
3063 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003064 if len(data) < block_size:
3065 hasher.update('\0' * (block_size - len(data)))
3066 level_output += hasher.digest()
3067 if digest_padding > 0:
3068 level_output += '\0' * digest_padding
3069
3070 padding_needed = (round_to_multiple(
3071 len(level_output), block_size) - len(level_output))
3072 level_output += '\0' * padding_needed
3073
David Zeuthena4fee8b2016-08-22 15:20:43 -04003074 # Copy level-output into resulting tree.
3075 offset = hash_level_offsets[level_num]
3076 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003077
David Zeuthena4fee8b2016-08-22 15:20:43 -04003078 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003079 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003080 level_num += 1
3081
3082 hasher = hashlib.new(name=hash_alg_name, string=salt)
3083 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003084 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003085
3086
3087class AvbTool(object):
3088 """Object for avbtool command-line tool."""
3089
3090 def __init__(self):
3091 """Initializer method."""
3092 self.avb = Avb()
3093
3094 def _add_common_args(self, sub_parser):
3095 """Adds arguments used by several sub-commands.
3096
3097 Arguments:
3098 sub_parser: The parser to add arguments to.
3099 """
3100 sub_parser.add_argument('--algorithm',
3101 help='Algorithm to use (default: NONE)',
3102 metavar='ALGORITHM',
3103 default='NONE')
3104 sub_parser.add_argument('--key',
3105 help='Path to RSA private key file',
3106 metavar='KEY',
3107 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003108 sub_parser.add_argument('--signing_helper',
3109 help='Path to helper used for signing',
3110 metavar='APP',
3111 default=None,
3112 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003113 sub_parser.add_argument('--public_key_metadata',
3114 help='Path to public key metadata file',
3115 metavar='KEY_METADATA',
3116 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003117 sub_parser.add_argument('--rollback_index',
3118 help='Rollback Index',
3119 type=parse_number,
3120 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003121 # This is used internally for unit tests. Do not include in --help output.
3122 sub_parser.add_argument('--internal_release_string',
3123 help=argparse.SUPPRESS)
3124 sub_parser.add_argument('--append_to_release_string',
3125 help='Text to append to release string',
3126 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003127 sub_parser.add_argument('--prop',
3128 help='Add property',
3129 metavar='KEY:VALUE',
3130 action='append')
3131 sub_parser.add_argument('--prop_from_file',
3132 help='Add property from file',
3133 metavar='KEY:PATH',
3134 action='append')
3135 sub_parser.add_argument('--kernel_cmdline',
3136 help='Add kernel cmdline',
3137 metavar='CMDLINE',
3138 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003139 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3140 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3141 # at some future point.
3142 sub_parser.add_argument('--setup_rootfs_from_kernel',
3143 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003144 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003145 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003146 type=argparse.FileType('rb'))
3147 sub_parser.add_argument('--include_descriptors_from_image',
3148 help='Include descriptors from image',
3149 metavar='IMAGE',
3150 action='append',
3151 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05003152 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3153 sub_parser.add_argument('--chain_partition',
3154 help='Allow signed integrity-data for partition',
3155 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3156 action='append')
3157 sub_parser.add_argument('--flags',
3158 help='VBMeta flags',
3159 type=parse_number,
3160 default=0)
3161 sub_parser.add_argument('--set_hashtree_disabled_flag',
3162 help='Set the HASHTREE_DISABLED flag',
3163 action='store_true')
3164
3165 def _fixup_common_args(self, args):
3166 """Common fixups needed by subcommands.
3167
3168 Arguments:
3169 args: Arguments to modify.
3170
3171 Returns:
3172 The modified arguments.
3173 """
3174 if args.set_hashtree_disabled_flag:
3175 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3176 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003177
3178 def run(self, argv):
3179 """Command-line processor.
3180
3181 Arguments:
3182 argv: Pass sys.argv from main.
3183 """
3184 parser = argparse.ArgumentParser()
3185 subparsers = parser.add_subparsers(title='subcommands')
3186
3187 sub_parser = subparsers.add_parser('version',
3188 help='Prints version of avbtool.')
3189 sub_parser.set_defaults(func=self.version)
3190
3191 sub_parser = subparsers.add_parser('extract_public_key',
3192 help='Extract public key.')
3193 sub_parser.add_argument('--key',
3194 help='Path to RSA private key file',
3195 required=True)
3196 sub_parser.add_argument('--output',
3197 help='Output file name',
3198 type=argparse.FileType('wb'),
3199 required=True)
3200 sub_parser.set_defaults(func=self.extract_public_key)
3201
3202 sub_parser = subparsers.add_parser('make_vbmeta_image',
3203 help='Makes a vbmeta image.')
3204 sub_parser.add_argument('--output',
3205 help='Output file name',
3206 type=argparse.FileType('wb'),
3207 required=True)
3208 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003209 sub_parser.set_defaults(func=self.make_vbmeta_image)
3210
3211 sub_parser = subparsers.add_parser('add_hash_footer',
3212 help='Add hashes and footer to image.')
3213 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003214 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003215 type=argparse.FileType('rab+'))
3216 sub_parser.add_argument('--partition_size',
3217 help='Partition size',
3218 type=parse_number,
3219 required=True)
3220 sub_parser.add_argument('--partition_name',
3221 help='Partition name',
3222 required=True)
3223 sub_parser.add_argument('--hash_algorithm',
3224 help='Hash algorithm to use (default: sha256)',
3225 default='sha256')
3226 sub_parser.add_argument('--salt',
3227 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003228 sub_parser.add_argument('--output_vbmeta_image',
3229 help='Also write vbmeta struct to file',
3230 type=argparse.FileType('wb'))
3231 sub_parser.add_argument('--do_not_append_vbmeta_image',
3232 help=('Do not append vbmeta struct or footer '
3233 'to the image'),
3234 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003235 self._add_common_args(sub_parser)
3236 sub_parser.set_defaults(func=self.add_hash_footer)
3237
David Zeuthenb1b994d2017-03-06 18:01:31 -05003238 sub_parser = subparsers.add_parser('append_vbmeta_image',
3239 help='Append vbmeta image to image.')
3240 sub_parser.add_argument('--image',
3241 help='Image to append vbmeta blob to',
3242 type=argparse.FileType('rab+'))
3243 sub_parser.add_argument('--partition_size',
3244 help='Partition size',
3245 type=parse_number,
3246 required=True)
3247 sub_parser.add_argument('--vbmeta_image',
3248 help='Image with vbmeta blob to append',
3249 type=argparse.FileType('rb'))
3250 sub_parser.set_defaults(func=self.append_vbmeta_image)
3251
David Zeuthen21e95262016-07-27 17:58:40 -04003252 sub_parser = subparsers.add_parser('add_hashtree_footer',
3253 help='Add hashtree and footer to image.')
3254 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003255 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003256 type=argparse.FileType('rab+'))
3257 sub_parser.add_argument('--partition_size',
3258 help='Partition size',
3259 type=parse_number,
3260 required=True)
3261 sub_parser.add_argument('--partition_name',
3262 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003263 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003264 sub_parser.add_argument('--hash_algorithm',
3265 help='Hash algorithm to use (default: sha1)',
3266 default='sha1')
3267 sub_parser.add_argument('--salt',
3268 help='Salt in hex (default: /dev/urandom)')
3269 sub_parser.add_argument('--block_size',
3270 help='Block size (default: 4096)',
3271 type=parse_number,
3272 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003273 sub_parser.add_argument('--generate_fec',
3274 help='Add forward-error-correction codes',
3275 action='store_true')
3276 sub_parser.add_argument('--fec_num_roots',
3277 help='Number of roots for FEC (default: 2)',
3278 type=parse_number,
3279 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003280 sub_parser.add_argument('--calc_max_image_size',
3281 help=('Don\'t store the hashtree or footer - '
3282 'instead calculate the maximum image size '
3283 'leaving enough room for hashtree '
3284 'and metadata with the given partition '
3285 'size.'),
3286 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003287 sub_parser.add_argument('--output_vbmeta_image',
3288 help='Also write vbmeta struct to file',
3289 type=argparse.FileType('wb'))
3290 sub_parser.add_argument('--do_not_append_vbmeta_image',
3291 help=('Do not append vbmeta struct or footer '
3292 'to the image'),
3293 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003294 self._add_common_args(sub_parser)
3295 sub_parser.set_defaults(func=self.add_hashtree_footer)
3296
3297 sub_parser = subparsers.add_parser('erase_footer',
3298 help='Erase footer from an image.')
3299 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003300 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003301 type=argparse.FileType('rwb+'),
3302 required=True)
3303 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003304 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003305 action='store_true')
3306 sub_parser.set_defaults(func=self.erase_footer)
3307
David Zeuthen2bc232b2017-04-19 14:25:19 -04003308 sub_parser = subparsers.add_parser('resize_image',
3309 help='Resize image with a footer.')
3310 sub_parser.add_argument('--image',
3311 help='Image with a footer',
3312 type=argparse.FileType('rwb+'),
3313 required=True)
3314 sub_parser.add_argument('--partition_size',
3315 help='New partition size',
3316 type=parse_number)
3317 sub_parser.set_defaults(func=self.resize_image)
3318
David Zeuthen21e95262016-07-27 17:58:40 -04003319 sub_parser = subparsers.add_parser(
3320 'info_image',
3321 help='Show information about vbmeta or footer.')
3322 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003323 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003324 type=argparse.FileType('rb'),
3325 required=True)
3326 sub_parser.add_argument('--output',
3327 help='Write info to file',
3328 type=argparse.FileType('wt'),
3329 default=sys.stdout)
3330 sub_parser.set_defaults(func=self.info_image)
3331
David Zeuthenb623d8b2017-04-04 16:05:53 -04003332 sub_parser = subparsers.add_parser(
3333 'verify_image',
3334 help='Verify an image.')
3335 sub_parser.add_argument('--image',
3336 help='Image to verify',
3337 type=argparse.FileType('rb'),
3338 required=True)
3339 sub_parser.set_defaults(func=self.verify_image)
3340
David Zeuthen8b6973b2016-09-20 12:39:49 -04003341 sub_parser = subparsers.add_parser('set_ab_metadata',
3342 help='Set A/B metadata.')
3343 sub_parser.add_argument('--misc_image',
3344 help=('The misc image to modify. If the image does '
3345 'not exist, it will be created.'),
3346 type=argparse.FileType('r+b'),
3347 required=True)
3348 sub_parser.add_argument('--slot_data',
3349 help=('Slot data of the form "priority", '
3350 '"tries_remaining", "sucessful_boot" for '
3351 'slot A followed by the same for slot B, '
3352 'separated by colons. The default value '
3353 'is 15:7:0:14:7:0.'),
3354 default='15:7:0:14:7:0')
3355 sub_parser.set_defaults(func=self.set_ab_metadata)
3356
Darren Krahn147b08d2016-12-20 16:38:29 -08003357 sub_parser = subparsers.add_parser(
3358 'make_atx_certificate',
3359 help='Create an Android Things eXtension (ATX) certificate.')
3360 sub_parser.add_argument('--output',
3361 help='Write certificate to file',
3362 type=argparse.FileType('wb'),
3363 default=sys.stdout)
3364 sub_parser.add_argument('--subject',
3365 help=('Path to subject file'),
3366 type=argparse.FileType('rb'),
3367 required=True)
3368 sub_parser.add_argument('--subject_key',
3369 help=('Path to subject RSA public key file'),
3370 type=argparse.FileType('rb'),
3371 required=True)
3372 sub_parser.add_argument('--subject_key_version',
3373 help=('Version of the subject key'),
3374 type=parse_number,
3375 required=False)
3376 sub_parser.add_argument('--subject_is_intermediate_authority',
3377 help=('Generate an intermediate authority '
3378 'certificate'),
3379 action='store_true')
3380 sub_parser.add_argument('--authority_key',
3381 help='Path to authority RSA private key file',
3382 required=False)
3383 sub_parser.add_argument('--signing_helper',
3384 help='Path to helper used for signing',
3385 metavar='APP',
3386 default=None,
3387 required=False)
3388 sub_parser.set_defaults(func=self.make_atx_certificate)
3389
3390 sub_parser = subparsers.add_parser(
3391 'make_atx_permanent_attributes',
3392 help='Create Android Things eXtension (ATX) permanent attributes.')
3393 sub_parser.add_argument('--output',
3394 help='Write attributes to file',
3395 type=argparse.FileType('wb'),
3396 default=sys.stdout)
3397 sub_parser.add_argument('--root_authority_key',
3398 help='Path to authority RSA public key file',
3399 type=argparse.FileType('rb'),
3400 required=True)
3401 sub_parser.add_argument('--product_id',
3402 help=('Path to Product ID file'),
3403 type=argparse.FileType('rb'),
3404 required=True)
3405 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3406
3407 sub_parser = subparsers.add_parser(
3408 'make_atx_metadata',
3409 help='Create Android Things eXtension (ATX) metadata.')
3410 sub_parser.add_argument('--output',
3411 help='Write metadata to file',
3412 type=argparse.FileType('wb'),
3413 default=sys.stdout)
3414 sub_parser.add_argument('--intermediate_key_certificate',
3415 help='Path to intermediate key certificate file',
3416 type=argparse.FileType('rb'),
3417 required=True)
3418 sub_parser.add_argument('--product_key_certificate',
3419 help='Path to product key certificate file',
3420 type=argparse.FileType('rb'),
3421 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003422 sub_parser.set_defaults(func=self.make_atx_metadata)
3423
David Zeuthen21e95262016-07-27 17:58:40 -04003424 args = parser.parse_args(argv[1:])
3425 try:
3426 args.func(args)
3427 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003428 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003429 sys.exit(1)
3430
3431 def version(self, _):
3432 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003433 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003434
3435 def extract_public_key(self, args):
3436 """Implements the 'extract_public_key' sub-command."""
3437 self.avb.extract_public_key(args.key, args.output)
3438
3439 def make_vbmeta_image(self, args):
3440 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003441 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003442 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003443 args.algorithm, args.key,
3444 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003445 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003446 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003447 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003448 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003449 args.signing_helper,
3450 args.internal_release_string,
3451 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003452
David Zeuthenb1b994d2017-03-06 18:01:31 -05003453 def append_vbmeta_image(self, args):
3454 """Implements the 'append_vbmeta_image' sub-command."""
3455 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3456 args.partition_size)
3457
David Zeuthen21e95262016-07-27 17:58:40 -04003458 def add_hash_footer(self, args):
3459 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003460 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003461 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003462 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003463 args.salt, args.chain_partition, args.algorithm,
3464 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003465 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003466 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003467 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003468 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003469 args.include_descriptors_from_image,
3470 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003471 args.internal_release_string,
3472 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003473 args.output_vbmeta_image,
3474 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003475
3476 def add_hashtree_footer(self, args):
3477 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003478 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003479 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3480 args.partition_size,
3481 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003482 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003483 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003484 args.salt, args.chain_partition, args.algorithm,
3485 args.key, args.public_key_metadata,
3486 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003487 args.prop_from_file,
3488 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003489 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003490 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003491 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003492 args.internal_release_string,
3493 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003494 args.output_vbmeta_image,
3495 args.do_not_append_vbmeta_image)
3496
David Zeuthen21e95262016-07-27 17:58:40 -04003497 def erase_footer(self, args):
3498 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003499 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003500
David Zeuthen2bc232b2017-04-19 14:25:19 -04003501 def resize_image(self, args):
3502 """Implements the 'resize_image' sub-command."""
3503 self.avb.resize_image(args.image.name, args.partition_size)
3504
David Zeuthen8b6973b2016-09-20 12:39:49 -04003505 def set_ab_metadata(self, args):
3506 """Implements the 'set_ab_metadata' sub-command."""
3507 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3508
David Zeuthen21e95262016-07-27 17:58:40 -04003509 def info_image(self, args):
3510 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003511 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003512
David Zeuthenb623d8b2017-04-04 16:05:53 -04003513 def verify_image(self, args):
3514 """Implements the 'verify_image' sub-command."""
3515 self.avb.verify_image(args.image.name)
3516
Darren Krahn147b08d2016-12-20 16:38:29 -08003517 def make_atx_certificate(self, args):
3518 """Implements the 'make_atx_certificate' sub-command."""
3519 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003520 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003521 args.subject_key_version,
3522 args.subject.read(),
3523 args.subject_is_intermediate_authority,
3524 args.signing_helper)
3525
3526 def make_atx_permanent_attributes(self, args):
3527 """Implements the 'make_atx_permanent_attributes' sub-command."""
3528 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003529 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003530 args.product_id.read())
3531
3532 def make_atx_metadata(self, args):
3533 """Implements the 'make_atx_metadata' sub-command."""
3534 self.avb.make_atx_metadata(args.output,
3535 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003536 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003537
David Zeuthen21e95262016-07-27 17:58:40 -04003538
3539if __name__ == '__main__':
3540 tool = AvbTool()
3541 tool.run(sys.argv)