blob: b62bd03bc57403a6cc5cdab40cc718fc393249a8 [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
Darren Krahn147b08d2016-12-20 16:38:29 -080036import time
David Zeuthen21e95262016-07-27 17:58:40 -040037
38import Crypto.PublicKey.RSA
39
40# Keep in sync with avb_vbmeta_header.h.
41AVB_VERSION_MAJOR = 1
42AVB_VERSION_MINOR = 0
David Zeuthen58305522017-01-11 17:42:47 -050043AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040044
45class AvbError(Exception):
46 """Application-specific errors.
47
48 These errors represent issues for which a stack-trace should not be
49 presented.
50
51 Attributes:
52 message: Error message.
53 """
54
55 def __init__(self, message):
56 Exception.__init__(self, message)
57
58
59class Algorithm(object):
60 """Contains details about an algorithm.
61
62 See the avb_vbmeta_header.h file for more details about
63 algorithms.
64
65 The constant |ALGORITHMS| is a dictionary from human-readable
66 names (e.g 'SHA256_RSA2048') to instances of this class.
67
68 Attributes:
69 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
70 hash_num_bytes: Number of bytes used to store the hash.
71 signature_num_bytes: Number of bytes used to store the signature.
72 public_key_num_bytes: Number of bytes used to store the public key.
73 padding: Padding used for signature, if any.
74 """
75
76 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
77 public_key_num_bytes, padding):
78 self.algorithm_type = algorithm_type
79 self.hash_num_bytes = hash_num_bytes
80 self.signature_num_bytes = signature_num_bytes
81 self.public_key_num_bytes = public_key_num_bytes
82 self.padding = padding
83
84# This must be kept in sync with the avb_crypto.h file.
85#
86# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
87# obtained from section 5.2.2 of RFC 4880.
88ALGORITHMS = {
89 'NONE': Algorithm(
90 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
91 hash_num_bytes=0,
92 signature_num_bytes=0,
93 public_key_num_bytes=0,
94 padding=[]),
95 'SHA256_RSA2048': Algorithm(
96 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
97 hash_num_bytes=32,
98 signature_num_bytes=256,
99 public_key_num_bytes=8 + 2*2048/8,
100 padding=[
101 # PKCS1-v1_5 padding
102 0x00, 0x01] + [0xff]*202 + [0x00] + [
103 # ASN.1 header
104 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
105 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
106 0x00, 0x04, 0x20,
107 ]),
108 'SHA256_RSA4096': Algorithm(
109 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
110 hash_num_bytes=32,
111 signature_num_bytes=512,
112 public_key_num_bytes=8 + 2*4096/8,
113 padding=[
114 # PKCS1-v1_5 padding
115 0x00, 0x01] + [0xff]*458 + [0x00] + [
116 # ASN.1 header
117 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
118 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
119 0x00, 0x04, 0x20,
120 ]),
121 'SHA256_RSA8192': Algorithm(
122 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
123 hash_num_bytes=32,
124 signature_num_bytes=1024,
125 public_key_num_bytes=8 + 2*8192/8,
126 padding=[
127 # PKCS1-v1_5 padding
128 0x00, 0x01] + [0xff]*970 + [0x00] + [
129 # ASN.1 header
130 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
131 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
132 0x00, 0x04, 0x20,
133 ]),
134 'SHA512_RSA2048': Algorithm(
135 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
136 hash_num_bytes=64,
137 signature_num_bytes=256,
138 public_key_num_bytes=8 + 2*2048/8,
139 padding=[
140 # PKCS1-v1_5 padding
141 0x00, 0x01] + [0xff]*170 + [0x00] + [
142 # ASN.1 header
143 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
144 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
145 0x00, 0x04, 0x40
146 ]),
147 'SHA512_RSA4096': Algorithm(
148 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
149 hash_num_bytes=64,
150 signature_num_bytes=512,
151 public_key_num_bytes=8 + 2*4096/8,
152 padding=[
153 # PKCS1-v1_5 padding
154 0x00, 0x01] + [0xff]*426 + [0x00] + [
155 # ASN.1 header
156 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
157 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
158 0x00, 0x04, 0x40
159 ]),
160 'SHA512_RSA8192': Algorithm(
161 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
162 hash_num_bytes=64,
163 signature_num_bytes=1024,
164 public_key_num_bytes=8 + 2*8192/8,
165 padding=[
166 # PKCS1-v1_5 padding
167 0x00, 0x01] + [0xff]*938 + [0x00] + [
168 # ASN.1 header
169 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
170 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
171 0x00, 0x04, 0x40
172 ]),
173}
174
175
176def round_to_multiple(number, size):
177 """Rounds a number up to nearest multiple of another number.
178
179 Args:
180 number: The number to round up.
181 size: The multiple to round up to.
182
183 Returns:
184 If |number| is a multiple of |size|, returns |number|, otherwise
185 returns |number| + |size|.
186 """
187 remainder = number % size
188 if remainder == 0:
189 return number
190 return number + size - remainder
191
192
193def round_to_pow2(number):
194 """Rounds a number up to the next power of 2.
195
196 Args:
197 number: The number to round up.
198
199 Returns:
200 If |number| is already a power of 2 then |number| is
201 returned. Otherwise the smallest power of 2 greater than |number|
202 is returned.
203 """
204 return 2**((number - 1).bit_length())
205
206
207def write_long(output, num_bits, value):
208 """Writes a long to an output stream using a given amount of bits.
209
210 This number is written big-endian, e.g. with the most significant
211 bit first.
212
213 Arguments:
214 output: The object to write the output to.
215 num_bits: The number of bits to write, e.g. 2048.
216 value: The value to write.
217 """
218 for bit_pos in range(num_bits, 0, -8):
219 octet = (value >> (bit_pos - 8)) & 0xff
220 output.write(struct.pack('!B', octet))
221
222
223def encode_long(num_bits, value):
224 """Encodes a long to a bytearray() using a given amount of bits.
225
226 This number is written big-endian, e.g. with the most significant
227 bit first.
228
229 Arguments:
230 num_bits: The number of bits to write, e.g. 2048.
231 value: The value to write.
232
233 Returns:
234 A bytearray() with the encoded long.
235 """
236 ret = bytearray()
237 for bit_pos in range(num_bits, 0, -8):
238 octet = (value >> (bit_pos - 8)) & 0xff
239 ret.extend(struct.pack('!B', octet))
240 return ret
241
242
243def egcd(a, b):
244 """Calculate greatest common divisor of two numbers.
245
246 This implementation uses a recursive version of the extended
247 Euclidian algorithm.
248
249 Arguments:
250 a: First number.
251 b: Second number.
252
253 Returns:
254 A tuple (gcd, x, y) that where |gcd| is the greatest common
255 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
256 """
257 if a == 0:
258 return (b, 0, 1)
259 else:
260 g, y, x = egcd(b % a, a)
261 return (g, x - (b // a) * y, y)
262
263
264def modinv(a, m):
265 """Calculate modular multiplicative inverse of |a| modulo |m|.
266
267 This calculates the number |x| such that |a| * |x| == 1 (modulo
268 |m|). This number only exists if |a| and |m| are co-prime - |None|
269 is returned if this isn't true.
270
271 Arguments:
272 a: The number to calculate a modular inverse of.
273 m: The modulo to use.
274
275 Returns:
276 The modular multiplicative inverse of |a| and |m| or |None| if
277 these numbers are not co-prime.
278 """
279 gcd, x, _ = egcd(a, m)
280 if gcd != 1:
281 return None # modular inverse does not exist
282 else:
283 return x % m
284
285
286def parse_number(string):
287 """Parse a string as a number.
288
289 This is just a short-hand for int(string, 0) suitable for use in the
290 |type| parameter of |ArgumentParser|'s add_argument() function. An
291 improvement to just using type=int is that this function supports
292 numbers in other bases, e.g. "0x1234".
293
294 Arguments:
295 string: The string to parse.
296
297 Returns:
298 The parsed integer.
299
300 Raises:
301 ValueError: If the number could not be parsed.
302 """
303 return int(string, 0)
304
305
306def write_rsa_key(output, key):
307 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
308
309 This writes the |AvbRSAPublicKeyHeader| as well as the two large
310 numbers (|key_num_bits| bits long) following it.
311
312 Arguments:
313 output: The object to write the output to.
314 key: A Crypto.PublicKey.RSA object.
315 """
316 # key.e is exponent
317 # key.n is modulus
318 key_num_bits = key.size() + 1
319 # Calculate n0inv = -1/n[0] (mod 2^32)
320 b = 2L**32
321 n0inv = b - modinv(key.n, b)
322 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
323 r = 2L**key.n.bit_length()
324 rrmodn = r * r % key.n
325 output.write(struct.pack('!II', key_num_bits, n0inv))
326 write_long(output, key_num_bits, key.n)
327 write_long(output, key_num_bits, rrmodn)
328
329
330def encode_rsa_key(key):
331 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
332
333 This creates a |AvbRSAPublicKeyHeader| as well as the two large
334 numbers (|key_num_bits| bits long) following it.
335
336 Arguments:
337 key: A Crypto.PublicKey.RSA object.
338
339 Returns:
340 A bytearray() with the |AvbRSAPublicKeyHeader|.
341 """
342 ret = bytearray()
343 # key.e is exponent
344 # key.n is modulus
345 key_num_bits = key.size() + 1
346 # Calculate n0inv = -1/n[0] (mod 2^32)
347 b = 2L**32
348 n0inv = b - modinv(key.n, b)
349 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
350 r = 2L**key.n.bit_length()
351 rrmodn = r * r % key.n
352 ret.extend(struct.pack('!II', key_num_bits, n0inv))
353 ret.extend(encode_long(key_num_bits, key.n))
354 ret.extend(encode_long(key_num_bits, rrmodn))
355 return ret
356
357
358def lookup_algorithm_by_type(alg_type):
359 """Looks up algorithm by type.
360
361 Arguments:
362 alg_type: The integer representing the type.
363
364 Returns:
365 A tuple with the algorithm name and an |Algorithm| instance.
366
367 Raises:
368 Exception: If the algorithm cannot be found
369 """
370 for alg_name in ALGORITHMS:
371 alg_data = ALGORITHMS[alg_name]
372 if alg_data.algorithm_type == alg_type:
373 return (alg_name, alg_data)
374 raise AvbError('Unknown algorithm type {}'.format(alg_type))
375
376
Darren Krahn147b08d2016-12-20 16:38:29 -0800377def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
378 """Computes a raw RSA signature using |signing_helper| or openssl.
379
380 Arguments:
381 signing_helper: Program which signs a hash and returns the signature.
382 algorithm_name: The algorithm name as per the ALGORITHMS dict.
383 key_path: Path to the private key file. Must be PEM format.
384 raw_data_to_sign: Data to sign (bytearray or str expected).
385
386 Returns:
387 A bytearray containing the signature.
388
389 Raises:
390 Exception: If an error occurs.
391 """
392 p = None
393 if signing_helper is not None:
394 p = subprocess.Popen(
395 [signing_helper, algorithm_name, key_path],
396 stdin=subprocess.PIPE,
397 stdout=subprocess.PIPE,
398 stderr=subprocess.PIPE)
399 else:
400 p = subprocess.Popen(
401 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
402 stdin=subprocess.PIPE,
403 stdout=subprocess.PIPE,
404 stderr=subprocess.PIPE)
405 (pout, perr) = p.communicate(str(raw_data_to_sign))
406 retcode = p.wait()
407 if retcode != 0:
408 raise AvbError('Error signing: {}'.format(perr))
409 return bytearray(pout)
410
411
David Zeuthena4fee8b2016-08-22 15:20:43 -0400412class ImageChunk(object):
413 """Data structure used for representing chunks in Android sparse files.
414
415 Attributes:
416 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
417 chunk_offset: Offset in the sparse file where this chunk begins.
418 output_offset: Offset in de-sparsified file where output begins.
419 output_size: Number of bytes in output.
420 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
421 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
422 """
423
424 FORMAT = '<2H2I'
425 TYPE_RAW = 0xcac1
426 TYPE_FILL = 0xcac2
427 TYPE_DONT_CARE = 0xcac3
428 TYPE_CRC32 = 0xcac4
429
430 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
431 input_offset, fill_data):
432 """Initializes an ImageChunk object.
433
434 Arguments:
435 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
436 chunk_offset: Offset in the sparse file where this chunk begins.
437 output_offset: Offset in de-sparsified file.
438 output_size: Number of bytes in output.
439 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
440 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
441
442 Raises:
443 ValueError: If data is not well-formed.
444 """
445 self.chunk_type = chunk_type
446 self.chunk_offset = chunk_offset
447 self.output_offset = output_offset
448 self.output_size = output_size
449 self.input_offset = input_offset
450 self.fill_data = fill_data
451 # Check invariants.
452 if self.chunk_type == self.TYPE_RAW:
453 if self.fill_data is not None:
454 raise ValueError('RAW chunk cannot have fill_data set.')
455 if not self.input_offset:
456 raise ValueError('RAW chunk must have input_offset set.')
457 elif self.chunk_type == self.TYPE_FILL:
458 if self.fill_data is None:
459 raise ValueError('FILL chunk must have fill_data set.')
460 if self.input_offset:
461 raise ValueError('FILL chunk cannot have input_offset set.')
462 elif self.chunk_type == self.TYPE_DONT_CARE:
463 if self.fill_data is not None:
464 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
465 if self.input_offset:
466 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
467 else:
468 raise ValueError('Invalid chunk type')
469
470
471class ImageHandler(object):
472 """Abstraction for image I/O with support for Android sparse images.
473
474 This class provides an interface for working with image files that
475 may be using the Android Sparse Image format. When an instance is
476 constructed, we test whether it's an Android sparse file. If so,
477 operations will be on the sparse file by interpreting the sparse
478 format, otherwise they will be directly on the file. Either way the
479 operations do the same.
480
481 For reading, this interface mimics a file object - it has seek(),
482 tell(), and read() methods. For writing, only truncation
483 (truncate()) and appending is supported (append_raw() and
484 append_dont_care()). Additionally, data can only be written in units
485 of the block size.
486
487 Attributes:
488 is_sparse: Whether the file being operated on is sparse.
489 block_size: The block size, typically 4096.
490 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400491 """
492 # See system/core/libsparse/sparse_format.h for details.
493 MAGIC = 0xed26ff3a
494 HEADER_FORMAT = '<I4H4I'
495
496 # These are formats and offset of just the |total_chunks| and
497 # |total_blocks| fields.
498 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
499 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
500
501 def __init__(self, image_filename):
502 """Initializes an image handler.
503
504 Arguments:
505 image_filename: The name of the file to operate on.
506
507 Raises:
508 ValueError: If data in the file is invalid.
509 """
510 self._image_filename = image_filename
511 self._read_header()
512
513 def _read_header(self):
514 """Initializes internal data structures used for reading file.
515
516 This may be called multiple times and is typically called after
517 modifying the file (e.g. appending, truncation).
518
519 Raises:
520 ValueError: If data in the file is invalid.
521 """
522 self.is_sparse = False
523 self.block_size = 4096
524 self._file_pos = 0
525 self._image = open(self._image_filename, 'r+b')
526 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400527 self.image_size = self._image.tell()
528
529 self._image.seek(0, os.SEEK_SET)
530 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
531 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
532 block_size, self._num_total_blocks, self._num_total_chunks,
533 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
534 if magic != self.MAGIC:
535 # Not a sparse image, our job here is done.
536 return
537 if not (major_version == 1 and minor_version == 0):
538 raise ValueError('Encountered sparse image format version {}.{} but '
539 'only 1.0 is supported'.format(major_version,
540 minor_version))
541 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
542 raise ValueError('Unexpected file_hdr_sz value {}.'.
543 format(file_hdr_sz))
544 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
545 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
546 format(chunk_hdr_sz))
547
548 self.block_size = block_size
549
550 # Build an list of chunks by parsing the file.
551 self._chunks = []
552
553 # Find the smallest offset where only "Don't care" chunks
554 # follow. This will be the size of the content in the sparse
555 # image.
556 offset = 0
557 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558 for _ in xrange(1, self._num_total_chunks + 1):
559 chunk_offset = self._image.tell()
560
561 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
562 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
563 header_bin)
564 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
565
David Zeuthena4fee8b2016-08-22 15:20:43 -0400566 if chunk_type == ImageChunk.TYPE_RAW:
567 if data_sz != (chunk_sz * self.block_size):
568 raise ValueError('Raw chunk input size ({}) does not match output '
569 'size ({})'.
570 format(data_sz, chunk_sz*self.block_size))
571 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
572 chunk_offset,
573 output_offset,
574 chunk_sz*self.block_size,
575 self._image.tell(),
576 None))
577 self._image.read(data_sz)
578
579 elif chunk_type == ImageChunk.TYPE_FILL:
580 if data_sz != 4:
581 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
582 'has {}'.format(data_sz))
583 fill_data = self._image.read(4)
584 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
585 chunk_offset,
586 output_offset,
587 chunk_sz*self.block_size,
588 None,
589 fill_data))
590 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
591 if data_sz != 0:
592 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
593 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400594 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
595 chunk_offset,
596 output_offset,
597 chunk_sz*self.block_size,
598 None,
599 None))
600 elif chunk_type == ImageChunk.TYPE_CRC32:
601 if data_sz != 4:
602 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
603 'this has {}'.format(data_sz))
604 self._image.read(4)
605 else:
606 raise ValueError('Unknown chunk type {}'.format(chunk_type))
607
608 offset += chunk_sz
609 output_offset += chunk_sz*self.block_size
610
611 # Record where sparse data end.
612 self._sparse_end = self._image.tell()
613
614 # Now that we've traversed all chunks, sanity check.
615 if self._num_total_blocks != offset:
616 raise ValueError('The header said we should have {} output blocks, '
617 'but we saw {}'.format(self._num_total_blocks, offset))
618 junk_len = len(self._image.read())
619 if junk_len > 0:
620 raise ValueError('There were {} bytes of extra data at the end of the '
621 'file.'.format(junk_len))
622
David Zeuthen09692692016-09-30 16:16:40 -0400623 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400624 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400625
626 # This is used when bisecting in read() to find the initial slice.
627 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
628
629 self.is_sparse = True
630
631 def _update_chunks_and_blocks(self):
632 """Helper function to update the image header.
633
634 The the |total_chunks| and |total_blocks| fields in the header
635 will be set to value of the |_num_total_blocks| and
636 |_num_total_chunks| attributes.
637
638 """
639 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
640 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
641 self._num_total_blocks,
642 self._num_total_chunks))
643
644 def append_dont_care(self, num_bytes):
645 """Appends a DONT_CARE chunk to the sparse file.
646
647 The given number of bytes must be a multiple of the block size.
648
649 Arguments:
650 num_bytes: Size in number of bytes of the DONT_CARE chunk.
651 """
652 assert num_bytes % self.block_size == 0
653
654 if not self.is_sparse:
655 self._image.seek(0, os.SEEK_END)
656 # This is more efficient that writing NUL bytes since it'll add
657 # a hole on file systems that support sparse files (native
658 # sparse, not Android sparse).
659 self._image.truncate(self._image.tell() + num_bytes)
660 self._read_header()
661 return
662
663 self._num_total_chunks += 1
664 self._num_total_blocks += num_bytes / self.block_size
665 self._update_chunks_and_blocks()
666
667 self._image.seek(self._sparse_end, os.SEEK_SET)
668 self._image.write(struct.pack(ImageChunk.FORMAT,
669 ImageChunk.TYPE_DONT_CARE,
670 0, # Reserved
671 num_bytes / self.block_size,
672 struct.calcsize(ImageChunk.FORMAT)))
673 self._read_header()
674
675 def append_raw(self, data):
676 """Appends a RAW chunk to the sparse file.
677
678 The length of the given data must be a multiple of the block size.
679
680 Arguments:
681 data: Data to append.
682 """
683 assert len(data) % self.block_size == 0
684
685 if not self.is_sparse:
686 self._image.seek(0, os.SEEK_END)
687 self._image.write(data)
688 self._read_header()
689 return
690
691 self._num_total_chunks += 1
692 self._num_total_blocks += len(data) / self.block_size
693 self._update_chunks_and_blocks()
694
695 self._image.seek(self._sparse_end, os.SEEK_SET)
696 self._image.write(struct.pack(ImageChunk.FORMAT,
697 ImageChunk.TYPE_RAW,
698 0, # Reserved
699 len(data) / self.block_size,
700 len(data) +
701 struct.calcsize(ImageChunk.FORMAT)))
702 self._image.write(data)
703 self._read_header()
704
705 def append_fill(self, fill_data, size):
706 """Appends a fill chunk to the sparse file.
707
708 The total length of the fill data must be a multiple of the block size.
709
710 Arguments:
711 fill_data: Fill data to append - must be four bytes.
712 size: Number of chunk - must be a multiple of four and the block size.
713 """
714 assert len(fill_data) == 4
715 assert size % 4 == 0
716 assert size % self.block_size == 0
717
718 if not self.is_sparse:
719 self._image.seek(0, os.SEEK_END)
720 self._image.write(fill_data * (size/4))
721 self._read_header()
722 return
723
724 self._num_total_chunks += 1
725 self._num_total_blocks += size / self.block_size
726 self._update_chunks_and_blocks()
727
728 self._image.seek(self._sparse_end, os.SEEK_SET)
729 self._image.write(struct.pack(ImageChunk.FORMAT,
730 ImageChunk.TYPE_FILL,
731 0, # Reserved
732 size / self.block_size,
733 4 + struct.calcsize(ImageChunk.FORMAT)))
734 self._image.write(fill_data)
735 self._read_header()
736
737 def seek(self, offset):
738 """Sets the cursor position for reading from unsparsified file.
739
740 Arguments:
741 offset: Offset to seek to from the beginning of the file.
742 """
743 self._file_pos = offset
744
745 def read(self, size):
746 """Reads data from the unsparsified file.
747
748 This method may return fewer than |size| bytes of data if the end
749 of the file was encountered.
750
751 The file cursor for reading is advanced by the number of bytes
752 read.
753
754 Arguments:
755 size: Number of bytes to read.
756
757 Returns:
758 The data.
759
760 """
761 if not self.is_sparse:
762 self._image.seek(self._file_pos)
763 data = self._image.read(size)
764 self._file_pos += len(data)
765 return data
766
767 # Iterate over all chunks.
768 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
769 self._file_pos) - 1
770 data = bytearray()
771 to_go = size
772 while to_go > 0:
773 chunk = self._chunks[chunk_idx]
774 chunk_pos_offset = self._file_pos - chunk.output_offset
775 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
776
777 if chunk.chunk_type == ImageChunk.TYPE_RAW:
778 self._image.seek(chunk.input_offset + chunk_pos_offset)
779 data.extend(self._image.read(chunk_pos_to_go))
780 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
781 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
782 offset_mod = chunk_pos_offset % len(chunk.fill_data)
783 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
784 else:
785 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
786 data.extend('\0' * chunk_pos_to_go)
787
788 to_go -= chunk_pos_to_go
789 self._file_pos += chunk_pos_to_go
790 chunk_idx += 1
791 # Generate partial read in case of EOF.
792 if chunk_idx >= len(self._chunks):
793 break
794
795 return data
796
797 def tell(self):
798 """Returns the file cursor position for reading from unsparsified file.
799
800 Returns:
801 The file cursor position for reading.
802 """
803 return self._file_pos
804
805 def truncate(self, size):
806 """Truncates the unsparsified file.
807
808 Arguments:
809 size: Desired size of unsparsified file.
810
811 Raises:
812 ValueError: If desired size isn't a multiple of the block size.
813 """
814 if not self.is_sparse:
815 self._image.truncate(size)
816 self._read_header()
817 return
818
819 if size % self.block_size != 0:
820 raise ValueError('Cannot truncate to a size which is not a multiple '
821 'of the block size')
822
823 if size == self.image_size:
824 # Trivial where there's nothing to do.
825 return
826 elif size < self.image_size:
827 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
828 chunk = self._chunks[chunk_idx]
829 if chunk.output_offset != size:
830 # Truncation in the middle of a trunk - need to keep the chunk
831 # and modify it.
832 chunk_idx_for_update = chunk_idx + 1
833 num_to_keep = size - chunk.output_offset
834 assert num_to_keep % self.block_size == 0
835 if chunk.chunk_type == ImageChunk.TYPE_RAW:
836 truncate_at = (chunk.chunk_offset +
837 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
838 data_sz = num_to_keep
839 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
840 truncate_at = (chunk.chunk_offset +
841 struct.calcsize(ImageChunk.FORMAT) + 4)
842 data_sz = 4
843 else:
844 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
845 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
846 data_sz = 0
847 chunk_sz = num_to_keep/self.block_size
848 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
849 self._image.seek(chunk.chunk_offset)
850 self._image.write(struct.pack(ImageChunk.FORMAT,
851 chunk.chunk_type,
852 0, # Reserved
853 chunk_sz,
854 total_sz))
855 chunk.output_size = num_to_keep
856 else:
857 # Truncation at trunk boundary.
858 truncate_at = chunk.chunk_offset
859 chunk_idx_for_update = chunk_idx
860
861 self._num_total_chunks = chunk_idx_for_update
862 self._num_total_blocks = 0
863 for i in range(0, chunk_idx_for_update):
864 self._num_total_blocks += self._chunks[i].output_size / self.block_size
865 self._update_chunks_and_blocks()
866 self._image.truncate(truncate_at)
867
868 # We've modified the file so re-read all data.
869 self._read_header()
870 else:
871 # Truncating to grow - just add a DONT_CARE section.
872 self.append_dont_care(size - self.image_size)
873
874
David Zeuthen21e95262016-07-27 17:58:40 -0400875class AvbDescriptor(object):
876 """Class for AVB descriptor.
877
878 See the |AvbDescriptor| C struct for more information.
879
880 Attributes:
881 tag: The tag identifying what kind of descriptor this is.
882 data: The data in the descriptor.
883 """
884
885 SIZE = 16
886 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
887
888 def __init__(self, data):
889 """Initializes a new property descriptor.
890
891 Arguments:
892 data: If not None, must be a bytearray().
893
894 Raises:
895 LookupError: If the given descriptor is malformed.
896 """
897 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
898
899 if data:
900 (self.tag, num_bytes_following) = (
901 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
902 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
903 else:
904 self.tag = None
905 self.data = None
906
907 def print_desc(self, o):
908 """Print the descriptor.
909
910 Arguments:
911 o: The object to write the output to.
912 """
913 o.write(' Unknown descriptor:\n')
914 o.write(' Tag: {}\n'.format(self.tag))
915 if len(self.data) < 256:
916 o.write(' Data: {} ({} bytes)\n'.format(
917 repr(str(self.data)), len(self.data)))
918 else:
919 o.write(' Data: {} bytes\n'.format(len(self.data)))
920
921 def encode(self):
922 """Serializes the descriptor.
923
924 Returns:
925 A bytearray() with the descriptor data.
926 """
927 num_bytes_following = len(self.data)
928 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
929 padding_size = nbf_with_padding - num_bytes_following
930 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
931 padding = struct.pack(str(padding_size) + 'x')
932 ret = desc + self.data + padding
933 return bytearray(ret)
934
935
936class AvbPropertyDescriptor(AvbDescriptor):
937 """A class for property descriptors.
938
939 See the |AvbPropertyDescriptor| C struct for more information.
940
941 Attributes:
942 key: The key.
943 value: The key.
944 """
945
946 TAG = 0
947 SIZE = 32
948 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
949 'Q' # key size (bytes)
950 'Q') # value size (bytes)
951
952 def __init__(self, data=None):
953 """Initializes a new property descriptor.
954
955 Arguments:
956 data: If not None, must be a bytearray of size |SIZE|.
957
958 Raises:
959 LookupError: If the given descriptor is malformed.
960 """
961 AvbDescriptor.__init__(self, None)
962 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
963
964 if data:
965 (tag, num_bytes_following, key_size,
966 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
967 expected_size = round_to_multiple(
968 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
969 if tag != self.TAG or num_bytes_following != expected_size:
970 raise LookupError('Given data does not look like a property '
971 'descriptor.')
972 self.key = data[self.SIZE:(self.SIZE + key_size)]
973 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
974 value_size)]
975 else:
976 self.key = ''
977 self.value = ''
978
979 def print_desc(self, o):
980 """Print the descriptor.
981
982 Arguments:
983 o: The object to write the output to.
984 """
985 if len(self.value) < 256:
986 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
987 else:
988 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
989
990 def encode(self):
991 """Serializes the descriptor.
992
993 Returns:
994 A bytearray() with the descriptor data.
995 """
996 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
997 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
998 padding_size = nbf_with_padding - num_bytes_following
999 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1000 len(self.key), len(self.value))
1001 padding = struct.pack(str(padding_size) + 'x')
1002 ret = desc + self.key + '\0' + self.value + '\0' + padding
1003 return bytearray(ret)
1004
1005
1006class AvbHashtreeDescriptor(AvbDescriptor):
1007 """A class for hashtree descriptors.
1008
1009 See the |AvbHashtreeDescriptor| C struct for more information.
1010
1011 Attributes:
1012 dm_verity_version: dm-verity version used.
1013 image_size: Size of the image, after rounding up to |block_size|.
1014 tree_offset: Offset of the hash tree in the file.
1015 tree_size: Size of the tree.
1016 data_block_size: Data block size
1017 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001018 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1019 fec_offset: Offset of FEC data (0 if FEC is not used).
1020 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001021 hash_algorithm: Hash algorithm used.
1022 partition_name: Partition name.
1023 salt: Salt used.
1024 root_digest: Root digest.
1025 """
1026
1027 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001028 RESERVED = 64
1029 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001030 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1031 'L' # dm-verity version used
1032 'Q' # image size (bytes)
1033 'Q' # tree offset (bytes)
1034 'Q' # tree size (bytes)
1035 'L' # data block size (bytes)
1036 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001037 'L' # FEC number of roots
1038 'Q' # FEC offset (bytes)
1039 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001040 '32s' # hash algorithm used
1041 'L' # partition name (bytes)
1042 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001043 'L' + # root digest length (bytes)
1044 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001045
1046 def __init__(self, data=None):
1047 """Initializes a new hashtree descriptor.
1048
1049 Arguments:
1050 data: If not None, must be a bytearray of size |SIZE|.
1051
1052 Raises:
1053 LookupError: If the given descriptor is malformed.
1054 """
1055 AvbDescriptor.__init__(self, None)
1056 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1057
1058 if data:
1059 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1060 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001061 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1062 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001063 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1064 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001065 expected_size = round_to_multiple(
1066 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1067 if tag != self.TAG or num_bytes_following != expected_size:
1068 raise LookupError('Given data does not look like a hashtree '
1069 'descriptor.')
1070 # Nuke NUL-bytes at the end.
1071 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1072 o = 0
1073 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1074 partition_name_len)])
1075 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1076 self.partition_name.decode('utf-8')
1077 o += partition_name_len
1078 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1079 o += salt_len
1080 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1081 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1082 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1083
1084 else:
1085 self.dm_verity_version = 0
1086 self.image_size = 0
1087 self.tree_offset = 0
1088 self.tree_size = 0
1089 self.data_block_size = 0
1090 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001091 self.fec_num_roots = 0
1092 self.fec_offset = 0
1093 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001094 self.hash_algorithm = ''
1095 self.partition_name = ''
1096 self.salt = bytearray()
1097 self.root_digest = bytearray()
1098
1099 def print_desc(self, o):
1100 """Print the descriptor.
1101
1102 Arguments:
1103 o: The object to write the output to.
1104 """
1105 o.write(' Hashtree descriptor:\n')
1106 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1107 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1108 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1109 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1110 o.write(' Data Block Size: {} bytes\n'.format(
1111 self.data_block_size))
1112 o.write(' Hash Block Size: {} bytes\n'.format(
1113 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001114 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1115 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1116 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001117 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1118 o.write(' Partition Name: {}\n'.format(self.partition_name))
1119 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1120 'hex')))
1121 o.write(' Root Digest: {}\n'.format(str(
1122 self.root_digest).encode('hex')))
1123
1124 def encode(self):
1125 """Serializes the descriptor.
1126
1127 Returns:
1128 A bytearray() with the descriptor data.
1129 """
1130 encoded_name = self.partition_name.encode('utf-8')
1131 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1132 len(self.root_digest) - 16)
1133 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1134 padding_size = nbf_with_padding - num_bytes_following
1135 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1136 self.dm_verity_version, self.image_size,
1137 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001138 self.hash_block_size, self.fec_num_roots,
1139 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001140 len(encoded_name), len(self.salt), len(self.root_digest),
1141 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001142 padding = struct.pack(str(padding_size) + 'x')
1143 ret = desc + encoded_name + self.salt + self.root_digest + padding
1144 return bytearray(ret)
1145
1146
1147class AvbHashDescriptor(AvbDescriptor):
1148 """A class for hash descriptors.
1149
1150 See the |AvbHashDescriptor| C struct for more information.
1151
1152 Attributes:
1153 image_size: Image size, in bytes.
1154 hash_algorithm: Hash algorithm used.
1155 partition_name: Partition name.
1156 salt: Salt used.
1157 digest: The hash value of salt and data combined.
1158 """
1159
1160 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001161 RESERVED = 64
1162 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001163 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1164 'Q' # image size (bytes)
1165 '32s' # hash algorithm used
1166 'L' # partition name (bytes)
1167 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001168 'L' + # digest length (bytes)
1169 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001170
1171 def __init__(self, data=None):
1172 """Initializes a new hash descriptor.
1173
1174 Arguments:
1175 data: If not None, must be a bytearray of size |SIZE|.
1176
1177 Raises:
1178 LookupError: If the given descriptor is malformed.
1179 """
1180 AvbDescriptor.__init__(self, None)
1181 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1182
1183 if data:
1184 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1185 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001186 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001187 expected_size = round_to_multiple(
1188 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1189 if tag != self.TAG or num_bytes_following != expected_size:
1190 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1191 # Nuke NUL-bytes at the end.
1192 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1193 o = 0
1194 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1195 partition_name_len)])
1196 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1197 self.partition_name.decode('utf-8')
1198 o += partition_name_len
1199 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1200 o += salt_len
1201 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1202 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1203 raise LookupError('digest_len doesn\'t match hash algorithm')
1204
1205 else:
1206 self.image_size = 0
1207 self.hash_algorithm = ''
1208 self.partition_name = ''
1209 self.salt = bytearray()
1210 self.digest = bytearray()
1211
1212 def print_desc(self, o):
1213 """Print the descriptor.
1214
1215 Arguments:
1216 o: The object to write the output to.
1217 """
1218 o.write(' Hash descriptor:\n')
1219 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1220 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1221 o.write(' Partition Name: {}\n'.format(self.partition_name))
1222 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1223 'hex')))
1224 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1225 'hex')))
1226
1227 def encode(self):
1228 """Serializes the descriptor.
1229
1230 Returns:
1231 A bytearray() with the descriptor data.
1232 """
1233 encoded_name = self.partition_name.encode('utf-8')
1234 num_bytes_following = (
1235 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1236 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1237 padding_size = nbf_with_padding - num_bytes_following
1238 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1239 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001240 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001241 padding = struct.pack(str(padding_size) + 'x')
1242 ret = desc + encoded_name + self.salt + self.digest + padding
1243 return bytearray(ret)
1244
1245
1246class AvbKernelCmdlineDescriptor(AvbDescriptor):
1247 """A class for kernel command-line descriptors.
1248
1249 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1250
1251 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001252 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001253 kernel_cmdline: The kernel command-line.
1254 """
1255
1256 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001257 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001258 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001259 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001260 'L') # cmdline length (bytes)
1261
David Zeuthenfd41eb92016-11-17 12:24:47 -05001262 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1263 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1264
David Zeuthen21e95262016-07-27 17:58:40 -04001265 def __init__(self, data=None):
1266 """Initializes a new kernel cmdline descriptor.
1267
1268 Arguments:
1269 data: If not None, must be a bytearray of size |SIZE|.
1270
1271 Raises:
1272 LookupError: If the given descriptor is malformed.
1273 """
1274 AvbDescriptor.__init__(self, None)
1275 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1276
1277 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001278 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001279 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1280 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1281 8)
1282 if tag != self.TAG or num_bytes_following != expected_size:
1283 raise LookupError('Given data does not look like a kernel cmdline '
1284 'descriptor.')
1285 # Nuke NUL-bytes at the end.
1286 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1287 kernel_cmdline_length)])
1288 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1289 self.kernel_cmdline.decode('utf-8')
1290 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001291 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001292 self.kernel_cmdline = ''
1293
1294 def print_desc(self, o):
1295 """Print the descriptor.
1296
1297 Arguments:
1298 o: The object to write the output to.
1299 """
1300 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001301 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001302 o.write(' Kernel Cmdline: {}\n'.format(repr(
1303 self.kernel_cmdline)))
1304
1305 def encode(self):
1306 """Serializes the descriptor.
1307
1308 Returns:
1309 A bytearray() with the descriptor data.
1310 """
1311 encoded_str = self.kernel_cmdline.encode('utf-8')
1312 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1313 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1314 padding_size = nbf_with_padding - num_bytes_following
1315 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001316 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001317 padding = struct.pack(str(padding_size) + 'x')
1318 ret = desc + encoded_str + padding
1319 return bytearray(ret)
1320
1321
1322class AvbChainPartitionDescriptor(AvbDescriptor):
1323 """A class for chained partition descriptors.
1324
1325 See the |AvbChainPartitionDescriptor| C struct for more information.
1326
1327 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001328 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001329 partition_name: Partition name.
1330 public_key: Bytes for the public key.
1331 """
1332
1333 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001334 RESERVED = 64
1335 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001336 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001337 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001338 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001339 'L' + # public_key_size (bytes)
1340 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001341
1342 def __init__(self, data=None):
1343 """Initializes a new chain partition descriptor.
1344
1345 Arguments:
1346 data: If not None, must be a bytearray of size |SIZE|.
1347
1348 Raises:
1349 LookupError: If the given descriptor is malformed.
1350 """
1351 AvbDescriptor.__init__(self, None)
1352 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1353
1354 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001355 (tag, num_bytes_following, self.rollback_index_location,
1356 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001357 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001358 expected_size = round_to_multiple(
1359 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1360 if tag != self.TAG or num_bytes_following != expected_size:
1361 raise LookupError('Given data does not look like a chain partition '
1362 'descriptor.')
1363 o = 0
1364 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1365 partition_name_len)])
1366 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1367 self.partition_name.decode('utf-8')
1368 o += partition_name_len
1369 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1370
1371 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001372 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001373 self.partition_name = ''
1374 self.public_key = bytearray()
1375
1376 def print_desc(self, o):
1377 """Print the descriptor.
1378
1379 Arguments:
1380 o: The object to write the output to.
1381 """
1382 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001383 o.write(' Partition Name: {}\n'.format(self.partition_name))
1384 o.write(' Rollback Index Location: {}\n'.format(
1385 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001386 # Just show the SHA1 of the key, for size reasons.
1387 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001388 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001389
1390 def encode(self):
1391 """Serializes the descriptor.
1392
1393 Returns:
1394 A bytearray() with the descriptor data.
1395 """
1396 encoded_name = self.partition_name.encode('utf-8')
1397 num_bytes_following = (
1398 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1399 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1400 padding_size = nbf_with_padding - num_bytes_following
1401 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001402 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001403 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001404 padding = struct.pack(str(padding_size) + 'x')
1405 ret = desc + encoded_name + self.public_key + padding
1406 return bytearray(ret)
1407
1408
1409DESCRIPTOR_CLASSES = [
1410 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1411 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1412]
1413
1414
1415def parse_descriptors(data):
1416 """Parses a blob of data into descriptors.
1417
1418 Arguments:
1419 data: A bytearray() with encoded descriptors.
1420
1421 Returns:
1422 A list of instances of objects derived from AvbDescriptor. For
1423 unknown descriptors, the class AvbDescriptor is used.
1424 """
1425 o = 0
1426 ret = []
1427 while o < len(data):
1428 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1429 if tag < len(DESCRIPTOR_CLASSES):
1430 c = DESCRIPTOR_CLASSES[tag]
1431 else:
1432 c = AvbDescriptor
1433 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1434 o += 16 + nb_following
1435 return ret
1436
1437
1438class AvbFooter(object):
1439 """A class for parsing and writing footers.
1440
1441 Footers are stored at the end of partitions and point to where the
1442 AvbVBMeta blob is located. They also contain the original size of
1443 the image before AVB information was added.
1444
1445 Attributes:
1446 magic: Magic for identifying the footer, see |MAGIC|.
1447 version_major: The major version of avbtool that wrote the footer.
1448 version_minor: The minor version of avbtool that wrote the footer.
1449 original_image_size: Original image size.
1450 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1451 vbmeta_size: Size of the AvbVBMeta blob.
1452 """
1453
1454 MAGIC = 'AVBf'
1455 SIZE = 64
1456 RESERVED = 28
1457 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1458 'Q' # Original image size.
1459 'Q' # Offset of VBMeta blob.
1460 'Q' + # Size of VBMeta blob.
1461 str(RESERVED) + 'x') # padding for reserved bytes
1462
1463 def __init__(self, data=None):
1464 """Initializes a new footer object.
1465
1466 Arguments:
1467 data: If not None, must be a bytearray of size 4096.
1468
1469 Raises:
1470 LookupError: If the given footer is malformed.
1471 struct.error: If the given data has no footer.
1472 """
1473 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1474
1475 if data:
1476 (self.magic, self.version_major, self.version_minor,
1477 self.original_image_size, self.vbmeta_offset,
1478 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1479 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001480 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001481 else:
1482 self.magic = self.MAGIC
1483 self.version_major = AVB_VERSION_MAJOR
1484 self.version_minor = AVB_VERSION_MINOR
1485 self.original_image_size = 0
1486 self.vbmeta_offset = 0
1487 self.vbmeta_size = 0
1488
David Zeuthena4fee8b2016-08-22 15:20:43 -04001489 def encode(self):
1490 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001491
David Zeuthena4fee8b2016-08-22 15:20:43 -04001492 Returns:
1493 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001494 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001495 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1496 self.version_minor, self.original_image_size,
1497 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001498
1499
1500class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001501 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001502
1503 Attributes:
1504 The attributes correspond to the |AvbVBMetaHeader| struct
1505 defined in avb_vbmeta_header.h.
1506 """
1507
1508 SIZE = 256
1509
1510 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001511 RESERVED = 132
David Zeuthen21e95262016-07-27 17:58:40 -04001512
1513 # Keep in sync with |AvbVBMetaImageHeader|.
1514 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1515 '2Q' # 2 x block size
1516 'L' # algorithm type
1517 '2Q' # offset, size (hash)
1518 '2Q' # offset, size (signature)
1519 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001520 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001521 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001522 'Q' # rollback_index
1523 'L' + # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001524 str(RESERVED) + 'x') # padding for reserved bytes
1525
1526 def __init__(self, data=None):
1527 """Initializes a new header object.
1528
1529 Arguments:
1530 data: If not None, must be a bytearray of size 8192.
1531
1532 Raises:
1533 Exception: If the given data is malformed.
1534 """
1535 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1536
1537 if data:
1538 (self.magic, self.header_version_major, self.header_version_minor,
1539 self.authentication_data_block_size, self.auxiliary_data_block_size,
1540 self.algorithm_type, self.hash_offset, self.hash_size,
1541 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001542 self.public_key_size, self.public_key_metadata_offset,
1543 self.public_key_metadata_size, self.descriptors_offset,
1544 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001545 self.rollback_index,
1546 self.flags) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001547 # Nuke NUL-bytes at the end of the string.
1548 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001549 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001550 else:
1551 self.magic = 'AVB0'
1552 self.header_version_major = AVB_VERSION_MAJOR
1553 self.header_version_minor = AVB_VERSION_MINOR
1554 self.authentication_data_block_size = 0
1555 self.auxiliary_data_block_size = 0
1556 self.algorithm_type = 0
1557 self.hash_offset = 0
1558 self.hash_size = 0
1559 self.signature_offset = 0
1560 self.signature_size = 0
1561 self.public_key_offset = 0
1562 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001563 self.public_key_metadata_offset = 0
1564 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001565 self.descriptors_offset = 0
1566 self.descriptors_size = 0
1567 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001568 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001569
1570 def save(self, output):
1571 """Serializes the header (256 bytes) to disk.
1572
1573 Arguments:
1574 output: The object to write the output to.
1575 """
1576 output.write(struct.pack(
1577 self.FORMAT_STRING, self.magic, self.header_version_major,
1578 self.header_version_minor, self.authentication_data_block_size,
1579 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1580 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001581 self.public_key_offset, self.public_key_size,
1582 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001583 self.descriptors_offset, self.descriptors_size, self.rollback_index,
1584 self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001585
1586 def encode(self):
1587 """Serializes the header (256) to a bytearray().
1588
1589 Returns:
1590 A bytearray() with the encoded header.
1591 """
1592 return struct.pack(self.FORMAT_STRING, self.magic,
1593 self.header_version_major, self.header_version_minor,
1594 self.authentication_data_block_size,
1595 self.auxiliary_data_block_size, self.algorithm_type,
1596 self.hash_offset, self.hash_size, self.signature_offset,
1597 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001598 self.public_key_size, self.public_key_metadata_offset,
1599 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001600 self.descriptors_size, self.rollback_index, self.flags)
David Zeuthen21e95262016-07-27 17:58:40 -04001601
1602
1603class Avb(object):
1604 """Business logic for avbtool command-line tool."""
1605
David Zeuthen8b6973b2016-09-20 12:39:49 -04001606 # Keep in sync with avb_ab_flow.h.
1607 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1608 AB_MAGIC = '\0AB0'
1609 AB_MAJOR_VERSION = 1
1610 AB_MINOR_VERSION = 0
1611 AB_MISC_METADATA_OFFSET = 2048
1612
David Zeuthen09692692016-09-30 16:16:40 -04001613 # Constants for maximum metadata size. These are used to give
1614 # meaningful errors if the value passed in via --partition_size is
1615 # too small and when --calc_max_image_size is used. We use
1616 # conservative figures.
1617 MAX_VBMETA_SIZE = 64 * 1024
1618 MAX_FOOTER_SIZE = 4096
1619
David Zeuthena4fee8b2016-08-22 15:20:43 -04001620 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001621 """Implements the 'erase_footer' command.
1622
1623 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001624 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001625 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001626
1627 Raises:
1628 AvbError: If there's no footer in the image.
1629 """
1630
David Zeuthena4fee8b2016-08-22 15:20:43 -04001631 image = ImageHandler(image_filename)
1632
David Zeuthen21e95262016-07-27 17:58:40 -04001633 (footer, _, descriptors, _) = self._parse_image(image)
1634
1635 if not footer:
1636 raise AvbError('Given image does not have a footer.')
1637
1638 new_image_size = None
1639 if not keep_hashtree:
1640 new_image_size = footer.original_image_size
1641 else:
1642 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001643 # descriptor to figure out the location and size of the hashtree
1644 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001645 for desc in descriptors:
1646 if isinstance(desc, AvbHashtreeDescriptor):
1647 # The hashtree is always just following the main data so the
1648 # new size is easily derived.
1649 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001650 # If the image has FEC codes, also keep those.
1651 if desc.fec_offset > 0:
1652 fec_end = desc.fec_offset + desc.fec_size
1653 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001654 break
1655 if not new_image_size:
1656 raise AvbError('Requested to keep hashtree but no hashtree '
1657 'descriptor was found.')
1658
1659 # And cut...
1660 image.truncate(new_image_size)
1661
David Zeuthen8b6973b2016-09-20 12:39:49 -04001662 def set_ab_metadata(self, misc_image, slot_data):
1663 """Implements the 'set_ab_metadata' command.
1664
1665 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1666 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1667
1668 Arguments:
1669 misc_image: The misc image to write to.
1670 slot_data: Slot data as a string
1671
1672 Raises:
1673 AvbError: If slot data is malformed.
1674 """
1675 tokens = slot_data.split(':')
1676 if len(tokens) != 6:
1677 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1678 a_priority = int(tokens[0])
1679 a_tries_remaining = int(tokens[1])
1680 a_success = True if int(tokens[2]) != 0 else False
1681 b_priority = int(tokens[3])
1682 b_tries_remaining = int(tokens[4])
1683 b_success = True if int(tokens[5]) != 0 else False
1684
1685 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1686 self.AB_MAGIC,
1687 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1688 a_priority, a_tries_remaining, a_success,
1689 b_priority, b_tries_remaining, b_success)
1690 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1691 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1692 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1693 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1694 misc_image.write(ab_data)
1695
David Zeuthena4fee8b2016-08-22 15:20:43 -04001696 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001697 """Implements the 'info_image' command.
1698
1699 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001700 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001701 output: Output file to write human-readable information to (file object).
1702 """
1703
David Zeuthena4fee8b2016-08-22 15:20:43 -04001704 image = ImageHandler(image_filename)
1705
David Zeuthen21e95262016-07-27 17:58:40 -04001706 o = output
1707
1708 (footer, header, descriptors, image_size) = self._parse_image(image)
1709
1710 if footer:
1711 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1712 footer.version_minor))
1713 o.write('Image size: {} bytes\n'.format(image_size))
1714 o.write('Original image size: {} bytes\n'.format(
1715 footer.original_image_size))
1716 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1717 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1718 o.write('--\n')
1719
1720 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1721
David Zeuthena4fee8b2016-08-22 15:20:43 -04001722 o.write('VBMeta image version: {}.{}{}\n'.format(
1723 header.header_version_major, header.header_version_minor,
1724 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001725 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1726 o.write('Authentication Block: {} bytes\n'.format(
1727 header.authentication_data_block_size))
1728 o.write('Auxiliary Block: {} bytes\n'.format(
1729 header.auxiliary_data_block_size))
1730 o.write('Algorithm: {}\n'.format(alg_name))
1731 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001732 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001733
1734 # Print descriptors.
1735 num_printed = 0
1736 o.write('Descriptors:\n')
1737 for desc in descriptors:
1738 desc.print_desc(o)
1739 num_printed += 1
1740 if num_printed == 0:
1741 o.write(' (none)\n')
1742
1743 def _parse_image(self, image):
1744 """Gets information about an image.
1745
1746 The image can either be a vbmeta or an image with a footer.
1747
1748 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001749 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001750
1751 Returns:
1752 A tuple where the first argument is a AvbFooter (None if there
1753 is no footer on the image), the second argument is a
1754 AvbVBMetaHeader, the third argument is a list of
1755 AvbDescriptor-derived instances, and the fourth argument is the
1756 size of |image|.
1757 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001758 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001759 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001760 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001761 try:
1762 footer = AvbFooter(image.read(AvbFooter.SIZE))
1763 except (LookupError, struct.error):
1764 # Nope, just seek back to the start.
1765 image.seek(0)
1766
1767 vbmeta_offset = 0
1768 if footer:
1769 vbmeta_offset = footer.vbmeta_offset
1770
1771 image.seek(vbmeta_offset)
1772 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1773
1774 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1775 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1776 desc_start_offset = aux_block_offset + h.descriptors_offset
1777 image.seek(desc_start_offset)
1778 descriptors = parse_descriptors(image.read(h.descriptors_size))
1779
David Zeuthen09692692016-09-30 16:16:40 -04001780 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001781
David Zeuthenfd41eb92016-11-17 12:24:47 -05001782 def _get_cmdline_descriptors_for_dm_verity(self, image):
1783 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001784
1785 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001786 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001787
1788 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001789 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1790 instructions. There is one for when hashtree is not disabled and one for
1791 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001792
1793 Raises:
1794 AvbError: If |image| doesn't have a hashtree descriptor.
1795
1796 """
1797
1798 (_, _, descriptors, _) = self._parse_image(image)
1799
1800 ht = None
1801 for desc in descriptors:
1802 if isinstance(desc, AvbHashtreeDescriptor):
1803 ht = desc
1804 break
1805
1806 if not ht:
1807 raise AvbError('No hashtree descriptor in given image')
1808
1809 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001810 c += '0' # start
1811 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1812 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1813 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1814 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1815 c += ' {}'.format(ht.data_block_size) # data_block
1816 c += ' {}'.format(ht.hash_block_size) # hash_block
1817 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1818 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1819 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1820 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1821 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1822 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001823 c += ' 10' # number of optional args
1824 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001825 c += ' ignore_zero_blocks'
1826 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1827 c += ' fec_roots {}'.format(ht.fec_num_roots)
1828 # Note that fec_blocks is the size that FEC covers, *not* the
1829 # size of the FEC data. Since we use FEC for everything up until
1830 # the FEC data, it's the same as the offset.
1831 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1832 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1833 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001834 c += ' 2' # number of optional args
1835 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001836 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001837 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001838
David Zeuthenfd41eb92016-11-17 12:24:47 -05001839 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001840 desc = AvbKernelCmdlineDescriptor()
1841 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001842 desc.flags = (
1843 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1844
1845 # The descriptor for when hashtree verification is disabled is a lot
1846 # simpler - we just set the root to the partition.
1847 desc_no_ht = AvbKernelCmdlineDescriptor()
1848 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1849 desc_no_ht.flags = (
1850 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1851
1852 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001853
1854 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001855 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001856 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 """Implements the 'make_vbmeta_image' command.
1860
1861 Arguments:
1862 output: File to write the image to.
1863 chain_partitions: List of partitions to chain.
1864 algorithm_name: Name of algorithm to use.
1865 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001866 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001867 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001868 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001869 props: Properties to insert (list of strings of the form 'key:value').
1870 props_from_file: Properties to insert (list of strings 'key:<path>').
1871 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001872 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001873 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001874 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001875
1876 Raises:
1877 AvbError: If a chained partition is malformed.
1878 """
1879
1880 descriptors = []
1881
1882 # Insert chained partition descriptors.
1883 if chain_partitions:
1884 for cp in chain_partitions:
1885 cp_tokens = cp.split(':')
1886 if len(cp_tokens) != 3:
1887 raise AvbError('Malformed chained partition "{}".'.format(cp))
1888 desc = AvbChainPartitionDescriptor()
1889 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001890 desc.rollback_index_location = int(cp_tokens[1])
1891 if desc.rollback_index_location < 1:
1892 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001893 file_path = cp_tokens[2]
1894 desc.public_key = open(file_path, 'rb').read()
1895 descriptors.append(desc)
1896
1897 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001898 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001899 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001900 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001901 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04001902
1903 # Write entire vbmeta blob (header, authentication, auxiliary).
1904 output.seek(0)
1905 output.write(vbmeta_blob)
1906
David Zeuthen18666ab2016-11-15 11:18:05 -05001907 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1908 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001909 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001910 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001911 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001912 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001913 """Generates a VBMeta blob.
1914
1915 This blob contains the header (struct AvbVBMetaHeader), the
1916 authentication data block (which contains the hash and signature
1917 for the header and auxiliary block), and the auxiliary block
1918 (which contains descriptors, the public key used, and other data).
1919
1920 The |key| parameter can |None| only if the |algorithm_name| is
1921 'NONE'.
1922
1923 Arguments:
1924 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1925 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001926 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001927 descriptors: A list of descriptors to insert or None.
1928 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001929 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001930 props: Properties to insert (List of strings of the form 'key:value').
1931 props_from_file: Properties to insert (List of strings 'key:<path>').
1932 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001933 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001934 dm-verity kernel cmdline from.
1935 include_descriptors_from_image: List of file objects for which
1936 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001937 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001938
1939 Returns:
1940 A bytearray() with the VBMeta blob.
1941
1942 Raises:
1943 Exception: If the |algorithm_name| is not found, if no key has
1944 been given and the given algorithm requires one, or the key is
1945 of the wrong size.
1946
1947 """
1948 try:
1949 alg = ALGORITHMS[algorithm_name]
1950 except KeyError:
1951 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1952
1953 # Descriptors.
1954 encoded_descriptors = bytearray()
1955 if descriptors:
1956 for desc in descriptors:
1957 encoded_descriptors.extend(desc.encode())
1958
1959 # Add properties.
1960 if props:
1961 for prop in props:
1962 idx = prop.find(':')
1963 if idx == -1:
1964 raise AvbError('Malformed property "{}".'.format(prop))
1965 desc = AvbPropertyDescriptor()
1966 desc.key = prop[0:idx]
1967 desc.value = prop[(idx + 1):]
1968 encoded_descriptors.extend(desc.encode())
1969 if props_from_file:
1970 for prop in props_from_file:
1971 idx = prop.find(':')
1972 if idx == -1:
1973 raise AvbError('Malformed property "{}".'.format(prop))
1974 desc = AvbPropertyDescriptor()
1975 desc.key = prop[0:idx]
1976 desc.value = prop[(idx + 1):]
1977 file_path = prop[(idx + 1):]
1978 desc.value = open(file_path, 'rb').read()
1979 encoded_descriptors.extend(desc.encode())
1980
1981 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001982 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001983 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001984 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001985 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1986 encoded_descriptors.extend(cmdline_desc[0].encode())
1987 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001988
1989 # Add kernel command-lines.
1990 if kernel_cmdlines:
1991 for i in kernel_cmdlines:
1992 desc = AvbKernelCmdlineDescriptor()
1993 desc.kernel_cmdline = i
1994 encoded_descriptors.extend(desc.encode())
1995
1996 # Add descriptors from other images.
1997 if include_descriptors_from_image:
1998 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001999 image_handler = ImageHandler(image.name)
2000 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002001 for desc in image_descriptors:
2002 encoded_descriptors.extend(desc.encode())
2003
David Zeuthen18666ab2016-11-15 11:18:05 -05002004 # Load public key metadata blob, if requested.
2005 pkmd_blob = []
2006 if public_key_metadata_path:
2007 with open(public_key_metadata_path) as f:
2008 pkmd_blob = f.read()
2009
David Zeuthen21e95262016-07-27 17:58:40 -04002010 key = None
2011 encoded_key = bytearray()
2012 if alg.public_key_num_bytes > 0:
2013 if not key_path:
2014 raise AvbError('Key is required for algorithm {}'.format(
2015 algorithm_name))
2016 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2017 encoded_key = encode_rsa_key(key)
2018 if len(encoded_key) != alg.public_key_num_bytes:
2019 raise AvbError('Key is wrong size for algorithm {}'.format(
2020 algorithm_name))
2021
2022 h = AvbVBMetaHeader()
2023
David Zeuthen18666ab2016-11-15 11:18:05 -05002024 # For the Auxiliary data block, descriptors are stored at offset 0,
2025 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002026 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002027 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002028 h.descriptors_offset = 0
2029 h.descriptors_size = len(encoded_descriptors)
2030 h.public_key_offset = h.descriptors_size
2031 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002032 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2033 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002034
2035 # For the Authentication data block, the hash is first and then
2036 # the signature.
2037 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002038 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002039 h.algorithm_type = alg.algorithm_type
2040 h.hash_offset = 0
2041 h.hash_size = alg.hash_num_bytes
2042 # Signature offset and size - it's stored right after the hash
2043 # (in Authentication data block).
2044 h.signature_offset = alg.hash_num_bytes
2045 h.signature_size = alg.signature_num_bytes
2046
2047 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002048 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002049
2050 # Generate Header data block.
2051 header_data_blob = h.encode()
2052
2053 # Generate Auxiliary data block.
2054 aux_data_blob = bytearray()
2055 aux_data_blob.extend(encoded_descriptors)
2056 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002057 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002058 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2059 aux_data_blob.extend('\0' * padding_bytes)
2060
2061 # Calculate the hash.
2062 binary_hash = bytearray()
2063 binary_signature = bytearray()
2064 if algorithm_name != 'NONE':
2065 if algorithm_name[0:6] == 'SHA256':
2066 ha = hashlib.sha256()
2067 elif algorithm_name[0:6] == 'SHA512':
2068 ha = hashlib.sha512()
2069 else:
2070 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2071 ha.update(header_data_blob)
2072 ha.update(aux_data_blob)
2073 binary_hash.extend(ha.digest())
2074
2075 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002076 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002077 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2078 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002079
2080 # Generate Authentication data block.
2081 auth_data_blob = bytearray()
2082 auth_data_blob.extend(binary_hash)
2083 auth_data_blob.extend(binary_signature)
2084 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2085 auth_data_blob.extend('\0' * padding_bytes)
2086
2087 return header_data_blob + auth_data_blob + aux_data_blob
2088
2089 def extract_public_key(self, key_path, output):
2090 """Implements the 'extract_public_key' command.
2091
2092 Arguments:
2093 key_path: The path to a RSA private key file.
2094 output: The file to write to.
2095 """
2096 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2097 write_rsa_key(output, key)
2098
David Zeuthena4fee8b2016-08-22 15:20:43 -04002099 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002100 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002101 public_key_metadata_path, rollback_index, props,
2102 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002103 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002104 include_descriptors_from_image, signing_helper):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002105 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002106
2107 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002108 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002109 partition_size: Size of partition.
2110 partition_name: Name of partition (without A/B suffix).
2111 hash_algorithm: Hash algorithm to use.
2112 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2113 algorithm_name: Name of algorithm to use.
2114 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002115 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002116 rollback_index: Rollback index.
2117 props: Properties to insert (List of strings of the form 'key:value').
2118 props_from_file: Properties to insert (List of strings 'key:<path>').
2119 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002120 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002121 dm-verity kernel cmdline from.
2122 include_descriptors_from_image: List of file objects for which
2123 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002124 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002125
2126 Raises:
2127 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002128 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002129 image = ImageHandler(image_filename)
2130
2131 if partition_size % image.block_size != 0:
2132 raise AvbError('Partition size of {} is not a multiple of the image '
2133 'block size {}.'.format(partition_size,
2134 image.block_size))
2135
David Zeuthen21e95262016-07-27 17:58:40 -04002136 # If there's already a footer, truncate the image to its original
2137 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2138 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002139 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002140 try:
2141 footer = AvbFooter(image.read(AvbFooter.SIZE))
2142 # Existing footer found. Just truncate.
2143 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002144 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002145 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002146 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002147
2148 # If anything goes wrong from here-on, restore the image back to
2149 # its original size.
2150 try:
David Zeuthen09692692016-09-30 16:16:40 -04002151 # First, calculate the maximum image size such that an image
2152 # this size + metadata (footer + vbmeta struct) fits in
2153 # |partition_size|.
2154 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2155 max_image_size = partition_size - max_metadata_size
2156
2157 # If image size exceeds the maximum image size, fail.
2158 if image.image_size > max_image_size:
2159 raise AvbError('Image size of {} exceeds maximum image '
2160 'size of {} in order to fit in a partition '
2161 'size of {}.'.format(image.image_size, max_image_size,
2162 partition_size))
2163
David Zeuthen21e95262016-07-27 17:58:40 -04002164 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2165 if salt:
2166 salt = salt.decode('hex')
2167 else:
2168 if salt is None:
2169 # If salt is not explicitly specified, choose a hash
2170 # that's the same size as the hash size.
2171 hash_size = digest_size
2172 salt = open('/dev/urandom').read(hash_size)
2173 else:
2174 salt = ''
2175
2176 hasher = hashlib.new(name=hash_algorithm, string=salt)
2177 # TODO(zeuthen): might want to read this in chunks to avoid
2178 # memory pressure, then again, this is only supposed to be used
2179 # on kernel/initramfs partitions. Possible optimization.
2180 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002181 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002182 digest = hasher.digest()
2183
2184 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002185 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002186 h_desc.hash_algorithm = hash_algorithm
2187 h_desc.partition_name = partition_name
2188 h_desc.salt = salt
2189 h_desc.digest = digest
2190
David Zeuthenfd41eb92016-11-17 12:24:47 -05002191 # Flags are only allowed on top-level vbmeta struct.
2192 flags = 0
2193
David Zeuthen21e95262016-07-27 17:58:40 -04002194 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002195 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002196 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002197 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002198 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002199 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002200
David Zeuthena4fee8b2016-08-22 15:20:43 -04002201 # If the image isn't sparse, its size might not be a multiple of
2202 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002203 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002204 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002205 padding_needed = image.block_size - (image.image_size%image.block_size)
2206 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002207
David Zeuthena4fee8b2016-08-22 15:20:43 -04002208 # The append_raw() method requires content with size being a
2209 # multiple of |block_size| so add padding as needed. Also record
2210 # where this is written to since we'll need to put that in the
2211 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002212 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002213 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2214 len(vbmeta_blob))
2215 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2216 image.append_raw(vbmeta_blob_with_padding)
2217 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2218
2219 # Now insert a DONT_CARE chunk with enough bytes such that the
2220 # final Footer block is at the end of partition_size..
2221 image.append_dont_care(partition_size - vbmeta_end_offset -
2222 1*image.block_size)
2223
2224 # Generate the Footer that tells where the VBMeta footer
2225 # is. Also put enough padding in the front of the footer since
2226 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002227 footer = AvbFooter()
2228 footer.original_image_size = original_image_size
2229 footer.vbmeta_offset = vbmeta_offset
2230 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002231 footer_blob = footer.encode()
2232 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2233 footer_blob)
2234 image.append_raw(footer_blob_with_padding)
2235
David Zeuthen21e95262016-07-27 17:58:40 -04002236 except:
2237 # Truncate back to original size, then re-raise
2238 image.truncate(original_image_size)
2239 raise
2240
David Zeuthena4fee8b2016-08-22 15:20:43 -04002241 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002242 generate_fec, fec_num_roots, hash_algorithm,
2243 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002244 public_key_metadata_path, rollback_index,
2245 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002246 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002247 include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002248 calc_max_image_size, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04002249 """Implements the 'add_hashtree_footer' command.
2250
2251 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2252 more information about dm-verity and these hashes.
2253
2254 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002255 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002256 partition_size: Size of partition.
2257 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002258 generate_fec: If True, generate FEC codes.
2259 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002260 hash_algorithm: Hash algorithm to use.
2261 block_size: Block size to use.
2262 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2263 algorithm_name: Name of algorithm to use.
2264 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002265 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002266 rollback_index: Rollback index.
2267 props: Properties to insert (List of strings of the form 'key:value').
2268 props_from_file: Properties to insert (List of strings 'key:<path>').
2269 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002270 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002271 dm-verity kernel cmdline from.
2272 include_descriptors_from_image: List of file objects for which
2273 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002274 calc_max_image_size: Don't store the hashtree or footer - instead
2275 calculate the maximum image size leaving enough room for hashtree
2276 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002277 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002278
2279 Raises:
2280 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002281 """
David Zeuthen09692692016-09-30 16:16:40 -04002282 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2283 digest_padding = round_to_pow2(digest_size) - digest_size
2284
2285 # First, calculate the maximum image size such that an image
2286 # this size + the hashtree + metadata (footer + vbmeta struct)
2287 # fits in |partition_size|. We use very conservative figures for
2288 # metadata.
2289 (_, max_tree_size) = calc_hash_level_offsets(
2290 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002291 max_fec_size = 0
2292 if generate_fec:
2293 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2294 max_metadata_size = (max_fec_size + max_tree_size +
2295 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002296 self.MAX_FOOTER_SIZE)
2297 max_image_size = partition_size - max_metadata_size
2298
2299 # If we're asked to only calculate the maximum image size, we're done.
2300 if calc_max_image_size:
2301 print '{}'.format(max_image_size)
2302 return
2303
David Zeuthena4fee8b2016-08-22 15:20:43 -04002304 image = ImageHandler(image_filename)
2305
2306 if partition_size % image.block_size != 0:
2307 raise AvbError('Partition size of {} is not a multiple of the image '
2308 'block size {}.'.format(partition_size,
2309 image.block_size))
2310
David Zeuthen21e95262016-07-27 17:58:40 -04002311 # If there's already a footer, truncate the image to its original
2312 # size. This way 'avbtool add_hashtree_footer' is idempotent
2313 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002314 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002315 try:
2316 footer = AvbFooter(image.read(AvbFooter.SIZE))
2317 # Existing footer found. Just truncate.
2318 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002319 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002320 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002321 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002322
2323 # If anything goes wrong from here-on, restore the image back to
2324 # its original size.
2325 try:
2326 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002327 rounded_image_size = round_to_multiple(image.image_size, block_size)
2328 if rounded_image_size > image.image_size:
2329 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002330
David Zeuthen09692692016-09-30 16:16:40 -04002331 # If image size exceeds the maximum image size, fail.
2332 if image.image_size > max_image_size:
2333 raise AvbError('Image size of {} exceeds maximum image '
2334 'size of {} in order to fit in a partition '
2335 'size of {}.'.format(image.image_size, max_image_size,
2336 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002337
2338 if salt:
2339 salt = salt.decode('hex')
2340 else:
2341 if salt is None:
2342 # If salt is not explicitly specified, choose a hash
2343 # that's the same size as the hash size.
2344 hash_size = digest_size
2345 salt = open('/dev/urandom').read(hash_size)
2346 else:
2347 salt = ''
2348
David Zeuthena4fee8b2016-08-22 15:20:43 -04002349 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002350 # offsets in advance.
2351 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002352 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002353
David Zeuthena4fee8b2016-08-22 15:20:43 -04002354 # If the image isn't sparse, its size might not be a multiple of
2355 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002356 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002357 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002358 padding_needed = image.block_size - (image.image_size%image.block_size)
2359 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002360
David Zeuthena4fee8b2016-08-22 15:20:43 -04002361 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002362 tree_offset = image.image_size
2363 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002364 block_size,
2365 hash_algorithm, salt,
2366 digest_padding,
2367 hash_level_offsets,
2368 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002369
2370 # Generate HashtreeDescriptor with details about the tree we
2371 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002372 ht_desc = AvbHashtreeDescriptor()
2373 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002374 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002375 ht_desc.tree_offset = tree_offset
2376 ht_desc.tree_size = tree_size
2377 ht_desc.data_block_size = block_size
2378 ht_desc.hash_block_size = block_size
2379 ht_desc.hash_algorithm = hash_algorithm
2380 ht_desc.partition_name = partition_name
2381 ht_desc.salt = salt
2382 ht_desc.root_digest = root_digest
2383
David Zeuthen09692692016-09-30 16:16:40 -04002384 # Write the hash tree
2385 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2386 len(hash_tree))
2387 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2388 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002389 len_hashtree_and_fec = len(hash_tree_with_padding)
2390
2391 # Generate FEC codes, if requested.
2392 if generate_fec:
2393 fec_data = generate_fec_data(image_filename, fec_num_roots)
2394 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2395 len(fec_data))
2396 fec_data_with_padding = fec_data + '\0'*padding_needed
2397 fec_offset = image.image_size
2398 image.append_raw(fec_data_with_padding)
2399 len_hashtree_and_fec += len(fec_data_with_padding)
2400 # Update the hashtree descriptor.
2401 ht_desc.fec_num_roots = fec_num_roots
2402 ht_desc.fec_offset = fec_offset
2403 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002404
David Zeuthenfd41eb92016-11-17 12:24:47 -05002405 # Flags are only allowed on top-level vbmeta struct.
2406 flags = 0
2407
David Zeuthena4fee8b2016-08-22 15:20:43 -04002408 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002409 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002410 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002411 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002412 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002413 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002414 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002415 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2416 len(vbmeta_blob))
2417 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2418 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002419
David Zeuthena4fee8b2016-08-22 15:20:43 -04002420 # Now insert a DONT_CARE chunk with enough bytes such that the
2421 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002422 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002423 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002424
David Zeuthena4fee8b2016-08-22 15:20:43 -04002425 # Generate the Footer that tells where the VBMeta footer
2426 # is. Also put enough padding in the front of the footer since
2427 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002428 footer = AvbFooter()
2429 footer.original_image_size = original_image_size
2430 footer.vbmeta_offset = vbmeta_offset
2431 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002432 footer_blob = footer.encode()
2433 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2434 footer_blob)
2435 image.append_raw(footer_blob_with_padding)
2436
David Zeuthen21e95262016-07-27 17:58:40 -04002437 except:
David Zeuthen09692692016-09-30 16:16:40 -04002438 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002439 image.truncate(original_image_size)
2440 raise
2441
Darren Krahn147b08d2016-12-20 16:38:29 -08002442 def make_atx_certificate(self, output, authority_key_path, subject_key,
2443 subject_key_version, subject,
2444 is_intermediate_authority, signing_helper):
2445 """Implements the 'make_atx_certificate' command.
2446
2447 Android Things certificates are required for Android Things public key
2448 metadata. They chain the vbmeta signing key for a particular product back to
2449 a fused, permanent root key. These certificates are fixed-length and fixed-
2450 format with the explicit goal of not parsing ASN.1 in bootloader code.
2451
2452 Arguments:
2453 output: Certificate will be written to this file on success.
2454 authority_key_path: A PEM file path with the authority private key.
2455 If None, then a certificate will be created without a
2456 signature. The signature can be created out-of-band
2457 and appended.
2458 subject_key: A PEM or DER subject public key.
2459 subject_key_version: A 64-bit version value. If this is None, the number
2460 of seconds since the epoch is used.
2461 subject: A subject identifier. For Product Signing Key certificates this
2462 should be the same Product ID found in the permanent attributes.
2463 is_intermediate_authority: True if the certificate is for an intermediate
2464 authority.
2465 signing_helper: Program which signs a hash and returns the signature.
2466 """
2467 signed_data = bytearray()
2468 signed_data.extend(struct.pack('<I', 1)) # Format Version
2469 signed_data.extend(
2470 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2471 hasher = hashlib.sha256()
2472 hasher.update(subject)
2473 signed_data.extend(hasher.digest())
2474 usage = 'com.google.android.things.vboot'
2475 if is_intermediate_authority:
2476 usage += '.ca'
2477 hasher = hashlib.sha256()
2478 hasher.update(usage)
2479 signed_data.extend(hasher.digest())
2480 if not subject_key_version:
2481 subject_key_version = int(time.time())
2482 signed_data.extend(struct.pack('<Q', subject_key_version))
2483 signature = bytearray()
2484 if authority_key_path:
2485 padding_and_hash = bytearray()
2486 algorithm_name = None
2487 hasher = None
2488 if is_intermediate_authority:
2489 hasher = hashlib.sha512()
2490 algorithm_name = 'SHA512_RSA4096'
2491 else:
2492 hasher = hashlib.sha256()
2493 algorithm_name = 'SHA256_RSA2048'
2494 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2495 hasher.update(signed_data)
2496 padding_and_hash.extend(hasher.digest())
2497 signature.extend(raw_sign(signing_helper, algorithm_name,
2498 authority_key_path, padding_and_hash))
2499 output.write(signed_data)
2500 output.write(signature)
2501
2502 def make_atx_permanent_attributes(self, output, root_authority_key,
2503 product_id):
2504 """Implements the 'make_atx_permanent_attributes' command.
2505
2506 Android Things permanent attributes are designed to be permanent for a
2507 particular product and a hash of these attributes should be fused into
2508 hardware to enforce this.
2509
2510 Arguments:
2511 output: Attributes will be written to this file on success.
2512 root_authority_key: A PEM or DER public key for the root authority.
2513 product_id: A 16-byte Product ID.
2514
2515 Raises:
2516 AvbError: If an argument is incorrect.
2517 """
2518 if len(product_id) != 16:
2519 raise AvbError('Invalid Product ID length.')
2520 output.write(struct.pack('<I', 1)) # Format Version
2521 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2522 output.write(product_id)
2523
2524 def make_atx_metadata(self, output, intermediate_key_certificate,
2525 product_key_certificate, google_key_version):
2526 """Implements the 'make_atx_metadata' command.
2527
2528 Android Things metadata are included in vbmeta images to facilitate
2529 verification. The output of this command can be used as the
2530 public_key_metadata argument to other commands.
2531
2532 Arguments:
2533 output: Metadata will be written to this file on success.
2534 intermediate_key_certificate: A certificate file as output by
2535 make_atx_certificate with
2536 is_intermediate_authority set to true.
2537 product_key_certificate: A certificate file as output by
2538 make_atx_certificate with
2539 is_intermediate_authority set to false.
2540 google_key_version: The version of the Google Signing Key used in the
2541 associated vbmeta image.
2542
2543 Raises:
2544 AvbError: If an argument is incorrect.
2545 """
2546 if len(intermediate_key_certificate) != 1108:
2547 raise AvbError('Invalid intermediate key certificate length.')
2548 if len(product_key_certificate) != 852:
2549 raise AvbError('Invalid product key certificate length.')
2550 output.write(struct.pack('<I', 1)) # Format Version
2551 output.write(intermediate_key_certificate)
2552 output.write(product_key_certificate)
2553 output.write(struct.pack('<Q', google_key_version))
2554
David Zeuthen21e95262016-07-27 17:58:40 -04002555
2556def calc_hash_level_offsets(image_size, block_size, digest_size):
2557 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2558
2559 Arguments:
2560 image_size: The size of the image to calculate a Merkle-tree for.
2561 block_size: The block size, e.g. 4096.
2562 digest_size: The size of each hash, e.g. 32 for SHA-256.
2563
2564 Returns:
2565 A tuple where the first argument is an array of offsets and the
2566 second is size of the tree, in bytes.
2567 """
2568 level_offsets = []
2569 level_sizes = []
2570 tree_size = 0
2571
2572 num_levels = 0
2573 size = image_size
2574 while size > block_size:
2575 num_blocks = (size + block_size - 1) / block_size
2576 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2577
2578 level_sizes.append(level_size)
2579 tree_size += level_size
2580 num_levels += 1
2581
2582 size = level_size
2583
2584 for n in range(0, num_levels):
2585 offset = 0
2586 for m in range(n + 1, num_levels):
2587 offset += level_sizes[m]
2588 level_offsets.append(offset)
2589
David Zeuthena4fee8b2016-08-22 15:20:43 -04002590 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002591
2592
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002593# See system/extras/libfec/include/fec/io.h for these definitions.
2594FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2595FEC_MAGIC = 0xfecfecfe
2596
2597
2598def calc_fec_data_size(image_size, num_roots):
2599 """Calculates how much space FEC data will take.
2600
2601 Args:
2602 image_size: The size of the image.
2603 num_roots: Number of roots.
2604
2605 Returns:
2606 The number of bytes needed for FEC for an image of the given size
2607 and with the requested number of FEC roots.
2608
2609 Raises:
2610 ValueError: If output from the 'fec' tool is invalid.
2611
2612 """
2613 p = subprocess.Popen(
2614 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2615 stdout=subprocess.PIPE,
2616 stderr=subprocess.PIPE)
2617 (pout, perr) = p.communicate()
2618 retcode = p.wait()
2619 if retcode != 0:
2620 raise ValueError('Error invoking fec: {}'.format(perr))
2621 return int(pout)
2622
2623
2624def generate_fec_data(image_filename, num_roots):
2625 """Generate FEC codes for an image.
2626
2627 Args:
2628 image_filename: The filename of the image.
2629 num_roots: Number of roots.
2630
2631 Returns:
2632 The FEC data blob.
2633
2634 Raises:
2635 ValueError: If output from the 'fec' tool is invalid.
2636 """
2637 fec_tmpfile = tempfile.NamedTemporaryFile()
2638 subprocess.check_call(
2639 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2640 fec_tmpfile.name],
2641 stderr=open(os.devnull))
2642 fec_data = fec_tmpfile.read()
2643 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2644 footer_data = fec_data[-footer_size:]
2645 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2646 footer_data)
2647 if magic != FEC_MAGIC:
2648 raise ValueError('Unexpected magic in FEC footer')
2649 return fec_data[0:fec_size]
2650
2651
David Zeuthen21e95262016-07-27 17:58:40 -04002652def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002653 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002654 """Generates a Merkle-tree for a file.
2655
2656 Args:
2657 image: The image, as a file.
2658 image_size: The size of the image.
2659 block_size: The block size, e.g. 4096.
2660 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2661 salt: The salt to use.
2662 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002663 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002664 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002665
2666 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002667 A tuple where the first element is the top-level hash and the
2668 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002669 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002670 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002671 hash_src_offset = 0
2672 hash_src_size = image_size
2673 level_num = 0
2674 while hash_src_size > block_size:
2675 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002676 remaining = hash_src_size
2677 while remaining > 0:
2678 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002679 # Only read from the file for the first level - for subsequent
2680 # levels, access the array we're building.
2681 if level_num == 0:
2682 image.seek(hash_src_offset + hash_src_size - remaining)
2683 data = image.read(min(remaining, block_size))
2684 else:
2685 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2686 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002687 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002688
2689 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002690 if len(data) < block_size:
2691 hasher.update('\0' * (block_size - len(data)))
2692 level_output += hasher.digest()
2693 if digest_padding > 0:
2694 level_output += '\0' * digest_padding
2695
2696 padding_needed = (round_to_multiple(
2697 len(level_output), block_size) - len(level_output))
2698 level_output += '\0' * padding_needed
2699
David Zeuthena4fee8b2016-08-22 15:20:43 -04002700 # Copy level-output into resulting tree.
2701 offset = hash_level_offsets[level_num]
2702 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002703
David Zeuthena4fee8b2016-08-22 15:20:43 -04002704 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002705 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002706 level_num += 1
2707
2708 hasher = hashlib.new(name=hash_alg_name, string=salt)
2709 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002710 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002711
2712
2713class AvbTool(object):
2714 """Object for avbtool command-line tool."""
2715
2716 def __init__(self):
2717 """Initializer method."""
2718 self.avb = Avb()
2719
2720 def _add_common_args(self, sub_parser):
2721 """Adds arguments used by several sub-commands.
2722
2723 Arguments:
2724 sub_parser: The parser to add arguments to.
2725 """
2726 sub_parser.add_argument('--algorithm',
2727 help='Algorithm to use (default: NONE)',
2728 metavar='ALGORITHM',
2729 default='NONE')
2730 sub_parser.add_argument('--key',
2731 help='Path to RSA private key file',
2732 metavar='KEY',
2733 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002734 sub_parser.add_argument('--signing_helper',
2735 help='Path to helper used for signing',
2736 metavar='APP',
2737 default=None,
2738 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002739 sub_parser.add_argument('--public_key_metadata',
2740 help='Path to public key metadata file',
2741 metavar='KEY_METADATA',
2742 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002743 sub_parser.add_argument('--rollback_index',
2744 help='Rollback Index',
2745 type=parse_number,
2746 default=0)
2747 sub_parser.add_argument('--prop',
2748 help='Add property',
2749 metavar='KEY:VALUE',
2750 action='append')
2751 sub_parser.add_argument('--prop_from_file',
2752 help='Add property from file',
2753 metavar='KEY:PATH',
2754 action='append')
2755 sub_parser.add_argument('--kernel_cmdline',
2756 help='Add kernel cmdline',
2757 metavar='CMDLINE',
2758 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002759 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2760 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2761 # at some future point.
2762 sub_parser.add_argument('--setup_rootfs_from_kernel',
2763 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002764 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002765 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002766 type=argparse.FileType('rb'))
2767 sub_parser.add_argument('--include_descriptors_from_image',
2768 help='Include descriptors from image',
2769 metavar='IMAGE',
2770 action='append',
2771 type=argparse.FileType('rb'))
2772
2773 def run(self, argv):
2774 """Command-line processor.
2775
2776 Arguments:
2777 argv: Pass sys.argv from main.
2778 """
2779 parser = argparse.ArgumentParser()
2780 subparsers = parser.add_subparsers(title='subcommands')
2781
2782 sub_parser = subparsers.add_parser('version',
2783 help='Prints version of avbtool.')
2784 sub_parser.set_defaults(func=self.version)
2785
2786 sub_parser = subparsers.add_parser('extract_public_key',
2787 help='Extract public key.')
2788 sub_parser.add_argument('--key',
2789 help='Path to RSA private key file',
2790 required=True)
2791 sub_parser.add_argument('--output',
2792 help='Output file name',
2793 type=argparse.FileType('wb'),
2794 required=True)
2795 sub_parser.set_defaults(func=self.extract_public_key)
2796
2797 sub_parser = subparsers.add_parser('make_vbmeta_image',
2798 help='Makes a vbmeta image.')
2799 sub_parser.add_argument('--output',
2800 help='Output file name',
2801 type=argparse.FileType('wb'),
2802 required=True)
2803 self._add_common_args(sub_parser)
2804 sub_parser.add_argument('--chain_partition',
2805 help='Allow signed integrity-data for partition',
2806 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2807 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002808 sub_parser.add_argument('--flags',
2809 help='VBMeta flags',
2810 type=parse_number,
2811 default=0)
David Zeuthen58305522017-01-11 17:42:47 -05002812 sub_parser.add_argument('--set_hashtree_disabled_flag',
2813 help='Set the HASHTREE_DISABLED flag',
2814 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002815 sub_parser.set_defaults(func=self.make_vbmeta_image)
2816
2817 sub_parser = subparsers.add_parser('add_hash_footer',
2818 help='Add hashes and footer to image.')
2819 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002820 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002821 type=argparse.FileType('rab+'))
2822 sub_parser.add_argument('--partition_size',
2823 help='Partition size',
2824 type=parse_number,
2825 required=True)
2826 sub_parser.add_argument('--partition_name',
2827 help='Partition name',
2828 required=True)
2829 sub_parser.add_argument('--hash_algorithm',
2830 help='Hash algorithm to use (default: sha256)',
2831 default='sha256')
2832 sub_parser.add_argument('--salt',
2833 help='Salt in hex (default: /dev/urandom)')
2834 self._add_common_args(sub_parser)
2835 sub_parser.set_defaults(func=self.add_hash_footer)
2836
2837 sub_parser = subparsers.add_parser('add_hashtree_footer',
2838 help='Add hashtree and footer to image.')
2839 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002840 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002841 type=argparse.FileType('rab+'))
2842 sub_parser.add_argument('--partition_size',
2843 help='Partition size',
2844 type=parse_number,
2845 required=True)
2846 sub_parser.add_argument('--partition_name',
2847 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002848 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002849 sub_parser.add_argument('--hash_algorithm',
2850 help='Hash algorithm to use (default: sha1)',
2851 default='sha1')
2852 sub_parser.add_argument('--salt',
2853 help='Salt in hex (default: /dev/urandom)')
2854 sub_parser.add_argument('--block_size',
2855 help='Block size (default: 4096)',
2856 type=parse_number,
2857 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002858 sub_parser.add_argument('--generate_fec',
2859 help='Add forward-error-correction codes',
2860 action='store_true')
2861 sub_parser.add_argument('--fec_num_roots',
2862 help='Number of roots for FEC (default: 2)',
2863 type=parse_number,
2864 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002865 sub_parser.add_argument('--calc_max_image_size',
2866 help=('Don\'t store the hashtree or footer - '
2867 'instead calculate the maximum image size '
2868 'leaving enough room for hashtree '
2869 'and metadata with the given partition '
2870 'size.'),
2871 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002872 self._add_common_args(sub_parser)
2873 sub_parser.set_defaults(func=self.add_hashtree_footer)
2874
2875 sub_parser = subparsers.add_parser('erase_footer',
2876 help='Erase footer from an image.')
2877 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002878 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002879 type=argparse.FileType('rwb+'),
2880 required=True)
2881 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002882 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04002883 action='store_true')
2884 sub_parser.set_defaults(func=self.erase_footer)
2885
2886 sub_parser = subparsers.add_parser(
2887 'info_image',
2888 help='Show information about vbmeta or footer.')
2889 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002890 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002891 type=argparse.FileType('rb'),
2892 required=True)
2893 sub_parser.add_argument('--output',
2894 help='Write info to file',
2895 type=argparse.FileType('wt'),
2896 default=sys.stdout)
2897 sub_parser.set_defaults(func=self.info_image)
2898
David Zeuthen8b6973b2016-09-20 12:39:49 -04002899 sub_parser = subparsers.add_parser('set_ab_metadata',
2900 help='Set A/B metadata.')
2901 sub_parser.add_argument('--misc_image',
2902 help=('The misc image to modify. If the image does '
2903 'not exist, it will be created.'),
2904 type=argparse.FileType('r+b'),
2905 required=True)
2906 sub_parser.add_argument('--slot_data',
2907 help=('Slot data of the form "priority", '
2908 '"tries_remaining", "sucessful_boot" for '
2909 'slot A followed by the same for slot B, '
2910 'separated by colons. The default value '
2911 'is 15:7:0:14:7:0.'),
2912 default='15:7:0:14:7:0')
2913 sub_parser.set_defaults(func=self.set_ab_metadata)
2914
Darren Krahn147b08d2016-12-20 16:38:29 -08002915 sub_parser = subparsers.add_parser(
2916 'make_atx_certificate',
2917 help='Create an Android Things eXtension (ATX) certificate.')
2918 sub_parser.add_argument('--output',
2919 help='Write certificate to file',
2920 type=argparse.FileType('wb'),
2921 default=sys.stdout)
2922 sub_parser.add_argument('--subject',
2923 help=('Path to subject file'),
2924 type=argparse.FileType('rb'),
2925 required=True)
2926 sub_parser.add_argument('--subject_key',
2927 help=('Path to subject RSA public key file'),
2928 type=argparse.FileType('rb'),
2929 required=True)
2930 sub_parser.add_argument('--subject_key_version',
2931 help=('Version of the subject key'),
2932 type=parse_number,
2933 required=False)
2934 sub_parser.add_argument('--subject_is_intermediate_authority',
2935 help=('Generate an intermediate authority '
2936 'certificate'),
2937 action='store_true')
2938 sub_parser.add_argument('--authority_key',
2939 help='Path to authority RSA private key file',
2940 required=False)
2941 sub_parser.add_argument('--signing_helper',
2942 help='Path to helper used for signing',
2943 metavar='APP',
2944 default=None,
2945 required=False)
2946 sub_parser.set_defaults(func=self.make_atx_certificate)
2947
2948 sub_parser = subparsers.add_parser(
2949 'make_atx_permanent_attributes',
2950 help='Create Android Things eXtension (ATX) permanent attributes.')
2951 sub_parser.add_argument('--output',
2952 help='Write attributes to file',
2953 type=argparse.FileType('wb'),
2954 default=sys.stdout)
2955 sub_parser.add_argument('--root_authority_key',
2956 help='Path to authority RSA public key file',
2957 type=argparse.FileType('rb'),
2958 required=True)
2959 sub_parser.add_argument('--product_id',
2960 help=('Path to Product ID file'),
2961 type=argparse.FileType('rb'),
2962 required=True)
2963 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
2964
2965 sub_parser = subparsers.add_parser(
2966 'make_atx_metadata',
2967 help='Create Android Things eXtension (ATX) metadata.')
2968 sub_parser.add_argument('--output',
2969 help='Write metadata to file',
2970 type=argparse.FileType('wb'),
2971 default=sys.stdout)
2972 sub_parser.add_argument('--intermediate_key_certificate',
2973 help='Path to intermediate key certificate file',
2974 type=argparse.FileType('rb'),
2975 required=True)
2976 sub_parser.add_argument('--product_key_certificate',
2977 help='Path to product key certificate file',
2978 type=argparse.FileType('rb'),
2979 required=True)
2980 sub_parser.add_argument('--google_key_version',
2981 help=('Version of the Google signing key'),
2982 type=parse_number,
2983 default=0)
2984 sub_parser.set_defaults(func=self.make_atx_metadata)
2985
David Zeuthen21e95262016-07-27 17:58:40 -04002986 args = parser.parse_args(argv[1:])
2987 try:
2988 args.func(args)
2989 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002990 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002991 sys.exit(1)
2992
2993 def version(self, _):
2994 """Implements the 'version' sub-command."""
2995 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2996
2997 def extract_public_key(self, args):
2998 """Implements the 'extract_public_key' sub-command."""
2999 self.avb.extract_public_key(args.key, args.output)
3000
3001 def make_vbmeta_image(self, args):
3002 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthen58305522017-01-11 17:42:47 -05003003 if args.set_hashtree_disabled_flag:
3004 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
David Zeuthen21e95262016-07-27 17:58:40 -04003005 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003006 args.algorithm, args.key,
3007 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003008 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003009 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003010 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003011 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003012
3013 def add_hash_footer(self, args):
3014 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003015 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003016 args.partition_name, args.hash_algorithm,
3017 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003018 args.public_key_metadata, args.rollback_index,
3019 args.prop, args.prop_from_file,
3020 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003021 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003022 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003023
3024 def add_hashtree_footer(self, args):
3025 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04003026 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3027 args.partition_size,
3028 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003029 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003030 args.hash_algorithm, args.block_size,
3031 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003032 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04003033 args.rollback_index, args.prop,
3034 args.prop_from_file,
3035 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003036 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003037 args.include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003038 args.calc_max_image_size, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003039
3040 def erase_footer(self, args):
3041 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003042 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003043
David Zeuthen8b6973b2016-09-20 12:39:49 -04003044 def set_ab_metadata(self, args):
3045 """Implements the 'set_ab_metadata' sub-command."""
3046 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3047
David Zeuthen21e95262016-07-27 17:58:40 -04003048 def info_image(self, args):
3049 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003050 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003051
Darren Krahn147b08d2016-12-20 16:38:29 -08003052 def make_atx_certificate(self, args):
3053 """Implements the 'make_atx_certificate' sub-command."""
3054 self.avb.make_atx_certificate(args.output, args.authority_key,
3055 args.subject_key.read(),
3056 args.subject_key_version,
3057 args.subject.read(),
3058 args.subject_is_intermediate_authority,
3059 args.signing_helper)
3060
3061 def make_atx_permanent_attributes(self, args):
3062 """Implements the 'make_atx_permanent_attributes' sub-command."""
3063 self.avb.make_atx_permanent_attributes(args.output,
3064 args.root_authority_key.read(),
3065 args.product_id.read())
3066
3067 def make_atx_metadata(self, args):
3068 """Implements the 'make_atx_metadata' sub-command."""
3069 self.avb.make_atx_metadata(args.output,
3070 args.intermediate_key_certificate.read(),
3071 args.product_key_certificate.read(),
3072 args.google_key_version)
3073
David Zeuthen21e95262016-07-27 17:58:40 -04003074
3075if __name__ == '__main__':
3076 tool = AvbTool()
3077 tool.run(sys.argv)