blob: aaa7f509fd45e5d7abeeacc15931f5b872aa0353 [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
31import os
32import struct
33import subprocess
34import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040035import tempfile
David Zeuthen21e95262016-07-27 17:58:40 -040036
37import Crypto.PublicKey.RSA
38
39# Keep in sync with avb_vbmeta_header.h.
40AVB_VERSION_MAJOR = 1
41AVB_VERSION_MINOR = 0
42
43
44class AvbError(Exception):
45 """Application-specific errors.
46
47 These errors represent issues for which a stack-trace should not be
48 presented.
49
50 Attributes:
51 message: Error message.
52 """
53
54 def __init__(self, message):
55 Exception.__init__(self, message)
56
57
58class Algorithm(object):
59 """Contains details about an algorithm.
60
61 See the avb_vbmeta_header.h file for more details about
62 algorithms.
63
64 The constant |ALGORITHMS| is a dictionary from human-readable
65 names (e.g 'SHA256_RSA2048') to instances of this class.
66
67 Attributes:
68 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
69 hash_num_bytes: Number of bytes used to store the hash.
70 signature_num_bytes: Number of bytes used to store the signature.
71 public_key_num_bytes: Number of bytes used to store the public key.
72 padding: Padding used for signature, if any.
73 """
74
75 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
76 public_key_num_bytes, padding):
77 self.algorithm_type = algorithm_type
78 self.hash_num_bytes = hash_num_bytes
79 self.signature_num_bytes = signature_num_bytes
80 self.public_key_num_bytes = public_key_num_bytes
81 self.padding = padding
82
83# This must be kept in sync with the avb_crypto.h file.
84#
85# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
86# obtained from section 5.2.2 of RFC 4880.
87ALGORITHMS = {
88 'NONE': Algorithm(
89 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
90 hash_num_bytes=0,
91 signature_num_bytes=0,
92 public_key_num_bytes=0,
93 padding=[]),
94 'SHA256_RSA2048': Algorithm(
95 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
96 hash_num_bytes=32,
97 signature_num_bytes=256,
98 public_key_num_bytes=8 + 2*2048/8,
99 padding=[
100 # PKCS1-v1_5 padding
101 0x00, 0x01] + [0xff]*202 + [0x00] + [
102 # ASN.1 header
103 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
104 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
105 0x00, 0x04, 0x20,
106 ]),
107 'SHA256_RSA4096': Algorithm(
108 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
109 hash_num_bytes=32,
110 signature_num_bytes=512,
111 public_key_num_bytes=8 + 2*4096/8,
112 padding=[
113 # PKCS1-v1_5 padding
114 0x00, 0x01] + [0xff]*458 + [0x00] + [
115 # ASN.1 header
116 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
117 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
118 0x00, 0x04, 0x20,
119 ]),
120 'SHA256_RSA8192': Algorithm(
121 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
122 hash_num_bytes=32,
123 signature_num_bytes=1024,
124 public_key_num_bytes=8 + 2*8192/8,
125 padding=[
126 # PKCS1-v1_5 padding
127 0x00, 0x01] + [0xff]*970 + [0x00] + [
128 # ASN.1 header
129 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
130 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
131 0x00, 0x04, 0x20,
132 ]),
133 'SHA512_RSA2048': Algorithm(
134 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
135 hash_num_bytes=64,
136 signature_num_bytes=256,
137 public_key_num_bytes=8 + 2*2048/8,
138 padding=[
139 # PKCS1-v1_5 padding
140 0x00, 0x01] + [0xff]*170 + [0x00] + [
141 # ASN.1 header
142 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
143 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
144 0x00, 0x04, 0x40
145 ]),
146 'SHA512_RSA4096': Algorithm(
147 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
148 hash_num_bytes=64,
149 signature_num_bytes=512,
150 public_key_num_bytes=8 + 2*4096/8,
151 padding=[
152 # PKCS1-v1_5 padding
153 0x00, 0x01] + [0xff]*426 + [0x00] + [
154 # ASN.1 header
155 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
156 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
157 0x00, 0x04, 0x40
158 ]),
159 'SHA512_RSA8192': Algorithm(
160 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
161 hash_num_bytes=64,
162 signature_num_bytes=1024,
163 public_key_num_bytes=8 + 2*8192/8,
164 padding=[
165 # PKCS1-v1_5 padding
166 0x00, 0x01] + [0xff]*938 + [0x00] + [
167 # ASN.1 header
168 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
169 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
170 0x00, 0x04, 0x40
171 ]),
172}
173
174
175def round_to_multiple(number, size):
176 """Rounds a number up to nearest multiple of another number.
177
178 Args:
179 number: The number to round up.
180 size: The multiple to round up to.
181
182 Returns:
183 If |number| is a multiple of |size|, returns |number|, otherwise
184 returns |number| + |size|.
185 """
186 remainder = number % size
187 if remainder == 0:
188 return number
189 return number + size - remainder
190
191
192def round_to_pow2(number):
193 """Rounds a number up to the next power of 2.
194
195 Args:
196 number: The number to round up.
197
198 Returns:
199 If |number| is already a power of 2 then |number| is
200 returned. Otherwise the smallest power of 2 greater than |number|
201 is returned.
202 """
203 return 2**((number - 1).bit_length())
204
205
206def write_long(output, num_bits, value):
207 """Writes a long to an output stream using a given amount of bits.
208
209 This number is written big-endian, e.g. with the most significant
210 bit first.
211
212 Arguments:
213 output: The object to write the output to.
214 num_bits: The number of bits to write, e.g. 2048.
215 value: The value to write.
216 """
217 for bit_pos in range(num_bits, 0, -8):
218 octet = (value >> (bit_pos - 8)) & 0xff
219 output.write(struct.pack('!B', octet))
220
221
222def encode_long(num_bits, value):
223 """Encodes a long to a bytearray() using a given amount of bits.
224
225 This number is written big-endian, e.g. with the most significant
226 bit first.
227
228 Arguments:
229 num_bits: The number of bits to write, e.g. 2048.
230 value: The value to write.
231
232 Returns:
233 A bytearray() with the encoded long.
234 """
235 ret = bytearray()
236 for bit_pos in range(num_bits, 0, -8):
237 octet = (value >> (bit_pos - 8)) & 0xff
238 ret.extend(struct.pack('!B', octet))
239 return ret
240
241
242def egcd(a, b):
243 """Calculate greatest common divisor of two numbers.
244
245 This implementation uses a recursive version of the extended
246 Euclidian algorithm.
247
248 Arguments:
249 a: First number.
250 b: Second number.
251
252 Returns:
253 A tuple (gcd, x, y) that where |gcd| is the greatest common
254 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
255 """
256 if a == 0:
257 return (b, 0, 1)
258 else:
259 g, y, x = egcd(b % a, a)
260 return (g, x - (b // a) * y, y)
261
262
263def modinv(a, m):
264 """Calculate modular multiplicative inverse of |a| modulo |m|.
265
266 This calculates the number |x| such that |a| * |x| == 1 (modulo
267 |m|). This number only exists if |a| and |m| are co-prime - |None|
268 is returned if this isn't true.
269
270 Arguments:
271 a: The number to calculate a modular inverse of.
272 m: The modulo to use.
273
274 Returns:
275 The modular multiplicative inverse of |a| and |m| or |None| if
276 these numbers are not co-prime.
277 """
278 gcd, x, _ = egcd(a, m)
279 if gcd != 1:
280 return None # modular inverse does not exist
281 else:
282 return x % m
283
284
285def parse_number(string):
286 """Parse a string as a number.
287
288 This is just a short-hand for int(string, 0) suitable for use in the
289 |type| parameter of |ArgumentParser|'s add_argument() function. An
290 improvement to just using type=int is that this function supports
291 numbers in other bases, e.g. "0x1234".
292
293 Arguments:
294 string: The string to parse.
295
296 Returns:
297 The parsed integer.
298
299 Raises:
300 ValueError: If the number could not be parsed.
301 """
302 return int(string, 0)
303
304
305def write_rsa_key(output, key):
306 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
307
308 This writes the |AvbRSAPublicKeyHeader| as well as the two large
309 numbers (|key_num_bits| bits long) following it.
310
311 Arguments:
312 output: The object to write the output to.
313 key: A Crypto.PublicKey.RSA object.
314 """
315 # key.e is exponent
316 # key.n is modulus
317 key_num_bits = key.size() + 1
318 # Calculate n0inv = -1/n[0] (mod 2^32)
319 b = 2L**32
320 n0inv = b - modinv(key.n, b)
321 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
322 r = 2L**key.n.bit_length()
323 rrmodn = r * r % key.n
324 output.write(struct.pack('!II', key_num_bits, n0inv))
325 write_long(output, key_num_bits, key.n)
326 write_long(output, key_num_bits, rrmodn)
327
328
329def encode_rsa_key(key):
330 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
331
332 This creates a |AvbRSAPublicKeyHeader| as well as the two large
333 numbers (|key_num_bits| bits long) following it.
334
335 Arguments:
336 key: A Crypto.PublicKey.RSA object.
337
338 Returns:
339 A bytearray() with the |AvbRSAPublicKeyHeader|.
340 """
341 ret = bytearray()
342 # key.e is exponent
343 # key.n is modulus
344 key_num_bits = key.size() + 1
345 # Calculate n0inv = -1/n[0] (mod 2^32)
346 b = 2L**32
347 n0inv = b - modinv(key.n, b)
348 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
349 r = 2L**key.n.bit_length()
350 rrmodn = r * r % key.n
351 ret.extend(struct.pack('!II', key_num_bits, n0inv))
352 ret.extend(encode_long(key_num_bits, key.n))
353 ret.extend(encode_long(key_num_bits, rrmodn))
354 return ret
355
356
357def lookup_algorithm_by_type(alg_type):
358 """Looks up algorithm by type.
359
360 Arguments:
361 alg_type: The integer representing the type.
362
363 Returns:
364 A tuple with the algorithm name and an |Algorithm| instance.
365
366 Raises:
367 Exception: If the algorithm cannot be found
368 """
369 for alg_name in ALGORITHMS:
370 alg_data = ALGORITHMS[alg_name]
371 if alg_data.algorithm_type == alg_type:
372 return (alg_name, alg_data)
373 raise AvbError('Unknown algorithm type {}'.format(alg_type))
374
375
David Zeuthena4fee8b2016-08-22 15:20:43 -0400376class ImageChunk(object):
377 """Data structure used for representing chunks in Android sparse files.
378
379 Attributes:
380 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
381 chunk_offset: Offset in the sparse file where this chunk begins.
382 output_offset: Offset in de-sparsified file where output begins.
383 output_size: Number of bytes in output.
384 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
385 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
386 """
387
388 FORMAT = '<2H2I'
389 TYPE_RAW = 0xcac1
390 TYPE_FILL = 0xcac2
391 TYPE_DONT_CARE = 0xcac3
392 TYPE_CRC32 = 0xcac4
393
394 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
395 input_offset, fill_data):
396 """Initializes an ImageChunk object.
397
398 Arguments:
399 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
400 chunk_offset: Offset in the sparse file where this chunk begins.
401 output_offset: Offset in de-sparsified file.
402 output_size: Number of bytes in output.
403 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
404 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
405
406 Raises:
407 ValueError: If data is not well-formed.
408 """
409 self.chunk_type = chunk_type
410 self.chunk_offset = chunk_offset
411 self.output_offset = output_offset
412 self.output_size = output_size
413 self.input_offset = input_offset
414 self.fill_data = fill_data
415 # Check invariants.
416 if self.chunk_type == self.TYPE_RAW:
417 if self.fill_data is not None:
418 raise ValueError('RAW chunk cannot have fill_data set.')
419 if not self.input_offset:
420 raise ValueError('RAW chunk must have input_offset set.')
421 elif self.chunk_type == self.TYPE_FILL:
422 if self.fill_data is None:
423 raise ValueError('FILL chunk must have fill_data set.')
424 if self.input_offset:
425 raise ValueError('FILL chunk cannot have input_offset set.')
426 elif self.chunk_type == self.TYPE_DONT_CARE:
427 if self.fill_data is not None:
428 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
429 if self.input_offset:
430 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
431 else:
432 raise ValueError('Invalid chunk type')
433
434
435class ImageHandler(object):
436 """Abstraction for image I/O with support for Android sparse images.
437
438 This class provides an interface for working with image files that
439 may be using the Android Sparse Image format. When an instance is
440 constructed, we test whether it's an Android sparse file. If so,
441 operations will be on the sparse file by interpreting the sparse
442 format, otherwise they will be directly on the file. Either way the
443 operations do the same.
444
445 For reading, this interface mimics a file object - it has seek(),
446 tell(), and read() methods. For writing, only truncation
447 (truncate()) and appending is supported (append_raw() and
448 append_dont_care()). Additionally, data can only be written in units
449 of the block size.
450
451 Attributes:
452 is_sparse: Whether the file being operated on is sparse.
453 block_size: The block size, typically 4096.
454 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400455 """
456 # See system/core/libsparse/sparse_format.h for details.
457 MAGIC = 0xed26ff3a
458 HEADER_FORMAT = '<I4H4I'
459
460 # These are formats and offset of just the |total_chunks| and
461 # |total_blocks| fields.
462 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
463 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
464
465 def __init__(self, image_filename):
466 """Initializes an image handler.
467
468 Arguments:
469 image_filename: The name of the file to operate on.
470
471 Raises:
472 ValueError: If data in the file is invalid.
473 """
474 self._image_filename = image_filename
475 self._read_header()
476
477 def _read_header(self):
478 """Initializes internal data structures used for reading file.
479
480 This may be called multiple times and is typically called after
481 modifying the file (e.g. appending, truncation).
482
483 Raises:
484 ValueError: If data in the file is invalid.
485 """
486 self.is_sparse = False
487 self.block_size = 4096
488 self._file_pos = 0
489 self._image = open(self._image_filename, 'r+b')
490 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400491 self.image_size = self._image.tell()
492
493 self._image.seek(0, os.SEEK_SET)
494 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
495 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
496 block_size, self._num_total_blocks, self._num_total_chunks,
497 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
498 if magic != self.MAGIC:
499 # Not a sparse image, our job here is done.
500 return
501 if not (major_version == 1 and minor_version == 0):
502 raise ValueError('Encountered sparse image format version {}.{} but '
503 'only 1.0 is supported'.format(major_version,
504 minor_version))
505 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
506 raise ValueError('Unexpected file_hdr_sz value {}.'.
507 format(file_hdr_sz))
508 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
509 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
510 format(chunk_hdr_sz))
511
512 self.block_size = block_size
513
514 # Build an list of chunks by parsing the file.
515 self._chunks = []
516
517 # Find the smallest offset where only "Don't care" chunks
518 # follow. This will be the size of the content in the sparse
519 # image.
520 offset = 0
521 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400522 for _ in xrange(1, self._num_total_chunks + 1):
523 chunk_offset = self._image.tell()
524
525 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
526 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
527 header_bin)
528 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
529
David Zeuthena4fee8b2016-08-22 15:20:43 -0400530 if chunk_type == ImageChunk.TYPE_RAW:
531 if data_sz != (chunk_sz * self.block_size):
532 raise ValueError('Raw chunk input size ({}) does not match output '
533 'size ({})'.
534 format(data_sz, chunk_sz*self.block_size))
535 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
536 chunk_offset,
537 output_offset,
538 chunk_sz*self.block_size,
539 self._image.tell(),
540 None))
541 self._image.read(data_sz)
542
543 elif chunk_type == ImageChunk.TYPE_FILL:
544 if data_sz != 4:
545 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
546 'has {}'.format(data_sz))
547 fill_data = self._image.read(4)
548 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
549 chunk_offset,
550 output_offset,
551 chunk_sz*self.block_size,
552 None,
553 fill_data))
554 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
555 if data_sz != 0:
556 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
557 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
559 chunk_offset,
560 output_offset,
561 chunk_sz*self.block_size,
562 None,
563 None))
564 elif chunk_type == ImageChunk.TYPE_CRC32:
565 if data_sz != 4:
566 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
567 'this has {}'.format(data_sz))
568 self._image.read(4)
569 else:
570 raise ValueError('Unknown chunk type {}'.format(chunk_type))
571
572 offset += chunk_sz
573 output_offset += chunk_sz*self.block_size
574
575 # Record where sparse data end.
576 self._sparse_end = self._image.tell()
577
578 # Now that we've traversed all chunks, sanity check.
579 if self._num_total_blocks != offset:
580 raise ValueError('The header said we should have {} output blocks, '
581 'but we saw {}'.format(self._num_total_blocks, offset))
582 junk_len = len(self._image.read())
583 if junk_len > 0:
584 raise ValueError('There were {} bytes of extra data at the end of the '
585 'file.'.format(junk_len))
586
David Zeuthen09692692016-09-30 16:16:40 -0400587 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400588 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400589
590 # This is used when bisecting in read() to find the initial slice.
591 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
592
593 self.is_sparse = True
594
595 def _update_chunks_and_blocks(self):
596 """Helper function to update the image header.
597
598 The the |total_chunks| and |total_blocks| fields in the header
599 will be set to value of the |_num_total_blocks| and
600 |_num_total_chunks| attributes.
601
602 """
603 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
604 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
605 self._num_total_blocks,
606 self._num_total_chunks))
607
608 def append_dont_care(self, num_bytes):
609 """Appends a DONT_CARE chunk to the sparse file.
610
611 The given number of bytes must be a multiple of the block size.
612
613 Arguments:
614 num_bytes: Size in number of bytes of the DONT_CARE chunk.
615 """
616 assert num_bytes % self.block_size == 0
617
618 if not self.is_sparse:
619 self._image.seek(0, os.SEEK_END)
620 # This is more efficient that writing NUL bytes since it'll add
621 # a hole on file systems that support sparse files (native
622 # sparse, not Android sparse).
623 self._image.truncate(self._image.tell() + num_bytes)
624 self._read_header()
625 return
626
627 self._num_total_chunks += 1
628 self._num_total_blocks += num_bytes / self.block_size
629 self._update_chunks_and_blocks()
630
631 self._image.seek(self._sparse_end, os.SEEK_SET)
632 self._image.write(struct.pack(ImageChunk.FORMAT,
633 ImageChunk.TYPE_DONT_CARE,
634 0, # Reserved
635 num_bytes / self.block_size,
636 struct.calcsize(ImageChunk.FORMAT)))
637 self._read_header()
638
639 def append_raw(self, data):
640 """Appends a RAW chunk to the sparse file.
641
642 The length of the given data must be a multiple of the block size.
643
644 Arguments:
645 data: Data to append.
646 """
647 assert len(data) % self.block_size == 0
648
649 if not self.is_sparse:
650 self._image.seek(0, os.SEEK_END)
651 self._image.write(data)
652 self._read_header()
653 return
654
655 self._num_total_chunks += 1
656 self._num_total_blocks += len(data) / self.block_size
657 self._update_chunks_and_blocks()
658
659 self._image.seek(self._sparse_end, os.SEEK_SET)
660 self._image.write(struct.pack(ImageChunk.FORMAT,
661 ImageChunk.TYPE_RAW,
662 0, # Reserved
663 len(data) / self.block_size,
664 len(data) +
665 struct.calcsize(ImageChunk.FORMAT)))
666 self._image.write(data)
667 self._read_header()
668
669 def append_fill(self, fill_data, size):
670 """Appends a fill chunk to the sparse file.
671
672 The total length of the fill data must be a multiple of the block size.
673
674 Arguments:
675 fill_data: Fill data to append - must be four bytes.
676 size: Number of chunk - must be a multiple of four and the block size.
677 """
678 assert len(fill_data) == 4
679 assert size % 4 == 0
680 assert size % self.block_size == 0
681
682 if not self.is_sparse:
683 self._image.seek(0, os.SEEK_END)
684 self._image.write(fill_data * (size/4))
685 self._read_header()
686 return
687
688 self._num_total_chunks += 1
689 self._num_total_blocks += size / self.block_size
690 self._update_chunks_and_blocks()
691
692 self._image.seek(self._sparse_end, os.SEEK_SET)
693 self._image.write(struct.pack(ImageChunk.FORMAT,
694 ImageChunk.TYPE_FILL,
695 0, # Reserved
696 size / self.block_size,
697 4 + struct.calcsize(ImageChunk.FORMAT)))
698 self._image.write(fill_data)
699 self._read_header()
700
701 def seek(self, offset):
702 """Sets the cursor position for reading from unsparsified file.
703
704 Arguments:
705 offset: Offset to seek to from the beginning of the file.
706 """
707 self._file_pos = offset
708
709 def read(self, size):
710 """Reads data from the unsparsified file.
711
712 This method may return fewer than |size| bytes of data if the end
713 of the file was encountered.
714
715 The file cursor for reading is advanced by the number of bytes
716 read.
717
718 Arguments:
719 size: Number of bytes to read.
720
721 Returns:
722 The data.
723
724 """
725 if not self.is_sparse:
726 self._image.seek(self._file_pos)
727 data = self._image.read(size)
728 self._file_pos += len(data)
729 return data
730
731 # Iterate over all chunks.
732 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
733 self._file_pos) - 1
734 data = bytearray()
735 to_go = size
736 while to_go > 0:
737 chunk = self._chunks[chunk_idx]
738 chunk_pos_offset = self._file_pos - chunk.output_offset
739 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
740
741 if chunk.chunk_type == ImageChunk.TYPE_RAW:
742 self._image.seek(chunk.input_offset + chunk_pos_offset)
743 data.extend(self._image.read(chunk_pos_to_go))
744 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
745 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
746 offset_mod = chunk_pos_offset % len(chunk.fill_data)
747 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
748 else:
749 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
750 data.extend('\0' * chunk_pos_to_go)
751
752 to_go -= chunk_pos_to_go
753 self._file_pos += chunk_pos_to_go
754 chunk_idx += 1
755 # Generate partial read in case of EOF.
756 if chunk_idx >= len(self._chunks):
757 break
758
759 return data
760
761 def tell(self):
762 """Returns the file cursor position for reading from unsparsified file.
763
764 Returns:
765 The file cursor position for reading.
766 """
767 return self._file_pos
768
769 def truncate(self, size):
770 """Truncates the unsparsified file.
771
772 Arguments:
773 size: Desired size of unsparsified file.
774
775 Raises:
776 ValueError: If desired size isn't a multiple of the block size.
777 """
778 if not self.is_sparse:
779 self._image.truncate(size)
780 self._read_header()
781 return
782
783 if size % self.block_size != 0:
784 raise ValueError('Cannot truncate to a size which is not a multiple '
785 'of the block size')
786
787 if size == self.image_size:
788 # Trivial where there's nothing to do.
789 return
790 elif size < self.image_size:
791 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
792 chunk = self._chunks[chunk_idx]
793 if chunk.output_offset != size:
794 # Truncation in the middle of a trunk - need to keep the chunk
795 # and modify it.
796 chunk_idx_for_update = chunk_idx + 1
797 num_to_keep = size - chunk.output_offset
798 assert num_to_keep % self.block_size == 0
799 if chunk.chunk_type == ImageChunk.TYPE_RAW:
800 truncate_at = (chunk.chunk_offset +
801 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
802 data_sz = num_to_keep
803 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
804 truncate_at = (chunk.chunk_offset +
805 struct.calcsize(ImageChunk.FORMAT) + 4)
806 data_sz = 4
807 else:
808 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
809 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
810 data_sz = 0
811 chunk_sz = num_to_keep/self.block_size
812 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
813 self._image.seek(chunk.chunk_offset)
814 self._image.write(struct.pack(ImageChunk.FORMAT,
815 chunk.chunk_type,
816 0, # Reserved
817 chunk_sz,
818 total_sz))
819 chunk.output_size = num_to_keep
820 else:
821 # Truncation at trunk boundary.
822 truncate_at = chunk.chunk_offset
823 chunk_idx_for_update = chunk_idx
824
825 self._num_total_chunks = chunk_idx_for_update
826 self._num_total_blocks = 0
827 for i in range(0, chunk_idx_for_update):
828 self._num_total_blocks += self._chunks[i].output_size / self.block_size
829 self._update_chunks_and_blocks()
830 self._image.truncate(truncate_at)
831
832 # We've modified the file so re-read all data.
833 self._read_header()
834 else:
835 # Truncating to grow - just add a DONT_CARE section.
836 self.append_dont_care(size - self.image_size)
837
838
David Zeuthen21e95262016-07-27 17:58:40 -0400839class AvbDescriptor(object):
840 """Class for AVB descriptor.
841
842 See the |AvbDescriptor| C struct for more information.
843
844 Attributes:
845 tag: The tag identifying what kind of descriptor this is.
846 data: The data in the descriptor.
847 """
848
849 SIZE = 16
850 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
851
852 def __init__(self, data):
853 """Initializes a new property descriptor.
854
855 Arguments:
856 data: If not None, must be a bytearray().
857
858 Raises:
859 LookupError: If the given descriptor is malformed.
860 """
861 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
862
863 if data:
864 (self.tag, num_bytes_following) = (
865 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
866 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
867 else:
868 self.tag = None
869 self.data = None
870
871 def print_desc(self, o):
872 """Print the descriptor.
873
874 Arguments:
875 o: The object to write the output to.
876 """
877 o.write(' Unknown descriptor:\n')
878 o.write(' Tag: {}\n'.format(self.tag))
879 if len(self.data) < 256:
880 o.write(' Data: {} ({} bytes)\n'.format(
881 repr(str(self.data)), len(self.data)))
882 else:
883 o.write(' Data: {} bytes\n'.format(len(self.data)))
884
885 def encode(self):
886 """Serializes the descriptor.
887
888 Returns:
889 A bytearray() with the descriptor data.
890 """
891 num_bytes_following = len(self.data)
892 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
893 padding_size = nbf_with_padding - num_bytes_following
894 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
895 padding = struct.pack(str(padding_size) + 'x')
896 ret = desc + self.data + padding
897 return bytearray(ret)
898
899
900class AvbPropertyDescriptor(AvbDescriptor):
901 """A class for property descriptors.
902
903 See the |AvbPropertyDescriptor| C struct for more information.
904
905 Attributes:
906 key: The key.
907 value: The key.
908 """
909
910 TAG = 0
911 SIZE = 32
912 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
913 'Q' # key size (bytes)
914 'Q') # value size (bytes)
915
916 def __init__(self, data=None):
917 """Initializes a new property descriptor.
918
919 Arguments:
920 data: If not None, must be a bytearray of size |SIZE|.
921
922 Raises:
923 LookupError: If the given descriptor is malformed.
924 """
925 AvbDescriptor.__init__(self, None)
926 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
927
928 if data:
929 (tag, num_bytes_following, key_size,
930 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
931 expected_size = round_to_multiple(
932 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
933 if tag != self.TAG or num_bytes_following != expected_size:
934 raise LookupError('Given data does not look like a property '
935 'descriptor.')
936 self.key = data[self.SIZE:(self.SIZE + key_size)]
937 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
938 value_size)]
939 else:
940 self.key = ''
941 self.value = ''
942
943 def print_desc(self, o):
944 """Print the descriptor.
945
946 Arguments:
947 o: The object to write the output to.
948 """
949 if len(self.value) < 256:
950 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
951 else:
952 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
953
954 def encode(self):
955 """Serializes the descriptor.
956
957 Returns:
958 A bytearray() with the descriptor data.
959 """
960 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
961 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
962 padding_size = nbf_with_padding - num_bytes_following
963 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
964 len(self.key), len(self.value))
965 padding = struct.pack(str(padding_size) + 'x')
966 ret = desc + self.key + '\0' + self.value + '\0' + padding
967 return bytearray(ret)
968
969
970class AvbHashtreeDescriptor(AvbDescriptor):
971 """A class for hashtree descriptors.
972
973 See the |AvbHashtreeDescriptor| C struct for more information.
974
975 Attributes:
976 dm_verity_version: dm-verity version used.
977 image_size: Size of the image, after rounding up to |block_size|.
978 tree_offset: Offset of the hash tree in the file.
979 tree_size: Size of the tree.
980 data_block_size: Data block size
981 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -0400982 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
983 fec_offset: Offset of FEC data (0 if FEC is not used).
984 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -0400985 hash_algorithm: Hash algorithm used.
986 partition_name: Partition name.
987 salt: Salt used.
988 root_digest: Root digest.
989 """
990
991 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -0400992 RESERVED = 64
993 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -0400994 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
995 'L' # dm-verity version used
996 'Q' # image size (bytes)
997 'Q' # tree offset (bytes)
998 'Q' # tree size (bytes)
999 'L' # data block size (bytes)
1000 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001001 'L' # FEC number of roots
1002 'Q' # FEC offset (bytes)
1003 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001004 '32s' # hash algorithm used
1005 'L' # partition name (bytes)
1006 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001007 'L' + # root digest length (bytes)
1008 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001009
1010 def __init__(self, data=None):
1011 """Initializes a new hashtree descriptor.
1012
1013 Arguments:
1014 data: If not None, must be a bytearray of size |SIZE|.
1015
1016 Raises:
1017 LookupError: If the given descriptor is malformed.
1018 """
1019 AvbDescriptor.__init__(self, None)
1020 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1021
1022 if data:
1023 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1024 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001025 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1026 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001027 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1028 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001029 expected_size = round_to_multiple(
1030 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1031 if tag != self.TAG or num_bytes_following != expected_size:
1032 raise LookupError('Given data does not look like a hashtree '
1033 'descriptor.')
1034 # Nuke NUL-bytes at the end.
1035 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1036 o = 0
1037 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1038 partition_name_len)])
1039 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1040 self.partition_name.decode('utf-8')
1041 o += partition_name_len
1042 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1043 o += salt_len
1044 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1045 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1046 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1047
1048 else:
1049 self.dm_verity_version = 0
1050 self.image_size = 0
1051 self.tree_offset = 0
1052 self.tree_size = 0
1053 self.data_block_size = 0
1054 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001055 self.fec_num_roots = 0
1056 self.fec_offset = 0
1057 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001058 self.hash_algorithm = ''
1059 self.partition_name = ''
1060 self.salt = bytearray()
1061 self.root_digest = bytearray()
1062
1063 def print_desc(self, o):
1064 """Print the descriptor.
1065
1066 Arguments:
1067 o: The object to write the output to.
1068 """
1069 o.write(' Hashtree descriptor:\n')
1070 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1071 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1072 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1073 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1074 o.write(' Data Block Size: {} bytes\n'.format(
1075 self.data_block_size))
1076 o.write(' Hash Block Size: {} bytes\n'.format(
1077 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001078 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1079 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1080 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001081 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1082 o.write(' Partition Name: {}\n'.format(self.partition_name))
1083 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1084 'hex')))
1085 o.write(' Root Digest: {}\n'.format(str(
1086 self.root_digest).encode('hex')))
1087
1088 def encode(self):
1089 """Serializes the descriptor.
1090
1091 Returns:
1092 A bytearray() with the descriptor data.
1093 """
1094 encoded_name = self.partition_name.encode('utf-8')
1095 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1096 len(self.root_digest) - 16)
1097 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1098 padding_size = nbf_with_padding - num_bytes_following
1099 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1100 self.dm_verity_version, self.image_size,
1101 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001102 self.hash_block_size, self.fec_num_roots,
1103 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001104 len(encoded_name), len(self.salt), len(self.root_digest),
1105 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001106 padding = struct.pack(str(padding_size) + 'x')
1107 ret = desc + encoded_name + self.salt + self.root_digest + padding
1108 return bytearray(ret)
1109
1110
1111class AvbHashDescriptor(AvbDescriptor):
1112 """A class for hash descriptors.
1113
1114 See the |AvbHashDescriptor| C struct for more information.
1115
1116 Attributes:
1117 image_size: Image size, in bytes.
1118 hash_algorithm: Hash algorithm used.
1119 partition_name: Partition name.
1120 salt: Salt used.
1121 digest: The hash value of salt and data combined.
1122 """
1123
1124 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001125 RESERVED = 64
1126 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001127 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1128 'Q' # image size (bytes)
1129 '32s' # hash algorithm used
1130 'L' # partition name (bytes)
1131 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001132 'L' + # digest length (bytes)
1133 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001134
1135 def __init__(self, data=None):
1136 """Initializes a new hash descriptor.
1137
1138 Arguments:
1139 data: If not None, must be a bytearray of size |SIZE|.
1140
1141 Raises:
1142 LookupError: If the given descriptor is malformed.
1143 """
1144 AvbDescriptor.__init__(self, None)
1145 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1146
1147 if data:
1148 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1149 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001150 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001151 expected_size = round_to_multiple(
1152 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1153 if tag != self.TAG or num_bytes_following != expected_size:
1154 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1155 # Nuke NUL-bytes at the end.
1156 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1157 o = 0
1158 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1159 partition_name_len)])
1160 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1161 self.partition_name.decode('utf-8')
1162 o += partition_name_len
1163 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1164 o += salt_len
1165 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1166 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1167 raise LookupError('digest_len doesn\'t match hash algorithm')
1168
1169 else:
1170 self.image_size = 0
1171 self.hash_algorithm = ''
1172 self.partition_name = ''
1173 self.salt = bytearray()
1174 self.digest = bytearray()
1175
1176 def print_desc(self, o):
1177 """Print the descriptor.
1178
1179 Arguments:
1180 o: The object to write the output to.
1181 """
1182 o.write(' Hash descriptor:\n')
1183 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1184 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1185 o.write(' Partition Name: {}\n'.format(self.partition_name))
1186 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1187 'hex')))
1188 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1189 'hex')))
1190
1191 def encode(self):
1192 """Serializes the descriptor.
1193
1194 Returns:
1195 A bytearray() with the descriptor data.
1196 """
1197 encoded_name = self.partition_name.encode('utf-8')
1198 num_bytes_following = (
1199 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1200 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1201 padding_size = nbf_with_padding - num_bytes_following
1202 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1203 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001204 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001205 padding = struct.pack(str(padding_size) + 'x')
1206 ret = desc + encoded_name + self.salt + self.digest + padding
1207 return bytearray(ret)
1208
1209
1210class AvbKernelCmdlineDescriptor(AvbDescriptor):
1211 """A class for kernel command-line descriptors.
1212
1213 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1214
1215 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001216 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001217 kernel_cmdline: The kernel command-line.
1218 """
1219
1220 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001221 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001222 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001223 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001224 'L') # cmdline length (bytes)
1225
David Zeuthenfd41eb92016-11-17 12:24:47 -05001226 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1227 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1228
David Zeuthen21e95262016-07-27 17:58:40 -04001229 def __init__(self, data=None):
1230 """Initializes a new kernel cmdline descriptor.
1231
1232 Arguments:
1233 data: If not None, must be a bytearray of size |SIZE|.
1234
1235 Raises:
1236 LookupError: If the given descriptor is malformed.
1237 """
1238 AvbDescriptor.__init__(self, None)
1239 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1240
1241 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001242 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001243 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1244 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1245 8)
1246 if tag != self.TAG or num_bytes_following != expected_size:
1247 raise LookupError('Given data does not look like a kernel cmdline '
1248 'descriptor.')
1249 # Nuke NUL-bytes at the end.
1250 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1251 kernel_cmdline_length)])
1252 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1253 self.kernel_cmdline.decode('utf-8')
1254 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001255 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001256 self.kernel_cmdline = ''
1257
1258 def print_desc(self, o):
1259 """Print the descriptor.
1260
1261 Arguments:
1262 o: The object to write the output to.
1263 """
1264 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001265 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001266 o.write(' Kernel Cmdline: {}\n'.format(repr(
1267 self.kernel_cmdline)))
1268
1269 def encode(self):
1270 """Serializes the descriptor.
1271
1272 Returns:
1273 A bytearray() with the descriptor data.
1274 """
1275 encoded_str = self.kernel_cmdline.encode('utf-8')
1276 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1277 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1278 padding_size = nbf_with_padding - num_bytes_following
1279 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001280 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001281 padding = struct.pack(str(padding_size) + 'x')
1282 ret = desc + encoded_str + padding
1283 return bytearray(ret)
1284
1285
1286class AvbChainPartitionDescriptor(AvbDescriptor):
1287 """A class for chained partition descriptors.
1288
1289 See the |AvbChainPartitionDescriptor| C struct for more information.
1290
1291 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001292 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001293 partition_name: Partition name.
1294 public_key: Bytes for the public key.
1295 """
1296
1297 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001298 RESERVED = 64
1299 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001300 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001301 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001302 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001303 'L' + # public_key_size (bytes)
1304 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001305
1306 def __init__(self, data=None):
1307 """Initializes a new chain partition descriptor.
1308
1309 Arguments:
1310 data: If not None, must be a bytearray of size |SIZE|.
1311
1312 Raises:
1313 LookupError: If the given descriptor is malformed.
1314 """
1315 AvbDescriptor.__init__(self, None)
1316 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1317
1318 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001319 (tag, num_bytes_following, self.rollback_index_location,
1320 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001321 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001322 expected_size = round_to_multiple(
1323 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1324 if tag != self.TAG or num_bytes_following != expected_size:
1325 raise LookupError('Given data does not look like a chain partition '
1326 'descriptor.')
1327 o = 0
1328 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1329 partition_name_len)])
1330 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1331 self.partition_name.decode('utf-8')
1332 o += partition_name_len
1333 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1334
1335 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001336 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001337 self.partition_name = ''
1338 self.public_key = bytearray()
1339
1340 def print_desc(self, o):
1341 """Print the descriptor.
1342
1343 Arguments:
1344 o: The object to write the output to.
1345 """
1346 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001347 o.write(' Partition Name: {}\n'.format(self.partition_name))
1348 o.write(' Rollback Index Location: {}\n'.format(
1349 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001350 # Just show the SHA1 of the key, for size reasons.
1351 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001352 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001353
1354 def encode(self):
1355 """Serializes the descriptor.
1356
1357 Returns:
1358 A bytearray() with the descriptor data.
1359 """
1360 encoded_name = self.partition_name.encode('utf-8')
1361 num_bytes_following = (
1362 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1363 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1364 padding_size = nbf_with_padding - num_bytes_following
1365 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001366 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001367 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001368 padding = struct.pack(str(padding_size) + 'x')
1369 ret = desc + encoded_name + self.public_key + padding
1370 return bytearray(ret)
1371
1372
1373DESCRIPTOR_CLASSES = [
1374 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1375 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1376]
1377
1378
1379def parse_descriptors(data):
1380 """Parses a blob of data into descriptors.
1381
1382 Arguments:
1383 data: A bytearray() with encoded descriptors.
1384
1385 Returns:
1386 A list of instances of objects derived from AvbDescriptor. For
1387 unknown descriptors, the class AvbDescriptor is used.
1388 """
1389 o = 0
1390 ret = []
1391 while o < len(data):
1392 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1393 if tag < len(DESCRIPTOR_CLASSES):
1394 c = DESCRIPTOR_CLASSES[tag]
1395 else:
1396 c = AvbDescriptor
1397 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1398 o += 16 + nb_following
1399 return ret
1400
1401
1402class AvbFooter(object):
1403 """A class for parsing and writing footers.
1404
1405 Footers are stored at the end of partitions and point to where the
1406 AvbVBMeta blob is located. They also contain the original size of
1407 the image before AVB information was added.
1408
1409 Attributes:
1410 magic: Magic for identifying the footer, see |MAGIC|.
1411 version_major: The major version of avbtool that wrote the footer.
1412 version_minor: The minor version of avbtool that wrote the footer.
1413 original_image_size: Original image size.
1414 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1415 vbmeta_size: Size of the AvbVBMeta blob.
1416 """
1417
1418 MAGIC = 'AVBf'
1419 SIZE = 64
1420 RESERVED = 28
1421 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1422 'Q' # Original image size.
1423 'Q' # Offset of VBMeta blob.
1424 'Q' + # Size of VBMeta blob.
1425 str(RESERVED) + 'x') # padding for reserved bytes
1426
1427 def __init__(self, data=None):
1428 """Initializes a new footer object.
1429
1430 Arguments:
1431 data: If not None, must be a bytearray of size 4096.
1432
1433 Raises:
1434 LookupError: If the given footer is malformed.
1435 struct.error: If the given data has no footer.
1436 """
1437 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1438
1439 if data:
1440 (self.magic, self.version_major, self.version_minor,
1441 self.original_image_size, self.vbmeta_offset,
1442 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1443 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001444 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001445 else:
1446 self.magic = self.MAGIC
1447 self.version_major = AVB_VERSION_MAJOR
1448 self.version_minor = AVB_VERSION_MINOR
1449 self.original_image_size = 0
1450 self.vbmeta_offset = 0
1451 self.vbmeta_size = 0
1452
David Zeuthena4fee8b2016-08-22 15:20:43 -04001453 def encode(self):
1454 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001455
David Zeuthena4fee8b2016-08-22 15:20:43 -04001456 Returns:
1457 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001458 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001459 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1460 self.version_minor, self.original_image_size,
1461 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001462
1463
1464class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001465 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001466
1467 Attributes:
1468 The attributes correspond to the |AvbVBMetaHeader| struct
1469 defined in avb_vbmeta_header.h.
1470 """
1471
1472 SIZE = 256
1473
1474 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001475 RESERVED = 132
David Zeuthen21e95262016-07-27 17:58:40 -04001476
1477 # Keep in sync with |AvbVBMetaImageHeader|.
1478 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1479 '2Q' # 2 x block size
1480 'L' # algorithm type
1481 '2Q' # offset, size (hash)
1482 '2Q' # offset, size (signature)
1483 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001484 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001485 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001486 'Q' # rollback_index
1487 'L' + # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001488 str(RESERVED) + 'x') # padding for reserved bytes
1489
1490 def __init__(self, data=None):
1491 """Initializes a new header object.
1492
1493 Arguments:
1494 data: If not None, must be a bytearray of size 8192.
1495
1496 Raises:
1497 Exception: If the given data is malformed.
1498 """
1499 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1500
1501 if data:
1502 (self.magic, self.header_version_major, self.header_version_minor,
1503 self.authentication_data_block_size, self.auxiliary_data_block_size,
1504 self.algorithm_type, self.hash_offset, self.hash_size,
1505 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001506 self.public_key_size, self.public_key_metadata_offset,
1507 self.public_key_metadata_size, self.descriptors_offset,
1508 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001509 self.rollback_index,
1510 self.flags) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001511 # Nuke NUL-bytes at the end of the string.
1512 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001513 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001514 else:
1515 self.magic = 'AVB0'
1516 self.header_version_major = AVB_VERSION_MAJOR
1517 self.header_version_minor = AVB_VERSION_MINOR
1518 self.authentication_data_block_size = 0
1519 self.auxiliary_data_block_size = 0
1520 self.algorithm_type = 0
1521 self.hash_offset = 0
1522 self.hash_size = 0
1523 self.signature_offset = 0
1524 self.signature_size = 0
1525 self.public_key_offset = 0
1526 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001527 self.public_key_metadata_offset = 0
1528 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001529 self.descriptors_offset = 0
1530 self.descriptors_size = 0
1531 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001532 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001533
1534 def save(self, output):
1535 """Serializes the header (256 bytes) to disk.
1536
1537 Arguments:
1538 output: The object to write the output to.
1539 """
1540 output.write(struct.pack(
1541 self.FORMAT_STRING, self.magic, self.header_version_major,
1542 self.header_version_minor, self.authentication_data_block_size,
1543 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1544 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001545 self.public_key_offset, self.public_key_size,
1546 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001547 self.descriptors_offset, self.descriptors_size, self.rollback_index,
1548 self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001549
1550 def encode(self):
1551 """Serializes the header (256) to a bytearray().
1552
1553 Returns:
1554 A bytearray() with the encoded header.
1555 """
1556 return struct.pack(self.FORMAT_STRING, self.magic,
1557 self.header_version_major, self.header_version_minor,
1558 self.authentication_data_block_size,
1559 self.auxiliary_data_block_size, self.algorithm_type,
1560 self.hash_offset, self.hash_size, self.signature_offset,
1561 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001562 self.public_key_size, self.public_key_metadata_offset,
1563 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001564 self.descriptors_size, self.rollback_index, self.flags)
David Zeuthen21e95262016-07-27 17:58:40 -04001565
1566
1567class Avb(object):
1568 """Business logic for avbtool command-line tool."""
1569
David Zeuthen8b6973b2016-09-20 12:39:49 -04001570 # Keep in sync with avb_ab_flow.h.
1571 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1572 AB_MAGIC = '\0AB0'
1573 AB_MAJOR_VERSION = 1
1574 AB_MINOR_VERSION = 0
1575 AB_MISC_METADATA_OFFSET = 2048
1576
David Zeuthen09692692016-09-30 16:16:40 -04001577 # Constants for maximum metadata size. These are used to give
1578 # meaningful errors if the value passed in via --partition_size is
1579 # too small and when --calc_max_image_size is used. We use
1580 # conservative figures.
1581 MAX_VBMETA_SIZE = 64 * 1024
1582 MAX_FOOTER_SIZE = 4096
1583
David Zeuthena4fee8b2016-08-22 15:20:43 -04001584 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001585 """Implements the 'erase_footer' command.
1586
1587 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001588 image_filename: File to erase a footer from.
David Zeuthen21e95262016-07-27 17:58:40 -04001589 keep_hashtree: If True, keep the hashtree around.
1590
1591 Raises:
1592 AvbError: If there's no footer in the image.
1593 """
1594
David Zeuthena4fee8b2016-08-22 15:20:43 -04001595 image = ImageHandler(image_filename)
1596
David Zeuthen21e95262016-07-27 17:58:40 -04001597 (footer, _, descriptors, _) = self._parse_image(image)
1598
1599 if not footer:
1600 raise AvbError('Given image does not have a footer.')
1601
1602 new_image_size = None
1603 if not keep_hashtree:
1604 new_image_size = footer.original_image_size
1605 else:
1606 # If requested to keep the hashtree, search for a hashtree
1607 # descriptor to figure out the location and size of the hashtree.
1608 for desc in descriptors:
1609 if isinstance(desc, AvbHashtreeDescriptor):
1610 # The hashtree is always just following the main data so the
1611 # new size is easily derived.
1612 new_image_size = desc.tree_offset + desc.tree_size
1613 break
1614 if not new_image_size:
1615 raise AvbError('Requested to keep hashtree but no hashtree '
1616 'descriptor was found.')
1617
1618 # And cut...
1619 image.truncate(new_image_size)
1620
David Zeuthen8b6973b2016-09-20 12:39:49 -04001621 def set_ab_metadata(self, misc_image, slot_data):
1622 """Implements the 'set_ab_metadata' command.
1623
1624 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1625 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1626
1627 Arguments:
1628 misc_image: The misc image to write to.
1629 slot_data: Slot data as a string
1630
1631 Raises:
1632 AvbError: If slot data is malformed.
1633 """
1634 tokens = slot_data.split(':')
1635 if len(tokens) != 6:
1636 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1637 a_priority = int(tokens[0])
1638 a_tries_remaining = int(tokens[1])
1639 a_success = True if int(tokens[2]) != 0 else False
1640 b_priority = int(tokens[3])
1641 b_tries_remaining = int(tokens[4])
1642 b_success = True if int(tokens[5]) != 0 else False
1643
1644 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1645 self.AB_MAGIC,
1646 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1647 a_priority, a_tries_remaining, a_success,
1648 b_priority, b_tries_remaining, b_success)
1649 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1650 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1651 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1652 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1653 misc_image.write(ab_data)
1654
David Zeuthena4fee8b2016-08-22 15:20:43 -04001655 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001656 """Implements the 'info_image' command.
1657
1658 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001659 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001660 output: Output file to write human-readable information to (file object).
1661 """
1662
David Zeuthena4fee8b2016-08-22 15:20:43 -04001663 image = ImageHandler(image_filename)
1664
David Zeuthen21e95262016-07-27 17:58:40 -04001665 o = output
1666
1667 (footer, header, descriptors, image_size) = self._parse_image(image)
1668
1669 if footer:
1670 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1671 footer.version_minor))
1672 o.write('Image size: {} bytes\n'.format(image_size))
1673 o.write('Original image size: {} bytes\n'.format(
1674 footer.original_image_size))
1675 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1676 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1677 o.write('--\n')
1678
1679 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1680
David Zeuthena4fee8b2016-08-22 15:20:43 -04001681 o.write('VBMeta image version: {}.{}{}\n'.format(
1682 header.header_version_major, header.header_version_minor,
1683 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001684 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1685 o.write('Authentication Block: {} bytes\n'.format(
1686 header.authentication_data_block_size))
1687 o.write('Auxiliary Block: {} bytes\n'.format(
1688 header.auxiliary_data_block_size))
1689 o.write('Algorithm: {}\n'.format(alg_name))
1690 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001691 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001692
1693 # Print descriptors.
1694 num_printed = 0
1695 o.write('Descriptors:\n')
1696 for desc in descriptors:
1697 desc.print_desc(o)
1698 num_printed += 1
1699 if num_printed == 0:
1700 o.write(' (none)\n')
1701
1702 def _parse_image(self, image):
1703 """Gets information about an image.
1704
1705 The image can either be a vbmeta or an image with a footer.
1706
1707 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001708 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001709
1710 Returns:
1711 A tuple where the first argument is a AvbFooter (None if there
1712 is no footer on the image), the second argument is a
1713 AvbVBMetaHeader, the third argument is a list of
1714 AvbDescriptor-derived instances, and the fourth argument is the
1715 size of |image|.
1716 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001717 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001718 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001719 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001720 try:
1721 footer = AvbFooter(image.read(AvbFooter.SIZE))
1722 except (LookupError, struct.error):
1723 # Nope, just seek back to the start.
1724 image.seek(0)
1725
1726 vbmeta_offset = 0
1727 if footer:
1728 vbmeta_offset = footer.vbmeta_offset
1729
1730 image.seek(vbmeta_offset)
1731 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1732
1733 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1734 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1735 desc_start_offset = aux_block_offset + h.descriptors_offset
1736 image.seek(desc_start_offset)
1737 descriptors = parse_descriptors(image.read(h.descriptors_size))
1738
David Zeuthen09692692016-09-30 16:16:40 -04001739 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001740
David Zeuthenfd41eb92016-11-17 12:24:47 -05001741 def _get_cmdline_descriptors_for_dm_verity(self, image):
1742 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001743
1744 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001745 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001746
1747 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001748 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1749 instructions. There is one for when hashtree is not disabled and one for
1750 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001751
1752 Raises:
1753 AvbError: If |image| doesn't have a hashtree descriptor.
1754
1755 """
1756
1757 (_, _, descriptors, _) = self._parse_image(image)
1758
1759 ht = None
1760 for desc in descriptors:
1761 if isinstance(desc, AvbHashtreeDescriptor):
1762 ht = desc
1763 break
1764
1765 if not ht:
1766 raise AvbError('No hashtree descriptor in given image')
1767
1768 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001769 c += '0' # start
1770 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1771 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1772 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1773 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1774 c += ' {}'.format(ht.data_block_size) # data_block
1775 c += ' {}'.format(ht.hash_block_size) # hash_block
1776 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1777 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1778 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1779 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1780 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1781 if ht.fec_num_roots > 0:
1782 c += ' 9' # number of optional args
1783 c += ' ignore_zero_blocks'
1784 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1785 c += ' fec_roots {}'.format(ht.fec_num_roots)
1786 # Note that fec_blocks is the size that FEC covers, *not* the
1787 # size of the FEC data. Since we use FEC for everything up until
1788 # the FEC data, it's the same as the offset.
1789 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1790 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1791 else:
1792 c += ' 1' # number of optional args
1793 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001794 c += '" root=0xfd00'
David Zeuthenbc8f6472016-11-21 14:59:15 -05001795 c += ' androidboot.vbmeta.device=PARTUUID=$(ANDROID_VBMETA_PARTUUID)'
David Zeuthen21e95262016-07-27 17:58:40 -04001796
David Zeuthenfd41eb92016-11-17 12:24:47 -05001797 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001798 desc = AvbKernelCmdlineDescriptor()
1799 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001800 desc.flags = (
1801 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1802
1803 # The descriptor for when hashtree verification is disabled is a lot
1804 # simpler - we just set the root to the partition.
1805 desc_no_ht = AvbKernelCmdlineDescriptor()
1806 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1807 desc_no_ht.flags = (
1808 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1809
1810 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001813 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001814 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001815 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001816 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001817 """Implements the 'make_vbmeta_image' command.
1818
1819 Arguments:
1820 output: File to write the image to.
1821 chain_partitions: List of partitions to chain.
1822 algorithm_name: Name of algorithm to use.
1823 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001824 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001825 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001826 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001827 props: Properties to insert (list of strings of the form 'key:value').
1828 props_from_file: Properties to insert (list of strings 'key:<path>').
1829 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1830 generate_dm_verity_cmdline_from_hashtree: None or file to generate from.
1831 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001832 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001833
1834 Raises:
1835 AvbError: If a chained partition is malformed.
1836 """
1837
1838 descriptors = []
1839
1840 # Insert chained partition descriptors.
1841 if chain_partitions:
1842 for cp in chain_partitions:
1843 cp_tokens = cp.split(':')
1844 if len(cp_tokens) != 3:
1845 raise AvbError('Malformed chained partition "{}".'.format(cp))
1846 desc = AvbChainPartitionDescriptor()
1847 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001848 desc.rollback_index_location = int(cp_tokens[1])
1849 if desc.rollback_index_location < 1:
1850 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001851 file_path = cp_tokens[2]
1852 desc.public_key = open(file_path, 'rb').read()
1853 descriptors.append(desc)
1854
1855 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001856 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001857 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04001858 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001859 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04001860
1861 # Write entire vbmeta blob (header, authentication, auxiliary).
1862 output.seek(0)
1863 output.write(vbmeta_blob)
1864
David Zeuthen18666ab2016-11-15 11:18:05 -05001865 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1866 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001867 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001868 kernel_cmdlines,
1869 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001870 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001871 """Generates a VBMeta blob.
1872
1873 This blob contains the header (struct AvbVBMetaHeader), the
1874 authentication data block (which contains the hash and signature
1875 for the header and auxiliary block), and the auxiliary block
1876 (which contains descriptors, the public key used, and other data).
1877
1878 The |key| parameter can |None| only if the |algorithm_name| is
1879 'NONE'.
1880
1881 Arguments:
1882 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1883 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001884 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001885 descriptors: A list of descriptors to insert or None.
1886 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001887 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001888 props: Properties to insert (List of strings of the form 'key:value').
1889 props_from_file: Properties to insert (List of strings 'key:<path>').
1890 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
1891 generate_dm_verity_cmdline_from_hashtree: None or file to generate
1892 dm-verity kernel cmdline from.
1893 include_descriptors_from_image: List of file objects for which
1894 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001895 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001896
1897 Returns:
1898 A bytearray() with the VBMeta blob.
1899
1900 Raises:
1901 Exception: If the |algorithm_name| is not found, if no key has
1902 been given and the given algorithm requires one, or the key is
1903 of the wrong size.
1904
1905 """
1906 try:
1907 alg = ALGORITHMS[algorithm_name]
1908 except KeyError:
1909 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1910
1911 # Descriptors.
1912 encoded_descriptors = bytearray()
1913 if descriptors:
1914 for desc in descriptors:
1915 encoded_descriptors.extend(desc.encode())
1916
1917 # Add properties.
1918 if props:
1919 for prop in props:
1920 idx = prop.find(':')
1921 if idx == -1:
1922 raise AvbError('Malformed property "{}".'.format(prop))
1923 desc = AvbPropertyDescriptor()
1924 desc.key = prop[0:idx]
1925 desc.value = prop[(idx + 1):]
1926 encoded_descriptors.extend(desc.encode())
1927 if props_from_file:
1928 for prop in props_from_file:
1929 idx = prop.find(':')
1930 if idx == -1:
1931 raise AvbError('Malformed property "{}".'.format(prop))
1932 desc = AvbPropertyDescriptor()
1933 desc.key = prop[0:idx]
1934 desc.value = prop[(idx + 1):]
1935 file_path = prop[(idx + 1):]
1936 desc.value = open(file_path, 'rb').read()
1937 encoded_descriptors.extend(desc.encode())
1938
1939 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
1940 if generate_dm_verity_cmdline_from_hashtree:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001941 image_handler = ImageHandler(
1942 generate_dm_verity_cmdline_from_hashtree.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001943 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1944 encoded_descriptors.extend(cmdline_desc[0].encode())
1945 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001946
1947 # Add kernel command-lines.
1948 if kernel_cmdlines:
1949 for i in kernel_cmdlines:
1950 desc = AvbKernelCmdlineDescriptor()
1951 desc.kernel_cmdline = i
1952 encoded_descriptors.extend(desc.encode())
1953
1954 # Add descriptors from other images.
1955 if include_descriptors_from_image:
1956 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001957 image_handler = ImageHandler(image.name)
1958 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001959 for desc in image_descriptors:
1960 encoded_descriptors.extend(desc.encode())
1961
David Zeuthen18666ab2016-11-15 11:18:05 -05001962 # Load public key metadata blob, if requested.
1963 pkmd_blob = []
1964 if public_key_metadata_path:
1965 with open(public_key_metadata_path) as f:
1966 pkmd_blob = f.read()
1967
David Zeuthen21e95262016-07-27 17:58:40 -04001968 key = None
1969 encoded_key = bytearray()
1970 if alg.public_key_num_bytes > 0:
1971 if not key_path:
1972 raise AvbError('Key is required for algorithm {}'.format(
1973 algorithm_name))
1974 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
1975 encoded_key = encode_rsa_key(key)
1976 if len(encoded_key) != alg.public_key_num_bytes:
1977 raise AvbError('Key is wrong size for algorithm {}'.format(
1978 algorithm_name))
1979
1980 h = AvbVBMetaHeader()
1981
David Zeuthen18666ab2016-11-15 11:18:05 -05001982 # For the Auxiliary data block, descriptors are stored at offset 0,
1983 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001984 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05001985 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04001986 h.descriptors_offset = 0
1987 h.descriptors_size = len(encoded_descriptors)
1988 h.public_key_offset = h.descriptors_size
1989 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001990 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
1991 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04001992
1993 # For the Authentication data block, the hash is first and then
1994 # the signature.
1995 h.authentication_data_block_size = round_to_multiple(
1996 alg.hash_num_bytes + alg.public_key_num_bytes, 64)
1997 h.algorithm_type = alg.algorithm_type
1998 h.hash_offset = 0
1999 h.hash_size = alg.hash_num_bytes
2000 # Signature offset and size - it's stored right after the hash
2001 # (in Authentication data block).
2002 h.signature_offset = alg.hash_num_bytes
2003 h.signature_size = alg.signature_num_bytes
2004
2005 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002006 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002007
2008 # Generate Header data block.
2009 header_data_blob = h.encode()
2010
2011 # Generate Auxiliary data block.
2012 aux_data_blob = bytearray()
2013 aux_data_blob.extend(encoded_descriptors)
2014 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002015 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002016 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2017 aux_data_blob.extend('\0' * padding_bytes)
2018
2019 # Calculate the hash.
2020 binary_hash = bytearray()
2021 binary_signature = bytearray()
2022 if algorithm_name != 'NONE':
2023 if algorithm_name[0:6] == 'SHA256':
2024 ha = hashlib.sha256()
2025 elif algorithm_name[0:6] == 'SHA512':
2026 ha = hashlib.sha512()
2027 else:
2028 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2029 ha.update(header_data_blob)
2030 ha.update(aux_data_blob)
2031 binary_hash.extend(ha.digest())
2032
2033 # Calculate the signature.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002034 p = None
2035 if signing_helper is not None:
2036 p = subprocess.Popen(
2037 [signing_helper, algorithm_name, key_path],
2038 stdin=subprocess.PIPE,
2039 stdout=subprocess.PIPE,
2040 stderr=subprocess.PIPE)
2041 else:
2042 p = subprocess.Popen(
2043 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
2044 stdin=subprocess.PIPE,
2045 stdout=subprocess.PIPE,
2046 stderr=subprocess.PIPE)
David Zeuthen21e95262016-07-27 17:58:40 -04002047 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
2048 (pout, perr) = p.communicate(padding_and_hash)
2049 retcode = p.wait()
2050 if retcode != 0:
2051 raise AvbError('Error signing: {}'.format(perr))
2052 binary_signature.extend(pout)
2053
2054 # Generate Authentication data block.
2055 auth_data_blob = bytearray()
2056 auth_data_blob.extend(binary_hash)
2057 auth_data_blob.extend(binary_signature)
2058 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2059 auth_data_blob.extend('\0' * padding_bytes)
2060
2061 return header_data_blob + auth_data_blob + aux_data_blob
2062
2063 def extract_public_key(self, key_path, output):
2064 """Implements the 'extract_public_key' command.
2065
2066 Arguments:
2067 key_path: The path to a RSA private key file.
2068 output: The file to write to.
2069 """
2070 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2071 write_rsa_key(output, key)
2072
David Zeuthena4fee8b2016-08-22 15:20:43 -04002073 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002074 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002075 public_key_metadata_path, rollback_index, props,
2076 props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002077 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002078 include_descriptors_from_image, signing_helper):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002079 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002080
2081 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002082 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002083 partition_size: Size of partition.
2084 partition_name: Name of partition (without A/B suffix).
2085 hash_algorithm: Hash algorithm to use.
2086 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2087 algorithm_name: Name of algorithm to use.
2088 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002089 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002090 rollback_index: Rollback index.
2091 props: Properties to insert (List of strings of the form 'key:value').
2092 props_from_file: Properties to insert (List of strings 'key:<path>').
2093 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2094 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2095 dm-verity kernel cmdline from.
2096 include_descriptors_from_image: List of file objects for which
2097 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002098 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002099
2100 Raises:
2101 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002102 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002103 image = ImageHandler(image_filename)
2104
2105 if partition_size % image.block_size != 0:
2106 raise AvbError('Partition size of {} is not a multiple of the image '
2107 'block size {}.'.format(partition_size,
2108 image.block_size))
2109
David Zeuthen21e95262016-07-27 17:58:40 -04002110 # If there's already a footer, truncate the image to its original
2111 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2112 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002113 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002114 try:
2115 footer = AvbFooter(image.read(AvbFooter.SIZE))
2116 # Existing footer found. Just truncate.
2117 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002118 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002119 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002120 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002121
2122 # If anything goes wrong from here-on, restore the image back to
2123 # its original size.
2124 try:
David Zeuthen09692692016-09-30 16:16:40 -04002125 # First, calculate the maximum image size such that an image
2126 # this size + metadata (footer + vbmeta struct) fits in
2127 # |partition_size|.
2128 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2129 max_image_size = partition_size - max_metadata_size
2130
2131 # If image size exceeds the maximum image size, fail.
2132 if image.image_size > max_image_size:
2133 raise AvbError('Image size of {} exceeds maximum image '
2134 'size of {} in order to fit in a partition '
2135 'size of {}.'.format(image.image_size, max_image_size,
2136 partition_size))
2137
David Zeuthen21e95262016-07-27 17:58:40 -04002138 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2139 if salt:
2140 salt = salt.decode('hex')
2141 else:
2142 if salt is None:
2143 # If salt is not explicitly specified, choose a hash
2144 # that's the same size as the hash size.
2145 hash_size = digest_size
2146 salt = open('/dev/urandom').read(hash_size)
2147 else:
2148 salt = ''
2149
2150 hasher = hashlib.new(name=hash_algorithm, string=salt)
2151 # TODO(zeuthen): might want to read this in chunks to avoid
2152 # memory pressure, then again, this is only supposed to be used
2153 # on kernel/initramfs partitions. Possible optimization.
2154 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002155 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002156 digest = hasher.digest()
2157
2158 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002159 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002160 h_desc.hash_algorithm = hash_algorithm
2161 h_desc.partition_name = partition_name
2162 h_desc.salt = salt
2163 h_desc.digest = digest
2164
David Zeuthenfd41eb92016-11-17 12:24:47 -05002165 # Flags are only allowed on top-level vbmeta struct.
2166 flags = 0
2167
David Zeuthen21e95262016-07-27 17:58:40 -04002168 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002169 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002170 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002171 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002172 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002173 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002174
David Zeuthena4fee8b2016-08-22 15:20:43 -04002175 # If the image isn't sparse, its size might not be a multiple of
2176 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002177 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002178 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002179 padding_needed = image.block_size - (image.image_size%image.block_size)
2180 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002181
David Zeuthena4fee8b2016-08-22 15:20:43 -04002182 # The append_raw() method requires content with size being a
2183 # multiple of |block_size| so add padding as needed. Also record
2184 # where this is written to since we'll need to put that in the
2185 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002186 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002187 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2188 len(vbmeta_blob))
2189 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2190 image.append_raw(vbmeta_blob_with_padding)
2191 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2192
2193 # Now insert a DONT_CARE chunk with enough bytes such that the
2194 # final Footer block is at the end of partition_size..
2195 image.append_dont_care(partition_size - vbmeta_end_offset -
2196 1*image.block_size)
2197
2198 # Generate the Footer that tells where the VBMeta footer
2199 # is. Also put enough padding in the front of the footer since
2200 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002201 footer = AvbFooter()
2202 footer.original_image_size = original_image_size
2203 footer.vbmeta_offset = vbmeta_offset
2204 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002205 footer_blob = footer.encode()
2206 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2207 footer_blob)
2208 image.append_raw(footer_blob_with_padding)
2209
David Zeuthen21e95262016-07-27 17:58:40 -04002210 except:
2211 # Truncate back to original size, then re-raise
2212 image.truncate(original_image_size)
2213 raise
2214
David Zeuthena4fee8b2016-08-22 15:20:43 -04002215 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002216 generate_fec, fec_num_roots, hash_algorithm,
2217 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002218 public_key_metadata_path, rollback_index,
2219 props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002220 generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002221 include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002222 calc_max_image_size, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04002223 """Implements the 'add_hashtree_footer' command.
2224
2225 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2226 more information about dm-verity and these hashes.
2227
2228 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002229 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002230 partition_size: Size of partition.
2231 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002232 generate_fec: If True, generate FEC codes.
2233 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002234 hash_algorithm: Hash algorithm to use.
2235 block_size: Block size to use.
2236 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2237 algorithm_name: Name of algorithm to use.
2238 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002239 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002240 rollback_index: Rollback index.
2241 props: Properties to insert (List of strings of the form 'key:value').
2242 props_from_file: Properties to insert (List of strings 'key:<path>').
2243 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2244 generate_dm_verity_cmdline_from_hashtree: None or file to generate
2245 dm-verity kernel cmdline from.
2246 include_descriptors_from_image: List of file objects for which
2247 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002248 calc_max_image_size: Don't store the hashtree or footer - instead
2249 calculate the maximum image size leaving enough room for hashtree
2250 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002251 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002252
2253 Raises:
2254 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002255 """
David Zeuthen09692692016-09-30 16:16:40 -04002256 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2257 digest_padding = round_to_pow2(digest_size) - digest_size
2258
2259 # First, calculate the maximum image size such that an image
2260 # this size + the hashtree + metadata (footer + vbmeta struct)
2261 # fits in |partition_size|. We use very conservative figures for
2262 # metadata.
2263 (_, max_tree_size) = calc_hash_level_offsets(
2264 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002265 max_fec_size = 0
2266 if generate_fec:
2267 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2268 max_metadata_size = (max_fec_size + max_tree_size +
2269 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002270 self.MAX_FOOTER_SIZE)
2271 max_image_size = partition_size - max_metadata_size
2272
2273 # If we're asked to only calculate the maximum image size, we're done.
2274 if calc_max_image_size:
2275 print '{}'.format(max_image_size)
2276 return
2277
David Zeuthena4fee8b2016-08-22 15:20:43 -04002278 image = ImageHandler(image_filename)
2279
2280 if partition_size % image.block_size != 0:
2281 raise AvbError('Partition size of {} is not a multiple of the image '
2282 'block size {}.'.format(partition_size,
2283 image.block_size))
2284
David Zeuthen21e95262016-07-27 17:58:40 -04002285 # If there's already a footer, truncate the image to its original
2286 # size. This way 'avbtool add_hashtree_footer' is idempotent
2287 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002288 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002289 try:
2290 footer = AvbFooter(image.read(AvbFooter.SIZE))
2291 # Existing footer found. Just truncate.
2292 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002293 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002294 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002295 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002296
2297 # If anything goes wrong from here-on, restore the image back to
2298 # its original size.
2299 try:
2300 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002301 rounded_image_size = round_to_multiple(image.image_size, block_size)
2302 if rounded_image_size > image.image_size:
2303 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002304
David Zeuthen09692692016-09-30 16:16:40 -04002305 # If image size exceeds the maximum image size, fail.
2306 if image.image_size > max_image_size:
2307 raise AvbError('Image size of {} exceeds maximum image '
2308 'size of {} in order to fit in a partition '
2309 'size of {}.'.format(image.image_size, max_image_size,
2310 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002311
2312 if salt:
2313 salt = salt.decode('hex')
2314 else:
2315 if salt is None:
2316 # If salt is not explicitly specified, choose a hash
2317 # that's the same size as the hash size.
2318 hash_size = digest_size
2319 salt = open('/dev/urandom').read(hash_size)
2320 else:
2321 salt = ''
2322
David Zeuthena4fee8b2016-08-22 15:20:43 -04002323 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002324 # offsets in advance.
2325 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002326 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002327
David Zeuthena4fee8b2016-08-22 15:20:43 -04002328 # If the image isn't sparse, its size might not be a multiple of
2329 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002330 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002331 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002332 padding_needed = image.block_size - (image.image_size%image.block_size)
2333 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002334
David Zeuthena4fee8b2016-08-22 15:20:43 -04002335 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002336 tree_offset = image.image_size
2337 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002338 block_size,
2339 hash_algorithm, salt,
2340 digest_padding,
2341 hash_level_offsets,
2342 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002343
2344 # Generate HashtreeDescriptor with details about the tree we
2345 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002346 ht_desc = AvbHashtreeDescriptor()
2347 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002348 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002349 ht_desc.tree_offset = tree_offset
2350 ht_desc.tree_size = tree_size
2351 ht_desc.data_block_size = block_size
2352 ht_desc.hash_block_size = block_size
2353 ht_desc.hash_algorithm = hash_algorithm
2354 ht_desc.partition_name = partition_name
2355 ht_desc.salt = salt
2356 ht_desc.root_digest = root_digest
2357
David Zeuthen09692692016-09-30 16:16:40 -04002358 # Write the hash tree
2359 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2360 len(hash_tree))
2361 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2362 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002363 len_hashtree_and_fec = len(hash_tree_with_padding)
2364
2365 # Generate FEC codes, if requested.
2366 if generate_fec:
2367 fec_data = generate_fec_data(image_filename, fec_num_roots)
2368 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2369 len(fec_data))
2370 fec_data_with_padding = fec_data + '\0'*padding_needed
2371 fec_offset = image.image_size
2372 image.append_raw(fec_data_with_padding)
2373 len_hashtree_and_fec += len(fec_data_with_padding)
2374 # Update the hashtree descriptor.
2375 ht_desc.fec_num_roots = fec_num_roots
2376 ht_desc.fec_offset = fec_offset
2377 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002378
David Zeuthenfd41eb92016-11-17 12:24:47 -05002379 # Flags are only allowed on top-level vbmeta struct.
2380 flags = 0
2381
David Zeuthena4fee8b2016-08-22 15:20:43 -04002382 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002383 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002384 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002385 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002386 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen21e95262016-07-27 17:58:40 -04002387 generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002388 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002389 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2390 len(vbmeta_blob))
2391 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2392 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002393
David Zeuthena4fee8b2016-08-22 15:20:43 -04002394 # Now insert a DONT_CARE chunk with enough bytes such that the
2395 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002396 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002397 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002398
David Zeuthena4fee8b2016-08-22 15:20:43 -04002399 # Generate the Footer that tells where the VBMeta footer
2400 # is. Also put enough padding in the front of the footer since
2401 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002402 footer = AvbFooter()
2403 footer.original_image_size = original_image_size
2404 footer.vbmeta_offset = vbmeta_offset
2405 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002406 footer_blob = footer.encode()
2407 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2408 footer_blob)
2409 image.append_raw(footer_blob_with_padding)
2410
David Zeuthen21e95262016-07-27 17:58:40 -04002411 except:
David Zeuthen09692692016-09-30 16:16:40 -04002412 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002413 image.truncate(original_image_size)
2414 raise
2415
2416
2417def calc_hash_level_offsets(image_size, block_size, digest_size):
2418 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2419
2420 Arguments:
2421 image_size: The size of the image to calculate a Merkle-tree for.
2422 block_size: The block size, e.g. 4096.
2423 digest_size: The size of each hash, e.g. 32 for SHA-256.
2424
2425 Returns:
2426 A tuple where the first argument is an array of offsets and the
2427 second is size of the tree, in bytes.
2428 """
2429 level_offsets = []
2430 level_sizes = []
2431 tree_size = 0
2432
2433 num_levels = 0
2434 size = image_size
2435 while size > block_size:
2436 num_blocks = (size + block_size - 1) / block_size
2437 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2438
2439 level_sizes.append(level_size)
2440 tree_size += level_size
2441 num_levels += 1
2442
2443 size = level_size
2444
2445 for n in range(0, num_levels):
2446 offset = 0
2447 for m in range(n + 1, num_levels):
2448 offset += level_sizes[m]
2449 level_offsets.append(offset)
2450
David Zeuthena4fee8b2016-08-22 15:20:43 -04002451 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002452
2453
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002454# See system/extras/libfec/include/fec/io.h for these definitions.
2455FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2456FEC_MAGIC = 0xfecfecfe
2457
2458
2459def calc_fec_data_size(image_size, num_roots):
2460 """Calculates how much space FEC data will take.
2461
2462 Args:
2463 image_size: The size of the image.
2464 num_roots: Number of roots.
2465
2466 Returns:
2467 The number of bytes needed for FEC for an image of the given size
2468 and with the requested number of FEC roots.
2469
2470 Raises:
2471 ValueError: If output from the 'fec' tool is invalid.
2472
2473 """
2474 p = subprocess.Popen(
2475 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2476 stdout=subprocess.PIPE,
2477 stderr=subprocess.PIPE)
2478 (pout, perr) = p.communicate()
2479 retcode = p.wait()
2480 if retcode != 0:
2481 raise ValueError('Error invoking fec: {}'.format(perr))
2482 return int(pout)
2483
2484
2485def generate_fec_data(image_filename, num_roots):
2486 """Generate FEC codes for an image.
2487
2488 Args:
2489 image_filename: The filename of the image.
2490 num_roots: Number of roots.
2491
2492 Returns:
2493 The FEC data blob.
2494
2495 Raises:
2496 ValueError: If output from the 'fec' tool is invalid.
2497 """
2498 fec_tmpfile = tempfile.NamedTemporaryFile()
2499 subprocess.check_call(
2500 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2501 fec_tmpfile.name],
2502 stderr=open(os.devnull))
2503 fec_data = fec_tmpfile.read()
2504 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2505 footer_data = fec_data[-footer_size:]
2506 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2507 footer_data)
2508 if magic != FEC_MAGIC:
2509 raise ValueError('Unexpected magic in FEC footer')
2510 return fec_data[0:fec_size]
2511
2512
David Zeuthen21e95262016-07-27 17:58:40 -04002513def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002514 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002515 """Generates a Merkle-tree for a file.
2516
2517 Args:
2518 image: The image, as a file.
2519 image_size: The size of the image.
2520 block_size: The block size, e.g. 4096.
2521 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2522 salt: The salt to use.
2523 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002524 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002525 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002526
2527 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002528 A tuple where the first element is the top-level hash and the
2529 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002530 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002531 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002532 hash_src_offset = 0
2533 hash_src_size = image_size
2534 level_num = 0
2535 while hash_src_size > block_size:
2536 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002537 remaining = hash_src_size
2538 while remaining > 0:
2539 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002540 # Only read from the file for the first level - for subsequent
2541 # levels, access the array we're building.
2542 if level_num == 0:
2543 image.seek(hash_src_offset + hash_src_size - remaining)
2544 data = image.read(min(remaining, block_size))
2545 else:
2546 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2547 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002548 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002549
2550 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002551 if len(data) < block_size:
2552 hasher.update('\0' * (block_size - len(data)))
2553 level_output += hasher.digest()
2554 if digest_padding > 0:
2555 level_output += '\0' * digest_padding
2556
2557 padding_needed = (round_to_multiple(
2558 len(level_output), block_size) - len(level_output))
2559 level_output += '\0' * padding_needed
2560
David Zeuthena4fee8b2016-08-22 15:20:43 -04002561 # Copy level-output into resulting tree.
2562 offset = hash_level_offsets[level_num]
2563 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002564
David Zeuthena4fee8b2016-08-22 15:20:43 -04002565 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002566 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002567 level_num += 1
2568
2569 hasher = hashlib.new(name=hash_alg_name, string=salt)
2570 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002571 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002572
2573
2574class AvbTool(object):
2575 """Object for avbtool command-line tool."""
2576
2577 def __init__(self):
2578 """Initializer method."""
2579 self.avb = Avb()
2580
2581 def _add_common_args(self, sub_parser):
2582 """Adds arguments used by several sub-commands.
2583
2584 Arguments:
2585 sub_parser: The parser to add arguments to.
2586 """
2587 sub_parser.add_argument('--algorithm',
2588 help='Algorithm to use (default: NONE)',
2589 metavar='ALGORITHM',
2590 default='NONE')
2591 sub_parser.add_argument('--key',
2592 help='Path to RSA private key file',
2593 metavar='KEY',
2594 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002595 sub_parser.add_argument('--signing_helper',
2596 help='Path to helper used for signing',
2597 metavar='APP',
2598 default=None,
2599 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002600 sub_parser.add_argument('--public_key_metadata',
2601 help='Path to public key metadata file',
2602 metavar='KEY_METADATA',
2603 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002604 sub_parser.add_argument('--rollback_index',
2605 help='Rollback Index',
2606 type=parse_number,
2607 default=0)
2608 sub_parser.add_argument('--prop',
2609 help='Add property',
2610 metavar='KEY:VALUE',
2611 action='append')
2612 sub_parser.add_argument('--prop_from_file',
2613 help='Add property from file',
2614 metavar='KEY:PATH',
2615 action='append')
2616 sub_parser.add_argument('--kernel_cmdline',
2617 help='Add kernel cmdline',
2618 metavar='CMDLINE',
2619 action='append')
2620 sub_parser.add_argument('--generate_dm_verity_cmdline_from_hashtree',
2621 metavar='IMAGE',
2622 help='Generate kernel cmdline for dm-verity',
2623 type=argparse.FileType('rb'))
2624 sub_parser.add_argument('--include_descriptors_from_image',
2625 help='Include descriptors from image',
2626 metavar='IMAGE',
2627 action='append',
2628 type=argparse.FileType('rb'))
2629
2630 def run(self, argv):
2631 """Command-line processor.
2632
2633 Arguments:
2634 argv: Pass sys.argv from main.
2635 """
2636 parser = argparse.ArgumentParser()
2637 subparsers = parser.add_subparsers(title='subcommands')
2638
2639 sub_parser = subparsers.add_parser('version',
2640 help='Prints version of avbtool.')
2641 sub_parser.set_defaults(func=self.version)
2642
2643 sub_parser = subparsers.add_parser('extract_public_key',
2644 help='Extract public key.')
2645 sub_parser.add_argument('--key',
2646 help='Path to RSA private key file',
2647 required=True)
2648 sub_parser.add_argument('--output',
2649 help='Output file name',
2650 type=argparse.FileType('wb'),
2651 required=True)
2652 sub_parser.set_defaults(func=self.extract_public_key)
2653
2654 sub_parser = subparsers.add_parser('make_vbmeta_image',
2655 help='Makes a vbmeta image.')
2656 sub_parser.add_argument('--output',
2657 help='Output file name',
2658 type=argparse.FileType('wb'),
2659 required=True)
2660 self._add_common_args(sub_parser)
2661 sub_parser.add_argument('--chain_partition',
2662 help='Allow signed integrity-data for partition',
2663 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2664 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002665 sub_parser.add_argument('--flags',
2666 help='VBMeta flags',
2667 type=parse_number,
2668 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04002669 sub_parser.set_defaults(func=self.make_vbmeta_image)
2670
2671 sub_parser = subparsers.add_parser('add_hash_footer',
2672 help='Add hashes and footer to image.')
2673 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002674 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002675 type=argparse.FileType('rab+'))
2676 sub_parser.add_argument('--partition_size',
2677 help='Partition size',
2678 type=parse_number,
2679 required=True)
2680 sub_parser.add_argument('--partition_name',
2681 help='Partition name',
2682 required=True)
2683 sub_parser.add_argument('--hash_algorithm',
2684 help='Hash algorithm to use (default: sha256)',
2685 default='sha256')
2686 sub_parser.add_argument('--salt',
2687 help='Salt in hex (default: /dev/urandom)')
2688 self._add_common_args(sub_parser)
2689 sub_parser.set_defaults(func=self.add_hash_footer)
2690
2691 sub_parser = subparsers.add_parser('add_hashtree_footer',
2692 help='Add hashtree and footer to image.')
2693 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002694 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002695 type=argparse.FileType('rab+'))
2696 sub_parser.add_argument('--partition_size',
2697 help='Partition size',
2698 type=parse_number,
2699 required=True)
2700 sub_parser.add_argument('--partition_name',
2701 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002702 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002703 sub_parser.add_argument('--hash_algorithm',
2704 help='Hash algorithm to use (default: sha1)',
2705 default='sha1')
2706 sub_parser.add_argument('--salt',
2707 help='Salt in hex (default: /dev/urandom)')
2708 sub_parser.add_argument('--block_size',
2709 help='Block size (default: 4096)',
2710 type=parse_number,
2711 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002712 sub_parser.add_argument('--generate_fec',
2713 help='Add forward-error-correction codes',
2714 action='store_true')
2715 sub_parser.add_argument('--fec_num_roots',
2716 help='Number of roots for FEC (default: 2)',
2717 type=parse_number,
2718 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002719 sub_parser.add_argument('--calc_max_image_size',
2720 help=('Don\'t store the hashtree or footer - '
2721 'instead calculate the maximum image size '
2722 'leaving enough room for hashtree '
2723 'and metadata with the given partition '
2724 'size.'),
2725 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002726 self._add_common_args(sub_parser)
2727 sub_parser.set_defaults(func=self.add_hashtree_footer)
2728
2729 sub_parser = subparsers.add_parser('erase_footer',
2730 help='Erase footer from an image.')
2731 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002732 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002733 type=argparse.FileType('rwb+'),
2734 required=True)
2735 sub_parser.add_argument('--keep_hashtree',
2736 help='Keep the hashtree in the image',
2737 action='store_true')
2738 sub_parser.set_defaults(func=self.erase_footer)
2739
2740 sub_parser = subparsers.add_parser(
2741 'info_image',
2742 help='Show information about vbmeta or footer.')
2743 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002744 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002745 type=argparse.FileType('rb'),
2746 required=True)
2747 sub_parser.add_argument('--output',
2748 help='Write info to file',
2749 type=argparse.FileType('wt'),
2750 default=sys.stdout)
2751 sub_parser.set_defaults(func=self.info_image)
2752
David Zeuthen8b6973b2016-09-20 12:39:49 -04002753 sub_parser = subparsers.add_parser('set_ab_metadata',
2754 help='Set A/B metadata.')
2755 sub_parser.add_argument('--misc_image',
2756 help=('The misc image to modify. If the image does '
2757 'not exist, it will be created.'),
2758 type=argparse.FileType('r+b'),
2759 required=True)
2760 sub_parser.add_argument('--slot_data',
2761 help=('Slot data of the form "priority", '
2762 '"tries_remaining", "sucessful_boot" for '
2763 'slot A followed by the same for slot B, '
2764 'separated by colons. The default value '
2765 'is 15:7:0:14:7:0.'),
2766 default='15:7:0:14:7:0')
2767 sub_parser.set_defaults(func=self.set_ab_metadata)
2768
David Zeuthen21e95262016-07-27 17:58:40 -04002769 args = parser.parse_args(argv[1:])
2770 try:
2771 args.func(args)
2772 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002773 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002774 sys.exit(1)
2775
2776 def version(self, _):
2777 """Implements the 'version' sub-command."""
2778 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2779
2780 def extract_public_key(self, args):
2781 """Implements the 'extract_public_key' sub-command."""
2782 self.avb.extract_public_key(args.key, args.output)
2783
2784 def make_vbmeta_image(self, args):
2785 """Implements the 'make_vbmeta_image' sub-command."""
2786 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05002787 args.algorithm, args.key,
2788 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002789 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002790 args.kernel_cmdline,
2791 args.generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002792 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002793
2794 def add_hash_footer(self, args):
2795 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002796 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002797 args.partition_name, args.hash_algorithm,
2798 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002799 args.public_key_metadata, args.rollback_index,
2800 args.prop, args.prop_from_file,
2801 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002802 args.generate_dm_verity_cmdline_from_hashtree,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002803 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002804
2805 def add_hashtree_footer(self, args):
2806 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04002807 self.avb.add_hashtree_footer(args.image.name if args.image else None,
2808 args.partition_size,
2809 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002810 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04002811 args.hash_algorithm, args.block_size,
2812 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002813 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04002814 args.rollback_index, args.prop,
2815 args.prop_from_file,
2816 args.kernel_cmdline,
David Zeuthen21e95262016-07-27 17:58:40 -04002817 args.generate_dm_verity_cmdline_from_hashtree,
David Zeuthen09692692016-09-30 16:16:40 -04002818 args.include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002819 args.calc_max_image_size, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002820
2821 def erase_footer(self, args):
2822 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002823 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04002824
David Zeuthen8b6973b2016-09-20 12:39:49 -04002825 def set_ab_metadata(self, args):
2826 """Implements the 'set_ab_metadata' sub-command."""
2827 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
2828
David Zeuthen21e95262016-07-27 17:58:40 -04002829 def info_image(self, args):
2830 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002831 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04002832
2833
2834if __name__ == '__main__':
2835 tool = AvbTool()
2836 tool.run(sys.argv)