blob: 9a7358f3b2baa02cb2247a057d956aa9f168edbb [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 Zeuthen21e95262016-07-27 17:58:40 -04001795
David Zeuthenfd41eb92016-11-17 12:24:47 -05001796 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001797 desc = AvbKernelCmdlineDescriptor()
1798 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001799 desc.flags = (
1800 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1801
1802 # The descriptor for when hashtree verification is disabled is a lot
1803 # simpler - we just set the root to the partition.
1804 desc_no_ht = AvbKernelCmdlineDescriptor()
1805 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1806 desc_no_ht.flags = (
1807 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1808
1809 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001810
1811 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001812 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001813 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001814 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001815 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001816 """Implements the 'make_vbmeta_image' command.
1817
1818 Arguments:
1819 output: File to write the image to.
1820 chain_partitions: List of partitions to chain.
1821 algorithm_name: Name of algorithm to use.
1822 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001823 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001824 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001825 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001826 props: Properties to insert (list of strings of the form 'key:value').
1827 props_from_file: Properties to insert (list of strings 'key:<path>').
1828 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001829 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001830 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001831 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001832
1833 Raises:
1834 AvbError: If a chained partition is malformed.
1835 """
1836
1837 descriptors = []
1838
1839 # Insert chained partition descriptors.
1840 if chain_partitions:
1841 for cp in chain_partitions:
1842 cp_tokens = cp.split(':')
1843 if len(cp_tokens) != 3:
1844 raise AvbError('Malformed chained partition "{}".'.format(cp))
1845 desc = AvbChainPartitionDescriptor()
1846 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001847 desc.rollback_index_location = int(cp_tokens[1])
1848 if desc.rollback_index_location < 1:
1849 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001850 file_path = cp_tokens[2]
1851 desc.public_key = open(file_path, 'rb').read()
1852 descriptors.append(desc)
1853
1854 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001855 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001856 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001857 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001858 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04001859
1860 # Write entire vbmeta blob (header, authentication, auxiliary).
1861 output.seek(0)
1862 output.write(vbmeta_blob)
1863
David Zeuthen18666ab2016-11-15 11:18:05 -05001864 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1865 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001866 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001867 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001868 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001869 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001870 """Generates a VBMeta blob.
1871
1872 This blob contains the header (struct AvbVBMetaHeader), the
1873 authentication data block (which contains the hash and signature
1874 for the header and auxiliary block), and the auxiliary block
1875 (which contains descriptors, the public key used, and other data).
1876
1877 The |key| parameter can |None| only if the |algorithm_name| is
1878 'NONE'.
1879
1880 Arguments:
1881 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1882 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001883 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001884 descriptors: A list of descriptors to insert or None.
1885 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001886 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001887 props: Properties to insert (List of strings of the form 'key:value').
1888 props_from_file: Properties to insert (List of strings 'key:<path>').
1889 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001890 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001891 dm-verity kernel cmdline from.
1892 include_descriptors_from_image: List of file objects for which
1893 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001894 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001895
1896 Returns:
1897 A bytearray() with the VBMeta blob.
1898
1899 Raises:
1900 Exception: If the |algorithm_name| is not found, if no key has
1901 been given and the given algorithm requires one, or the key is
1902 of the wrong size.
1903
1904 """
1905 try:
1906 alg = ALGORITHMS[algorithm_name]
1907 except KeyError:
1908 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1909
1910 # Descriptors.
1911 encoded_descriptors = bytearray()
1912 if descriptors:
1913 for desc in descriptors:
1914 encoded_descriptors.extend(desc.encode())
1915
1916 # Add properties.
1917 if props:
1918 for prop in props:
1919 idx = prop.find(':')
1920 if idx == -1:
1921 raise AvbError('Malformed property "{}".'.format(prop))
1922 desc = AvbPropertyDescriptor()
1923 desc.key = prop[0:idx]
1924 desc.value = prop[(idx + 1):]
1925 encoded_descriptors.extend(desc.encode())
1926 if props_from_file:
1927 for prop in props_from_file:
1928 idx = prop.find(':')
1929 if idx == -1:
1930 raise AvbError('Malformed property "{}".'.format(prop))
1931 desc = AvbPropertyDescriptor()
1932 desc.key = prop[0:idx]
1933 desc.value = prop[(idx + 1):]
1934 file_path = prop[(idx + 1):]
1935 desc.value = open(file_path, 'rb').read()
1936 encoded_descriptors.extend(desc.encode())
1937
1938 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001939 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001940 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001941 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001942 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1943 encoded_descriptors.extend(cmdline_desc[0].encode())
1944 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001945
1946 # Add kernel command-lines.
1947 if kernel_cmdlines:
1948 for i in kernel_cmdlines:
1949 desc = AvbKernelCmdlineDescriptor()
1950 desc.kernel_cmdline = i
1951 encoded_descriptors.extend(desc.encode())
1952
1953 # Add descriptors from other images.
1954 if include_descriptors_from_image:
1955 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001956 image_handler = ImageHandler(image.name)
1957 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001958 for desc in image_descriptors:
1959 encoded_descriptors.extend(desc.encode())
1960
David Zeuthen18666ab2016-11-15 11:18:05 -05001961 # Load public key metadata blob, if requested.
1962 pkmd_blob = []
1963 if public_key_metadata_path:
1964 with open(public_key_metadata_path) as f:
1965 pkmd_blob = f.read()
1966
David Zeuthen21e95262016-07-27 17:58:40 -04001967 key = None
1968 encoded_key = bytearray()
1969 if alg.public_key_num_bytes > 0:
1970 if not key_path:
1971 raise AvbError('Key is required for algorithm {}'.format(
1972 algorithm_name))
1973 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
1974 encoded_key = encode_rsa_key(key)
1975 if len(encoded_key) != alg.public_key_num_bytes:
1976 raise AvbError('Key is wrong size for algorithm {}'.format(
1977 algorithm_name))
1978
1979 h = AvbVBMetaHeader()
1980
David Zeuthen18666ab2016-11-15 11:18:05 -05001981 # For the Auxiliary data block, descriptors are stored at offset 0,
1982 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001983 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05001984 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04001985 h.descriptors_offset = 0
1986 h.descriptors_size = len(encoded_descriptors)
1987 h.public_key_offset = h.descriptors_size
1988 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001989 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
1990 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04001991
1992 # For the Authentication data block, the hash is first and then
1993 # the signature.
1994 h.authentication_data_block_size = round_to_multiple(
1995 alg.hash_num_bytes + alg.public_key_num_bytes, 64)
1996 h.algorithm_type = alg.algorithm_type
1997 h.hash_offset = 0
1998 h.hash_size = alg.hash_num_bytes
1999 # Signature offset and size - it's stored right after the hash
2000 # (in Authentication data block).
2001 h.signature_offset = alg.hash_num_bytes
2002 h.signature_size = alg.signature_num_bytes
2003
2004 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002005 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002006
2007 # Generate Header data block.
2008 header_data_blob = h.encode()
2009
2010 # Generate Auxiliary data block.
2011 aux_data_blob = bytearray()
2012 aux_data_blob.extend(encoded_descriptors)
2013 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002014 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002015 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2016 aux_data_blob.extend('\0' * padding_bytes)
2017
2018 # Calculate the hash.
2019 binary_hash = bytearray()
2020 binary_signature = bytearray()
2021 if algorithm_name != 'NONE':
2022 if algorithm_name[0:6] == 'SHA256':
2023 ha = hashlib.sha256()
2024 elif algorithm_name[0:6] == 'SHA512':
2025 ha = hashlib.sha512()
2026 else:
2027 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2028 ha.update(header_data_blob)
2029 ha.update(aux_data_blob)
2030 binary_hash.extend(ha.digest())
2031
2032 # Calculate the signature.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002033 p = None
2034 if signing_helper is not None:
2035 p = subprocess.Popen(
2036 [signing_helper, algorithm_name, key_path],
2037 stdin=subprocess.PIPE,
2038 stdout=subprocess.PIPE,
2039 stderr=subprocess.PIPE)
2040 else:
2041 p = subprocess.Popen(
2042 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
2043 stdin=subprocess.PIPE,
2044 stdout=subprocess.PIPE,
2045 stderr=subprocess.PIPE)
David Zeuthen21e95262016-07-27 17:58:40 -04002046 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
2047 (pout, perr) = p.communicate(padding_and_hash)
2048 retcode = p.wait()
2049 if retcode != 0:
2050 raise AvbError('Error signing: {}'.format(perr))
2051 binary_signature.extend(pout)
2052
2053 # Generate Authentication data block.
2054 auth_data_blob = bytearray()
2055 auth_data_blob.extend(binary_hash)
2056 auth_data_blob.extend(binary_signature)
2057 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2058 auth_data_blob.extend('\0' * padding_bytes)
2059
2060 return header_data_blob + auth_data_blob + aux_data_blob
2061
2062 def extract_public_key(self, key_path, output):
2063 """Implements the 'extract_public_key' command.
2064
2065 Arguments:
2066 key_path: The path to a RSA private key file.
2067 output: The file to write to.
2068 """
2069 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2070 write_rsa_key(output, key)
2071
David Zeuthena4fee8b2016-08-22 15:20:43 -04002072 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002073 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002074 public_key_metadata_path, rollback_index, props,
2075 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002076 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002077 include_descriptors_from_image, signing_helper):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002078 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002079
2080 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002081 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002082 partition_size: Size of partition.
2083 partition_name: Name of partition (without A/B suffix).
2084 hash_algorithm: Hash algorithm to use.
2085 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2086 algorithm_name: Name of algorithm to use.
2087 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002088 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002089 rollback_index: Rollback index.
2090 props: Properties to insert (List of strings of the form 'key:value').
2091 props_from_file: Properties to insert (List of strings 'key:<path>').
2092 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002093 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002094 dm-verity kernel cmdline from.
2095 include_descriptors_from_image: List of file objects for which
2096 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002097 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002098
2099 Raises:
2100 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002101 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002102 image = ImageHandler(image_filename)
2103
2104 if partition_size % image.block_size != 0:
2105 raise AvbError('Partition size of {} is not a multiple of the image '
2106 'block size {}.'.format(partition_size,
2107 image.block_size))
2108
David Zeuthen21e95262016-07-27 17:58:40 -04002109 # If there's already a footer, truncate the image to its original
2110 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2111 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002112 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002113 try:
2114 footer = AvbFooter(image.read(AvbFooter.SIZE))
2115 # Existing footer found. Just truncate.
2116 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002117 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002118 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002119 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002120
2121 # If anything goes wrong from here-on, restore the image back to
2122 # its original size.
2123 try:
David Zeuthen09692692016-09-30 16:16:40 -04002124 # First, calculate the maximum image size such that an image
2125 # this size + metadata (footer + vbmeta struct) fits in
2126 # |partition_size|.
2127 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2128 max_image_size = partition_size - max_metadata_size
2129
2130 # If image size exceeds the maximum image size, fail.
2131 if image.image_size > max_image_size:
2132 raise AvbError('Image size of {} exceeds maximum image '
2133 'size of {} in order to fit in a partition '
2134 'size of {}.'.format(image.image_size, max_image_size,
2135 partition_size))
2136
David Zeuthen21e95262016-07-27 17:58:40 -04002137 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2138 if salt:
2139 salt = salt.decode('hex')
2140 else:
2141 if salt is None:
2142 # If salt is not explicitly specified, choose a hash
2143 # that's the same size as the hash size.
2144 hash_size = digest_size
2145 salt = open('/dev/urandom').read(hash_size)
2146 else:
2147 salt = ''
2148
2149 hasher = hashlib.new(name=hash_algorithm, string=salt)
2150 # TODO(zeuthen): might want to read this in chunks to avoid
2151 # memory pressure, then again, this is only supposed to be used
2152 # on kernel/initramfs partitions. Possible optimization.
2153 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002154 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002155 digest = hasher.digest()
2156
2157 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002158 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002159 h_desc.hash_algorithm = hash_algorithm
2160 h_desc.partition_name = partition_name
2161 h_desc.salt = salt
2162 h_desc.digest = digest
2163
David Zeuthenfd41eb92016-11-17 12:24:47 -05002164 # Flags are only allowed on top-level vbmeta struct.
2165 flags = 0
2166
David Zeuthen21e95262016-07-27 17:58:40 -04002167 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002168 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002169 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002170 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002171 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002172 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002173
David Zeuthena4fee8b2016-08-22 15:20:43 -04002174 # If the image isn't sparse, its size might not be a multiple of
2175 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002176 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002177 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002178 padding_needed = image.block_size - (image.image_size%image.block_size)
2179 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002180
David Zeuthena4fee8b2016-08-22 15:20:43 -04002181 # The append_raw() method requires content with size being a
2182 # multiple of |block_size| so add padding as needed. Also record
2183 # where this is written to since we'll need to put that in the
2184 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002185 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002186 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2187 len(vbmeta_blob))
2188 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2189 image.append_raw(vbmeta_blob_with_padding)
2190 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2191
2192 # Now insert a DONT_CARE chunk with enough bytes such that the
2193 # final Footer block is at the end of partition_size..
2194 image.append_dont_care(partition_size - vbmeta_end_offset -
2195 1*image.block_size)
2196
2197 # Generate the Footer that tells where the VBMeta footer
2198 # is. Also put enough padding in the front of the footer since
2199 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002200 footer = AvbFooter()
2201 footer.original_image_size = original_image_size
2202 footer.vbmeta_offset = vbmeta_offset
2203 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002204 footer_blob = footer.encode()
2205 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2206 footer_blob)
2207 image.append_raw(footer_blob_with_padding)
2208
David Zeuthen21e95262016-07-27 17:58:40 -04002209 except:
2210 # Truncate back to original size, then re-raise
2211 image.truncate(original_image_size)
2212 raise
2213
David Zeuthena4fee8b2016-08-22 15:20:43 -04002214 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002215 generate_fec, fec_num_roots, hash_algorithm,
2216 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002217 public_key_metadata_path, rollback_index,
2218 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002219 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002220 include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002221 calc_max_image_size, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04002222 """Implements the 'add_hashtree_footer' command.
2223
2224 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2225 more information about dm-verity and these hashes.
2226
2227 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002228 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002229 partition_size: Size of partition.
2230 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002231 generate_fec: If True, generate FEC codes.
2232 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002233 hash_algorithm: Hash algorithm to use.
2234 block_size: Block size to use.
2235 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2236 algorithm_name: Name of algorithm to use.
2237 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002238 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002239 rollback_index: Rollback index.
2240 props: Properties to insert (List of strings of the form 'key:value').
2241 props_from_file: Properties to insert (List of strings 'key:<path>').
2242 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002243 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002244 dm-verity kernel cmdline from.
2245 include_descriptors_from_image: List of file objects for which
2246 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002247 calc_max_image_size: Don't store the hashtree or footer - instead
2248 calculate the maximum image size leaving enough room for hashtree
2249 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002250 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002251
2252 Raises:
2253 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002254 """
David Zeuthen09692692016-09-30 16:16:40 -04002255 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2256 digest_padding = round_to_pow2(digest_size) - digest_size
2257
2258 # First, calculate the maximum image size such that an image
2259 # this size + the hashtree + metadata (footer + vbmeta struct)
2260 # fits in |partition_size|. We use very conservative figures for
2261 # metadata.
2262 (_, max_tree_size) = calc_hash_level_offsets(
2263 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002264 max_fec_size = 0
2265 if generate_fec:
2266 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2267 max_metadata_size = (max_fec_size + max_tree_size +
2268 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002269 self.MAX_FOOTER_SIZE)
2270 max_image_size = partition_size - max_metadata_size
2271
2272 # If we're asked to only calculate the maximum image size, we're done.
2273 if calc_max_image_size:
2274 print '{}'.format(max_image_size)
2275 return
2276
David Zeuthena4fee8b2016-08-22 15:20:43 -04002277 image = ImageHandler(image_filename)
2278
2279 if partition_size % image.block_size != 0:
2280 raise AvbError('Partition size of {} is not a multiple of the image '
2281 'block size {}.'.format(partition_size,
2282 image.block_size))
2283
David Zeuthen21e95262016-07-27 17:58:40 -04002284 # If there's already a footer, truncate the image to its original
2285 # size. This way 'avbtool add_hashtree_footer' is idempotent
2286 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002287 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002288 try:
2289 footer = AvbFooter(image.read(AvbFooter.SIZE))
2290 # Existing footer found. Just truncate.
2291 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002292 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002293 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002294 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002295
2296 # If anything goes wrong from here-on, restore the image back to
2297 # its original size.
2298 try:
2299 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002300 rounded_image_size = round_to_multiple(image.image_size, block_size)
2301 if rounded_image_size > image.image_size:
2302 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002303
David Zeuthen09692692016-09-30 16:16:40 -04002304 # If image size exceeds the maximum image size, fail.
2305 if image.image_size > max_image_size:
2306 raise AvbError('Image size of {} exceeds maximum image '
2307 'size of {} in order to fit in a partition '
2308 'size of {}.'.format(image.image_size, max_image_size,
2309 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002310
2311 if salt:
2312 salt = salt.decode('hex')
2313 else:
2314 if salt is None:
2315 # If salt is not explicitly specified, choose a hash
2316 # that's the same size as the hash size.
2317 hash_size = digest_size
2318 salt = open('/dev/urandom').read(hash_size)
2319 else:
2320 salt = ''
2321
David Zeuthena4fee8b2016-08-22 15:20:43 -04002322 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002323 # offsets in advance.
2324 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002325 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002326
David Zeuthena4fee8b2016-08-22 15:20:43 -04002327 # If the image isn't sparse, its size might not be a multiple of
2328 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002329 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002330 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002331 padding_needed = image.block_size - (image.image_size%image.block_size)
2332 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002333
David Zeuthena4fee8b2016-08-22 15:20:43 -04002334 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002335 tree_offset = image.image_size
2336 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002337 block_size,
2338 hash_algorithm, salt,
2339 digest_padding,
2340 hash_level_offsets,
2341 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002342
2343 # Generate HashtreeDescriptor with details about the tree we
2344 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002345 ht_desc = AvbHashtreeDescriptor()
2346 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002347 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002348 ht_desc.tree_offset = tree_offset
2349 ht_desc.tree_size = tree_size
2350 ht_desc.data_block_size = block_size
2351 ht_desc.hash_block_size = block_size
2352 ht_desc.hash_algorithm = hash_algorithm
2353 ht_desc.partition_name = partition_name
2354 ht_desc.salt = salt
2355 ht_desc.root_digest = root_digest
2356
David Zeuthen09692692016-09-30 16:16:40 -04002357 # Write the hash tree
2358 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2359 len(hash_tree))
2360 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2361 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002362 len_hashtree_and_fec = len(hash_tree_with_padding)
2363
2364 # Generate FEC codes, if requested.
2365 if generate_fec:
2366 fec_data = generate_fec_data(image_filename, fec_num_roots)
2367 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2368 len(fec_data))
2369 fec_data_with_padding = fec_data + '\0'*padding_needed
2370 fec_offset = image.image_size
2371 image.append_raw(fec_data_with_padding)
2372 len_hashtree_and_fec += len(fec_data_with_padding)
2373 # Update the hashtree descriptor.
2374 ht_desc.fec_num_roots = fec_num_roots
2375 ht_desc.fec_offset = fec_offset
2376 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002377
David Zeuthenfd41eb92016-11-17 12:24:47 -05002378 # Flags are only allowed on top-level vbmeta struct.
2379 flags = 0
2380
David Zeuthena4fee8b2016-08-22 15:20:43 -04002381 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002382 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002383 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002384 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002385 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002386 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002387 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002388 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2389 len(vbmeta_blob))
2390 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2391 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002392
David Zeuthena4fee8b2016-08-22 15:20:43 -04002393 # Now insert a DONT_CARE chunk with enough bytes such that the
2394 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002395 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002396 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002397
David Zeuthena4fee8b2016-08-22 15:20:43 -04002398 # Generate the Footer that tells where the VBMeta footer
2399 # is. Also put enough padding in the front of the footer since
2400 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002401 footer = AvbFooter()
2402 footer.original_image_size = original_image_size
2403 footer.vbmeta_offset = vbmeta_offset
2404 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002405 footer_blob = footer.encode()
2406 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2407 footer_blob)
2408 image.append_raw(footer_blob_with_padding)
2409
David Zeuthen21e95262016-07-27 17:58:40 -04002410 except:
David Zeuthen09692692016-09-30 16:16:40 -04002411 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002412 image.truncate(original_image_size)
2413 raise
2414
2415
2416def calc_hash_level_offsets(image_size, block_size, digest_size):
2417 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2418
2419 Arguments:
2420 image_size: The size of the image to calculate a Merkle-tree for.
2421 block_size: The block size, e.g. 4096.
2422 digest_size: The size of each hash, e.g. 32 for SHA-256.
2423
2424 Returns:
2425 A tuple where the first argument is an array of offsets and the
2426 second is size of the tree, in bytes.
2427 """
2428 level_offsets = []
2429 level_sizes = []
2430 tree_size = 0
2431
2432 num_levels = 0
2433 size = image_size
2434 while size > block_size:
2435 num_blocks = (size + block_size - 1) / block_size
2436 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2437
2438 level_sizes.append(level_size)
2439 tree_size += level_size
2440 num_levels += 1
2441
2442 size = level_size
2443
2444 for n in range(0, num_levels):
2445 offset = 0
2446 for m in range(n + 1, num_levels):
2447 offset += level_sizes[m]
2448 level_offsets.append(offset)
2449
David Zeuthena4fee8b2016-08-22 15:20:43 -04002450 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002451
2452
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002453# See system/extras/libfec/include/fec/io.h for these definitions.
2454FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2455FEC_MAGIC = 0xfecfecfe
2456
2457
2458def calc_fec_data_size(image_size, num_roots):
2459 """Calculates how much space FEC data will take.
2460
2461 Args:
2462 image_size: The size of the image.
2463 num_roots: Number of roots.
2464
2465 Returns:
2466 The number of bytes needed for FEC for an image of the given size
2467 and with the requested number of FEC roots.
2468
2469 Raises:
2470 ValueError: If output from the 'fec' tool is invalid.
2471
2472 """
2473 p = subprocess.Popen(
2474 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2475 stdout=subprocess.PIPE,
2476 stderr=subprocess.PIPE)
2477 (pout, perr) = p.communicate()
2478 retcode = p.wait()
2479 if retcode != 0:
2480 raise ValueError('Error invoking fec: {}'.format(perr))
2481 return int(pout)
2482
2483
2484def generate_fec_data(image_filename, num_roots):
2485 """Generate FEC codes for an image.
2486
2487 Args:
2488 image_filename: The filename of the image.
2489 num_roots: Number of roots.
2490
2491 Returns:
2492 The FEC data blob.
2493
2494 Raises:
2495 ValueError: If output from the 'fec' tool is invalid.
2496 """
2497 fec_tmpfile = tempfile.NamedTemporaryFile()
2498 subprocess.check_call(
2499 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2500 fec_tmpfile.name],
2501 stderr=open(os.devnull))
2502 fec_data = fec_tmpfile.read()
2503 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2504 footer_data = fec_data[-footer_size:]
2505 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2506 footer_data)
2507 if magic != FEC_MAGIC:
2508 raise ValueError('Unexpected magic in FEC footer')
2509 return fec_data[0:fec_size]
2510
2511
David Zeuthen21e95262016-07-27 17:58:40 -04002512def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002513 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002514 """Generates a Merkle-tree for a file.
2515
2516 Args:
2517 image: The image, as a file.
2518 image_size: The size of the image.
2519 block_size: The block size, e.g. 4096.
2520 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2521 salt: The salt to use.
2522 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002523 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002524 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002525
2526 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002527 A tuple where the first element is the top-level hash and the
2528 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002529 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002530 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002531 hash_src_offset = 0
2532 hash_src_size = image_size
2533 level_num = 0
2534 while hash_src_size > block_size:
2535 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002536 remaining = hash_src_size
2537 while remaining > 0:
2538 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002539 # Only read from the file for the first level - for subsequent
2540 # levels, access the array we're building.
2541 if level_num == 0:
2542 image.seek(hash_src_offset + hash_src_size - remaining)
2543 data = image.read(min(remaining, block_size))
2544 else:
2545 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2546 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002547 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002548
2549 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002550 if len(data) < block_size:
2551 hasher.update('\0' * (block_size - len(data)))
2552 level_output += hasher.digest()
2553 if digest_padding > 0:
2554 level_output += '\0' * digest_padding
2555
2556 padding_needed = (round_to_multiple(
2557 len(level_output), block_size) - len(level_output))
2558 level_output += '\0' * padding_needed
2559
David Zeuthena4fee8b2016-08-22 15:20:43 -04002560 # Copy level-output into resulting tree.
2561 offset = hash_level_offsets[level_num]
2562 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002563
David Zeuthena4fee8b2016-08-22 15:20:43 -04002564 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002565 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002566 level_num += 1
2567
2568 hasher = hashlib.new(name=hash_alg_name, string=salt)
2569 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002570 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002571
2572
2573class AvbTool(object):
2574 """Object for avbtool command-line tool."""
2575
2576 def __init__(self):
2577 """Initializer method."""
2578 self.avb = Avb()
2579
2580 def _add_common_args(self, sub_parser):
2581 """Adds arguments used by several sub-commands.
2582
2583 Arguments:
2584 sub_parser: The parser to add arguments to.
2585 """
2586 sub_parser.add_argument('--algorithm',
2587 help='Algorithm to use (default: NONE)',
2588 metavar='ALGORITHM',
2589 default='NONE')
2590 sub_parser.add_argument('--key',
2591 help='Path to RSA private key file',
2592 metavar='KEY',
2593 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002594 sub_parser.add_argument('--signing_helper',
2595 help='Path to helper used for signing',
2596 metavar='APP',
2597 default=None,
2598 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002599 sub_parser.add_argument('--public_key_metadata',
2600 help='Path to public key metadata file',
2601 metavar='KEY_METADATA',
2602 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002603 sub_parser.add_argument('--rollback_index',
2604 help='Rollback Index',
2605 type=parse_number,
2606 default=0)
2607 sub_parser.add_argument('--prop',
2608 help='Add property',
2609 metavar='KEY:VALUE',
2610 action='append')
2611 sub_parser.add_argument('--prop_from_file',
2612 help='Add property from file',
2613 metavar='KEY:PATH',
2614 action='append')
2615 sub_parser.add_argument('--kernel_cmdline',
2616 help='Add kernel cmdline',
2617 metavar='CMDLINE',
2618 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002619 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2620 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2621 # at some future point.
2622 sub_parser.add_argument('--setup_rootfs_from_kernel',
2623 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002624 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002625 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002626 type=argparse.FileType('rb'))
2627 sub_parser.add_argument('--include_descriptors_from_image',
2628 help='Include descriptors from image',
2629 metavar='IMAGE',
2630 action='append',
2631 type=argparse.FileType('rb'))
2632
2633 def run(self, argv):
2634 """Command-line processor.
2635
2636 Arguments:
2637 argv: Pass sys.argv from main.
2638 """
2639 parser = argparse.ArgumentParser()
2640 subparsers = parser.add_subparsers(title='subcommands')
2641
2642 sub_parser = subparsers.add_parser('version',
2643 help='Prints version of avbtool.')
2644 sub_parser.set_defaults(func=self.version)
2645
2646 sub_parser = subparsers.add_parser('extract_public_key',
2647 help='Extract public key.')
2648 sub_parser.add_argument('--key',
2649 help='Path to RSA private key file',
2650 required=True)
2651 sub_parser.add_argument('--output',
2652 help='Output file name',
2653 type=argparse.FileType('wb'),
2654 required=True)
2655 sub_parser.set_defaults(func=self.extract_public_key)
2656
2657 sub_parser = subparsers.add_parser('make_vbmeta_image',
2658 help='Makes a vbmeta image.')
2659 sub_parser.add_argument('--output',
2660 help='Output file name',
2661 type=argparse.FileType('wb'),
2662 required=True)
2663 self._add_common_args(sub_parser)
2664 sub_parser.add_argument('--chain_partition',
2665 help='Allow signed integrity-data for partition',
2666 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2667 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002668 sub_parser.add_argument('--flags',
2669 help='VBMeta flags',
2670 type=parse_number,
2671 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04002672 sub_parser.set_defaults(func=self.make_vbmeta_image)
2673
2674 sub_parser = subparsers.add_parser('add_hash_footer',
2675 help='Add hashes and footer to image.')
2676 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002677 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002678 type=argparse.FileType('rab+'))
2679 sub_parser.add_argument('--partition_size',
2680 help='Partition size',
2681 type=parse_number,
2682 required=True)
2683 sub_parser.add_argument('--partition_name',
2684 help='Partition name',
2685 required=True)
2686 sub_parser.add_argument('--hash_algorithm',
2687 help='Hash algorithm to use (default: sha256)',
2688 default='sha256')
2689 sub_parser.add_argument('--salt',
2690 help='Salt in hex (default: /dev/urandom)')
2691 self._add_common_args(sub_parser)
2692 sub_parser.set_defaults(func=self.add_hash_footer)
2693
2694 sub_parser = subparsers.add_parser('add_hashtree_footer',
2695 help='Add hashtree and footer to image.')
2696 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002697 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002698 type=argparse.FileType('rab+'))
2699 sub_parser.add_argument('--partition_size',
2700 help='Partition size',
2701 type=parse_number,
2702 required=True)
2703 sub_parser.add_argument('--partition_name',
2704 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002705 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002706 sub_parser.add_argument('--hash_algorithm',
2707 help='Hash algorithm to use (default: sha1)',
2708 default='sha1')
2709 sub_parser.add_argument('--salt',
2710 help='Salt in hex (default: /dev/urandom)')
2711 sub_parser.add_argument('--block_size',
2712 help='Block size (default: 4096)',
2713 type=parse_number,
2714 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002715 sub_parser.add_argument('--generate_fec',
2716 help='Add forward-error-correction codes',
2717 action='store_true')
2718 sub_parser.add_argument('--fec_num_roots',
2719 help='Number of roots for FEC (default: 2)',
2720 type=parse_number,
2721 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002722 sub_parser.add_argument('--calc_max_image_size',
2723 help=('Don\'t store the hashtree or footer - '
2724 'instead calculate the maximum image size '
2725 'leaving enough room for hashtree '
2726 'and metadata with the given partition '
2727 'size.'),
2728 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002729 self._add_common_args(sub_parser)
2730 sub_parser.set_defaults(func=self.add_hashtree_footer)
2731
2732 sub_parser = subparsers.add_parser('erase_footer',
2733 help='Erase footer from an image.')
2734 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002735 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002736 type=argparse.FileType('rwb+'),
2737 required=True)
2738 sub_parser.add_argument('--keep_hashtree',
2739 help='Keep the hashtree in the image',
2740 action='store_true')
2741 sub_parser.set_defaults(func=self.erase_footer)
2742
2743 sub_parser = subparsers.add_parser(
2744 'info_image',
2745 help='Show information about vbmeta or footer.')
2746 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002747 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002748 type=argparse.FileType('rb'),
2749 required=True)
2750 sub_parser.add_argument('--output',
2751 help='Write info to file',
2752 type=argparse.FileType('wt'),
2753 default=sys.stdout)
2754 sub_parser.set_defaults(func=self.info_image)
2755
David Zeuthen8b6973b2016-09-20 12:39:49 -04002756 sub_parser = subparsers.add_parser('set_ab_metadata',
2757 help='Set A/B metadata.')
2758 sub_parser.add_argument('--misc_image',
2759 help=('The misc image to modify. If the image does '
2760 'not exist, it will be created.'),
2761 type=argparse.FileType('r+b'),
2762 required=True)
2763 sub_parser.add_argument('--slot_data',
2764 help=('Slot data of the form "priority", '
2765 '"tries_remaining", "sucessful_boot" for '
2766 'slot A followed by the same for slot B, '
2767 'separated by colons. The default value '
2768 'is 15:7:0:14:7:0.'),
2769 default='15:7:0:14:7:0')
2770 sub_parser.set_defaults(func=self.set_ab_metadata)
2771
David Zeuthen21e95262016-07-27 17:58:40 -04002772 args = parser.parse_args(argv[1:])
2773 try:
2774 args.func(args)
2775 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002776 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002777 sys.exit(1)
2778
2779 def version(self, _):
2780 """Implements the 'version' sub-command."""
2781 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2782
2783 def extract_public_key(self, args):
2784 """Implements the 'extract_public_key' sub-command."""
2785 self.avb.extract_public_key(args.key, args.output)
2786
2787 def make_vbmeta_image(self, args):
2788 """Implements the 'make_vbmeta_image' sub-command."""
2789 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05002790 args.algorithm, args.key,
2791 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002792 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002793 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002794 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002795 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002796
2797 def add_hash_footer(self, args):
2798 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002799 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002800 args.partition_name, args.hash_algorithm,
2801 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002802 args.public_key_metadata, args.rollback_index,
2803 args.prop, args.prop_from_file,
2804 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002805 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002806 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002807
2808 def add_hashtree_footer(self, args):
2809 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04002810 self.avb.add_hashtree_footer(args.image.name if args.image else None,
2811 args.partition_size,
2812 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002813 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04002814 args.hash_algorithm, args.block_size,
2815 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05002816 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04002817 args.rollback_index, args.prop,
2818 args.prop_from_file,
2819 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002820 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002821 args.include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002822 args.calc_max_image_size, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002823
2824 def erase_footer(self, args):
2825 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002826 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04002827
David Zeuthen8b6973b2016-09-20 12:39:49 -04002828 def set_ab_metadata(self, args):
2829 """Implements the 'set_ab_metadata' sub-command."""
2830 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
2831
David Zeuthen21e95262016-07-27 17:58:40 -04002832 def info_image(self, args):
2833 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04002834 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04002835
2836
2837if __name__ == '__main__':
2838 tool = AvbTool()
2839 tool.run(sys.argv)