blob: 78b87c177e28c77f98f41b6725aeab491b6df591 [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,
David Zeuthend247fcb2017-02-16 12:09:27 -05002104 include_descriptors_from_image, signing_helper,
2105 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002106 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002107
2108 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002109 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002110 partition_size: Size of partition.
2111 partition_name: Name of partition (without A/B suffix).
2112 hash_algorithm: Hash algorithm to use.
2113 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2114 algorithm_name: Name of algorithm to use.
2115 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002116 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002117 rollback_index: Rollback index.
2118 props: Properties to insert (List of strings of the form 'key:value').
2119 props_from_file: Properties to insert (List of strings 'key:<path>').
2120 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002121 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002122 dm-verity kernel cmdline from.
2123 include_descriptors_from_image: List of file objects for which
2124 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002125 signing_helper: Program which signs a hash and return signature.
David Zeuthend247fcb2017-02-16 12:09:27 -05002126 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2127 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002128
2129 Raises:
2130 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002131 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002132 image = ImageHandler(image_filename)
2133
2134 if partition_size % image.block_size != 0:
2135 raise AvbError('Partition size of {} is not a multiple of the image '
2136 'block size {}.'.format(partition_size,
2137 image.block_size))
2138
David Zeuthen21e95262016-07-27 17:58:40 -04002139 # If there's already a footer, truncate the image to its original
2140 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2141 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002142 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002143 try:
2144 footer = AvbFooter(image.read(AvbFooter.SIZE))
2145 # Existing footer found. Just truncate.
2146 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002147 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002148 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002149 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002150
2151 # If anything goes wrong from here-on, restore the image back to
2152 # its original size.
2153 try:
David Zeuthen09692692016-09-30 16:16:40 -04002154 # First, calculate the maximum image size such that an image
2155 # this size + metadata (footer + vbmeta struct) fits in
2156 # |partition_size|.
2157 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2158 max_image_size = partition_size - max_metadata_size
2159
2160 # If image size exceeds the maximum image size, fail.
2161 if image.image_size > max_image_size:
2162 raise AvbError('Image size of {} exceeds maximum image '
2163 'size of {} in order to fit in a partition '
2164 'size of {}.'.format(image.image_size, max_image_size,
2165 partition_size))
2166
David Zeuthen21e95262016-07-27 17:58:40 -04002167 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2168 if salt:
2169 salt = salt.decode('hex')
2170 else:
2171 if salt is None:
2172 # If salt is not explicitly specified, choose a hash
2173 # that's the same size as the hash size.
2174 hash_size = digest_size
2175 salt = open('/dev/urandom').read(hash_size)
2176 else:
2177 salt = ''
2178
2179 hasher = hashlib.new(name=hash_algorithm, string=salt)
2180 # TODO(zeuthen): might want to read this in chunks to avoid
2181 # memory pressure, then again, this is only supposed to be used
2182 # on kernel/initramfs partitions. Possible optimization.
2183 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002184 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002185 digest = hasher.digest()
2186
2187 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002188 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002189 h_desc.hash_algorithm = hash_algorithm
2190 h_desc.partition_name = partition_name
2191 h_desc.salt = salt
2192 h_desc.digest = digest
2193
David Zeuthenfd41eb92016-11-17 12:24:47 -05002194 # Flags are only allowed on top-level vbmeta struct.
2195 flags = 0
2196
David Zeuthen21e95262016-07-27 17:58:40 -04002197 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002198 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002199 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002200 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002201 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002202 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002203
David Zeuthena4fee8b2016-08-22 15:20:43 -04002204 # If the image isn't sparse, its size might not be a multiple of
2205 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002206 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002207 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002208 padding_needed = image.block_size - (image.image_size%image.block_size)
2209 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002210
David Zeuthena4fee8b2016-08-22 15:20:43 -04002211 # The append_raw() method requires content with size being a
2212 # multiple of |block_size| so add padding as needed. Also record
2213 # where this is written to since we'll need to put that in the
2214 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002215 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002216 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2217 len(vbmeta_blob))
2218 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002219
David Zeuthend247fcb2017-02-16 12:09:27 -05002220 # Write vbmeta blob, if requested.
2221 if output_vbmeta_image:
2222 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002223
David Zeuthend247fcb2017-02-16 12:09:27 -05002224 # Append vbmeta blob and footer, unless requested not to.
2225 if not do_not_append_vbmeta_image:
2226 image.append_raw(vbmeta_blob_with_padding)
2227 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2228
2229 # Now insert a DONT_CARE chunk with enough bytes such that the
2230 # final Footer block is at the end of partition_size..
2231 image.append_dont_care(partition_size - vbmeta_end_offset -
2232 1*image.block_size)
2233
2234 # Generate the Footer that tells where the VBMeta footer
2235 # is. Also put enough padding in the front of the footer since
2236 # we'll write out an entire block.
2237 footer = AvbFooter()
2238 footer.original_image_size = original_image_size
2239 footer.vbmeta_offset = vbmeta_offset
2240 footer.vbmeta_size = len(vbmeta_blob)
2241 footer_blob = footer.encode()
2242 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2243 footer_blob)
2244 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002245
David Zeuthen21e95262016-07-27 17:58:40 -04002246 except:
2247 # Truncate back to original size, then re-raise
2248 image.truncate(original_image_size)
2249 raise
2250
David Zeuthena4fee8b2016-08-22 15:20:43 -04002251 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002252 generate_fec, fec_num_roots, hash_algorithm,
2253 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002254 public_key_metadata_path, rollback_index,
2255 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002256 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002257 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002258 calc_max_image_size, signing_helper,
2259 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002260 """Implements the 'add_hashtree_footer' command.
2261
2262 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2263 more information about dm-verity and these hashes.
2264
2265 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002266 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002267 partition_size: Size of partition.
2268 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002269 generate_fec: If True, generate FEC codes.
2270 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002271 hash_algorithm: Hash algorithm to use.
2272 block_size: Block size to use.
2273 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2274 algorithm_name: Name of algorithm to use.
2275 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002276 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002277 rollback_index: Rollback index.
2278 props: Properties to insert (List of strings of the form 'key:value').
2279 props_from_file: Properties to insert (List of strings 'key:<path>').
2280 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002281 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002282 dm-verity kernel cmdline from.
2283 include_descriptors_from_image: List of file objects for which
2284 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002285 calc_max_image_size: Don't store the hashtree or footer - instead
2286 calculate the maximum image size leaving enough room for hashtree
2287 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002288 signing_helper: Program which signs a hash and return signature.
David Zeuthend247fcb2017-02-16 12:09:27 -05002289 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2290 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002291
2292 Raises:
2293 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002294 """
David Zeuthen09692692016-09-30 16:16:40 -04002295 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2296 digest_padding = round_to_pow2(digest_size) - digest_size
2297
2298 # First, calculate the maximum image size such that an image
2299 # this size + the hashtree + metadata (footer + vbmeta struct)
2300 # fits in |partition_size|. We use very conservative figures for
2301 # metadata.
2302 (_, max_tree_size) = calc_hash_level_offsets(
2303 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002304 max_fec_size = 0
2305 if generate_fec:
2306 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2307 max_metadata_size = (max_fec_size + max_tree_size +
2308 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002309 self.MAX_FOOTER_SIZE)
2310 max_image_size = partition_size - max_metadata_size
2311
2312 # If we're asked to only calculate the maximum image size, we're done.
2313 if calc_max_image_size:
2314 print '{}'.format(max_image_size)
2315 return
2316
David Zeuthena4fee8b2016-08-22 15:20:43 -04002317 image = ImageHandler(image_filename)
2318
2319 if partition_size % image.block_size != 0:
2320 raise AvbError('Partition size of {} is not a multiple of the image '
2321 'block size {}.'.format(partition_size,
2322 image.block_size))
2323
David Zeuthen21e95262016-07-27 17:58:40 -04002324 # If there's already a footer, truncate the image to its original
2325 # size. This way 'avbtool add_hashtree_footer' is idempotent
2326 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002327 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002328 try:
2329 footer = AvbFooter(image.read(AvbFooter.SIZE))
2330 # Existing footer found. Just truncate.
2331 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002332 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002333 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002334 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002335
2336 # If anything goes wrong from here-on, restore the image back to
2337 # its original size.
2338 try:
2339 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002340 rounded_image_size = round_to_multiple(image.image_size, block_size)
2341 if rounded_image_size > image.image_size:
2342 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002343
David Zeuthen09692692016-09-30 16:16:40 -04002344 # If image size exceeds the maximum image size, fail.
2345 if image.image_size > max_image_size:
2346 raise AvbError('Image size of {} exceeds maximum image '
2347 'size of {} in order to fit in a partition '
2348 'size of {}.'.format(image.image_size, max_image_size,
2349 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002350
2351 if salt:
2352 salt = salt.decode('hex')
2353 else:
2354 if salt is None:
2355 # If salt is not explicitly specified, choose a hash
2356 # that's the same size as the hash size.
2357 hash_size = digest_size
2358 salt = open('/dev/urandom').read(hash_size)
2359 else:
2360 salt = ''
2361
David Zeuthena4fee8b2016-08-22 15:20:43 -04002362 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002363 # offsets in advance.
2364 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002365 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002366
David Zeuthena4fee8b2016-08-22 15:20:43 -04002367 # If the image isn't sparse, its size might not be a multiple of
2368 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002369 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002370 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002371 padding_needed = image.block_size - (image.image_size%image.block_size)
2372 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002373
David Zeuthena4fee8b2016-08-22 15:20:43 -04002374 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002375 tree_offset = image.image_size
2376 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002377 block_size,
2378 hash_algorithm, salt,
2379 digest_padding,
2380 hash_level_offsets,
2381 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002382
2383 # Generate HashtreeDescriptor with details about the tree we
2384 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002385 ht_desc = AvbHashtreeDescriptor()
2386 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002387 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002388 ht_desc.tree_offset = tree_offset
2389 ht_desc.tree_size = tree_size
2390 ht_desc.data_block_size = block_size
2391 ht_desc.hash_block_size = block_size
2392 ht_desc.hash_algorithm = hash_algorithm
2393 ht_desc.partition_name = partition_name
2394 ht_desc.salt = salt
2395 ht_desc.root_digest = root_digest
2396
David Zeuthen09692692016-09-30 16:16:40 -04002397 # Write the hash tree
2398 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2399 len(hash_tree))
2400 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2401 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002402 len_hashtree_and_fec = len(hash_tree_with_padding)
2403
2404 # Generate FEC codes, if requested.
2405 if generate_fec:
2406 fec_data = generate_fec_data(image_filename, fec_num_roots)
2407 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2408 len(fec_data))
2409 fec_data_with_padding = fec_data + '\0'*padding_needed
2410 fec_offset = image.image_size
2411 image.append_raw(fec_data_with_padding)
2412 len_hashtree_and_fec += len(fec_data_with_padding)
2413 # Update the hashtree descriptor.
2414 ht_desc.fec_num_roots = fec_num_roots
2415 ht_desc.fec_offset = fec_offset
2416 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002417
David Zeuthenfd41eb92016-11-17 12:24:47 -05002418 # Flags are only allowed on top-level vbmeta struct.
2419 flags = 0
2420
David Zeuthena4fee8b2016-08-22 15:20:43 -04002421 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002422 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002423 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002424 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002425 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002426 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002427 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002428 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2429 len(vbmeta_blob))
2430 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002431
David Zeuthend247fcb2017-02-16 12:09:27 -05002432 # Write vbmeta blob, if requested.
2433 if output_vbmeta_image:
2434 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002435
David Zeuthend247fcb2017-02-16 12:09:27 -05002436 # Append vbmeta blob and footer, unless requested not to.
2437 if not do_not_append_vbmeta_image:
2438 image.append_raw(vbmeta_blob_with_padding)
2439
2440 # Now insert a DONT_CARE chunk with enough bytes such that the
2441 # final Footer block is at the end of partition_size..
2442 image.append_dont_care(partition_size - image.image_size -
2443 1*image.block_size)
2444
2445 # Generate the Footer that tells where the VBMeta footer
2446 # is. Also put enough padding in the front of the footer since
2447 # we'll write out an entire block.
2448 footer = AvbFooter()
2449 footer.original_image_size = original_image_size
2450 footer.vbmeta_offset = vbmeta_offset
2451 footer.vbmeta_size = len(vbmeta_blob)
2452 footer_blob = footer.encode()
2453 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2454 footer_blob)
2455 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002456
David Zeuthen21e95262016-07-27 17:58:40 -04002457 except:
David Zeuthen09692692016-09-30 16:16:40 -04002458 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002459 image.truncate(original_image_size)
2460 raise
2461
Darren Krahn147b08d2016-12-20 16:38:29 -08002462 def make_atx_certificate(self, output, authority_key_path, subject_key,
2463 subject_key_version, subject,
2464 is_intermediate_authority, signing_helper):
2465 """Implements the 'make_atx_certificate' command.
2466
2467 Android Things certificates are required for Android Things public key
2468 metadata. They chain the vbmeta signing key for a particular product back to
2469 a fused, permanent root key. These certificates are fixed-length and fixed-
2470 format with the explicit goal of not parsing ASN.1 in bootloader code.
2471
2472 Arguments:
2473 output: Certificate will be written to this file on success.
2474 authority_key_path: A PEM file path with the authority private key.
2475 If None, then a certificate will be created without a
2476 signature. The signature can be created out-of-band
2477 and appended.
2478 subject_key: A PEM or DER subject public key.
2479 subject_key_version: A 64-bit version value. If this is None, the number
2480 of seconds since the epoch is used.
2481 subject: A subject identifier. For Product Signing Key certificates this
2482 should be the same Product ID found in the permanent attributes.
2483 is_intermediate_authority: True if the certificate is for an intermediate
2484 authority.
2485 signing_helper: Program which signs a hash and returns the signature.
2486 """
2487 signed_data = bytearray()
2488 signed_data.extend(struct.pack('<I', 1)) # Format Version
2489 signed_data.extend(
2490 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2491 hasher = hashlib.sha256()
2492 hasher.update(subject)
2493 signed_data.extend(hasher.digest())
2494 usage = 'com.google.android.things.vboot'
2495 if is_intermediate_authority:
2496 usage += '.ca'
2497 hasher = hashlib.sha256()
2498 hasher.update(usage)
2499 signed_data.extend(hasher.digest())
2500 if not subject_key_version:
2501 subject_key_version = int(time.time())
2502 signed_data.extend(struct.pack('<Q', subject_key_version))
2503 signature = bytearray()
2504 if authority_key_path:
2505 padding_and_hash = bytearray()
2506 algorithm_name = None
2507 hasher = None
2508 if is_intermediate_authority:
2509 hasher = hashlib.sha512()
2510 algorithm_name = 'SHA512_RSA4096'
2511 else:
2512 hasher = hashlib.sha256()
2513 algorithm_name = 'SHA256_RSA2048'
2514 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2515 hasher.update(signed_data)
2516 padding_and_hash.extend(hasher.digest())
2517 signature.extend(raw_sign(signing_helper, algorithm_name,
2518 authority_key_path, padding_and_hash))
2519 output.write(signed_data)
2520 output.write(signature)
2521
2522 def make_atx_permanent_attributes(self, output, root_authority_key,
2523 product_id):
2524 """Implements the 'make_atx_permanent_attributes' command.
2525
2526 Android Things permanent attributes are designed to be permanent for a
2527 particular product and a hash of these attributes should be fused into
2528 hardware to enforce this.
2529
2530 Arguments:
2531 output: Attributes will be written to this file on success.
2532 root_authority_key: A PEM or DER public key for the root authority.
2533 product_id: A 16-byte Product ID.
2534
2535 Raises:
2536 AvbError: If an argument is incorrect.
2537 """
2538 if len(product_id) != 16:
2539 raise AvbError('Invalid Product ID length.')
2540 output.write(struct.pack('<I', 1)) # Format Version
2541 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2542 output.write(product_id)
2543
2544 def make_atx_metadata(self, output, intermediate_key_certificate,
2545 product_key_certificate, google_key_version):
2546 """Implements the 'make_atx_metadata' command.
2547
2548 Android Things metadata are included in vbmeta images to facilitate
2549 verification. The output of this command can be used as the
2550 public_key_metadata argument to other commands.
2551
2552 Arguments:
2553 output: Metadata will be written to this file on success.
2554 intermediate_key_certificate: A certificate file as output by
2555 make_atx_certificate with
2556 is_intermediate_authority set to true.
2557 product_key_certificate: A certificate file as output by
2558 make_atx_certificate with
2559 is_intermediate_authority set to false.
2560 google_key_version: The version of the Google Signing Key used in the
2561 associated vbmeta image.
2562
2563 Raises:
2564 AvbError: If an argument is incorrect.
2565 """
2566 if len(intermediate_key_certificate) != 1108:
2567 raise AvbError('Invalid intermediate key certificate length.')
2568 if len(product_key_certificate) != 852:
2569 raise AvbError('Invalid product key certificate length.')
2570 output.write(struct.pack('<I', 1)) # Format Version
2571 output.write(intermediate_key_certificate)
2572 output.write(product_key_certificate)
2573 output.write(struct.pack('<Q', google_key_version))
2574
David Zeuthen21e95262016-07-27 17:58:40 -04002575
2576def calc_hash_level_offsets(image_size, block_size, digest_size):
2577 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2578
2579 Arguments:
2580 image_size: The size of the image to calculate a Merkle-tree for.
2581 block_size: The block size, e.g. 4096.
2582 digest_size: The size of each hash, e.g. 32 for SHA-256.
2583
2584 Returns:
2585 A tuple where the first argument is an array of offsets and the
2586 second is size of the tree, in bytes.
2587 """
2588 level_offsets = []
2589 level_sizes = []
2590 tree_size = 0
2591
2592 num_levels = 0
2593 size = image_size
2594 while size > block_size:
2595 num_blocks = (size + block_size - 1) / block_size
2596 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2597
2598 level_sizes.append(level_size)
2599 tree_size += level_size
2600 num_levels += 1
2601
2602 size = level_size
2603
2604 for n in range(0, num_levels):
2605 offset = 0
2606 for m in range(n + 1, num_levels):
2607 offset += level_sizes[m]
2608 level_offsets.append(offset)
2609
David Zeuthena4fee8b2016-08-22 15:20:43 -04002610 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002611
2612
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002613# See system/extras/libfec/include/fec/io.h for these definitions.
2614FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2615FEC_MAGIC = 0xfecfecfe
2616
2617
2618def calc_fec_data_size(image_size, num_roots):
2619 """Calculates how much space FEC data will take.
2620
2621 Args:
2622 image_size: The size of the image.
2623 num_roots: Number of roots.
2624
2625 Returns:
2626 The number of bytes needed for FEC for an image of the given size
2627 and with the requested number of FEC roots.
2628
2629 Raises:
2630 ValueError: If output from the 'fec' tool is invalid.
2631
2632 """
2633 p = subprocess.Popen(
2634 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2635 stdout=subprocess.PIPE,
2636 stderr=subprocess.PIPE)
2637 (pout, perr) = p.communicate()
2638 retcode = p.wait()
2639 if retcode != 0:
2640 raise ValueError('Error invoking fec: {}'.format(perr))
2641 return int(pout)
2642
2643
2644def generate_fec_data(image_filename, num_roots):
2645 """Generate FEC codes for an image.
2646
2647 Args:
2648 image_filename: The filename of the image.
2649 num_roots: Number of roots.
2650
2651 Returns:
2652 The FEC data blob.
2653
2654 Raises:
2655 ValueError: If output from the 'fec' tool is invalid.
2656 """
2657 fec_tmpfile = tempfile.NamedTemporaryFile()
2658 subprocess.check_call(
2659 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2660 fec_tmpfile.name],
2661 stderr=open(os.devnull))
2662 fec_data = fec_tmpfile.read()
2663 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2664 footer_data = fec_data[-footer_size:]
2665 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2666 footer_data)
2667 if magic != FEC_MAGIC:
2668 raise ValueError('Unexpected magic in FEC footer')
2669 return fec_data[0:fec_size]
2670
2671
David Zeuthen21e95262016-07-27 17:58:40 -04002672def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002673 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002674 """Generates a Merkle-tree for a file.
2675
2676 Args:
2677 image: The image, as a file.
2678 image_size: The size of the image.
2679 block_size: The block size, e.g. 4096.
2680 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2681 salt: The salt to use.
2682 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002683 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002684 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002685
2686 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002687 A tuple where the first element is the top-level hash and the
2688 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002689 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002690 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002691 hash_src_offset = 0
2692 hash_src_size = image_size
2693 level_num = 0
2694 while hash_src_size > block_size:
2695 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002696 remaining = hash_src_size
2697 while remaining > 0:
2698 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002699 # Only read from the file for the first level - for subsequent
2700 # levels, access the array we're building.
2701 if level_num == 0:
2702 image.seek(hash_src_offset + hash_src_size - remaining)
2703 data = image.read(min(remaining, block_size))
2704 else:
2705 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2706 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002707 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002708
2709 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002710 if len(data) < block_size:
2711 hasher.update('\0' * (block_size - len(data)))
2712 level_output += hasher.digest()
2713 if digest_padding > 0:
2714 level_output += '\0' * digest_padding
2715
2716 padding_needed = (round_to_multiple(
2717 len(level_output), block_size) - len(level_output))
2718 level_output += '\0' * padding_needed
2719
David Zeuthena4fee8b2016-08-22 15:20:43 -04002720 # Copy level-output into resulting tree.
2721 offset = hash_level_offsets[level_num]
2722 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002723
David Zeuthena4fee8b2016-08-22 15:20:43 -04002724 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002725 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002726 level_num += 1
2727
2728 hasher = hashlib.new(name=hash_alg_name, string=salt)
2729 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002730 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002731
2732
2733class AvbTool(object):
2734 """Object for avbtool command-line tool."""
2735
2736 def __init__(self):
2737 """Initializer method."""
2738 self.avb = Avb()
2739
2740 def _add_common_args(self, sub_parser):
2741 """Adds arguments used by several sub-commands.
2742
2743 Arguments:
2744 sub_parser: The parser to add arguments to.
2745 """
2746 sub_parser.add_argument('--algorithm',
2747 help='Algorithm to use (default: NONE)',
2748 metavar='ALGORITHM',
2749 default='NONE')
2750 sub_parser.add_argument('--key',
2751 help='Path to RSA private key file',
2752 metavar='KEY',
2753 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002754 sub_parser.add_argument('--signing_helper',
2755 help='Path to helper used for signing',
2756 metavar='APP',
2757 default=None,
2758 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002759 sub_parser.add_argument('--public_key_metadata',
2760 help='Path to public key metadata file',
2761 metavar='KEY_METADATA',
2762 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002763 sub_parser.add_argument('--rollback_index',
2764 help='Rollback Index',
2765 type=parse_number,
2766 default=0)
2767 sub_parser.add_argument('--prop',
2768 help='Add property',
2769 metavar='KEY:VALUE',
2770 action='append')
2771 sub_parser.add_argument('--prop_from_file',
2772 help='Add property from file',
2773 metavar='KEY:PATH',
2774 action='append')
2775 sub_parser.add_argument('--kernel_cmdline',
2776 help='Add kernel cmdline',
2777 metavar='CMDLINE',
2778 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002779 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2780 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2781 # at some future point.
2782 sub_parser.add_argument('--setup_rootfs_from_kernel',
2783 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002784 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002785 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002786 type=argparse.FileType('rb'))
2787 sub_parser.add_argument('--include_descriptors_from_image',
2788 help='Include descriptors from image',
2789 metavar='IMAGE',
2790 action='append',
2791 type=argparse.FileType('rb'))
2792
2793 def run(self, argv):
2794 """Command-line processor.
2795
2796 Arguments:
2797 argv: Pass sys.argv from main.
2798 """
2799 parser = argparse.ArgumentParser()
2800 subparsers = parser.add_subparsers(title='subcommands')
2801
2802 sub_parser = subparsers.add_parser('version',
2803 help='Prints version of avbtool.')
2804 sub_parser.set_defaults(func=self.version)
2805
2806 sub_parser = subparsers.add_parser('extract_public_key',
2807 help='Extract public key.')
2808 sub_parser.add_argument('--key',
2809 help='Path to RSA private key file',
2810 required=True)
2811 sub_parser.add_argument('--output',
2812 help='Output file name',
2813 type=argparse.FileType('wb'),
2814 required=True)
2815 sub_parser.set_defaults(func=self.extract_public_key)
2816
2817 sub_parser = subparsers.add_parser('make_vbmeta_image',
2818 help='Makes a vbmeta image.')
2819 sub_parser.add_argument('--output',
2820 help='Output file name',
2821 type=argparse.FileType('wb'),
2822 required=True)
2823 self._add_common_args(sub_parser)
2824 sub_parser.add_argument('--chain_partition',
2825 help='Allow signed integrity-data for partition',
2826 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2827 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002828 sub_parser.add_argument('--flags',
2829 help='VBMeta flags',
2830 type=parse_number,
2831 default=0)
David Zeuthen58305522017-01-11 17:42:47 -05002832 sub_parser.add_argument('--set_hashtree_disabled_flag',
2833 help='Set the HASHTREE_DISABLED flag',
2834 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002835 sub_parser.set_defaults(func=self.make_vbmeta_image)
2836
2837 sub_parser = subparsers.add_parser('add_hash_footer',
2838 help='Add hashes and footer to image.')
2839 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002840 help='Image to add hashes 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',
2848 required=True)
2849 sub_parser.add_argument('--hash_algorithm',
2850 help='Hash algorithm to use (default: sha256)',
2851 default='sha256')
2852 sub_parser.add_argument('--salt',
2853 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05002854 sub_parser.add_argument('--output_vbmeta_image',
2855 help='Also write vbmeta struct to file',
2856 type=argparse.FileType('wb'))
2857 sub_parser.add_argument('--do_not_append_vbmeta_image',
2858 help=('Do not append vbmeta struct or footer '
2859 'to the image'),
2860 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002861 self._add_common_args(sub_parser)
2862 sub_parser.set_defaults(func=self.add_hash_footer)
2863
2864 sub_parser = subparsers.add_parser('add_hashtree_footer',
2865 help='Add hashtree and footer to image.')
2866 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002867 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002868 type=argparse.FileType('rab+'))
2869 sub_parser.add_argument('--partition_size',
2870 help='Partition size',
2871 type=parse_number,
2872 required=True)
2873 sub_parser.add_argument('--partition_name',
2874 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002875 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002876 sub_parser.add_argument('--hash_algorithm',
2877 help='Hash algorithm to use (default: sha1)',
2878 default='sha1')
2879 sub_parser.add_argument('--salt',
2880 help='Salt in hex (default: /dev/urandom)')
2881 sub_parser.add_argument('--block_size',
2882 help='Block size (default: 4096)',
2883 type=parse_number,
2884 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002885 sub_parser.add_argument('--generate_fec',
2886 help='Add forward-error-correction codes',
2887 action='store_true')
2888 sub_parser.add_argument('--fec_num_roots',
2889 help='Number of roots for FEC (default: 2)',
2890 type=parse_number,
2891 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002892 sub_parser.add_argument('--calc_max_image_size',
2893 help=('Don\'t store the hashtree or footer - '
2894 'instead calculate the maximum image size '
2895 'leaving enough room for hashtree '
2896 'and metadata with the given partition '
2897 'size.'),
2898 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05002899 sub_parser.add_argument('--output_vbmeta_image',
2900 help='Also write vbmeta struct to file',
2901 type=argparse.FileType('wb'))
2902 sub_parser.add_argument('--do_not_append_vbmeta_image',
2903 help=('Do not append vbmeta struct or footer '
2904 'to the image'),
2905 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002906 self._add_common_args(sub_parser)
2907 sub_parser.set_defaults(func=self.add_hashtree_footer)
2908
2909 sub_parser = subparsers.add_parser('erase_footer',
2910 help='Erase footer from an image.')
2911 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002912 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002913 type=argparse.FileType('rwb+'),
2914 required=True)
2915 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002916 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04002917 action='store_true')
2918 sub_parser.set_defaults(func=self.erase_footer)
2919
2920 sub_parser = subparsers.add_parser(
2921 'info_image',
2922 help='Show information about vbmeta or footer.')
2923 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002924 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002925 type=argparse.FileType('rb'),
2926 required=True)
2927 sub_parser.add_argument('--output',
2928 help='Write info to file',
2929 type=argparse.FileType('wt'),
2930 default=sys.stdout)
2931 sub_parser.set_defaults(func=self.info_image)
2932
David Zeuthen8b6973b2016-09-20 12:39:49 -04002933 sub_parser = subparsers.add_parser('set_ab_metadata',
2934 help='Set A/B metadata.')
2935 sub_parser.add_argument('--misc_image',
2936 help=('The misc image to modify. If the image does '
2937 'not exist, it will be created.'),
2938 type=argparse.FileType('r+b'),
2939 required=True)
2940 sub_parser.add_argument('--slot_data',
2941 help=('Slot data of the form "priority", '
2942 '"tries_remaining", "sucessful_boot" for '
2943 'slot A followed by the same for slot B, '
2944 'separated by colons. The default value '
2945 'is 15:7:0:14:7:0.'),
2946 default='15:7:0:14:7:0')
2947 sub_parser.set_defaults(func=self.set_ab_metadata)
2948
Darren Krahn147b08d2016-12-20 16:38:29 -08002949 sub_parser = subparsers.add_parser(
2950 'make_atx_certificate',
2951 help='Create an Android Things eXtension (ATX) certificate.')
2952 sub_parser.add_argument('--output',
2953 help='Write certificate to file',
2954 type=argparse.FileType('wb'),
2955 default=sys.stdout)
2956 sub_parser.add_argument('--subject',
2957 help=('Path to subject file'),
2958 type=argparse.FileType('rb'),
2959 required=True)
2960 sub_parser.add_argument('--subject_key',
2961 help=('Path to subject RSA public key file'),
2962 type=argparse.FileType('rb'),
2963 required=True)
2964 sub_parser.add_argument('--subject_key_version',
2965 help=('Version of the subject key'),
2966 type=parse_number,
2967 required=False)
2968 sub_parser.add_argument('--subject_is_intermediate_authority',
2969 help=('Generate an intermediate authority '
2970 'certificate'),
2971 action='store_true')
2972 sub_parser.add_argument('--authority_key',
2973 help='Path to authority RSA private key file',
2974 required=False)
2975 sub_parser.add_argument('--signing_helper',
2976 help='Path to helper used for signing',
2977 metavar='APP',
2978 default=None,
2979 required=False)
2980 sub_parser.set_defaults(func=self.make_atx_certificate)
2981
2982 sub_parser = subparsers.add_parser(
2983 'make_atx_permanent_attributes',
2984 help='Create Android Things eXtension (ATX) permanent attributes.')
2985 sub_parser.add_argument('--output',
2986 help='Write attributes to file',
2987 type=argparse.FileType('wb'),
2988 default=sys.stdout)
2989 sub_parser.add_argument('--root_authority_key',
2990 help='Path to authority RSA public key file',
2991 type=argparse.FileType('rb'),
2992 required=True)
2993 sub_parser.add_argument('--product_id',
2994 help=('Path to Product ID file'),
2995 type=argparse.FileType('rb'),
2996 required=True)
2997 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
2998
2999 sub_parser = subparsers.add_parser(
3000 'make_atx_metadata',
3001 help='Create Android Things eXtension (ATX) metadata.')
3002 sub_parser.add_argument('--output',
3003 help='Write metadata to file',
3004 type=argparse.FileType('wb'),
3005 default=sys.stdout)
3006 sub_parser.add_argument('--intermediate_key_certificate',
3007 help='Path to intermediate key certificate file',
3008 type=argparse.FileType('rb'),
3009 required=True)
3010 sub_parser.add_argument('--product_key_certificate',
3011 help='Path to product key certificate file',
3012 type=argparse.FileType('rb'),
3013 required=True)
3014 sub_parser.add_argument('--google_key_version',
3015 help=('Version of the Google signing key'),
3016 type=parse_number,
3017 default=0)
3018 sub_parser.set_defaults(func=self.make_atx_metadata)
3019
David Zeuthen21e95262016-07-27 17:58:40 -04003020 args = parser.parse_args(argv[1:])
3021 try:
3022 args.func(args)
3023 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003024 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003025 sys.exit(1)
3026
3027 def version(self, _):
3028 """Implements the 'version' sub-command."""
3029 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
3030
3031 def extract_public_key(self, args):
3032 """Implements the 'extract_public_key' sub-command."""
3033 self.avb.extract_public_key(args.key, args.output)
3034
3035 def make_vbmeta_image(self, args):
3036 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthen58305522017-01-11 17:42:47 -05003037 if args.set_hashtree_disabled_flag:
3038 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
David Zeuthen21e95262016-07-27 17:58:40 -04003039 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003040 args.algorithm, args.key,
3041 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003042 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003043 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003044 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003045 args.include_descriptors_from_image,
3046 args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003047
3048 def add_hash_footer(self, args):
3049 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003050 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003051 args.partition_name, args.hash_algorithm,
3052 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003053 args.public_key_metadata, args.rollback_index,
3054 args.prop, args.prop_from_file,
3055 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003056 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003057 args.include_descriptors_from_image,
3058 args.signing_helper,
3059 args.output_vbmeta_image,
3060 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003061
3062 def add_hashtree_footer(self, args):
3063 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04003064 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3065 args.partition_size,
3066 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003067 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003068 args.hash_algorithm, args.block_size,
3069 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003070 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04003071 args.rollback_index, args.prop,
3072 args.prop_from_file,
3073 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003074 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003075 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003076 args.calc_max_image_size, args.signing_helper,
3077 args.output_vbmeta_image,
3078 args.do_not_append_vbmeta_image)
3079
David Zeuthen21e95262016-07-27 17:58:40 -04003080
3081 def erase_footer(self, args):
3082 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003083 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003084
David Zeuthen8b6973b2016-09-20 12:39:49 -04003085 def set_ab_metadata(self, args):
3086 """Implements the 'set_ab_metadata' sub-command."""
3087 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3088
David Zeuthen21e95262016-07-27 17:58:40 -04003089 def info_image(self, args):
3090 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003091 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003092
Darren Krahn147b08d2016-12-20 16:38:29 -08003093 def make_atx_certificate(self, args):
3094 """Implements the 'make_atx_certificate' sub-command."""
3095 self.avb.make_atx_certificate(args.output, args.authority_key,
3096 args.subject_key.read(),
3097 args.subject_key_version,
3098 args.subject.read(),
3099 args.subject_is_intermediate_authority,
3100 args.signing_helper)
3101
3102 def make_atx_permanent_attributes(self, args):
3103 """Implements the 'make_atx_permanent_attributes' sub-command."""
3104 self.avb.make_atx_permanent_attributes(args.output,
3105 args.root_authority_key.read(),
3106 args.product_id.read())
3107
3108 def make_atx_metadata(self, args):
3109 """Implements the 'make_atx_metadata' sub-command."""
3110 self.avb.make_atx_metadata(args.output,
3111 args.intermediate_key_certificate.read(),
3112 args.product_key_certificate.read(),
3113 args.google_key_version)
3114
David Zeuthen21e95262016-07-27 17:58:40 -04003115
3116if __name__ == '__main__':
3117 tool = AvbTool()
3118 tool.run(sys.argv)