blob: 3a04de674ba8dadc82231f73ab0071b8c5c9a9fe [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:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001292 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001293 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)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001301 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001302 '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:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001319 (tag, num_bytes_following, self.rollback_index_location,
1320 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001321 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001322 expected_size = round_to_multiple(
1323 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1324 if tag != self.TAG or num_bytes_following != expected_size:
1325 raise LookupError('Given data does not look like a chain partition '
1326 'descriptor.')
1327 o = 0
1328 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1329 partition_name_len)])
1330 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1331 self.partition_name.decode('utf-8')
1332 o += partition_name_len
1333 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1334
1335 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001336 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001337 self.partition_name = ''
1338 self.public_key = bytearray()
1339
1340 def print_desc(self, o):
1341 """Print the descriptor.
1342
1343 Arguments:
1344 o: The object to write the output to.
1345 """
1346 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001347 o.write(' Partition Name: {}\n'.format(self.partition_name))
1348 o.write(' Rollback Index Location: {}\n'.format(
1349 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001350 # Just show the SHA1 of the key, for size reasons.
1351 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001352 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001353
1354 def encode(self):
1355 """Serializes the descriptor.
1356
1357 Returns:
1358 A bytearray() with the descriptor data.
1359 """
1360 encoded_name = self.partition_name.encode('utf-8')
1361 num_bytes_following = (
1362 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1363 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1364 padding_size = nbf_with_padding - num_bytes_following
1365 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001366 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001367 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001368 padding = struct.pack(str(padding_size) + 'x')
1369 ret = desc + encoded_name + self.public_key + padding
1370 return bytearray(ret)
1371
1372
1373DESCRIPTOR_CLASSES = [
1374 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1375 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1376]
1377
1378
1379def parse_descriptors(data):
1380 """Parses a blob of data into descriptors.
1381
1382 Arguments:
1383 data: A bytearray() with encoded descriptors.
1384
1385 Returns:
1386 A list of instances of objects derived from AvbDescriptor. For
1387 unknown descriptors, the class AvbDescriptor is used.
1388 """
1389 o = 0
1390 ret = []
1391 while o < len(data):
1392 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1393 if tag < len(DESCRIPTOR_CLASSES):
1394 c = DESCRIPTOR_CLASSES[tag]
1395 else:
1396 c = AvbDescriptor
1397 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1398 o += 16 + nb_following
1399 return ret
1400
1401
1402class AvbFooter(object):
1403 """A class for parsing and writing footers.
1404
1405 Footers are stored at the end of partitions and point to where the
1406 AvbVBMeta blob is located. They also contain the original size of
1407 the image before AVB information was added.
1408
1409 Attributes:
1410 magic: Magic for identifying the footer, see |MAGIC|.
1411 version_major: The major version of avbtool that wrote the footer.
1412 version_minor: The minor version of avbtool that wrote the footer.
1413 original_image_size: Original image size.
1414 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1415 vbmeta_size: Size of the AvbVBMeta blob.
1416 """
1417
1418 MAGIC = 'AVBf'
1419 SIZE = 64
1420 RESERVED = 28
1421 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1422 'Q' # Original image size.
1423 'Q' # Offset of VBMeta blob.
1424 'Q' + # Size of VBMeta blob.
1425 str(RESERVED) + 'x') # padding for reserved bytes
1426
1427 def __init__(self, data=None):
1428 """Initializes a new footer object.
1429
1430 Arguments:
1431 data: If not None, must be a bytearray of size 4096.
1432
1433 Raises:
1434 LookupError: If the given footer is malformed.
1435 struct.error: If the given data has no footer.
1436 """
1437 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1438
1439 if data:
1440 (self.magic, self.version_major, self.version_minor,
1441 self.original_image_size, self.vbmeta_offset,
1442 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1443 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001444 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001445 else:
1446 self.magic = self.MAGIC
1447 self.version_major = AVB_VERSION_MAJOR
1448 self.version_minor = AVB_VERSION_MINOR
1449 self.original_image_size = 0
1450 self.vbmeta_offset = 0
1451 self.vbmeta_size = 0
1452
David Zeuthena4fee8b2016-08-22 15:20:43 -04001453 def encode(self):
1454 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001455
David Zeuthena4fee8b2016-08-22 15:20:43 -04001456 Returns:
1457 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001458 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001459 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1460 self.version_minor, self.original_image_size,
1461 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001462
1463
1464class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001465 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001466
1467 Attributes:
1468 The attributes correspond to the |AvbVBMetaHeader| struct
1469 defined in avb_vbmeta_header.h.
1470 """
1471
1472 SIZE = 256
1473
1474 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001475 RESERVED = 132
David Zeuthen21e95262016-07-27 17:58:40 -04001476
1477 # Keep in sync with |AvbVBMetaImageHeader|.
1478 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1479 '2Q' # 2 x block size
1480 'L' # algorithm type
1481 '2Q' # offset, size (hash)
1482 '2Q' # offset, size (signature)
1483 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001484 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001485 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001486 'Q' # rollback_index
1487 'L' + # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001488 str(RESERVED) + 'x') # padding for reserved bytes
1489
1490 def __init__(self, data=None):
1491 """Initializes a new header object.
1492
1493 Arguments:
1494 data: If not None, must be a bytearray of size 8192.
1495
1496 Raises:
1497 Exception: If the given data is malformed.
1498 """
1499 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1500
1501 if data:
1502 (self.magic, self.header_version_major, self.header_version_minor,
1503 self.authentication_data_block_size, self.auxiliary_data_block_size,
1504 self.algorithm_type, self.hash_offset, self.hash_size,
1505 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001506 self.public_key_size, self.public_key_metadata_offset,
1507 self.public_key_metadata_size, self.descriptors_offset,
1508 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001509 self.rollback_index,
1510 self.flags) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001511 # Nuke NUL-bytes at the end of the string.
1512 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001513 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001514 else:
1515 self.magic = 'AVB0'
1516 self.header_version_major = AVB_VERSION_MAJOR
1517 self.header_version_minor = AVB_VERSION_MINOR
1518 self.authentication_data_block_size = 0
1519 self.auxiliary_data_block_size = 0
1520 self.algorithm_type = 0
1521 self.hash_offset = 0
1522 self.hash_size = 0
1523 self.signature_offset = 0
1524 self.signature_size = 0
1525 self.public_key_offset = 0
1526 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001527 self.public_key_metadata_offset = 0
1528 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001529 self.descriptors_offset = 0
1530 self.descriptors_size = 0
1531 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001532 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001533
1534 def save(self, output):
1535 """Serializes the header (256 bytes) to disk.
1536
1537 Arguments:
1538 output: The object to write the output to.
1539 """
1540 output.write(struct.pack(
1541 self.FORMAT_STRING, self.magic, self.header_version_major,
1542 self.header_version_minor, self.authentication_data_block_size,
1543 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1544 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001545 self.public_key_offset, self.public_key_size,
1546 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001547 self.descriptors_offset, self.descriptors_size, self.rollback_index,
1548 self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001549
1550 def encode(self):
1551 """Serializes the header (256) to a bytearray().
1552
1553 Returns:
1554 A bytearray() with the encoded header.
1555 """
1556 return struct.pack(self.FORMAT_STRING, self.magic,
1557 self.header_version_major, self.header_version_minor,
1558 self.authentication_data_block_size,
1559 self.auxiliary_data_block_size, self.algorithm_type,
1560 self.hash_offset, self.hash_size, self.signature_offset,
1561 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001562 self.public_key_size, self.public_key_metadata_offset,
1563 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001564 self.descriptors_size, self.rollback_index, self.flags)
David Zeuthen21e95262016-07-27 17:58:40 -04001565
1566
1567class Avb(object):
1568 """Business logic for avbtool command-line tool."""
1569
David Zeuthen8b6973b2016-09-20 12:39:49 -04001570 # Keep in sync with avb_ab_flow.h.
1571 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1572 AB_MAGIC = '\0AB0'
1573 AB_MAJOR_VERSION = 1
1574 AB_MINOR_VERSION = 0
1575 AB_MISC_METADATA_OFFSET = 2048
1576
David Zeuthen09692692016-09-30 16:16:40 -04001577 # Constants for maximum metadata size. These are used to give
1578 # meaningful errors if the value passed in via --partition_size is
1579 # too small and when --calc_max_image_size is used. We use
1580 # conservative figures.
1581 MAX_VBMETA_SIZE = 64 * 1024
1582 MAX_FOOTER_SIZE = 4096
1583
David Zeuthena4fee8b2016-08-22 15:20:43 -04001584 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001585 """Implements the 'erase_footer' command.
1586
1587 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001588 image_filename: File to erase a footer from.
David Zeuthen21e95262016-07-27 17:58:40 -04001589 keep_hashtree: If True, keep the hashtree around.
1590
1591 Raises:
1592 AvbError: If there's no footer in the image.
1593 """
1594
David Zeuthena4fee8b2016-08-22 15:20:43 -04001595 image = ImageHandler(image_filename)
1596
David Zeuthen21e95262016-07-27 17:58:40 -04001597 (footer, _, descriptors, _) = self._parse_image(image)
1598
1599 if not footer:
1600 raise AvbError('Given image does not have a footer.')
1601
1602 new_image_size = None
1603 if not keep_hashtree:
1604 new_image_size = footer.original_image_size
1605 else:
1606 # If requested to keep the hashtree, search for a hashtree
1607 # descriptor to figure out the location and size of the hashtree.
1608 for desc in descriptors:
1609 if isinstance(desc, AvbHashtreeDescriptor):
1610 # The hashtree is always just following the main data so the
1611 # new size is easily derived.
1612 new_image_size = desc.tree_offset + desc.tree_size
1613 break
1614 if not new_image_size:
1615 raise AvbError('Requested to keep hashtree but no hashtree '
1616 'descriptor was found.')
1617
1618 # And cut...
1619 image.truncate(new_image_size)
1620
David Zeuthen8b6973b2016-09-20 12:39:49 -04001621 def set_ab_metadata(self, misc_image, slot_data):
1622 """Implements the 'set_ab_metadata' command.
1623
1624 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1625 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1626
1627 Arguments:
1628 misc_image: The misc image to write to.
1629 slot_data: Slot data as a string
1630
1631 Raises:
1632 AvbError: If slot data is malformed.
1633 """
1634 tokens = slot_data.split(':')
1635 if len(tokens) != 6:
1636 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1637 a_priority = int(tokens[0])
1638 a_tries_remaining = int(tokens[1])
1639 a_success = True if int(tokens[2]) != 0 else False
1640 b_priority = int(tokens[3])
1641 b_tries_remaining = int(tokens[4])
1642 b_success = True if int(tokens[5]) != 0 else False
1643
1644 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1645 self.AB_MAGIC,
1646 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1647 a_priority, a_tries_remaining, a_success,
1648 b_priority, b_tries_remaining, b_success)
1649 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1650 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1651 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1652 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1653 misc_image.write(ab_data)
1654
David Zeuthena4fee8b2016-08-22 15:20:43 -04001655 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001656 """Implements the 'info_image' command.
1657
1658 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001659 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001660 output: Output file to write human-readable information to (file object).
1661 """
1662
David Zeuthena4fee8b2016-08-22 15:20:43 -04001663 image = ImageHandler(image_filename)
1664
David Zeuthen21e95262016-07-27 17:58:40 -04001665 o = output
1666
1667 (footer, header, descriptors, image_size) = self._parse_image(image)
1668
1669 if footer:
1670 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1671 footer.version_minor))
1672 o.write('Image size: {} bytes\n'.format(image_size))
1673 o.write('Original image size: {} bytes\n'.format(
1674 footer.original_image_size))
1675 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1676 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1677 o.write('--\n')
1678
1679 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1680
David Zeuthena4fee8b2016-08-22 15:20:43 -04001681 o.write('VBMeta image version: {}.{}{}\n'.format(
1682 header.header_version_major, header.header_version_minor,
1683 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001684 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1685 o.write('Authentication Block: {} bytes\n'.format(
1686 header.authentication_data_block_size))
1687 o.write('Auxiliary Block: {} bytes\n'.format(
1688 header.auxiliary_data_block_size))
1689 o.write('Algorithm: {}\n'.format(alg_name))
1690 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001691 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001692
1693 # Print descriptors.
1694 num_printed = 0
1695 o.write('Descriptors:\n')
1696 for desc in descriptors:
1697 desc.print_desc(o)
1698 num_printed += 1
1699 if num_printed == 0:
1700 o.write(' (none)\n')
1701
1702 def _parse_image(self, image):
1703 """Gets information about an image.
1704
1705 The image can either be a vbmeta or an image with a footer.
1706
1707 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001708 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001709
1710 Returns:
1711 A tuple where the first argument is a AvbFooter (None if there
1712 is no footer on the image), the second argument is a
1713 AvbVBMetaHeader, the third argument is a list of
1714 AvbDescriptor-derived instances, and the fourth argument is the
1715 size of |image|.
1716 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001717 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001718 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001719 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001720 try:
1721 footer = AvbFooter(image.read(AvbFooter.SIZE))
1722 except (LookupError, struct.error):
1723 # Nope, just seek back to the start.
1724 image.seek(0)
1725
1726 vbmeta_offset = 0
1727 if footer:
1728 vbmeta_offset = footer.vbmeta_offset
1729
1730 image.seek(vbmeta_offset)
1731 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1732
1733 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1734 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1735 desc_start_offset = aux_block_offset + h.descriptors_offset
1736 image.seek(desc_start_offset)
1737 descriptors = parse_descriptors(image.read(h.descriptors_size))
1738
David Zeuthen09692692016-09-30 16:16:40 -04001739 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001740
David Zeuthenfd41eb92016-11-17 12:24:47 -05001741 def _get_cmdline_descriptors_for_dm_verity(self, image):
1742 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001743
1744 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001745 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001746
1747 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001748 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1749 instructions. There is one for when hashtree is not disabled and one for
1750 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001751
1752 Raises:
1753 AvbError: If |image| doesn't have a hashtree descriptor.
1754
1755 """
1756
1757 (_, _, descriptors, _) = self._parse_image(image)
1758
1759 ht = None
1760 for desc in descriptors:
1761 if isinstance(desc, AvbHashtreeDescriptor):
1762 ht = desc
1763 break
1764
1765 if not ht:
1766 raise AvbError('No hashtree descriptor in given image')
1767
1768 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001769 c += '0' # start
1770 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1771 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1772 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1773 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1774 c += ' {}'.format(ht.data_block_size) # data_block
1775 c += ' {}'.format(ht.hash_block_size) # hash_block
1776 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1777 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1778 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1779 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1780 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1781 if ht.fec_num_roots > 0:
1782 c += ' 9' # number of optional args
1783 c += ' ignore_zero_blocks'
1784 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1785 c += ' fec_roots {}'.format(ht.fec_num_roots)
1786 # Note that fec_blocks is the size that FEC covers, *not* the
1787 # size of the FEC data. Since we use FEC for everything up until
1788 # the FEC data, it's the same as the offset.
1789 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1790 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1791 else:
1792 c += ' 1' # number of optional args
1793 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001794 c += '" root=0xfd00'
David Zeuthenbc8f6472016-11-21 14:59:15 -05001795 c += ' androidboot.vbmeta.device=PARTUUID=$(ANDROID_VBMETA_PARTUUID)'
David Zeuthen21e95262016-07-27 17:58:40 -04001796
David Zeuthenfd41eb92016-11-17 12:24:47 -05001797 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001798 desc = AvbKernelCmdlineDescriptor()
1799 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001800 desc.flags = (
1801 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1802
1803 # The descriptor for when hashtree verification is disabled is a lot
1804 # simpler - we just set the root to the partition.
1805 desc_no_ht = AvbKernelCmdlineDescriptor()
1806 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1807 desc_no_ht.flags = (
1808 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1809
1810 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001813 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001814 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001815 generate_dm_verity_cmdline_from_hashtree,
1816 include_descriptors_from_image):
1817 """Implements the 'make_vbmeta_image' command.
1818
1819 Arguments:
1820 output: File to write the image to.
1821 chain_partitions: List of partitions to chain.
1822 algorithm_name: Name of algorithm to use.
1823 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001824 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001825 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001826 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001827 props: Properties to insert (list of strings of the form 'key:value').
1828 props_from_file: Properties to insert (list of strings 'key:<path>').
1829 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1830 generate_dm_verity_cmdline_from_hashtree: None or file to generate from.
1831 include_descriptors_from_image: List of file objects with descriptors.
1832
1833 Raises:
1834 AvbError: If a chained partition is malformed.
1835 """
1836
1837 descriptors = []
1838
1839 # Insert chained partition descriptors.
1840 if chain_partitions:
1841 for cp in chain_partitions:
1842 cp_tokens = cp.split(':')
1843 if len(cp_tokens) != 3:
1844 raise AvbError('Malformed chained partition "{}".'.format(cp))
1845 desc = AvbChainPartitionDescriptor()
1846 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001847 desc.rollback_index_location = int(cp_tokens[1])
1848 if desc.rollback_index_location < 1:
1849 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001850 file_path = cp_tokens[2]
1851 desc.public_key = open(file_path, 'rb').read()
1852 descriptors.append(desc)
1853
1854 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001855 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001856 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001857 generate_dm_verity_cmdline_from_hashtree,
1858 include_descriptors_from_image)
1859
1860 # Write entire vbmeta blob (header, authentication, auxiliary).
1861 output.seek(0)
1862 output.write(vbmeta_blob)
1863
David Zeuthen18666ab2016-11-15 11:18:05 -05001864 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1865 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001866 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001867 kernel_cmdlines,
1868 generate_dm_verity_cmdline_from_hashtree,
1869 include_descriptors_from_image):
1870 """Generates a VBMeta blob.
1871
1872 This blob contains the header (struct AvbVBMetaHeader), the
1873 authentication data block (which contains the hash and signature
1874 for the header and auxiliary block), and the auxiliary block
1875 (which contains descriptors, the public key used, and other data).
1876
1877 The |key| parameter can |None| only if the |algorithm_name| is
1878 'NONE'.
1879
1880 Arguments:
1881 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1882 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001883 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001884 descriptors: A list of descriptors to insert or None.
1885 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001886 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001887 props: Properties to insert (List of strings of the form 'key:value').
1888 props_from_file: Properties to insert (List of strings 'key:<path>').
1889 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1890 generate_dm_verity_cmdline_from_hashtree: None or file to generate
1891 dm-verity kernel cmdline from.
1892 include_descriptors_from_image: List of file objects for which
1893 to insert descriptors from.
1894
1895 Returns:
1896 A bytearray() with the VBMeta blob.
1897
1898 Raises:
1899 Exception: If the |algorithm_name| is not found, if no key has
1900 been given and the given algorithm requires one, or the key is
1901 of the wrong size.
1902
1903 """
1904 try:
1905 alg = ALGORITHMS[algorithm_name]
1906 except KeyError:
1907 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1908
1909 # Descriptors.
1910 encoded_descriptors = bytearray()
1911 if descriptors:
1912 for desc in descriptors:
1913 encoded_descriptors.extend(desc.encode())
1914
1915 # Add properties.
1916 if props:
1917 for prop in props:
1918 idx = prop.find(':')
1919 if idx == -1:
1920 raise AvbError('Malformed property "{}".'.format(prop))
1921 desc = AvbPropertyDescriptor()
1922 desc.key = prop[0:idx]
1923 desc.value = prop[(idx + 1):]
1924 encoded_descriptors.extend(desc.encode())
1925 if props_from_file:
1926 for prop in props_from_file:
1927 idx = prop.find(':')
1928 if idx == -1:
1929 raise AvbError('Malformed property "{}".'.format(prop))
1930 desc = AvbPropertyDescriptor()
1931 desc.key = prop[0:idx]
1932 desc.value = prop[(idx + 1):]
1933 file_path = prop[(idx + 1):]
1934 desc.value = open(file_path, 'rb').read()
1935 encoded_descriptors.extend(desc.encode())
1936
1937 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
1938 if generate_dm_verity_cmdline_from_hashtree:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001939 image_handler = ImageHandler(
1940 generate_dm_verity_cmdline_from_hashtree.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001941 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1942 encoded_descriptors.extend(cmdline_desc[0].encode())
1943 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001944
1945 # Add kernel command-lines.
1946 if kernel_cmdlines:
1947 for i in kernel_cmdlines:
1948 desc = AvbKernelCmdlineDescriptor()
1949 desc.kernel_cmdline = i
1950 encoded_descriptors.extend(desc.encode())
1951
1952 # Add descriptors from other images.
1953 if include_descriptors_from_image:
1954 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001955 image_handler = ImageHandler(image.name)
1956 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001957 for desc in image_descriptors:
1958 encoded_descriptors.extend(desc.encode())
1959
David Zeuthen18666ab2016-11-15 11:18:05 -05001960 # Load public key metadata blob, if requested.
1961 pkmd_blob = []
1962 if public_key_metadata_path:
1963 with open(public_key_metadata_path) as f:
1964 pkmd_blob = f.read()
1965
David Zeuthen21e95262016-07-27 17:58:40 -04001966 key = None
1967 encoded_key = bytearray()
1968 if alg.public_key_num_bytes > 0:
1969 if not key_path:
1970 raise AvbError('Key is required for algorithm {}'.format(
1971 algorithm_name))
1972 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
1973 encoded_key = encode_rsa_key(key)
1974 if len(encoded_key) != alg.public_key_num_bytes:
1975 raise AvbError('Key is wrong size for algorithm {}'.format(
1976 algorithm_name))
1977
1978 h = AvbVBMetaHeader()
1979
David Zeuthen18666ab2016-11-15 11:18:05 -05001980 # For the Auxiliary data block, descriptors are stored at offset 0,
1981 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001982 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05001983 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04001984 h.descriptors_offset = 0
1985 h.descriptors_size = len(encoded_descriptors)
1986 h.public_key_offset = h.descriptors_size
1987 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001988 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
1989 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04001990
1991 # For the Authentication data block, the hash is first and then
1992 # the signature.
1993 h.authentication_data_block_size = round_to_multiple(
1994 alg.hash_num_bytes + alg.public_key_num_bytes, 64)
1995 h.algorithm_type = alg.algorithm_type
1996 h.hash_offset = 0
1997 h.hash_size = alg.hash_num_bytes
1998 # Signature offset and size - it's stored right after the hash
1999 # (in Authentication data block).
2000 h.signature_offset = alg.hash_num_bytes
2001 h.signature_size = alg.signature_num_bytes
2002
2003 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002004 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002005
2006 # Generate Header data block.
2007 header_data_blob = h.encode()
2008
2009 # Generate Auxiliary data block.
2010 aux_data_blob = bytearray()
2011 aux_data_blob.extend(encoded_descriptors)
2012 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002013 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002014 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2015 aux_data_blob.extend('\0' * padding_bytes)
2016
2017 # Calculate the hash.
2018 binary_hash = bytearray()
2019 binary_signature = bytearray()
2020 if algorithm_name != 'NONE':
2021 if algorithm_name[0:6] == 'SHA256':
2022 ha = hashlib.sha256()
2023 elif algorithm_name[0:6] == 'SHA512':
2024 ha = hashlib.sha512()
2025 else:
2026 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2027 ha.update(header_data_blob)
2028 ha.update(aux_data_blob)
2029 binary_hash.extend(ha.digest())
2030
2031 # Calculate the signature.
2032 p = subprocess.Popen(
2033 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
2034 stdin=subprocess.PIPE,
2035 stdout=subprocess.PIPE,
2036 stderr=subprocess.PIPE)
2037 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
2038 (pout, perr) = p.communicate(padding_and_hash)
2039 retcode = p.wait()
2040 if retcode != 0:
2041 raise AvbError('Error signing: {}'.format(perr))
2042 binary_signature.extend(pout)
2043
2044 # Generate Authentication data block.
2045 auth_data_blob = bytearray()
2046 auth_data_blob.extend(binary_hash)
2047 auth_data_blob.extend(binary_signature)
2048 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2049 auth_data_blob.extend('\0' * padding_bytes)
2050
2051 return header_data_blob + auth_data_blob + aux_data_blob
2052
2053 def extract_public_key(self, key_path, output):
2054 """Implements the 'extract_public_key' command.
2055
2056 Arguments:
2057 key_path: The path to a RSA private key file.
2058 output: The file to write to.
2059 """
2060 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2061 write_rsa_key(output, key)
2062
David Zeuthena4fee8b2016-08-22 15:20:43 -04002063 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002064 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002065 public_key_metadata_path, rollback_index, props,
2066 props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002067 generate_dm_verity_cmdline_from_hashtree,
2068 include_descriptors_from_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002069 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002070
2071 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002072 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002073 partition_size: Size of partition.
2074 partition_name: Name of partition (without A/B suffix).
2075 hash_algorithm: Hash algorithm to use.
2076 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2077 algorithm_name: Name of algorithm to use.
2078 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002079 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002080 rollback_index: Rollback index.
2081 props: Properties to insert (List of strings of the form 'key:value').
2082 props_from_file: Properties to insert (List of strings 'key:<path>').
2083 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2084 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2085 dm-verity kernel cmdline from.
2086 include_descriptors_from_image: List of file objects for which
2087 to insert descriptors from.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002088
2089 Raises:
2090 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002091 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002092 image = ImageHandler(image_filename)
2093
2094 if partition_size % image.block_size != 0:
2095 raise AvbError('Partition size of {} is not a multiple of the image '
2096 'block size {}.'.format(partition_size,
2097 image.block_size))
2098
David Zeuthen21e95262016-07-27 17:58:40 -04002099 # If there's already a footer, truncate the image to its original
2100 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2101 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002102 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002103 try:
2104 footer = AvbFooter(image.read(AvbFooter.SIZE))
2105 # Existing footer found. Just truncate.
2106 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002107 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002108 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002109 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002110
2111 # If anything goes wrong from here-on, restore the image back to
2112 # its original size.
2113 try:
David Zeuthen09692692016-09-30 16:16:40 -04002114 # First, calculate the maximum image size such that an image
2115 # this size + metadata (footer + vbmeta struct) fits in
2116 # |partition_size|.
2117 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2118 max_image_size = partition_size - max_metadata_size
2119
2120 # If image size exceeds the maximum image size, fail.
2121 if image.image_size > max_image_size:
2122 raise AvbError('Image size of {} exceeds maximum image '
2123 'size of {} in order to fit in a partition '
2124 'size of {}.'.format(image.image_size, max_image_size,
2125 partition_size))
2126
David Zeuthen21e95262016-07-27 17:58:40 -04002127 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2128 if salt:
2129 salt = salt.decode('hex')
2130 else:
2131 if salt is None:
2132 # If salt is not explicitly specified, choose a hash
2133 # that's the same size as the hash size.
2134 hash_size = digest_size
2135 salt = open('/dev/urandom').read(hash_size)
2136 else:
2137 salt = ''
2138
2139 hasher = hashlib.new(name=hash_algorithm, string=salt)
2140 # TODO(zeuthen): might want to read this in chunks to avoid
2141 # memory pressure, then again, this is only supposed to be used
2142 # on kernel/initramfs partitions. Possible optimization.
2143 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002144 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002145 digest = hasher.digest()
2146
2147 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002148 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002149 h_desc.hash_algorithm = hash_algorithm
2150 h_desc.partition_name = partition_name
2151 h_desc.salt = salt
2152 h_desc.digest = digest
2153
David Zeuthenfd41eb92016-11-17 12:24:47 -05002154 # Flags are only allowed on top-level vbmeta struct.
2155 flags = 0
2156
David Zeuthen21e95262016-07-27 17:58:40 -04002157 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002158 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002159 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002160 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002161 generate_dm_verity_cmdline_from_hashtree,
2162 include_descriptors_from_image)
2163
David Zeuthena4fee8b2016-08-22 15:20:43 -04002164 # If the image isn't sparse, its size might not be a multiple of
2165 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002166 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002167 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002168 padding_needed = image.block_size - (image.image_size%image.block_size)
2169 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002170
David Zeuthena4fee8b2016-08-22 15:20:43 -04002171 # The append_raw() method requires content with size being a
2172 # multiple of |block_size| so add padding as needed. Also record
2173 # where this is written to since we'll need to put that in the
2174 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002175 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002176 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2177 len(vbmeta_blob))
2178 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2179 image.append_raw(vbmeta_blob_with_padding)
2180 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2181
2182 # Now insert a DONT_CARE chunk with enough bytes such that the
2183 # final Footer block is at the end of partition_size..
2184 image.append_dont_care(partition_size - vbmeta_end_offset -
2185 1*image.block_size)
2186
2187 # Generate the Footer that tells where the VBMeta footer
2188 # is. Also put enough padding in the front of the footer since
2189 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002190 footer = AvbFooter()
2191 footer.original_image_size = original_image_size
2192 footer.vbmeta_offset = vbmeta_offset
2193 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002194 footer_blob = footer.encode()
2195 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2196 footer_blob)
2197 image.append_raw(footer_blob_with_padding)
2198
David Zeuthen21e95262016-07-27 17:58:40 -04002199 except:
2200 # Truncate back to original size, then re-raise
2201 image.truncate(original_image_size)
2202 raise
2203
David Zeuthena4fee8b2016-08-22 15:20:43 -04002204 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002205 generate_fec, fec_num_roots, hash_algorithm,
2206 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002207 public_key_metadata_path, rollback_index,
2208 props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002209 generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002210 include_descriptors_from_image,
2211 calc_max_image_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002212 """Implements the 'add_hashtree_footer' command.
2213
2214 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2215 more information about dm-verity and these hashes.
2216
2217 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002218 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002219 partition_size: Size of partition.
2220 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002221 generate_fec: If True, generate FEC codes.
2222 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002223 hash_algorithm: Hash algorithm to use.
2224 block_size: Block size to use.
2225 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2226 algorithm_name: Name of algorithm to use.
2227 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002228 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002229 rollback_index: Rollback index.
2230 props: Properties to insert (List of strings of the form 'key:value').
2231 props_from_file: Properties to insert (List of strings 'key:<path>').
2232 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2233 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2234 dm-verity kernel cmdline from.
2235 include_descriptors_from_image: List of file objects for which
2236 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002237 calc_max_image_size: Don't store the hashtree or footer - instead
2238 calculate the maximum image size leaving enough room for hashtree
2239 and metadata with the given |partition_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002240
2241 Raises:
2242 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002243 """
David Zeuthen09692692016-09-30 16:16:40 -04002244 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2245 digest_padding = round_to_pow2(digest_size) - digest_size
2246
2247 # First, calculate the maximum image size such that an image
2248 # this size + the hashtree + metadata (footer + vbmeta struct)
2249 # fits in |partition_size|. We use very conservative figures for
2250 # metadata.
2251 (_, max_tree_size) = calc_hash_level_offsets(
2252 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002253 max_fec_size = 0
2254 if generate_fec:
2255 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2256 max_metadata_size = (max_fec_size + max_tree_size +
2257 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002258 self.MAX_FOOTER_SIZE)
2259 max_image_size = partition_size - max_metadata_size
2260
2261 # If we're asked to only calculate the maximum image size, we're done.
2262 if calc_max_image_size:
2263 print '{}'.format(max_image_size)
2264 return
2265
David Zeuthena4fee8b2016-08-22 15:20:43 -04002266 image = ImageHandler(image_filename)
2267
2268 if partition_size % image.block_size != 0:
2269 raise AvbError('Partition size of {} is not a multiple of the image '
2270 'block size {}.'.format(partition_size,
2271 image.block_size))
2272
David Zeuthen21e95262016-07-27 17:58:40 -04002273 # If there's already a footer, truncate the image to its original
2274 # size. This way 'avbtool add_hashtree_footer' is idempotent
2275 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002276 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002277 try:
2278 footer = AvbFooter(image.read(AvbFooter.SIZE))
2279 # Existing footer found. Just truncate.
2280 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002281 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002282 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002283 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002284
2285 # If anything goes wrong from here-on, restore the image back to
2286 # its original size.
2287 try:
2288 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002289 rounded_image_size = round_to_multiple(image.image_size, block_size)
2290 if rounded_image_size > image.image_size:
2291 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002292
David Zeuthen09692692016-09-30 16:16:40 -04002293 # If image size exceeds the maximum image size, fail.
2294 if image.image_size > max_image_size:
2295 raise AvbError('Image size of {} exceeds maximum image '
2296 'size of {} in order to fit in a partition '
2297 'size of {}.'.format(image.image_size, max_image_size,
2298 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002299
2300 if salt:
2301 salt = salt.decode('hex')
2302 else:
2303 if salt is None:
2304 # If salt is not explicitly specified, choose a hash
2305 # that's the same size as the hash size.
2306 hash_size = digest_size
2307 salt = open('/dev/urandom').read(hash_size)
2308 else:
2309 salt = ''
2310
David Zeuthena4fee8b2016-08-22 15:20:43 -04002311 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002312 # offsets in advance.
2313 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002314 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002315
David Zeuthena4fee8b2016-08-22 15:20:43 -04002316 # If the image isn't sparse, its size might not be a multiple of
2317 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002318 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002319 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002320 padding_needed = image.block_size - (image.image_size%image.block_size)
2321 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002322
David Zeuthena4fee8b2016-08-22 15:20:43 -04002323 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002324 tree_offset = image.image_size
2325 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002326 block_size,
2327 hash_algorithm, salt,
2328 digest_padding,
2329 hash_level_offsets,
2330 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002331
2332 # Generate HashtreeDescriptor with details about the tree we
2333 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002334 ht_desc = AvbHashtreeDescriptor()
2335 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002336 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002337 ht_desc.tree_offset = tree_offset
2338 ht_desc.tree_size = tree_size
2339 ht_desc.data_block_size = block_size
2340 ht_desc.hash_block_size = block_size
2341 ht_desc.hash_algorithm = hash_algorithm
2342 ht_desc.partition_name = partition_name
2343 ht_desc.salt = salt
2344 ht_desc.root_digest = root_digest
2345
David Zeuthen09692692016-09-30 16:16:40 -04002346 # Write the hash tree
2347 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2348 len(hash_tree))
2349 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2350 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002351 len_hashtree_and_fec = len(hash_tree_with_padding)
2352
2353 # Generate FEC codes, if requested.
2354 if generate_fec:
2355 fec_data = generate_fec_data(image_filename, fec_num_roots)
2356 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2357 len(fec_data))
2358 fec_data_with_padding = fec_data + '\0'*padding_needed
2359 fec_offset = image.image_size
2360 image.append_raw(fec_data_with_padding)
2361 len_hashtree_and_fec += len(fec_data_with_padding)
2362 # Update the hashtree descriptor.
2363 ht_desc.fec_num_roots = fec_num_roots
2364 ht_desc.fec_offset = fec_offset
2365 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002366
David Zeuthenfd41eb92016-11-17 12:24:47 -05002367 # Flags are only allowed on top-level vbmeta struct.
2368 flags = 0
2369
David Zeuthena4fee8b2016-08-22 15:20:43 -04002370 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002371 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002372 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002373 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002374 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002375 generate_dm_verity_cmdline_from_hashtree,
2376 include_descriptors_from_image)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002377 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2378 len(vbmeta_blob))
2379 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2380 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002381
David Zeuthena4fee8b2016-08-22 15:20:43 -04002382 # Now insert a DONT_CARE chunk with enough bytes such that the
2383 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002384 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002385 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002386
David Zeuthena4fee8b2016-08-22 15:20:43 -04002387 # Generate the Footer that tells where the VBMeta footer
2388 # is. Also put enough padding in the front of the footer since
2389 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002390 footer = AvbFooter()
2391 footer.original_image_size = original_image_size
2392 footer.vbmeta_offset = vbmeta_offset
2393 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002394 footer_blob = footer.encode()
2395 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2396 footer_blob)
2397 image.append_raw(footer_blob_with_padding)
2398
David Zeuthen21e95262016-07-27 17:58:40 -04002399 except:
David Zeuthen09692692016-09-30 16:16:40 -04002400 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002401 image.truncate(original_image_size)
2402 raise
2403
2404
2405def calc_hash_level_offsets(image_size, block_size, digest_size):
2406 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2407
2408 Arguments:
2409 image_size: The size of the image to calculate a Merkle-tree for.
2410 block_size: The block size, e.g. 4096.
2411 digest_size: The size of each hash, e.g. 32 for SHA-256.
2412
2413 Returns:
2414 A tuple where the first argument is an array of offsets and the
2415 second is size of the tree, in bytes.
2416 """
2417 level_offsets = []
2418 level_sizes = []
2419 tree_size = 0
2420
2421 num_levels = 0
2422 size = image_size
2423 while size > block_size:
2424 num_blocks = (size + block_size - 1) / block_size
2425 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2426
2427 level_sizes.append(level_size)
2428 tree_size += level_size
2429 num_levels += 1
2430
2431 size = level_size
2432
2433 for n in range(0, num_levels):
2434 offset = 0
2435 for m in range(n + 1, num_levels):
2436 offset += level_sizes[m]
2437 level_offsets.append(offset)
2438
David Zeuthena4fee8b2016-08-22 15:20:43 -04002439 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002440
2441
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002442# See system/extras/libfec/include/fec/io.h for these definitions.
2443FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2444FEC_MAGIC = 0xfecfecfe
2445
2446
2447def calc_fec_data_size(image_size, num_roots):
2448 """Calculates how much space FEC data will take.
2449
2450 Args:
2451 image_size: The size of the image.
2452 num_roots: Number of roots.
2453
2454 Returns:
2455 The number of bytes needed for FEC for an image of the given size
2456 and with the requested number of FEC roots.
2457
2458 Raises:
2459 ValueError: If output from the 'fec' tool is invalid.
2460
2461 """
2462 p = subprocess.Popen(
2463 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2464 stdout=subprocess.PIPE,
2465 stderr=subprocess.PIPE)
2466 (pout, perr) = p.communicate()
2467 retcode = p.wait()
2468 if retcode != 0:
2469 raise ValueError('Error invoking fec: {}'.format(perr))
2470 return int(pout)
2471
2472
2473def generate_fec_data(image_filename, num_roots):
2474 """Generate FEC codes for an image.
2475
2476 Args:
2477 image_filename: The filename of the image.
2478 num_roots: Number of roots.
2479
2480 Returns:
2481 The FEC data blob.
2482
2483 Raises:
2484 ValueError: If output from the 'fec' tool is invalid.
2485 """
2486 fec_tmpfile = tempfile.NamedTemporaryFile()
2487 subprocess.check_call(
2488 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2489 fec_tmpfile.name],
2490 stderr=open(os.devnull))
2491 fec_data = fec_tmpfile.read()
2492 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2493 footer_data = fec_data[-footer_size:]
2494 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2495 footer_data)
2496 if magic != FEC_MAGIC:
2497 raise ValueError('Unexpected magic in FEC footer')
2498 return fec_data[0:fec_size]
2499
2500
David Zeuthen21e95262016-07-27 17:58:40 -04002501def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002502 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002503 """Generates a Merkle-tree for a file.
2504
2505 Args:
2506 image: The image, as a file.
2507 image_size: The size of the image.
2508 block_size: The block size, e.g. 4096.
2509 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2510 salt: The salt to use.
2511 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002512 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002513 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002514
2515 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002516 A tuple where the first element is the top-level hash and the
2517 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002518 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002519 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002520 hash_src_offset = 0
2521 hash_src_size = image_size
2522 level_num = 0
2523 while hash_src_size > block_size:
2524 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002525 remaining = hash_src_size
2526 while remaining > 0:
2527 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002528 # Only read from the file for the first level - for subsequent
2529 # levels, access the array we're building.
2530 if level_num == 0:
2531 image.seek(hash_src_offset + hash_src_size - remaining)
2532 data = image.read(min(remaining, block_size))
2533 else:
2534 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2535 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002536 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002537
2538 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002539 if len(data) < block_size:
2540 hasher.update('\0' * (block_size - len(data)))
2541 level_output += hasher.digest()
2542 if digest_padding > 0:
2543 level_output += '\0' * digest_padding
2544
2545 padding_needed = (round_to_multiple(
2546 len(level_output), block_size) - len(level_output))
2547 level_output += '\0' * padding_needed
2548
David Zeuthena4fee8b2016-08-22 15:20:43 -04002549 # Copy level-output into resulting tree.
2550 offset = hash_level_offsets[level_num]
2551 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002552
David Zeuthena4fee8b2016-08-22 15:20:43 -04002553 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002554 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002555 level_num += 1
2556
2557 hasher = hashlib.new(name=hash_alg_name, string=salt)
2558 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002559 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002560
2561
2562class AvbTool(object):
2563 """Object for avbtool command-line tool."""
2564
2565 def __init__(self):
2566 """Initializer method."""
2567 self.avb = Avb()
2568
2569 def _add_common_args(self, sub_parser):
2570 """Adds arguments used by several sub-commands.
2571
2572 Arguments:
2573 sub_parser: The parser to add arguments to.
2574 """
2575 sub_parser.add_argument('--algorithm',
2576 help='Algorithm to use (default: NONE)',
2577 metavar='ALGORITHM',
2578 default='NONE')
2579 sub_parser.add_argument('--key',
2580 help='Path to RSA private key file',
2581 metavar='KEY',
2582 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002583 sub_parser.add_argument('--public_key_metadata',
2584 help='Path to public key metadata file',
2585 metavar='KEY_METADATA',
2586 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002587 sub_parser.add_argument('--rollback_index',
2588 help='Rollback Index',
2589 type=parse_number,
2590 default=0)
2591 sub_parser.add_argument('--prop',
2592 help='Add property',
2593 metavar='KEY:VALUE',
2594 action='append')
2595 sub_parser.add_argument('--prop_from_file',
2596 help='Add property from file',
2597 metavar='KEY:PATH',
2598 action='append')
2599 sub_parser.add_argument('--kernel_cmdline',
2600 help='Add kernel cmdline',
2601 metavar='CMDLINE',
2602 action='append')
2603 sub_parser.add_argument('--generate_dm_verity_cmdline_from_hashtree',
2604 metavar='IMAGE',
2605 help='Generate kernel cmdline for dm-verity',
2606 type=argparse.FileType('rb'))
2607 sub_parser.add_argument('--include_descriptors_from_image',
2608 help='Include descriptors from image',
2609 metavar='IMAGE',
2610 action='append',
2611 type=argparse.FileType('rb'))
2612
2613 def run(self, argv):
2614 """Command-line processor.
2615
2616 Arguments:
2617 argv: Pass sys.argv from main.
2618 """
2619 parser = argparse.ArgumentParser()
2620 subparsers = parser.add_subparsers(title='subcommands')
2621
2622 sub_parser = subparsers.add_parser('version',
2623 help='Prints version of avbtool.')
2624 sub_parser.set_defaults(func=self.version)
2625
2626 sub_parser = subparsers.add_parser('extract_public_key',
2627 help='Extract public key.')
2628 sub_parser.add_argument('--key',
2629 help='Path to RSA private key file',
2630 required=True)
2631 sub_parser.add_argument('--output',
2632 help='Output file name',
2633 type=argparse.FileType('wb'),
2634 required=True)
2635 sub_parser.set_defaults(func=self.extract_public_key)
2636
2637 sub_parser = subparsers.add_parser('make_vbmeta_image',
2638 help='Makes a vbmeta image.')
2639 sub_parser.add_argument('--output',
2640 help='Output file name',
2641 type=argparse.FileType('wb'),
2642 required=True)
2643 self._add_common_args(sub_parser)
2644 sub_parser.add_argument('--chain_partition',
2645 help='Allow signed integrity-data for partition',
2646 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2647 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002648 sub_parser.add_argument('--flags',
2649 help='VBMeta flags',
2650 type=parse_number,
2651 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04002652 sub_parser.set_defaults(func=self.make_vbmeta_image)
2653
2654 sub_parser = subparsers.add_parser('add_hash_footer',
2655 help='Add hashes and footer to image.')
2656 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002657 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002658 type=argparse.FileType('rab+'))
2659 sub_parser.add_argument('--partition_size',
2660 help='Partition size',
2661 type=parse_number,
2662 required=True)
2663 sub_parser.add_argument('--partition_name',
2664 help='Partition name',
2665 required=True)
2666 sub_parser.add_argument('--hash_algorithm',
2667 help='Hash algorithm to use (default: sha256)',
2668 default='sha256')
2669 sub_parser.add_argument('--salt',
2670 help='Salt in hex (default: /dev/urandom)')
2671 self._add_common_args(sub_parser)
2672 sub_parser.set_defaults(func=self.add_hash_footer)
2673
2674 sub_parser = subparsers.add_parser('add_hashtree_footer',
2675 help='Add hashtree and footer to image.')
2676 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002677 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002678 type=argparse.FileType('rab+'))
2679 sub_parser.add_argument('--partition_size',
2680 help='Partition size',
2681 type=parse_number,
2682 required=True)
2683 sub_parser.add_argument('--partition_name',
2684 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002685 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002686 sub_parser.add_argument('--hash_algorithm',
2687 help='Hash algorithm to use (default: sha1)',
2688 default='sha1')
2689 sub_parser.add_argument('--salt',
2690 help='Salt in hex (default: /dev/urandom)')
2691 sub_parser.add_argument('--block_size',
2692 help='Block size (default: 4096)',
2693 type=parse_number,
2694 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002695 sub_parser.add_argument('--generate_fec',
2696 help='Add forward-error-correction codes',
2697 action='store_true')
2698 sub_parser.add_argument('--fec_num_roots',
2699 help='Number of roots for FEC (default: 2)',
2700 type=parse_number,
2701 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002702 sub_parser.add_argument('--calc_max_image_size',
2703 help=('Don\'t store the hashtree or footer - '
2704 'instead calculate the maximum image size '
2705 'leaving enough room for hashtree '
2706 'and metadata with the given partition '
2707 'size.'),
2708 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002709 self._add_common_args(sub_parser)
2710 sub_parser.set_defaults(func=self.add_hashtree_footer)
2711
2712 sub_parser = subparsers.add_parser('erase_footer',
2713 help='Erase footer from an image.')
2714 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002715 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002716 type=argparse.FileType('rwb+'),
2717 required=True)
2718 sub_parser.add_argument('--keep_hashtree',
2719 help='Keep the hashtree in the image',
2720 action='store_true')
2721 sub_parser.set_defaults(func=self.erase_footer)
2722
2723 sub_parser = subparsers.add_parser(
2724 'info_image',
2725 help='Show information about vbmeta or footer.')
2726 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002727 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002728 type=argparse.FileType('rb'),
2729 required=True)
2730 sub_parser.add_argument('--output',
2731 help='Write info to file',
2732 type=argparse.FileType('wt'),
2733 default=sys.stdout)
2734 sub_parser.set_defaults(func=self.info_image)
2735
David Zeuthen8b6973b2016-09-20 12:39:49 -04002736 sub_parser = subparsers.add_parser('set_ab_metadata',
2737 help='Set A/B metadata.')
2738 sub_parser.add_argument('--misc_image',
2739 help=('The misc image to modify. If the image does '
2740 'not exist, it will be created.'),
2741 type=argparse.FileType('r+b'),
2742 required=True)
2743 sub_parser.add_argument('--slot_data',
2744 help=('Slot data of the form "priority", '
2745 '"tries_remaining", "sucessful_boot" for '
2746 'slot A followed by the same for slot B, '
2747 'separated by colons. The default value '
2748 'is 15:7:0:14:7:0.'),
2749 default='15:7:0:14:7:0')
2750 sub_parser.set_defaults(func=self.set_ab_metadata)
2751
David Zeuthen21e95262016-07-27 17:58:40 -04002752 args = parser.parse_args(argv[1:])
2753 try:
2754 args.func(args)
2755 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002756 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002757 sys.exit(1)
2758
2759 def version(self, _):
2760 """Implements the 'version' sub-command."""
2761 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2762
2763 def extract_public_key(self, args):
2764 """Implements the 'extract_public_key' sub-command."""
2765 self.avb.extract_public_key(args.key, args.output)
2766
2767 def make_vbmeta_image(self, args):
2768 """Implements the 'make_vbmeta_image' sub-command."""
2769 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05002770 args.algorithm, args.key,
2771 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002772 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002773 args.kernel_cmdline,
2774 args.generate_dm_verity_cmdline_from_hashtree,
2775 args.include_descriptors_from_image)
2776
2777 def add_hash_footer(self, args):
2778 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002779 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002780 args.partition_name, args.hash_algorithm,
2781 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002782 args.public_key_metadata, args.rollback_index,
2783 args.prop, args.prop_from_file,
2784 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002785 args.generate_dm_verity_cmdline_from_hashtree,
2786 args.include_descriptors_from_image)
2787
2788 def add_hashtree_footer(self, args):
2789 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04002790 self.avb.add_hashtree_footer(args.image.name if args.image else None,
2791 args.partition_size,
2792 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002793 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04002794 args.hash_algorithm, args.block_size,
2795 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002796 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04002797 args.rollback_index, args.prop,
2798 args.prop_from_file,
2799 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002800 args.generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002801 args.include_descriptors_from_image,
2802 args.calc_max_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002803
2804 def erase_footer(self, args):
2805 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002806 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04002807
David Zeuthen8b6973b2016-09-20 12:39:49 -04002808 def set_ab_metadata(self, args):
2809 """Implements the 'set_ab_metadata' sub-command."""
2810 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
2811
David Zeuthen21e95262016-07-27 17:58:40 -04002812 def info_image(self, args):
2813 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002814 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04002815
2816
2817if __name__ == '__main__':
2818 tool = AvbTool()
2819 tool.run(sys.argv)