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