blob: 3d562133d9d0000b139e262b94654dc0b7cfec05 [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
31import os
32import struct
33import subprocess
34import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040035import tempfile
David Zeuthen21e95262016-07-27 17:58:40 -040036
37import Crypto.PublicKey.RSA
38
39# Keep in sync with avb_vbmeta_header.h.
40AVB_VERSION_MAJOR = 1
41AVB_VERSION_MINOR = 0
42
43
44class AvbError(Exception):
45 """Application-specific errors.
46
47 These errors represent issues for which a stack-trace should not be
48 presented.
49
50 Attributes:
51 message: Error message.
52 """
53
54 def __init__(self, message):
55 Exception.__init__(self, message)
56
57
58class Algorithm(object):
59 """Contains details about an algorithm.
60
61 See the avb_vbmeta_header.h file for more details about
62 algorithms.
63
64 The constant |ALGORITHMS| is a dictionary from human-readable
65 names (e.g 'SHA256_RSA2048') to instances of this class.
66
67 Attributes:
68 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
69 hash_num_bytes: Number of bytes used to store the hash.
70 signature_num_bytes: Number of bytes used to store the signature.
71 public_key_num_bytes: Number of bytes used to store the public key.
72 padding: Padding used for signature, if any.
73 """
74
75 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
76 public_key_num_bytes, padding):
77 self.algorithm_type = algorithm_type
78 self.hash_num_bytes = hash_num_bytes
79 self.signature_num_bytes = signature_num_bytes
80 self.public_key_num_bytes = public_key_num_bytes
81 self.padding = padding
82
83# This must be kept in sync with the avb_crypto.h file.
84#
85# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
86# obtained from section 5.2.2 of RFC 4880.
87ALGORITHMS = {
88 'NONE': Algorithm(
89 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
90 hash_num_bytes=0,
91 signature_num_bytes=0,
92 public_key_num_bytes=0,
93 padding=[]),
94 'SHA256_RSA2048': Algorithm(
95 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
96 hash_num_bytes=32,
97 signature_num_bytes=256,
98 public_key_num_bytes=8 + 2*2048/8,
99 padding=[
100 # PKCS1-v1_5 padding
101 0x00, 0x01] + [0xff]*202 + [0x00] + [
102 # ASN.1 header
103 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
104 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
105 0x00, 0x04, 0x20,
106 ]),
107 'SHA256_RSA4096': Algorithm(
108 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
109 hash_num_bytes=32,
110 signature_num_bytes=512,
111 public_key_num_bytes=8 + 2*4096/8,
112 padding=[
113 # PKCS1-v1_5 padding
114 0x00, 0x01] + [0xff]*458 + [0x00] + [
115 # ASN.1 header
116 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
117 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
118 0x00, 0x04, 0x20,
119 ]),
120 'SHA256_RSA8192': Algorithm(
121 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
122 hash_num_bytes=32,
123 signature_num_bytes=1024,
124 public_key_num_bytes=8 + 2*8192/8,
125 padding=[
126 # PKCS1-v1_5 padding
127 0x00, 0x01] + [0xff]*970 + [0x00] + [
128 # ASN.1 header
129 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
130 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
131 0x00, 0x04, 0x20,
132 ]),
133 'SHA512_RSA2048': Algorithm(
134 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
135 hash_num_bytes=64,
136 signature_num_bytes=256,
137 public_key_num_bytes=8 + 2*2048/8,
138 padding=[
139 # PKCS1-v1_5 padding
140 0x00, 0x01] + [0xff]*170 + [0x00] + [
141 # ASN.1 header
142 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
143 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
144 0x00, 0x04, 0x40
145 ]),
146 'SHA512_RSA4096': Algorithm(
147 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
148 hash_num_bytes=64,
149 signature_num_bytes=512,
150 public_key_num_bytes=8 + 2*4096/8,
151 padding=[
152 # PKCS1-v1_5 padding
153 0x00, 0x01] + [0xff]*426 + [0x00] + [
154 # ASN.1 header
155 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
156 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
157 0x00, 0x04, 0x40
158 ]),
159 'SHA512_RSA8192': Algorithm(
160 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
161 hash_num_bytes=64,
162 signature_num_bytes=1024,
163 public_key_num_bytes=8 + 2*8192/8,
164 padding=[
165 # PKCS1-v1_5 padding
166 0x00, 0x01] + [0xff]*938 + [0x00] + [
167 # ASN.1 header
168 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
169 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
170 0x00, 0x04, 0x40
171 ]),
172}
173
174
175def round_to_multiple(number, size):
176 """Rounds a number up to nearest multiple of another number.
177
178 Args:
179 number: The number to round up.
180 size: The multiple to round up to.
181
182 Returns:
183 If |number| is a multiple of |size|, returns |number|, otherwise
184 returns |number| + |size|.
185 """
186 remainder = number % size
187 if remainder == 0:
188 return number
189 return number + size - remainder
190
191
192def round_to_pow2(number):
193 """Rounds a number up to the next power of 2.
194
195 Args:
196 number: The number to round up.
197
198 Returns:
199 If |number| is already a power of 2 then |number| is
200 returned. Otherwise the smallest power of 2 greater than |number|
201 is returned.
202 """
203 return 2**((number - 1).bit_length())
204
205
206def write_long(output, num_bits, value):
207 """Writes a long to an output stream using a given amount of bits.
208
209 This number is written big-endian, e.g. with the most significant
210 bit first.
211
212 Arguments:
213 output: The object to write the output to.
214 num_bits: The number of bits to write, e.g. 2048.
215 value: The value to write.
216 """
217 for bit_pos in range(num_bits, 0, -8):
218 octet = (value >> (bit_pos - 8)) & 0xff
219 output.write(struct.pack('!B', octet))
220
221
222def encode_long(num_bits, value):
223 """Encodes a long to a bytearray() using a given amount of bits.
224
225 This number is written big-endian, e.g. with the most significant
226 bit first.
227
228 Arguments:
229 num_bits: The number of bits to write, e.g. 2048.
230 value: The value to write.
231
232 Returns:
233 A bytearray() with the encoded long.
234 """
235 ret = bytearray()
236 for bit_pos in range(num_bits, 0, -8):
237 octet = (value >> (bit_pos - 8)) & 0xff
238 ret.extend(struct.pack('!B', octet))
239 return ret
240
241
242def egcd(a, b):
243 """Calculate greatest common divisor of two numbers.
244
245 This implementation uses a recursive version of the extended
246 Euclidian algorithm.
247
248 Arguments:
249 a: First number.
250 b: Second number.
251
252 Returns:
253 A tuple (gcd, x, y) that where |gcd| is the greatest common
254 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
255 """
256 if a == 0:
257 return (b, 0, 1)
258 else:
259 g, y, x = egcd(b % a, a)
260 return (g, x - (b // a) * y, y)
261
262
263def modinv(a, m):
264 """Calculate modular multiplicative inverse of |a| modulo |m|.
265
266 This calculates the number |x| such that |a| * |x| == 1 (modulo
267 |m|). This number only exists if |a| and |m| are co-prime - |None|
268 is returned if this isn't true.
269
270 Arguments:
271 a: The number to calculate a modular inverse of.
272 m: The modulo to use.
273
274 Returns:
275 The modular multiplicative inverse of |a| and |m| or |None| if
276 these numbers are not co-prime.
277 """
278 gcd, x, _ = egcd(a, m)
279 if gcd != 1:
280 return None # modular inverse does not exist
281 else:
282 return x % m
283
284
285def parse_number(string):
286 """Parse a string as a number.
287
288 This is just a short-hand for int(string, 0) suitable for use in the
289 |type| parameter of |ArgumentParser|'s add_argument() function. An
290 improvement to just using type=int is that this function supports
291 numbers in other bases, e.g. "0x1234".
292
293 Arguments:
294 string: The string to parse.
295
296 Returns:
297 The parsed integer.
298
299 Raises:
300 ValueError: If the number could not be parsed.
301 """
302 return int(string, 0)
303
304
305def write_rsa_key(output, key):
306 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
307
308 This writes the |AvbRSAPublicKeyHeader| as well as the two large
309 numbers (|key_num_bits| bits long) following it.
310
311 Arguments:
312 output: The object to write the output to.
313 key: A Crypto.PublicKey.RSA object.
314 """
315 # key.e is exponent
316 # key.n is modulus
317 key_num_bits = key.size() + 1
318 # Calculate n0inv = -1/n[0] (mod 2^32)
319 b = 2L**32
320 n0inv = b - modinv(key.n, b)
321 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
322 r = 2L**key.n.bit_length()
323 rrmodn = r * r % key.n
324 output.write(struct.pack('!II', key_num_bits, n0inv))
325 write_long(output, key_num_bits, key.n)
326 write_long(output, key_num_bits, rrmodn)
327
328
329def encode_rsa_key(key):
330 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
331
332 This creates a |AvbRSAPublicKeyHeader| as well as the two large
333 numbers (|key_num_bits| bits long) following it.
334
335 Arguments:
336 key: A Crypto.PublicKey.RSA object.
337
338 Returns:
339 A bytearray() with the |AvbRSAPublicKeyHeader|.
340 """
341 ret = bytearray()
342 # key.e is exponent
343 # key.n is modulus
344 key_num_bits = key.size() + 1
345 # Calculate n0inv = -1/n[0] (mod 2^32)
346 b = 2L**32
347 n0inv = b - modinv(key.n, b)
348 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
349 r = 2L**key.n.bit_length()
350 rrmodn = r * r % key.n
351 ret.extend(struct.pack('!II', key_num_bits, n0inv))
352 ret.extend(encode_long(key_num_bits, key.n))
353 ret.extend(encode_long(key_num_bits, rrmodn))
354 return ret
355
356
357def lookup_algorithm_by_type(alg_type):
358 """Looks up algorithm by type.
359
360 Arguments:
361 alg_type: The integer representing the type.
362
363 Returns:
364 A tuple with the algorithm name and an |Algorithm| instance.
365
366 Raises:
367 Exception: If the algorithm cannot be found
368 """
369 for alg_name in ALGORITHMS:
370 alg_data = ALGORITHMS[alg_name]
371 if alg_data.algorithm_type == alg_type:
372 return (alg_name, alg_data)
373 raise AvbError('Unknown algorithm type {}'.format(alg_type))
374
375
David Zeuthena4fee8b2016-08-22 15:20:43 -0400376class ImageChunk(object):
377 """Data structure used for representing chunks in Android sparse files.
378
379 Attributes:
380 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
381 chunk_offset: Offset in the sparse file where this chunk begins.
382 output_offset: Offset in de-sparsified file where output begins.
383 output_size: Number of bytes in output.
384 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
385 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
386 """
387
388 FORMAT = '<2H2I'
389 TYPE_RAW = 0xcac1
390 TYPE_FILL = 0xcac2
391 TYPE_DONT_CARE = 0xcac3
392 TYPE_CRC32 = 0xcac4
393
394 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
395 input_offset, fill_data):
396 """Initializes an ImageChunk object.
397
398 Arguments:
399 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
400 chunk_offset: Offset in the sparse file where this chunk begins.
401 output_offset: Offset in de-sparsified file.
402 output_size: Number of bytes in output.
403 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
404 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
405
406 Raises:
407 ValueError: If data is not well-formed.
408 """
409 self.chunk_type = chunk_type
410 self.chunk_offset = chunk_offset
411 self.output_offset = output_offset
412 self.output_size = output_size
413 self.input_offset = input_offset
414 self.fill_data = fill_data
415 # Check invariants.
416 if self.chunk_type == self.TYPE_RAW:
417 if self.fill_data is not None:
418 raise ValueError('RAW chunk cannot have fill_data set.')
419 if not self.input_offset:
420 raise ValueError('RAW chunk must have input_offset set.')
421 elif self.chunk_type == self.TYPE_FILL:
422 if self.fill_data is None:
423 raise ValueError('FILL chunk must have fill_data set.')
424 if self.input_offset:
425 raise ValueError('FILL chunk cannot have input_offset set.')
426 elif self.chunk_type == self.TYPE_DONT_CARE:
427 if self.fill_data is not None:
428 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
429 if self.input_offset:
430 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
431 else:
432 raise ValueError('Invalid chunk type')
433
434
435class ImageHandler(object):
436 """Abstraction for image I/O with support for Android sparse images.
437
438 This class provides an interface for working with image files that
439 may be using the Android Sparse Image format. When an instance is
440 constructed, we test whether it's an Android sparse file. If so,
441 operations will be on the sparse file by interpreting the sparse
442 format, otherwise they will be directly on the file. Either way the
443 operations do the same.
444
445 For reading, this interface mimics a file object - it has seek(),
446 tell(), and read() methods. For writing, only truncation
447 (truncate()) and appending is supported (append_raw() and
448 append_dont_care()). Additionally, data can only be written in units
449 of the block size.
450
451 Attributes:
452 is_sparse: Whether the file being operated on is sparse.
453 block_size: The block size, typically 4096.
454 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400455 """
456 # See system/core/libsparse/sparse_format.h for details.
457 MAGIC = 0xed26ff3a
458 HEADER_FORMAT = '<I4H4I'
459
460 # These are formats and offset of just the |total_chunks| and
461 # |total_blocks| fields.
462 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
463 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
464
465 def __init__(self, image_filename):
466 """Initializes an image handler.
467
468 Arguments:
469 image_filename: The name of the file to operate on.
470
471 Raises:
472 ValueError: If data in the file is invalid.
473 """
474 self._image_filename = image_filename
475 self._read_header()
476
477 def _read_header(self):
478 """Initializes internal data structures used for reading file.
479
480 This may be called multiple times and is typically called after
481 modifying the file (e.g. appending, truncation).
482
483 Raises:
484 ValueError: If data in the file is invalid.
485 """
486 self.is_sparse = False
487 self.block_size = 4096
488 self._file_pos = 0
489 self._image = open(self._image_filename, 'r+b')
490 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400491 self.image_size = self._image.tell()
492
493 self._image.seek(0, os.SEEK_SET)
494 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
495 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
496 block_size, self._num_total_blocks, self._num_total_chunks,
497 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
498 if magic != self.MAGIC:
499 # Not a sparse image, our job here is done.
500 return
501 if not (major_version == 1 and minor_version == 0):
502 raise ValueError('Encountered sparse image format version {}.{} but '
503 'only 1.0 is supported'.format(major_version,
504 minor_version))
505 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
506 raise ValueError('Unexpected file_hdr_sz value {}.'.
507 format(file_hdr_sz))
508 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
509 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
510 format(chunk_hdr_sz))
511
512 self.block_size = block_size
513
514 # Build an list of chunks by parsing the file.
515 self._chunks = []
516
517 # Find the smallest offset where only "Don't care" chunks
518 # follow. This will be the size of the content in the sparse
519 # image.
520 offset = 0
521 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400522 for _ in xrange(1, self._num_total_chunks + 1):
523 chunk_offset = self._image.tell()
524
525 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
526 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
527 header_bin)
528 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
529
David Zeuthena4fee8b2016-08-22 15:20:43 -0400530 if chunk_type == ImageChunk.TYPE_RAW:
531 if data_sz != (chunk_sz * self.block_size):
532 raise ValueError('Raw chunk input size ({}) does not match output '
533 'size ({})'.
534 format(data_sz, chunk_sz*self.block_size))
535 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
536 chunk_offset,
537 output_offset,
538 chunk_sz*self.block_size,
539 self._image.tell(),
540 None))
541 self._image.read(data_sz)
542
543 elif chunk_type == ImageChunk.TYPE_FILL:
544 if data_sz != 4:
545 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
546 'has {}'.format(data_sz))
547 fill_data = self._image.read(4)
548 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
549 chunk_offset,
550 output_offset,
551 chunk_sz*self.block_size,
552 None,
553 fill_data))
554 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
555 if data_sz != 0:
556 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
557 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
559 chunk_offset,
560 output_offset,
561 chunk_sz*self.block_size,
562 None,
563 None))
564 elif chunk_type == ImageChunk.TYPE_CRC32:
565 if data_sz != 4:
566 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
567 'this has {}'.format(data_sz))
568 self._image.read(4)
569 else:
570 raise ValueError('Unknown chunk type {}'.format(chunk_type))
571
572 offset += chunk_sz
573 output_offset += chunk_sz*self.block_size
574
575 # Record where sparse data end.
576 self._sparse_end = self._image.tell()
577
578 # Now that we've traversed all chunks, sanity check.
579 if self._num_total_blocks != offset:
580 raise ValueError('The header said we should have {} output blocks, '
581 'but we saw {}'.format(self._num_total_blocks, offset))
582 junk_len = len(self._image.read())
583 if junk_len > 0:
584 raise ValueError('There were {} bytes of extra data at the end of the '
585 'file.'.format(junk_len))
586
David Zeuthen09692692016-09-30 16:16:40 -0400587 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400588 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400589
590 # This is used when bisecting in read() to find the initial slice.
591 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
592
593 self.is_sparse = True
594
595 def _update_chunks_and_blocks(self):
596 """Helper function to update the image header.
597
598 The the |total_chunks| and |total_blocks| fields in the header
599 will be set to value of the |_num_total_blocks| and
600 |_num_total_chunks| attributes.
601
602 """
603 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
604 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
605 self._num_total_blocks,
606 self._num_total_chunks))
607
608 def append_dont_care(self, num_bytes):
609 """Appends a DONT_CARE chunk to the sparse file.
610
611 The given number of bytes must be a multiple of the block size.
612
613 Arguments:
614 num_bytes: Size in number of bytes of the DONT_CARE chunk.
615 """
616 assert num_bytes % self.block_size == 0
617
618 if not self.is_sparse:
619 self._image.seek(0, os.SEEK_END)
620 # This is more efficient that writing NUL bytes since it'll add
621 # a hole on file systems that support sparse files (native
622 # sparse, not Android sparse).
623 self._image.truncate(self._image.tell() + num_bytes)
624 self._read_header()
625 return
626
627 self._num_total_chunks += 1
628 self._num_total_blocks += num_bytes / self.block_size
629 self._update_chunks_and_blocks()
630
631 self._image.seek(self._sparse_end, os.SEEK_SET)
632 self._image.write(struct.pack(ImageChunk.FORMAT,
633 ImageChunk.TYPE_DONT_CARE,
634 0, # Reserved
635 num_bytes / self.block_size,
636 struct.calcsize(ImageChunk.FORMAT)))
637 self._read_header()
638
639 def append_raw(self, data):
640 """Appends a RAW chunk to the sparse file.
641
642 The length of the given data must be a multiple of the block size.
643
644 Arguments:
645 data: Data to append.
646 """
647 assert len(data) % self.block_size == 0
648
649 if not self.is_sparse:
650 self._image.seek(0, os.SEEK_END)
651 self._image.write(data)
652 self._read_header()
653 return
654
655 self._num_total_chunks += 1
656 self._num_total_blocks += len(data) / self.block_size
657 self._update_chunks_and_blocks()
658
659 self._image.seek(self._sparse_end, os.SEEK_SET)
660 self._image.write(struct.pack(ImageChunk.FORMAT,
661 ImageChunk.TYPE_RAW,
662 0, # Reserved
663 len(data) / self.block_size,
664 len(data) +
665 struct.calcsize(ImageChunk.FORMAT)))
666 self._image.write(data)
667 self._read_header()
668
669 def append_fill(self, fill_data, size):
670 """Appends a fill chunk to the sparse file.
671
672 The total length of the fill data must be a multiple of the block size.
673
674 Arguments:
675 fill_data: Fill data to append - must be four bytes.
676 size: Number of chunk - must be a multiple of four and the block size.
677 """
678 assert len(fill_data) == 4
679 assert size % 4 == 0
680 assert size % self.block_size == 0
681
682 if not self.is_sparse:
683 self._image.seek(0, os.SEEK_END)
684 self._image.write(fill_data * (size/4))
685 self._read_header()
686 return
687
688 self._num_total_chunks += 1
689 self._num_total_blocks += size / self.block_size
690 self._update_chunks_and_blocks()
691
692 self._image.seek(self._sparse_end, os.SEEK_SET)
693 self._image.write(struct.pack(ImageChunk.FORMAT,
694 ImageChunk.TYPE_FILL,
695 0, # Reserved
696 size / self.block_size,
697 4 + struct.calcsize(ImageChunk.FORMAT)))
698 self._image.write(fill_data)
699 self._read_header()
700
701 def seek(self, offset):
702 """Sets the cursor position for reading from unsparsified file.
703
704 Arguments:
705 offset: Offset to seek to from the beginning of the file.
706 """
707 self._file_pos = offset
708
709 def read(self, size):
710 """Reads data from the unsparsified file.
711
712 This method may return fewer than |size| bytes of data if the end
713 of the file was encountered.
714
715 The file cursor for reading is advanced by the number of bytes
716 read.
717
718 Arguments:
719 size: Number of bytes to read.
720
721 Returns:
722 The data.
723
724 """
725 if not self.is_sparse:
726 self._image.seek(self._file_pos)
727 data = self._image.read(size)
728 self._file_pos += len(data)
729 return data
730
731 # Iterate over all chunks.
732 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
733 self._file_pos) - 1
734 data = bytearray()
735 to_go = size
736 while to_go > 0:
737 chunk = self._chunks[chunk_idx]
738 chunk_pos_offset = self._file_pos - chunk.output_offset
739 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
740
741 if chunk.chunk_type == ImageChunk.TYPE_RAW:
742 self._image.seek(chunk.input_offset + chunk_pos_offset)
743 data.extend(self._image.read(chunk_pos_to_go))
744 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
745 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
746 offset_mod = chunk_pos_offset % len(chunk.fill_data)
747 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
748 else:
749 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
750 data.extend('\0' * chunk_pos_to_go)
751
752 to_go -= chunk_pos_to_go
753 self._file_pos += chunk_pos_to_go
754 chunk_idx += 1
755 # Generate partial read in case of EOF.
756 if chunk_idx >= len(self._chunks):
757 break
758
759 return data
760
761 def tell(self):
762 """Returns the file cursor position for reading from unsparsified file.
763
764 Returns:
765 The file cursor position for reading.
766 """
767 return self._file_pos
768
769 def truncate(self, size):
770 """Truncates the unsparsified file.
771
772 Arguments:
773 size: Desired size of unsparsified file.
774
775 Raises:
776 ValueError: If desired size isn't a multiple of the block size.
777 """
778 if not self.is_sparse:
779 self._image.truncate(size)
780 self._read_header()
781 return
782
783 if size % self.block_size != 0:
784 raise ValueError('Cannot truncate to a size which is not a multiple '
785 'of the block size')
786
787 if size == self.image_size:
788 # Trivial where there's nothing to do.
789 return
790 elif size < self.image_size:
791 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
792 chunk = self._chunks[chunk_idx]
793 if chunk.output_offset != size:
794 # Truncation in the middle of a trunk - need to keep the chunk
795 # and modify it.
796 chunk_idx_for_update = chunk_idx + 1
797 num_to_keep = size - chunk.output_offset
798 assert num_to_keep % self.block_size == 0
799 if chunk.chunk_type == ImageChunk.TYPE_RAW:
800 truncate_at = (chunk.chunk_offset +
801 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
802 data_sz = num_to_keep
803 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
804 truncate_at = (chunk.chunk_offset +
805 struct.calcsize(ImageChunk.FORMAT) + 4)
806 data_sz = 4
807 else:
808 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
809 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
810 data_sz = 0
811 chunk_sz = num_to_keep/self.block_size
812 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
813 self._image.seek(chunk.chunk_offset)
814 self._image.write(struct.pack(ImageChunk.FORMAT,
815 chunk.chunk_type,
816 0, # Reserved
817 chunk_sz,
818 total_sz))
819 chunk.output_size = num_to_keep
820 else:
821 # Truncation at trunk boundary.
822 truncate_at = chunk.chunk_offset
823 chunk_idx_for_update = chunk_idx
824
825 self._num_total_chunks = chunk_idx_for_update
826 self._num_total_blocks = 0
827 for i in range(0, chunk_idx_for_update):
828 self._num_total_blocks += self._chunks[i].output_size / self.block_size
829 self._update_chunks_and_blocks()
830 self._image.truncate(truncate_at)
831
832 # We've modified the file so re-read all data.
833 self._read_header()
834 else:
835 # Truncating to grow - just add a DONT_CARE section.
836 self.append_dont_care(size - self.image_size)
837
838
David Zeuthen21e95262016-07-27 17:58:40 -0400839class AvbDescriptor(object):
840 """Class for AVB descriptor.
841
842 See the |AvbDescriptor| C struct for more information.
843
844 Attributes:
845 tag: The tag identifying what kind of descriptor this is.
846 data: The data in the descriptor.
847 """
848
849 SIZE = 16
850 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
851
852 def __init__(self, data):
853 """Initializes a new property descriptor.
854
855 Arguments:
856 data: If not None, must be a bytearray().
857
858 Raises:
859 LookupError: If the given descriptor is malformed.
860 """
861 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
862
863 if data:
864 (self.tag, num_bytes_following) = (
865 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
866 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
867 else:
868 self.tag = None
869 self.data = None
870
871 def print_desc(self, o):
872 """Print the descriptor.
873
874 Arguments:
875 o: The object to write the output to.
876 """
877 o.write(' Unknown descriptor:\n')
878 o.write(' Tag: {}\n'.format(self.tag))
879 if len(self.data) < 256:
880 o.write(' Data: {} ({} bytes)\n'.format(
881 repr(str(self.data)), len(self.data)))
882 else:
883 o.write(' Data: {} bytes\n'.format(len(self.data)))
884
885 def encode(self):
886 """Serializes the descriptor.
887
888 Returns:
889 A bytearray() with the descriptor data.
890 """
891 num_bytes_following = len(self.data)
892 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
893 padding_size = nbf_with_padding - num_bytes_following
894 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
895 padding = struct.pack(str(padding_size) + 'x')
896 ret = desc + self.data + padding
897 return bytearray(ret)
898
899
900class AvbPropertyDescriptor(AvbDescriptor):
901 """A class for property descriptors.
902
903 See the |AvbPropertyDescriptor| C struct for more information.
904
905 Attributes:
906 key: The key.
907 value: The key.
908 """
909
910 TAG = 0
911 SIZE = 32
912 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
913 'Q' # key size (bytes)
914 'Q') # value size (bytes)
915
916 def __init__(self, data=None):
917 """Initializes a new property descriptor.
918
919 Arguments:
920 data: If not None, must be a bytearray of size |SIZE|.
921
922 Raises:
923 LookupError: If the given descriptor is malformed.
924 """
925 AvbDescriptor.__init__(self, None)
926 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
927
928 if data:
929 (tag, num_bytes_following, key_size,
930 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
931 expected_size = round_to_multiple(
932 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
933 if tag != self.TAG or num_bytes_following != expected_size:
934 raise LookupError('Given data does not look like a property '
935 'descriptor.')
936 self.key = data[self.SIZE:(self.SIZE + key_size)]
937 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
938 value_size)]
939 else:
940 self.key = ''
941 self.value = ''
942
943 def print_desc(self, o):
944 """Print the descriptor.
945
946 Arguments:
947 o: The object to write the output to.
948 """
949 if len(self.value) < 256:
950 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
951 else:
952 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
953
954 def encode(self):
955 """Serializes the descriptor.
956
957 Returns:
958 A bytearray() with the descriptor data.
959 """
960 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
961 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
962 padding_size = nbf_with_padding - num_bytes_following
963 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
964 len(self.key), len(self.value))
965 padding = struct.pack(str(padding_size) + 'x')
966 ret = desc + self.key + '\0' + self.value + '\0' + padding
967 return bytearray(ret)
968
969
970class AvbHashtreeDescriptor(AvbDescriptor):
971 """A class for hashtree descriptors.
972
973 See the |AvbHashtreeDescriptor| C struct for more information.
974
975 Attributes:
976 dm_verity_version: dm-verity version used.
977 image_size: Size of the image, after rounding up to |block_size|.
978 tree_offset: Offset of the hash tree in the file.
979 tree_size: Size of the tree.
980 data_block_size: Data block size
981 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -0400982 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
983 fec_offset: Offset of FEC data (0 if FEC is not used).
984 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -0400985 hash_algorithm: Hash algorithm used.
986 partition_name: Partition name.
987 salt: Salt used.
988 root_digest: Root digest.
989 """
990
991 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -0400992 RESERVED = 64
993 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -0400994 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
995 'L' # dm-verity version used
996 'Q' # image size (bytes)
997 'Q' # tree offset (bytes)
998 'Q' # tree size (bytes)
999 'L' # data block size (bytes)
1000 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001001 'L' # FEC number of roots
1002 'Q' # FEC offset (bytes)
1003 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001004 '32s' # hash algorithm used
1005 'L' # partition name (bytes)
1006 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001007 'L' + # root digest length (bytes)
1008 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001009
1010 def __init__(self, data=None):
1011 """Initializes a new hashtree descriptor.
1012
1013 Arguments:
1014 data: If not None, must be a bytearray of size |SIZE|.
1015
1016 Raises:
1017 LookupError: If the given descriptor is malformed.
1018 """
1019 AvbDescriptor.__init__(self, None)
1020 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1021
1022 if data:
1023 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1024 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001025 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1026 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001027 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1028 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001029 expected_size = round_to_multiple(
1030 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1031 if tag != self.TAG or num_bytes_following != expected_size:
1032 raise LookupError('Given data does not look like a hashtree '
1033 'descriptor.')
1034 # Nuke NUL-bytes at the end.
1035 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1036 o = 0
1037 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1038 partition_name_len)])
1039 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1040 self.partition_name.decode('utf-8')
1041 o += partition_name_len
1042 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1043 o += salt_len
1044 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1045 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1046 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1047
1048 else:
1049 self.dm_verity_version = 0
1050 self.image_size = 0
1051 self.tree_offset = 0
1052 self.tree_size = 0
1053 self.data_block_size = 0
1054 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001055 self.fec_num_roots = 0
1056 self.fec_offset = 0
1057 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001058 self.hash_algorithm = ''
1059 self.partition_name = ''
1060 self.salt = bytearray()
1061 self.root_digest = bytearray()
1062
1063 def print_desc(self, o):
1064 """Print the descriptor.
1065
1066 Arguments:
1067 o: The object to write the output to.
1068 """
1069 o.write(' Hashtree descriptor:\n')
1070 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1071 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1072 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1073 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1074 o.write(' Data Block Size: {} bytes\n'.format(
1075 self.data_block_size))
1076 o.write(' Hash Block Size: {} bytes\n'.format(
1077 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001078 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1079 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1080 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001081 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1082 o.write(' Partition Name: {}\n'.format(self.partition_name))
1083 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1084 'hex')))
1085 o.write(' Root Digest: {}\n'.format(str(
1086 self.root_digest).encode('hex')))
1087
1088 def encode(self):
1089 """Serializes the descriptor.
1090
1091 Returns:
1092 A bytearray() with the descriptor data.
1093 """
1094 encoded_name = self.partition_name.encode('utf-8')
1095 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1096 len(self.root_digest) - 16)
1097 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1098 padding_size = nbf_with_padding - num_bytes_following
1099 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1100 self.dm_verity_version, self.image_size,
1101 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001102 self.hash_block_size, self.fec_num_roots,
1103 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001104 len(encoded_name), len(self.salt), len(self.root_digest),
1105 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001106 padding = struct.pack(str(padding_size) + 'x')
1107 ret = desc + encoded_name + self.salt + self.root_digest + padding
1108 return bytearray(ret)
1109
1110
1111class AvbHashDescriptor(AvbDescriptor):
1112 """A class for hash descriptors.
1113
1114 See the |AvbHashDescriptor| C struct for more information.
1115
1116 Attributes:
1117 image_size: Image size, in bytes.
1118 hash_algorithm: Hash algorithm used.
1119 partition_name: Partition name.
1120 salt: Salt used.
1121 digest: The hash value of salt and data combined.
1122 """
1123
1124 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001125 RESERVED = 64
1126 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001127 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1128 'Q' # image size (bytes)
1129 '32s' # hash algorithm used
1130 'L' # partition name (bytes)
1131 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001132 'L' + # digest length (bytes)
1133 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001134
1135 def __init__(self, data=None):
1136 """Initializes a new hash descriptor.
1137
1138 Arguments:
1139 data: If not None, must be a bytearray of size |SIZE|.
1140
1141 Raises:
1142 LookupError: If the given descriptor is malformed.
1143 """
1144 AvbDescriptor.__init__(self, None)
1145 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1146
1147 if data:
1148 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1149 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001150 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001151 expected_size = round_to_multiple(
1152 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1153 if tag != self.TAG or num_bytes_following != expected_size:
1154 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1155 # Nuke NUL-bytes at the end.
1156 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1157 o = 0
1158 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1159 partition_name_len)])
1160 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1161 self.partition_name.decode('utf-8')
1162 o += partition_name_len
1163 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1164 o += salt_len
1165 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1166 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1167 raise LookupError('digest_len doesn\'t match hash algorithm')
1168
1169 else:
1170 self.image_size = 0
1171 self.hash_algorithm = ''
1172 self.partition_name = ''
1173 self.salt = bytearray()
1174 self.digest = bytearray()
1175
1176 def print_desc(self, o):
1177 """Print the descriptor.
1178
1179 Arguments:
1180 o: The object to write the output to.
1181 """
1182 o.write(' Hash descriptor:\n')
1183 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1184 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1185 o.write(' Partition Name: {}\n'.format(self.partition_name))
1186 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1187 'hex')))
1188 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1189 'hex')))
1190
1191 def encode(self):
1192 """Serializes the descriptor.
1193
1194 Returns:
1195 A bytearray() with the descriptor data.
1196 """
1197 encoded_name = self.partition_name.encode('utf-8')
1198 num_bytes_following = (
1199 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1200 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1201 padding_size = nbf_with_padding - num_bytes_following
1202 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1203 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001204 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001205 padding = struct.pack(str(padding_size) + 'x')
1206 ret = desc + encoded_name + self.salt + self.digest + padding
1207 return bytearray(ret)
1208
1209
1210class AvbKernelCmdlineDescriptor(AvbDescriptor):
1211 """A class for kernel command-line descriptors.
1212
1213 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1214
1215 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001216 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001217 kernel_cmdline: The kernel command-line.
1218 """
1219
1220 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001221 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001222 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001223 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001224 'L') # cmdline length (bytes)
1225
David Zeuthenfd41eb92016-11-17 12:24:47 -05001226 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1227 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1228
David Zeuthen21e95262016-07-27 17:58:40 -04001229 def __init__(self, data=None):
1230 """Initializes a new kernel cmdline descriptor.
1231
1232 Arguments:
1233 data: If not None, must be a bytearray of size |SIZE|.
1234
1235 Raises:
1236 LookupError: If the given descriptor is malformed.
1237 """
1238 AvbDescriptor.__init__(self, None)
1239 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1240
1241 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001242 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001243 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1244 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1245 8)
1246 if tag != self.TAG or num_bytes_following != expected_size:
1247 raise LookupError('Given data does not look like a kernel cmdline '
1248 'descriptor.')
1249 # Nuke NUL-bytes at the end.
1250 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1251 kernel_cmdline_length)])
1252 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1253 self.kernel_cmdline.decode('utf-8')
1254 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001255 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001256 self.kernel_cmdline = ''
1257
1258 def print_desc(self, o):
1259 """Print the descriptor.
1260
1261 Arguments:
1262 o: The object to write the output to.
1263 """
1264 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001265 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001266 o.write(' Kernel Cmdline: {}\n'.format(repr(
1267 self.kernel_cmdline)))
1268
1269 def encode(self):
1270 """Serializes the descriptor.
1271
1272 Returns:
1273 A bytearray() with the descriptor data.
1274 """
1275 encoded_str = self.kernel_cmdline.encode('utf-8')
1276 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1277 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1278 padding_size = nbf_with_padding - num_bytes_following
1279 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001280 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001281 padding = struct.pack(str(padding_size) + 'x')
1282 ret = desc + encoded_str + padding
1283 return bytearray(ret)
1284
1285
1286class AvbChainPartitionDescriptor(AvbDescriptor):
1287 """A class for chained partition descriptors.
1288
1289 See the |AvbChainPartitionDescriptor| C struct for more information.
1290
1291 Attributes:
1292 rollback_index_slot: The rollback index slot to use.
1293 partition_name: Partition name.
1294 public_key: Bytes for the public key.
1295 """
1296
1297 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001298 RESERVED = 64
1299 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001300 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1301 'L' # rollback_index_slot
1302 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001303 'L' + # public_key_size (bytes)
1304 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001305
1306 def __init__(self, data=None):
1307 """Initializes a new chain partition descriptor.
1308
1309 Arguments:
1310 data: If not None, must be a bytearray of size |SIZE|.
1311
1312 Raises:
1313 LookupError: If the given descriptor is malformed.
1314 """
1315 AvbDescriptor.__init__(self, None)
1316 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1317
1318 if data:
1319 (tag, num_bytes_following, self.rollback_index_slot, partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001320 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001321 expected_size = round_to_multiple(
1322 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1323 if tag != self.TAG or num_bytes_following != expected_size:
1324 raise LookupError('Given data does not look like a chain partition '
1325 'descriptor.')
1326 o = 0
1327 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1328 partition_name_len)])
1329 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1330 self.partition_name.decode('utf-8')
1331 o += partition_name_len
1332 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1333
1334 else:
1335 self.rollback_index_slot = 0
1336 self.partition_name = ''
1337 self.public_key = bytearray()
1338
1339 def print_desc(self, o):
1340 """Print the descriptor.
1341
1342 Arguments:
1343 o: The object to write the output to.
1344 """
1345 o.write(' Chain Partition descriptor:\n')
1346 o.write(' Partition Name: {}\n'.format(self.partition_name))
1347 o.write(' Rollback Index Slot: {}\n'.format(
1348 self.rollback_index_slot))
1349 # Just show the SHA1 of the key, for size reasons.
1350 hexdig = hashlib.sha1(self.public_key).hexdigest()
1351 o.write(' Public key (sha1): {}\n'.format(hexdig))
1352
1353 def encode(self):
1354 """Serializes the descriptor.
1355
1356 Returns:
1357 A bytearray() with the descriptor data.
1358 """
1359 encoded_name = self.partition_name.encode('utf-8')
1360 num_bytes_following = (
1361 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1362 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1363 padding_size = nbf_with_padding - num_bytes_following
1364 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1365 self.rollback_index_slot, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001366 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001367 padding = struct.pack(str(padding_size) + 'x')
1368 ret = desc + encoded_name + self.public_key + padding
1369 return bytearray(ret)
1370
1371
1372DESCRIPTOR_CLASSES = [
1373 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1374 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1375]
1376
1377
1378def parse_descriptors(data):
1379 """Parses a blob of data into descriptors.
1380
1381 Arguments:
1382 data: A bytearray() with encoded descriptors.
1383
1384 Returns:
1385 A list of instances of objects derived from AvbDescriptor. For
1386 unknown descriptors, the class AvbDescriptor is used.
1387 """
1388 o = 0
1389 ret = []
1390 while o < len(data):
1391 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1392 if tag < len(DESCRIPTOR_CLASSES):
1393 c = DESCRIPTOR_CLASSES[tag]
1394 else:
1395 c = AvbDescriptor
1396 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1397 o += 16 + nb_following
1398 return ret
1399
1400
1401class AvbFooter(object):
1402 """A class for parsing and writing footers.
1403
1404 Footers are stored at the end of partitions and point to where the
1405 AvbVBMeta blob is located. They also contain the original size of
1406 the image before AVB information was added.
1407
1408 Attributes:
1409 magic: Magic for identifying the footer, see |MAGIC|.
1410 version_major: The major version of avbtool that wrote the footer.
1411 version_minor: The minor version of avbtool that wrote the footer.
1412 original_image_size: Original image size.
1413 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1414 vbmeta_size: Size of the AvbVBMeta blob.
1415 """
1416
1417 MAGIC = 'AVBf'
1418 SIZE = 64
1419 RESERVED = 28
1420 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1421 'Q' # Original image size.
1422 'Q' # Offset of VBMeta blob.
1423 'Q' + # Size of VBMeta blob.
1424 str(RESERVED) + 'x') # padding for reserved bytes
1425
1426 def __init__(self, data=None):
1427 """Initializes a new footer object.
1428
1429 Arguments:
1430 data: If not None, must be a bytearray of size 4096.
1431
1432 Raises:
1433 LookupError: If the given footer is malformed.
1434 struct.error: If the given data has no footer.
1435 """
1436 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1437
1438 if data:
1439 (self.magic, self.version_major, self.version_minor,
1440 self.original_image_size, self.vbmeta_offset,
1441 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1442 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001443 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001444 else:
1445 self.magic = self.MAGIC
1446 self.version_major = AVB_VERSION_MAJOR
1447 self.version_minor = AVB_VERSION_MINOR
1448 self.original_image_size = 0
1449 self.vbmeta_offset = 0
1450 self.vbmeta_size = 0
1451
David Zeuthena4fee8b2016-08-22 15:20:43 -04001452 def encode(self):
1453 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001454
David Zeuthena4fee8b2016-08-22 15:20:43 -04001455 Returns:
1456 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001457 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001458 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1459 self.version_minor, self.original_image_size,
1460 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001461
1462
1463class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001464 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001465
1466 Attributes:
1467 The attributes correspond to the |AvbVBMetaHeader| struct
1468 defined in avb_vbmeta_header.h.
1469 """
1470
1471 SIZE = 256
1472
1473 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001474 RESERVED = 132
David Zeuthen21e95262016-07-27 17:58:40 -04001475
1476 # Keep in sync with |AvbVBMetaImageHeader|.
1477 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1478 '2Q' # 2 x block size
1479 'L' # algorithm type
1480 '2Q' # offset, size (hash)
1481 '2Q' # offset, size (signature)
1482 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001483 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001484 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001485 'Q' # rollback_index
1486 'L' + # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001487 str(RESERVED) + 'x') # padding for reserved bytes
1488
1489 def __init__(self, data=None):
1490 """Initializes a new header object.
1491
1492 Arguments:
1493 data: If not None, must be a bytearray of size 8192.
1494
1495 Raises:
1496 Exception: If the given data is malformed.
1497 """
1498 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1499
1500 if data:
1501 (self.magic, self.header_version_major, self.header_version_minor,
1502 self.authentication_data_block_size, self.auxiliary_data_block_size,
1503 self.algorithm_type, self.hash_offset, self.hash_size,
1504 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001505 self.public_key_size, self.public_key_metadata_offset,
1506 self.public_key_metadata_size, self.descriptors_offset,
1507 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001508 self.rollback_index,
1509 self.flags) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001510 # Nuke NUL-bytes at the end of the string.
1511 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001512 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001513 else:
1514 self.magic = 'AVB0'
1515 self.header_version_major = AVB_VERSION_MAJOR
1516 self.header_version_minor = AVB_VERSION_MINOR
1517 self.authentication_data_block_size = 0
1518 self.auxiliary_data_block_size = 0
1519 self.algorithm_type = 0
1520 self.hash_offset = 0
1521 self.hash_size = 0
1522 self.signature_offset = 0
1523 self.signature_size = 0
1524 self.public_key_offset = 0
1525 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001526 self.public_key_metadata_offset = 0
1527 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001528 self.descriptors_offset = 0
1529 self.descriptors_size = 0
1530 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001531 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001532
1533 def save(self, output):
1534 """Serializes the header (256 bytes) to disk.
1535
1536 Arguments:
1537 output: The object to write the output to.
1538 """
1539 output.write(struct.pack(
1540 self.FORMAT_STRING, self.magic, self.header_version_major,
1541 self.header_version_minor, self.authentication_data_block_size,
1542 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1543 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001544 self.public_key_offset, self.public_key_size,
1545 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001546 self.descriptors_offset, self.descriptors_size, self.rollback_index,
1547 self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001548
1549 def encode(self):
1550 """Serializes the header (256) to a bytearray().
1551
1552 Returns:
1553 A bytearray() with the encoded header.
1554 """
1555 return struct.pack(self.FORMAT_STRING, self.magic,
1556 self.header_version_major, self.header_version_minor,
1557 self.authentication_data_block_size,
1558 self.auxiliary_data_block_size, self.algorithm_type,
1559 self.hash_offset, self.hash_size, self.signature_offset,
1560 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001561 self.public_key_size, self.public_key_metadata_offset,
1562 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001563 self.descriptors_size, self.rollback_index, self.flags)
David Zeuthen21e95262016-07-27 17:58:40 -04001564
1565
1566class Avb(object):
1567 """Business logic for avbtool command-line tool."""
1568
David Zeuthen8b6973b2016-09-20 12:39:49 -04001569 # Keep in sync with avb_ab_flow.h.
1570 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1571 AB_MAGIC = '\0AB0'
1572 AB_MAJOR_VERSION = 1
1573 AB_MINOR_VERSION = 0
1574 AB_MISC_METADATA_OFFSET = 2048
1575
David Zeuthen09692692016-09-30 16:16:40 -04001576 # Constants for maximum metadata size. These are used to give
1577 # meaningful errors if the value passed in via --partition_size is
1578 # too small and when --calc_max_image_size is used. We use
1579 # conservative figures.
1580 MAX_VBMETA_SIZE = 64 * 1024
1581 MAX_FOOTER_SIZE = 4096
1582
David Zeuthena4fee8b2016-08-22 15:20:43 -04001583 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001584 """Implements the 'erase_footer' command.
1585
1586 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001587 image_filename: File to erase a footer from.
David Zeuthen21e95262016-07-27 17:58:40 -04001588 keep_hashtree: If True, keep the hashtree around.
1589
1590 Raises:
1591 AvbError: If there's no footer in the image.
1592 """
1593
David Zeuthena4fee8b2016-08-22 15:20:43 -04001594 image = ImageHandler(image_filename)
1595
David Zeuthen21e95262016-07-27 17:58:40 -04001596 (footer, _, descriptors, _) = self._parse_image(image)
1597
1598 if not footer:
1599 raise AvbError('Given image does not have a footer.')
1600
1601 new_image_size = None
1602 if not keep_hashtree:
1603 new_image_size = footer.original_image_size
1604 else:
1605 # If requested to keep the hashtree, search for a hashtree
1606 # descriptor to figure out the location and size of the hashtree.
1607 for desc in descriptors:
1608 if isinstance(desc, AvbHashtreeDescriptor):
1609 # The hashtree is always just following the main data so the
1610 # new size is easily derived.
1611 new_image_size = desc.tree_offset + desc.tree_size
1612 break
1613 if not new_image_size:
1614 raise AvbError('Requested to keep hashtree but no hashtree '
1615 'descriptor was found.')
1616
1617 # And cut...
1618 image.truncate(new_image_size)
1619
David Zeuthen8b6973b2016-09-20 12:39:49 -04001620 def set_ab_metadata(self, misc_image, slot_data):
1621 """Implements the 'set_ab_metadata' command.
1622
1623 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1624 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1625
1626 Arguments:
1627 misc_image: The misc image to write to.
1628 slot_data: Slot data as a string
1629
1630 Raises:
1631 AvbError: If slot data is malformed.
1632 """
1633 tokens = slot_data.split(':')
1634 if len(tokens) != 6:
1635 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1636 a_priority = int(tokens[0])
1637 a_tries_remaining = int(tokens[1])
1638 a_success = True if int(tokens[2]) != 0 else False
1639 b_priority = int(tokens[3])
1640 b_tries_remaining = int(tokens[4])
1641 b_success = True if int(tokens[5]) != 0 else False
1642
1643 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1644 self.AB_MAGIC,
1645 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1646 a_priority, a_tries_remaining, a_success,
1647 b_priority, b_tries_remaining, b_success)
1648 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1649 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1650 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1651 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1652 misc_image.write(ab_data)
1653
David Zeuthena4fee8b2016-08-22 15:20:43 -04001654 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001655 """Implements the 'info_image' command.
1656
1657 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001658 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001659 output: Output file to write human-readable information to (file object).
1660 """
1661
David Zeuthena4fee8b2016-08-22 15:20:43 -04001662 image = ImageHandler(image_filename)
1663
David Zeuthen21e95262016-07-27 17:58:40 -04001664 o = output
1665
1666 (footer, header, descriptors, image_size) = self._parse_image(image)
1667
1668 if footer:
1669 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1670 footer.version_minor))
1671 o.write('Image size: {} bytes\n'.format(image_size))
1672 o.write('Original image size: {} bytes\n'.format(
1673 footer.original_image_size))
1674 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1675 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1676 o.write('--\n')
1677
1678 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1679
David Zeuthena4fee8b2016-08-22 15:20:43 -04001680 o.write('VBMeta image version: {}.{}{}\n'.format(
1681 header.header_version_major, header.header_version_minor,
1682 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001683 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1684 o.write('Authentication Block: {} bytes\n'.format(
1685 header.authentication_data_block_size))
1686 o.write('Auxiliary Block: {} bytes\n'.format(
1687 header.auxiliary_data_block_size))
1688 o.write('Algorithm: {}\n'.format(alg_name))
1689 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001690 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001691
1692 # Print descriptors.
1693 num_printed = 0
1694 o.write('Descriptors:\n')
1695 for desc in descriptors:
1696 desc.print_desc(o)
1697 num_printed += 1
1698 if num_printed == 0:
1699 o.write(' (none)\n')
1700
1701 def _parse_image(self, image):
1702 """Gets information about an image.
1703
1704 The image can either be a vbmeta or an image with a footer.
1705
1706 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001707 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001708
1709 Returns:
1710 A tuple where the first argument is a AvbFooter (None if there
1711 is no footer on the image), the second argument is a
1712 AvbVBMetaHeader, the third argument is a list of
1713 AvbDescriptor-derived instances, and the fourth argument is the
1714 size of |image|.
1715 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001716 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001717 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001718 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001719 try:
1720 footer = AvbFooter(image.read(AvbFooter.SIZE))
1721 except (LookupError, struct.error):
1722 # Nope, just seek back to the start.
1723 image.seek(0)
1724
1725 vbmeta_offset = 0
1726 if footer:
1727 vbmeta_offset = footer.vbmeta_offset
1728
1729 image.seek(vbmeta_offset)
1730 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1731
1732 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1733 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1734 desc_start_offset = aux_block_offset + h.descriptors_offset
1735 image.seek(desc_start_offset)
1736 descriptors = parse_descriptors(image.read(h.descriptors_size))
1737
David Zeuthen09692692016-09-30 16:16:40 -04001738 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001739
David Zeuthenfd41eb92016-11-17 12:24:47 -05001740 def _get_cmdline_descriptors_for_dm_verity(self, image):
1741 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001742
1743 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001744 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001745
1746 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001747 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1748 instructions. There is one for when hashtree is not disabled and one for
1749 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001750
1751 Raises:
1752 AvbError: If |image| doesn't have a hashtree descriptor.
1753
1754 """
1755
1756 (_, _, descriptors, _) = self._parse_image(image)
1757
1758 ht = None
1759 for desc in descriptors:
1760 if isinstance(desc, AvbHashtreeDescriptor):
1761 ht = desc
1762 break
1763
1764 if not ht:
1765 raise AvbError('No hashtree descriptor in given image')
1766
1767 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001768 c += '0' # start
1769 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1770 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1771 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1772 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1773 c += ' {}'.format(ht.data_block_size) # data_block
1774 c += ' {}'.format(ht.hash_block_size) # hash_block
1775 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1776 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1777 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1778 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1779 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1780 if ht.fec_num_roots > 0:
1781 c += ' 9' # number of optional args
1782 c += ' ignore_zero_blocks'
1783 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1784 c += ' fec_roots {}'.format(ht.fec_num_roots)
1785 # Note that fec_blocks is the size that FEC covers, *not* the
1786 # size of the FEC data. Since we use FEC for everything up until
1787 # the FEC data, it's the same as the offset.
1788 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1789 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1790 else:
1791 c += ' 1' # number of optional args
1792 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001793 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001794
David Zeuthenfd41eb92016-11-17 12:24:47 -05001795 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001796 desc = AvbKernelCmdlineDescriptor()
1797 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001798 desc.flags = (
1799 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1800
1801 # The descriptor for when hashtree verification is disabled is a lot
1802 # simpler - we just set the root to the partition.
1803 desc_no_ht = AvbKernelCmdlineDescriptor()
1804 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1805 desc_no_ht.flags = (
1806 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1807
1808 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001809
1810 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001811 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001812 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001813 generate_dm_verity_cmdline_from_hashtree,
1814 include_descriptors_from_image):
1815 """Implements the 'make_vbmeta_image' command.
1816
1817 Arguments:
1818 output: File to write the image to.
1819 chain_partitions: List of partitions to chain.
1820 algorithm_name: Name of algorithm to use.
1821 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001822 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001823 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001824 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001825 props: Properties to insert (list of strings of the form 'key:value').
1826 props_from_file: Properties to insert (list of strings 'key:<path>').
1827 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1828 generate_dm_verity_cmdline_from_hashtree: None or file to generate from.
1829 include_descriptors_from_image: List of file objects with descriptors.
1830
1831 Raises:
1832 AvbError: If a chained partition is malformed.
1833 """
1834
1835 descriptors = []
1836
1837 # Insert chained partition descriptors.
1838 if chain_partitions:
1839 for cp in chain_partitions:
1840 cp_tokens = cp.split(':')
1841 if len(cp_tokens) != 3:
1842 raise AvbError('Malformed chained partition "{}".'.format(cp))
1843 desc = AvbChainPartitionDescriptor()
1844 desc.partition_name = cp_tokens[0]
1845 desc.rollback_index_slot = int(cp_tokens[1])
1846 if desc.rollback_index_slot < 1:
1847 raise AvbError('Rollback index slot must be 1 or larger.')
1848 file_path = cp_tokens[2]
1849 desc.public_key = open(file_path, 'rb').read()
1850 descriptors.append(desc)
1851
1852 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001853 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001854 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001855 generate_dm_verity_cmdline_from_hashtree,
1856 include_descriptors_from_image)
1857
1858 # Write entire vbmeta blob (header, authentication, auxiliary).
1859 output.seek(0)
1860 output.write(vbmeta_blob)
1861
David Zeuthen18666ab2016-11-15 11:18:05 -05001862 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1863 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001864 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001865 kernel_cmdlines,
1866 generate_dm_verity_cmdline_from_hashtree,
1867 include_descriptors_from_image):
1868 """Generates a VBMeta blob.
1869
1870 This blob contains the header (struct AvbVBMetaHeader), the
1871 authentication data block (which contains the hash and signature
1872 for the header and auxiliary block), and the auxiliary block
1873 (which contains descriptors, the public key used, and other data).
1874
1875 The |key| parameter can |None| only if the |algorithm_name| is
1876 'NONE'.
1877
1878 Arguments:
1879 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1880 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001881 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001882 descriptors: A list of descriptors to insert or None.
1883 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001884 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001885 props: Properties to insert (List of strings of the form 'key:value').
1886 props_from_file: Properties to insert (List of strings 'key:<path>').
1887 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1888 generate_dm_verity_cmdline_from_hashtree: None or file to generate
1889 dm-verity kernel cmdline from.
1890 include_descriptors_from_image: List of file objects for which
1891 to insert descriptors from.
1892
1893 Returns:
1894 A bytearray() with the VBMeta blob.
1895
1896 Raises:
1897 Exception: If the |algorithm_name| is not found, if no key has
1898 been given and the given algorithm requires one, or the key is
1899 of the wrong size.
1900
1901 """
1902 try:
1903 alg = ALGORITHMS[algorithm_name]
1904 except KeyError:
1905 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1906
1907 # Descriptors.
1908 encoded_descriptors = bytearray()
1909 if descriptors:
1910 for desc in descriptors:
1911 encoded_descriptors.extend(desc.encode())
1912
1913 # Add properties.
1914 if props:
1915 for prop in props:
1916 idx = prop.find(':')
1917 if idx == -1:
1918 raise AvbError('Malformed property "{}".'.format(prop))
1919 desc = AvbPropertyDescriptor()
1920 desc.key = prop[0:idx]
1921 desc.value = prop[(idx + 1):]
1922 encoded_descriptors.extend(desc.encode())
1923 if props_from_file:
1924 for prop in props_from_file:
1925 idx = prop.find(':')
1926 if idx == -1:
1927 raise AvbError('Malformed property "{}".'.format(prop))
1928 desc = AvbPropertyDescriptor()
1929 desc.key = prop[0:idx]
1930 desc.value = prop[(idx + 1):]
1931 file_path = prop[(idx + 1):]
1932 desc.value = open(file_path, 'rb').read()
1933 encoded_descriptors.extend(desc.encode())
1934
1935 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
1936 if generate_dm_verity_cmdline_from_hashtree:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001937 image_handler = ImageHandler(
1938 generate_dm_verity_cmdline_from_hashtree.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001939 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1940 encoded_descriptors.extend(cmdline_desc[0].encode())
1941 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001942
1943 # Add kernel command-lines.
1944 if kernel_cmdlines:
1945 for i in kernel_cmdlines:
1946 desc = AvbKernelCmdlineDescriptor()
1947 desc.kernel_cmdline = i
1948 encoded_descriptors.extend(desc.encode())
1949
1950 # Add descriptors from other images.
1951 if include_descriptors_from_image:
1952 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001953 image_handler = ImageHandler(image.name)
1954 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001955 for desc in image_descriptors:
1956 encoded_descriptors.extend(desc.encode())
1957
David Zeuthen18666ab2016-11-15 11:18:05 -05001958 # Load public key metadata blob, if requested.
1959 pkmd_blob = []
1960 if public_key_metadata_path:
1961 with open(public_key_metadata_path) as f:
1962 pkmd_blob = f.read()
1963
David Zeuthen21e95262016-07-27 17:58:40 -04001964 key = None
1965 encoded_key = bytearray()
1966 if alg.public_key_num_bytes > 0:
1967 if not key_path:
1968 raise AvbError('Key is required for algorithm {}'.format(
1969 algorithm_name))
1970 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
1971 encoded_key = encode_rsa_key(key)
1972 if len(encoded_key) != alg.public_key_num_bytes:
1973 raise AvbError('Key is wrong size for algorithm {}'.format(
1974 algorithm_name))
1975
1976 h = AvbVBMetaHeader()
1977
David Zeuthen18666ab2016-11-15 11:18:05 -05001978 # For the Auxiliary data block, descriptors are stored at offset 0,
1979 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001980 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05001981 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04001982 h.descriptors_offset = 0
1983 h.descriptors_size = len(encoded_descriptors)
1984 h.public_key_offset = h.descriptors_size
1985 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001986 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
1987 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04001988
1989 # For the Authentication data block, the hash is first and then
1990 # the signature.
1991 h.authentication_data_block_size = round_to_multiple(
1992 alg.hash_num_bytes + alg.public_key_num_bytes, 64)
1993 h.algorithm_type = alg.algorithm_type
1994 h.hash_offset = 0
1995 h.hash_size = alg.hash_num_bytes
1996 # Signature offset and size - it's stored right after the hash
1997 # (in Authentication data block).
1998 h.signature_offset = alg.hash_num_bytes
1999 h.signature_size = alg.signature_num_bytes
2000
2001 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002002 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002003
2004 # Generate Header data block.
2005 header_data_blob = h.encode()
2006
2007 # Generate Auxiliary data block.
2008 aux_data_blob = bytearray()
2009 aux_data_blob.extend(encoded_descriptors)
2010 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002011 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002012 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2013 aux_data_blob.extend('\0' * padding_bytes)
2014
2015 # Calculate the hash.
2016 binary_hash = bytearray()
2017 binary_signature = bytearray()
2018 if algorithm_name != 'NONE':
2019 if algorithm_name[0:6] == 'SHA256':
2020 ha = hashlib.sha256()
2021 elif algorithm_name[0:6] == 'SHA512':
2022 ha = hashlib.sha512()
2023 else:
2024 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2025 ha.update(header_data_blob)
2026 ha.update(aux_data_blob)
2027 binary_hash.extend(ha.digest())
2028
2029 # Calculate the signature.
2030 p = subprocess.Popen(
2031 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
2032 stdin=subprocess.PIPE,
2033 stdout=subprocess.PIPE,
2034 stderr=subprocess.PIPE)
2035 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
2036 (pout, perr) = p.communicate(padding_and_hash)
2037 retcode = p.wait()
2038 if retcode != 0:
2039 raise AvbError('Error signing: {}'.format(perr))
2040 binary_signature.extend(pout)
2041
2042 # Generate Authentication data block.
2043 auth_data_blob = bytearray()
2044 auth_data_blob.extend(binary_hash)
2045 auth_data_blob.extend(binary_signature)
2046 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2047 auth_data_blob.extend('\0' * padding_bytes)
2048
2049 return header_data_blob + auth_data_blob + aux_data_blob
2050
2051 def extract_public_key(self, key_path, output):
2052 """Implements the 'extract_public_key' command.
2053
2054 Arguments:
2055 key_path: The path to a RSA private key file.
2056 output: The file to write to.
2057 """
2058 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2059 write_rsa_key(output, key)
2060
David Zeuthena4fee8b2016-08-22 15:20:43 -04002061 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002062 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002063 public_key_metadata_path, rollback_index, props,
2064 props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002065 generate_dm_verity_cmdline_from_hashtree,
2066 include_descriptors_from_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002067 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002068
2069 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002070 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002071 partition_size: Size of partition.
2072 partition_name: Name of partition (without A/B suffix).
2073 hash_algorithm: Hash algorithm to use.
2074 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2075 algorithm_name: Name of algorithm to use.
2076 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002077 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002078 rollback_index: Rollback index.
2079 props: Properties to insert (List of strings of the form 'key:value').
2080 props_from_file: Properties to insert (List of strings 'key:<path>').
2081 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2082 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2083 dm-verity kernel cmdline from.
2084 include_descriptors_from_image: List of file objects for which
2085 to insert descriptors from.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002086
2087 Raises:
2088 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002089 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002090 image = ImageHandler(image_filename)
2091
2092 if partition_size % image.block_size != 0:
2093 raise AvbError('Partition size of {} is not a multiple of the image '
2094 'block size {}.'.format(partition_size,
2095 image.block_size))
2096
David Zeuthen21e95262016-07-27 17:58:40 -04002097 # If there's already a footer, truncate the image to its original
2098 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2099 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002100 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002101 try:
2102 footer = AvbFooter(image.read(AvbFooter.SIZE))
2103 # Existing footer found. Just truncate.
2104 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002105 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002106 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002107 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002108
2109 # If anything goes wrong from here-on, restore the image back to
2110 # its original size.
2111 try:
David Zeuthen09692692016-09-30 16:16:40 -04002112 # First, calculate the maximum image size such that an image
2113 # this size + metadata (footer + vbmeta struct) fits in
2114 # |partition_size|.
2115 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2116 max_image_size = partition_size - max_metadata_size
2117
2118 # If image size exceeds the maximum image size, fail.
2119 if image.image_size > max_image_size:
2120 raise AvbError('Image size of {} exceeds maximum image '
2121 'size of {} in order to fit in a partition '
2122 'size of {}.'.format(image.image_size, max_image_size,
2123 partition_size))
2124
David Zeuthen21e95262016-07-27 17:58:40 -04002125 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2126 if salt:
2127 salt = salt.decode('hex')
2128 else:
2129 if salt is None:
2130 # If salt is not explicitly specified, choose a hash
2131 # that's the same size as the hash size.
2132 hash_size = digest_size
2133 salt = open('/dev/urandom').read(hash_size)
2134 else:
2135 salt = ''
2136
2137 hasher = hashlib.new(name=hash_algorithm, string=salt)
2138 # TODO(zeuthen): might want to read this in chunks to avoid
2139 # memory pressure, then again, this is only supposed to be used
2140 # on kernel/initramfs partitions. Possible optimization.
2141 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002142 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002143 digest = hasher.digest()
2144
2145 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002146 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002147 h_desc.hash_algorithm = hash_algorithm
2148 h_desc.partition_name = partition_name
2149 h_desc.salt = salt
2150 h_desc.digest = digest
2151
David Zeuthenfd41eb92016-11-17 12:24:47 -05002152 # Flags are only allowed on top-level vbmeta struct.
2153 flags = 0
2154
David Zeuthen21e95262016-07-27 17:58:40 -04002155 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002156 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002157 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002158 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002159 generate_dm_verity_cmdline_from_hashtree,
2160 include_descriptors_from_image)
2161
David Zeuthena4fee8b2016-08-22 15:20:43 -04002162 # If the image isn't sparse, its size might not be a multiple of
2163 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002164 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002165 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002166 padding_needed = image.block_size - (image.image_size%image.block_size)
2167 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002168
David Zeuthena4fee8b2016-08-22 15:20:43 -04002169 # The append_raw() method requires content with size being a
2170 # multiple of |block_size| so add padding as needed. Also record
2171 # where this is written to since we'll need to put that in the
2172 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002173 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002174 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2175 len(vbmeta_blob))
2176 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2177 image.append_raw(vbmeta_blob_with_padding)
2178 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2179
2180 # Now insert a DONT_CARE chunk with enough bytes such that the
2181 # final Footer block is at the end of partition_size..
2182 image.append_dont_care(partition_size - vbmeta_end_offset -
2183 1*image.block_size)
2184
2185 # Generate the Footer that tells where the VBMeta footer
2186 # is. Also put enough padding in the front of the footer since
2187 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002188 footer = AvbFooter()
2189 footer.original_image_size = original_image_size
2190 footer.vbmeta_offset = vbmeta_offset
2191 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002192 footer_blob = footer.encode()
2193 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2194 footer_blob)
2195 image.append_raw(footer_blob_with_padding)
2196
David Zeuthen21e95262016-07-27 17:58:40 -04002197 except:
2198 # Truncate back to original size, then re-raise
2199 image.truncate(original_image_size)
2200 raise
2201
David Zeuthena4fee8b2016-08-22 15:20:43 -04002202 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002203 generate_fec, fec_num_roots, hash_algorithm,
2204 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002205 public_key_metadata_path, rollback_index,
2206 props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002207 generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002208 include_descriptors_from_image,
2209 calc_max_image_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002210 """Implements the 'add_hashtree_footer' command.
2211
2212 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2213 more information about dm-verity and these hashes.
2214
2215 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002216 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002217 partition_size: Size of partition.
2218 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002219 generate_fec: If True, generate FEC codes.
2220 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002221 hash_algorithm: Hash algorithm to use.
2222 block_size: Block size to use.
2223 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2224 algorithm_name: Name of algorithm to use.
2225 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002226 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002227 rollback_index: Rollback index.
2228 props: Properties to insert (List of strings of the form 'key:value').
2229 props_from_file: Properties to insert (List of strings 'key:<path>').
2230 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2231 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2232 dm-verity kernel cmdline from.
2233 include_descriptors_from_image: List of file objects for which
2234 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002235 calc_max_image_size: Don't store the hashtree or footer - instead
2236 calculate the maximum image size leaving enough room for hashtree
2237 and metadata with the given |partition_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002238
2239 Raises:
2240 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002241 """
David Zeuthen09692692016-09-30 16:16:40 -04002242 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2243 digest_padding = round_to_pow2(digest_size) - digest_size
2244
2245 # First, calculate the maximum image size such that an image
2246 # this size + the hashtree + metadata (footer + vbmeta struct)
2247 # fits in |partition_size|. We use very conservative figures for
2248 # metadata.
2249 (_, max_tree_size) = calc_hash_level_offsets(
2250 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002251 max_fec_size = 0
2252 if generate_fec:
2253 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2254 max_metadata_size = (max_fec_size + max_tree_size +
2255 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002256 self.MAX_FOOTER_SIZE)
2257 max_image_size = partition_size - max_metadata_size
2258
2259 # If we're asked to only calculate the maximum image size, we're done.
2260 if calc_max_image_size:
2261 print '{}'.format(max_image_size)
2262 return
2263
David Zeuthena4fee8b2016-08-22 15:20:43 -04002264 image = ImageHandler(image_filename)
2265
2266 if partition_size % image.block_size != 0:
2267 raise AvbError('Partition size of {} is not a multiple of the image '
2268 'block size {}.'.format(partition_size,
2269 image.block_size))
2270
David Zeuthen21e95262016-07-27 17:58:40 -04002271 # If there's already a footer, truncate the image to its original
2272 # size. This way 'avbtool add_hashtree_footer' is idempotent
2273 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002274 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002275 try:
2276 footer = AvbFooter(image.read(AvbFooter.SIZE))
2277 # Existing footer found. Just truncate.
2278 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002279 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002280 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002281 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002282
2283 # If anything goes wrong from here-on, restore the image back to
2284 # its original size.
2285 try:
2286 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002287 rounded_image_size = round_to_multiple(image.image_size, block_size)
2288 if rounded_image_size > image.image_size:
2289 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002290
David Zeuthen09692692016-09-30 16:16:40 -04002291 # If image size exceeds the maximum image size, fail.
2292 if image.image_size > max_image_size:
2293 raise AvbError('Image size of {} exceeds maximum image '
2294 'size of {} in order to fit in a partition '
2295 'size of {}.'.format(image.image_size, max_image_size,
2296 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002297
2298 if salt:
2299 salt = salt.decode('hex')
2300 else:
2301 if salt is None:
2302 # If salt is not explicitly specified, choose a hash
2303 # that's the same size as the hash size.
2304 hash_size = digest_size
2305 salt = open('/dev/urandom').read(hash_size)
2306 else:
2307 salt = ''
2308
David Zeuthena4fee8b2016-08-22 15:20:43 -04002309 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002310 # offsets in advance.
2311 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002312 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002313
David Zeuthena4fee8b2016-08-22 15:20:43 -04002314 # If the image isn't sparse, its size might not be a multiple of
2315 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002316 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002317 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002318 padding_needed = image.block_size - (image.image_size%image.block_size)
2319 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002320
David Zeuthena4fee8b2016-08-22 15:20:43 -04002321 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002322 tree_offset = image.image_size
2323 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002324 block_size,
2325 hash_algorithm, salt,
2326 digest_padding,
2327 hash_level_offsets,
2328 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002329
2330 # Generate HashtreeDescriptor with details about the tree we
2331 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002332 ht_desc = AvbHashtreeDescriptor()
2333 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002334 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002335 ht_desc.tree_offset = tree_offset
2336 ht_desc.tree_size = tree_size
2337 ht_desc.data_block_size = block_size
2338 ht_desc.hash_block_size = block_size
2339 ht_desc.hash_algorithm = hash_algorithm
2340 ht_desc.partition_name = partition_name
2341 ht_desc.salt = salt
2342 ht_desc.root_digest = root_digest
2343
David Zeuthen09692692016-09-30 16:16:40 -04002344 # Write the hash tree
2345 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2346 len(hash_tree))
2347 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2348 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002349 len_hashtree_and_fec = len(hash_tree_with_padding)
2350
2351 # Generate FEC codes, if requested.
2352 if generate_fec:
2353 fec_data = generate_fec_data(image_filename, fec_num_roots)
2354 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2355 len(fec_data))
2356 fec_data_with_padding = fec_data + '\0'*padding_needed
2357 fec_offset = image.image_size
2358 image.append_raw(fec_data_with_padding)
2359 len_hashtree_and_fec += len(fec_data_with_padding)
2360 # Update the hashtree descriptor.
2361 ht_desc.fec_num_roots = fec_num_roots
2362 ht_desc.fec_offset = fec_offset
2363 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002364
David Zeuthenfd41eb92016-11-17 12:24:47 -05002365 # Flags are only allowed on top-level vbmeta struct.
2366 flags = 0
2367
David Zeuthena4fee8b2016-08-22 15:20:43 -04002368 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002369 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002370 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002371 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002372 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002373 generate_dm_verity_cmdline_from_hashtree,
2374 include_descriptors_from_image)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002375 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2376 len(vbmeta_blob))
2377 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2378 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002379
David Zeuthena4fee8b2016-08-22 15:20:43 -04002380 # Now insert a DONT_CARE chunk with enough bytes such that the
2381 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002382 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002383 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002384
David Zeuthena4fee8b2016-08-22 15:20:43 -04002385 # Generate the Footer that tells where the VBMeta footer
2386 # is. Also put enough padding in the front of the footer since
2387 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002388 footer = AvbFooter()
2389 footer.original_image_size = original_image_size
2390 footer.vbmeta_offset = vbmeta_offset
2391 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002392 footer_blob = footer.encode()
2393 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2394 footer_blob)
2395 image.append_raw(footer_blob_with_padding)
2396
David Zeuthen21e95262016-07-27 17:58:40 -04002397 except:
David Zeuthen09692692016-09-30 16:16:40 -04002398 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002399 image.truncate(original_image_size)
2400 raise
2401
2402
2403def calc_hash_level_offsets(image_size, block_size, digest_size):
2404 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2405
2406 Arguments:
2407 image_size: The size of the image to calculate a Merkle-tree for.
2408 block_size: The block size, e.g. 4096.
2409 digest_size: The size of each hash, e.g. 32 for SHA-256.
2410
2411 Returns:
2412 A tuple where the first argument is an array of offsets and the
2413 second is size of the tree, in bytes.
2414 """
2415 level_offsets = []
2416 level_sizes = []
2417 tree_size = 0
2418
2419 num_levels = 0
2420 size = image_size
2421 while size > block_size:
2422 num_blocks = (size + block_size - 1) / block_size
2423 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2424
2425 level_sizes.append(level_size)
2426 tree_size += level_size
2427 num_levels += 1
2428
2429 size = level_size
2430
2431 for n in range(0, num_levels):
2432 offset = 0
2433 for m in range(n + 1, num_levels):
2434 offset += level_sizes[m]
2435 level_offsets.append(offset)
2436
David Zeuthena4fee8b2016-08-22 15:20:43 -04002437 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002438
2439
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002440# See system/extras/libfec/include/fec/io.h for these definitions.
2441FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2442FEC_MAGIC = 0xfecfecfe
2443
2444
2445def calc_fec_data_size(image_size, num_roots):
2446 """Calculates how much space FEC data will take.
2447
2448 Args:
2449 image_size: The size of the image.
2450 num_roots: Number of roots.
2451
2452 Returns:
2453 The number of bytes needed for FEC for an image of the given size
2454 and with the requested number of FEC roots.
2455
2456 Raises:
2457 ValueError: If output from the 'fec' tool is invalid.
2458
2459 """
2460 p = subprocess.Popen(
2461 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2462 stdout=subprocess.PIPE,
2463 stderr=subprocess.PIPE)
2464 (pout, perr) = p.communicate()
2465 retcode = p.wait()
2466 if retcode != 0:
2467 raise ValueError('Error invoking fec: {}'.format(perr))
2468 return int(pout)
2469
2470
2471def generate_fec_data(image_filename, num_roots):
2472 """Generate FEC codes for an image.
2473
2474 Args:
2475 image_filename: The filename of the image.
2476 num_roots: Number of roots.
2477
2478 Returns:
2479 The FEC data blob.
2480
2481 Raises:
2482 ValueError: If output from the 'fec' tool is invalid.
2483 """
2484 fec_tmpfile = tempfile.NamedTemporaryFile()
2485 subprocess.check_call(
2486 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2487 fec_tmpfile.name],
2488 stderr=open(os.devnull))
2489 fec_data = fec_tmpfile.read()
2490 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2491 footer_data = fec_data[-footer_size:]
2492 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2493 footer_data)
2494 if magic != FEC_MAGIC:
2495 raise ValueError('Unexpected magic in FEC footer')
2496 return fec_data[0:fec_size]
2497
2498
David Zeuthen21e95262016-07-27 17:58:40 -04002499def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002500 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002501 """Generates a Merkle-tree for a file.
2502
2503 Args:
2504 image: The image, as a file.
2505 image_size: The size of the image.
2506 block_size: The block size, e.g. 4096.
2507 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2508 salt: The salt to use.
2509 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002510 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002511 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002512
2513 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002514 A tuple where the first element is the top-level hash and the
2515 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002516 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002517 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002518 hash_src_offset = 0
2519 hash_src_size = image_size
2520 level_num = 0
2521 while hash_src_size > block_size:
2522 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002523 remaining = hash_src_size
2524 while remaining > 0:
2525 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002526 # Only read from the file for the first level - for subsequent
2527 # levels, access the array we're building.
2528 if level_num == 0:
2529 image.seek(hash_src_offset + hash_src_size - remaining)
2530 data = image.read(min(remaining, block_size))
2531 else:
2532 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2533 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002534 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002535
2536 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002537 if len(data) < block_size:
2538 hasher.update('\0' * (block_size - len(data)))
2539 level_output += hasher.digest()
2540 if digest_padding > 0:
2541 level_output += '\0' * digest_padding
2542
2543 padding_needed = (round_to_multiple(
2544 len(level_output), block_size) - len(level_output))
2545 level_output += '\0' * padding_needed
2546
David Zeuthena4fee8b2016-08-22 15:20:43 -04002547 # Copy level-output into resulting tree.
2548 offset = hash_level_offsets[level_num]
2549 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002550
David Zeuthena4fee8b2016-08-22 15:20:43 -04002551 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002552 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002553 level_num += 1
2554
2555 hasher = hashlib.new(name=hash_alg_name, string=salt)
2556 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002557 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002558
2559
2560class AvbTool(object):
2561 """Object for avbtool command-line tool."""
2562
2563 def __init__(self):
2564 """Initializer method."""
2565 self.avb = Avb()
2566
2567 def _add_common_args(self, sub_parser):
2568 """Adds arguments used by several sub-commands.
2569
2570 Arguments:
2571 sub_parser: The parser to add arguments to.
2572 """
2573 sub_parser.add_argument('--algorithm',
2574 help='Algorithm to use (default: NONE)',
2575 metavar='ALGORITHM',
2576 default='NONE')
2577 sub_parser.add_argument('--key',
2578 help='Path to RSA private key file',
2579 metavar='KEY',
2580 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002581 sub_parser.add_argument('--public_key_metadata',
2582 help='Path to public key metadata file',
2583 metavar='KEY_METADATA',
2584 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002585 sub_parser.add_argument('--rollback_index',
2586 help='Rollback Index',
2587 type=parse_number,
2588 default=0)
2589 sub_parser.add_argument('--prop',
2590 help='Add property',
2591 metavar='KEY:VALUE',
2592 action='append')
2593 sub_parser.add_argument('--prop_from_file',
2594 help='Add property from file',
2595 metavar='KEY:PATH',
2596 action='append')
2597 sub_parser.add_argument('--kernel_cmdline',
2598 help='Add kernel cmdline',
2599 metavar='CMDLINE',
2600 action='append')
2601 sub_parser.add_argument('--generate_dm_verity_cmdline_from_hashtree',
2602 metavar='IMAGE',
2603 help='Generate kernel cmdline for dm-verity',
2604 type=argparse.FileType('rb'))
2605 sub_parser.add_argument('--include_descriptors_from_image',
2606 help='Include descriptors from image',
2607 metavar='IMAGE',
2608 action='append',
2609 type=argparse.FileType('rb'))
2610
2611 def run(self, argv):
2612 """Command-line processor.
2613
2614 Arguments:
2615 argv: Pass sys.argv from main.
2616 """
2617 parser = argparse.ArgumentParser()
2618 subparsers = parser.add_subparsers(title='subcommands')
2619
2620 sub_parser = subparsers.add_parser('version',
2621 help='Prints version of avbtool.')
2622 sub_parser.set_defaults(func=self.version)
2623
2624 sub_parser = subparsers.add_parser('extract_public_key',
2625 help='Extract public key.')
2626 sub_parser.add_argument('--key',
2627 help='Path to RSA private key file',
2628 required=True)
2629 sub_parser.add_argument('--output',
2630 help='Output file name',
2631 type=argparse.FileType('wb'),
2632 required=True)
2633 sub_parser.set_defaults(func=self.extract_public_key)
2634
2635 sub_parser = subparsers.add_parser('make_vbmeta_image',
2636 help='Makes a vbmeta image.')
2637 sub_parser.add_argument('--output',
2638 help='Output file name',
2639 type=argparse.FileType('wb'),
2640 required=True)
2641 self._add_common_args(sub_parser)
2642 sub_parser.add_argument('--chain_partition',
2643 help='Allow signed integrity-data for partition',
2644 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2645 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002646 sub_parser.add_argument('--flags',
2647 help='VBMeta flags',
2648 type=parse_number,
2649 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04002650 sub_parser.set_defaults(func=self.make_vbmeta_image)
2651
2652 sub_parser = subparsers.add_parser('add_hash_footer',
2653 help='Add hashes and footer to image.')
2654 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002655 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002656 type=argparse.FileType('rab+'))
2657 sub_parser.add_argument('--partition_size',
2658 help='Partition size',
2659 type=parse_number,
2660 required=True)
2661 sub_parser.add_argument('--partition_name',
2662 help='Partition name',
2663 required=True)
2664 sub_parser.add_argument('--hash_algorithm',
2665 help='Hash algorithm to use (default: sha256)',
2666 default='sha256')
2667 sub_parser.add_argument('--salt',
2668 help='Salt in hex (default: /dev/urandom)')
2669 self._add_common_args(sub_parser)
2670 sub_parser.set_defaults(func=self.add_hash_footer)
2671
2672 sub_parser = subparsers.add_parser('add_hashtree_footer',
2673 help='Add hashtree and footer to image.')
2674 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002675 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002676 type=argparse.FileType('rab+'))
2677 sub_parser.add_argument('--partition_size',
2678 help='Partition size',
2679 type=parse_number,
2680 required=True)
2681 sub_parser.add_argument('--partition_name',
2682 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002683 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002684 sub_parser.add_argument('--hash_algorithm',
2685 help='Hash algorithm to use (default: sha1)',
2686 default='sha1')
2687 sub_parser.add_argument('--salt',
2688 help='Salt in hex (default: /dev/urandom)')
2689 sub_parser.add_argument('--block_size',
2690 help='Block size (default: 4096)',
2691 type=parse_number,
2692 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002693 sub_parser.add_argument('--generate_fec',
2694 help='Add forward-error-correction codes',
2695 action='store_true')
2696 sub_parser.add_argument('--fec_num_roots',
2697 help='Number of roots for FEC (default: 2)',
2698 type=parse_number,
2699 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002700 sub_parser.add_argument('--calc_max_image_size',
2701 help=('Don\'t store the hashtree or footer - '
2702 'instead calculate the maximum image size '
2703 'leaving enough room for hashtree '
2704 'and metadata with the given partition '
2705 'size.'),
2706 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002707 self._add_common_args(sub_parser)
2708 sub_parser.set_defaults(func=self.add_hashtree_footer)
2709
2710 sub_parser = subparsers.add_parser('erase_footer',
2711 help='Erase footer from an image.')
2712 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002713 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002714 type=argparse.FileType('rwb+'),
2715 required=True)
2716 sub_parser.add_argument('--keep_hashtree',
2717 help='Keep the hashtree in the image',
2718 action='store_true')
2719 sub_parser.set_defaults(func=self.erase_footer)
2720
2721 sub_parser = subparsers.add_parser(
2722 'info_image',
2723 help='Show information about vbmeta or footer.')
2724 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002725 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002726 type=argparse.FileType('rb'),
2727 required=True)
2728 sub_parser.add_argument('--output',
2729 help='Write info to file',
2730 type=argparse.FileType('wt'),
2731 default=sys.stdout)
2732 sub_parser.set_defaults(func=self.info_image)
2733
David Zeuthen8b6973b2016-09-20 12:39:49 -04002734 sub_parser = subparsers.add_parser('set_ab_metadata',
2735 help='Set A/B metadata.')
2736 sub_parser.add_argument('--misc_image',
2737 help=('The misc image to modify. If the image does '
2738 'not exist, it will be created.'),
2739 type=argparse.FileType('r+b'),
2740 required=True)
2741 sub_parser.add_argument('--slot_data',
2742 help=('Slot data of the form "priority", '
2743 '"tries_remaining", "sucessful_boot" for '
2744 'slot A followed by the same for slot B, '
2745 'separated by colons. The default value '
2746 'is 15:7:0:14:7:0.'),
2747 default='15:7:0:14:7:0')
2748 sub_parser.set_defaults(func=self.set_ab_metadata)
2749
David Zeuthen21e95262016-07-27 17:58:40 -04002750 args = parser.parse_args(argv[1:])
2751 try:
2752 args.func(args)
2753 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002754 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002755 sys.exit(1)
2756
2757 def version(self, _):
2758 """Implements the 'version' sub-command."""
2759 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2760
2761 def extract_public_key(self, args):
2762 """Implements the 'extract_public_key' sub-command."""
2763 self.avb.extract_public_key(args.key, args.output)
2764
2765 def make_vbmeta_image(self, args):
2766 """Implements the 'make_vbmeta_image' sub-command."""
2767 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05002768 args.algorithm, args.key,
2769 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002770 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002771 args.kernel_cmdline,
2772 args.generate_dm_verity_cmdline_from_hashtree,
2773 args.include_descriptors_from_image)
2774
2775 def add_hash_footer(self, args):
2776 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002777 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002778 args.partition_name, args.hash_algorithm,
2779 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002780 args.public_key_metadata, args.rollback_index,
2781 args.prop, args.prop_from_file,
2782 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002783 args.generate_dm_verity_cmdline_from_hashtree,
2784 args.include_descriptors_from_image)
2785
2786 def add_hashtree_footer(self, args):
2787 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04002788 self.avb.add_hashtree_footer(args.image.name if args.image else None,
2789 args.partition_size,
2790 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002791 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04002792 args.hash_algorithm, args.block_size,
2793 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002794 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04002795 args.rollback_index, args.prop,
2796 args.prop_from_file,
2797 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002798 args.generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002799 args.include_descriptors_from_image,
2800 args.calc_max_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002801
2802 def erase_footer(self, args):
2803 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002804 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04002805
David Zeuthen8b6973b2016-09-20 12:39:49 -04002806 def set_ab_metadata(self, args):
2807 """Implements the 'set_ab_metadata' sub-command."""
2808 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
2809
David Zeuthen21e95262016-07-27 17:58:40 -04002810 def info_image(self, args):
2811 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002812 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04002813
2814
2815if __name__ == '__main__':
2816 tool = AvbTool()
2817 tool.run(sys.argv)