blob: 3da02797a2488b2f2f8bb8b42f76237a2d4679aa [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 Zeuthen21e95262016-07-27 17:58:40 -04001625 keep_hashtree: If True, keep the hashtree around.
1626
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
1643 # descriptor to figure out the location and size of the hashtree.
1644 for desc in descriptors:
1645 if isinstance(desc, AvbHashtreeDescriptor):
1646 # The hashtree is always just following the main data so the
1647 # new size is easily derived.
1648 new_image_size = desc.tree_offset + desc.tree_size
1649 break
1650 if not new_image_size:
1651 raise AvbError('Requested to keep hashtree but no hashtree '
1652 'descriptor was found.')
1653
1654 # And cut...
1655 image.truncate(new_image_size)
1656
David Zeuthen8b6973b2016-09-20 12:39:49 -04001657 def set_ab_metadata(self, misc_image, slot_data):
1658 """Implements the 'set_ab_metadata' command.
1659
1660 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1661 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1662
1663 Arguments:
1664 misc_image: The misc image to write to.
1665 slot_data: Slot data as a string
1666
1667 Raises:
1668 AvbError: If slot data is malformed.
1669 """
1670 tokens = slot_data.split(':')
1671 if len(tokens) != 6:
1672 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1673 a_priority = int(tokens[0])
1674 a_tries_remaining = int(tokens[1])
1675 a_success = True if int(tokens[2]) != 0 else False
1676 b_priority = int(tokens[3])
1677 b_tries_remaining = int(tokens[4])
1678 b_success = True if int(tokens[5]) != 0 else False
1679
1680 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1681 self.AB_MAGIC,
1682 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1683 a_priority, a_tries_remaining, a_success,
1684 b_priority, b_tries_remaining, b_success)
1685 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1686 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1687 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1688 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1689 misc_image.write(ab_data)
1690
David Zeuthena4fee8b2016-08-22 15:20:43 -04001691 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001692 """Implements the 'info_image' command.
1693
1694 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001695 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001696 output: Output file to write human-readable information to (file object).
1697 """
1698
David Zeuthena4fee8b2016-08-22 15:20:43 -04001699 image = ImageHandler(image_filename)
1700
David Zeuthen21e95262016-07-27 17:58:40 -04001701 o = output
1702
1703 (footer, header, descriptors, image_size) = self._parse_image(image)
1704
1705 if footer:
1706 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1707 footer.version_minor))
1708 o.write('Image size: {} bytes\n'.format(image_size))
1709 o.write('Original image size: {} bytes\n'.format(
1710 footer.original_image_size))
1711 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1712 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1713 o.write('--\n')
1714
1715 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1716
David Zeuthena4fee8b2016-08-22 15:20:43 -04001717 o.write('VBMeta image version: {}.{}{}\n'.format(
1718 header.header_version_major, header.header_version_minor,
1719 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001720 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1721 o.write('Authentication Block: {} bytes\n'.format(
1722 header.authentication_data_block_size))
1723 o.write('Auxiliary Block: {} bytes\n'.format(
1724 header.auxiliary_data_block_size))
1725 o.write('Algorithm: {}\n'.format(alg_name))
1726 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001727 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001728
1729 # Print descriptors.
1730 num_printed = 0
1731 o.write('Descriptors:\n')
1732 for desc in descriptors:
1733 desc.print_desc(o)
1734 num_printed += 1
1735 if num_printed == 0:
1736 o.write(' (none)\n')
1737
1738 def _parse_image(self, image):
1739 """Gets information about an image.
1740
1741 The image can either be a vbmeta or an image with a footer.
1742
1743 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001744 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001745
1746 Returns:
1747 A tuple where the first argument is a AvbFooter (None if there
1748 is no footer on the image), the second argument is a
1749 AvbVBMetaHeader, the third argument is a list of
1750 AvbDescriptor-derived instances, and the fourth argument is the
1751 size of |image|.
1752 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001753 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001754 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001755 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001756 try:
1757 footer = AvbFooter(image.read(AvbFooter.SIZE))
1758 except (LookupError, struct.error):
1759 # Nope, just seek back to the start.
1760 image.seek(0)
1761
1762 vbmeta_offset = 0
1763 if footer:
1764 vbmeta_offset = footer.vbmeta_offset
1765
1766 image.seek(vbmeta_offset)
1767 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1768
1769 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1770 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1771 desc_start_offset = aux_block_offset + h.descriptors_offset
1772 image.seek(desc_start_offset)
1773 descriptors = parse_descriptors(image.read(h.descriptors_size))
1774
David Zeuthen09692692016-09-30 16:16:40 -04001775 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001776
David Zeuthenfd41eb92016-11-17 12:24:47 -05001777 def _get_cmdline_descriptors_for_dm_verity(self, image):
1778 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001779
1780 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001781 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001782
1783 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001784 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1785 instructions. There is one for when hashtree is not disabled and one for
1786 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001787
1788 Raises:
1789 AvbError: If |image| doesn't have a hashtree descriptor.
1790
1791 """
1792
1793 (_, _, descriptors, _) = self._parse_image(image)
1794
1795 ht = None
1796 for desc in descriptors:
1797 if isinstance(desc, AvbHashtreeDescriptor):
1798 ht = desc
1799 break
1800
1801 if not ht:
1802 raise AvbError('No hashtree descriptor in given image')
1803
1804 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001805 c += '0' # start
1806 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1807 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1808 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1809 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1810 c += ' {}'.format(ht.data_block_size) # data_block
1811 c += ' {}'.format(ht.hash_block_size) # hash_block
1812 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1813 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1814 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1815 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1816 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1817 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001818 c += ' 10' # number of optional args
1819 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001820 c += ' ignore_zero_blocks'
1821 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1822 c += ' fec_roots {}'.format(ht.fec_num_roots)
1823 # Note that fec_blocks is the size that FEC covers, *not* the
1824 # size of the FEC data. Since we use FEC for everything up until
1825 # the FEC data, it's the same as the offset.
1826 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1827 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1828 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001829 c += ' 2' # number of optional args
1830 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001831 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001832 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001833
David Zeuthenfd41eb92016-11-17 12:24:47 -05001834 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001835 desc = AvbKernelCmdlineDescriptor()
1836 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001837 desc.flags = (
1838 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1839
1840 # The descriptor for when hashtree verification is disabled is a lot
1841 # simpler - we just set the root to the partition.
1842 desc_no_ht = AvbKernelCmdlineDescriptor()
1843 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1844 desc_no_ht.flags = (
1845 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1846
1847 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001848
1849 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001850 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001851 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001852 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001853 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001854 """Implements the 'make_vbmeta_image' command.
1855
1856 Arguments:
1857 output: File to write the image to.
1858 chain_partitions: List of partitions to chain.
1859 algorithm_name: Name of algorithm to use.
1860 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001861 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001862 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001863 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001864 props: Properties to insert (list of strings of the form 'key:value').
1865 props_from_file: Properties to insert (list of strings 'key:<path>').
1866 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001867 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001868 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001869 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001870
1871 Raises:
1872 AvbError: If a chained partition is malformed.
1873 """
1874
1875 descriptors = []
1876
1877 # Insert chained partition descriptors.
1878 if chain_partitions:
1879 for cp in chain_partitions:
1880 cp_tokens = cp.split(':')
1881 if len(cp_tokens) != 3:
1882 raise AvbError('Malformed chained partition "{}".'.format(cp))
1883 desc = AvbChainPartitionDescriptor()
1884 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001885 desc.rollback_index_location = int(cp_tokens[1])
1886 if desc.rollback_index_location < 1:
1887 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001888 file_path = cp_tokens[2]
1889 desc.public_key = open(file_path, 'rb').read()
1890 descriptors.append(desc)
1891
1892 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001893 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001894 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001895 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001896 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04001897
1898 # Write entire vbmeta blob (header, authentication, auxiliary).
1899 output.seek(0)
1900 output.write(vbmeta_blob)
1901
David Zeuthen18666ab2016-11-15 11:18:05 -05001902 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1903 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001904 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001905 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001906 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001907 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001908 """Generates a VBMeta blob.
1909
1910 This blob contains the header (struct AvbVBMetaHeader), the
1911 authentication data block (which contains the hash and signature
1912 for the header and auxiliary block), and the auxiliary block
1913 (which contains descriptors, the public key used, and other data).
1914
1915 The |key| parameter can |None| only if the |algorithm_name| is
1916 'NONE'.
1917
1918 Arguments:
1919 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1920 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001921 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001922 descriptors: A list of descriptors to insert or None.
1923 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001924 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001925 props: Properties to insert (List of strings of the form 'key:value').
1926 props_from_file: Properties to insert (List of strings 'key:<path>').
1927 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001928 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001929 dm-verity kernel cmdline from.
1930 include_descriptors_from_image: List of file objects for which
1931 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001932 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001933
1934 Returns:
1935 A bytearray() with the VBMeta blob.
1936
1937 Raises:
1938 Exception: If the |algorithm_name| is not found, if no key has
1939 been given and the given algorithm requires one, or the key is
1940 of the wrong size.
1941
1942 """
1943 try:
1944 alg = ALGORITHMS[algorithm_name]
1945 except KeyError:
1946 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1947
1948 # Descriptors.
1949 encoded_descriptors = bytearray()
1950 if descriptors:
1951 for desc in descriptors:
1952 encoded_descriptors.extend(desc.encode())
1953
1954 # Add properties.
1955 if props:
1956 for prop in props:
1957 idx = prop.find(':')
1958 if idx == -1:
1959 raise AvbError('Malformed property "{}".'.format(prop))
1960 desc = AvbPropertyDescriptor()
1961 desc.key = prop[0:idx]
1962 desc.value = prop[(idx + 1):]
1963 encoded_descriptors.extend(desc.encode())
1964 if props_from_file:
1965 for prop in props_from_file:
1966 idx = prop.find(':')
1967 if idx == -1:
1968 raise AvbError('Malformed property "{}".'.format(prop))
1969 desc = AvbPropertyDescriptor()
1970 desc.key = prop[0:idx]
1971 desc.value = prop[(idx + 1):]
1972 file_path = prop[(idx + 1):]
1973 desc.value = open(file_path, 'rb').read()
1974 encoded_descriptors.extend(desc.encode())
1975
1976 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001977 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001978 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001979 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001980 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1981 encoded_descriptors.extend(cmdline_desc[0].encode())
1982 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001983
1984 # Add kernel command-lines.
1985 if kernel_cmdlines:
1986 for i in kernel_cmdlines:
1987 desc = AvbKernelCmdlineDescriptor()
1988 desc.kernel_cmdline = i
1989 encoded_descriptors.extend(desc.encode())
1990
1991 # Add descriptors from other images.
1992 if include_descriptors_from_image:
1993 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001994 image_handler = ImageHandler(image.name)
1995 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001996 for desc in image_descriptors:
1997 encoded_descriptors.extend(desc.encode())
1998
David Zeuthen18666ab2016-11-15 11:18:05 -05001999 # Load public key metadata blob, if requested.
2000 pkmd_blob = []
2001 if public_key_metadata_path:
2002 with open(public_key_metadata_path) as f:
2003 pkmd_blob = f.read()
2004
David Zeuthen21e95262016-07-27 17:58:40 -04002005 key = None
2006 encoded_key = bytearray()
2007 if alg.public_key_num_bytes > 0:
2008 if not key_path:
2009 raise AvbError('Key is required for algorithm {}'.format(
2010 algorithm_name))
2011 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2012 encoded_key = encode_rsa_key(key)
2013 if len(encoded_key) != alg.public_key_num_bytes:
2014 raise AvbError('Key is wrong size for algorithm {}'.format(
2015 algorithm_name))
2016
2017 h = AvbVBMetaHeader()
2018
David Zeuthen18666ab2016-11-15 11:18:05 -05002019 # For the Auxiliary data block, descriptors are stored at offset 0,
2020 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002021 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002022 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002023 h.descriptors_offset = 0
2024 h.descriptors_size = len(encoded_descriptors)
2025 h.public_key_offset = h.descriptors_size
2026 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002027 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2028 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002029
2030 # For the Authentication data block, the hash is first and then
2031 # the signature.
2032 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002033 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002034 h.algorithm_type = alg.algorithm_type
2035 h.hash_offset = 0
2036 h.hash_size = alg.hash_num_bytes
2037 # Signature offset and size - it's stored right after the hash
2038 # (in Authentication data block).
2039 h.signature_offset = alg.hash_num_bytes
2040 h.signature_size = alg.signature_num_bytes
2041
2042 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002043 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002044
2045 # Generate Header data block.
2046 header_data_blob = h.encode()
2047
2048 # Generate Auxiliary data block.
2049 aux_data_blob = bytearray()
2050 aux_data_blob.extend(encoded_descriptors)
2051 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002052 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002053 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2054 aux_data_blob.extend('\0' * padding_bytes)
2055
2056 # Calculate the hash.
2057 binary_hash = bytearray()
2058 binary_signature = bytearray()
2059 if algorithm_name != 'NONE':
2060 if algorithm_name[0:6] == 'SHA256':
2061 ha = hashlib.sha256()
2062 elif algorithm_name[0:6] == 'SHA512':
2063 ha = hashlib.sha512()
2064 else:
2065 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2066 ha.update(header_data_blob)
2067 ha.update(aux_data_blob)
2068 binary_hash.extend(ha.digest())
2069
2070 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002071 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002072 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2073 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002074
2075 # Generate Authentication data block.
2076 auth_data_blob = bytearray()
2077 auth_data_blob.extend(binary_hash)
2078 auth_data_blob.extend(binary_signature)
2079 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2080 auth_data_blob.extend('\0' * padding_bytes)
2081
2082 return header_data_blob + auth_data_blob + aux_data_blob
2083
2084 def extract_public_key(self, key_path, output):
2085 """Implements the 'extract_public_key' command.
2086
2087 Arguments:
2088 key_path: The path to a RSA private key file.
2089 output: The file to write to.
2090 """
2091 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2092 write_rsa_key(output, key)
2093
David Zeuthena4fee8b2016-08-22 15:20:43 -04002094 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002095 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002096 public_key_metadata_path, rollback_index, props,
2097 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002098 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002099 include_descriptors_from_image, signing_helper):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002100 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002101
2102 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002103 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002104 partition_size: Size of partition.
2105 partition_name: Name of partition (without A/B suffix).
2106 hash_algorithm: Hash algorithm to use.
2107 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2108 algorithm_name: Name of algorithm to use.
2109 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002110 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002111 rollback_index: Rollback index.
2112 props: Properties to insert (List of strings of the form 'key:value').
2113 props_from_file: Properties to insert (List of strings 'key:<path>').
2114 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002115 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002116 dm-verity kernel cmdline from.
2117 include_descriptors_from_image: List of file objects for which
2118 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002119 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002120
2121 Raises:
2122 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002123 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002124 image = ImageHandler(image_filename)
2125
2126 if partition_size % image.block_size != 0:
2127 raise AvbError('Partition size of {} is not a multiple of the image '
2128 'block size {}.'.format(partition_size,
2129 image.block_size))
2130
David Zeuthen21e95262016-07-27 17:58:40 -04002131 # If there's already a footer, truncate the image to its original
2132 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2133 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002134 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002135 try:
2136 footer = AvbFooter(image.read(AvbFooter.SIZE))
2137 # Existing footer found. Just truncate.
2138 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002139 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002140 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002141 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002142
2143 # If anything goes wrong from here-on, restore the image back to
2144 # its original size.
2145 try:
David Zeuthen09692692016-09-30 16:16:40 -04002146 # First, calculate the maximum image size such that an image
2147 # this size + metadata (footer + vbmeta struct) fits in
2148 # |partition_size|.
2149 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2150 max_image_size = partition_size - max_metadata_size
2151
2152 # If image size exceeds the maximum image size, fail.
2153 if image.image_size > max_image_size:
2154 raise AvbError('Image size of {} exceeds maximum image '
2155 'size of {} in order to fit in a partition '
2156 'size of {}.'.format(image.image_size, max_image_size,
2157 partition_size))
2158
David Zeuthen21e95262016-07-27 17:58:40 -04002159 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2160 if salt:
2161 salt = salt.decode('hex')
2162 else:
2163 if salt is None:
2164 # If salt is not explicitly specified, choose a hash
2165 # that's the same size as the hash size.
2166 hash_size = digest_size
2167 salt = open('/dev/urandom').read(hash_size)
2168 else:
2169 salt = ''
2170
2171 hasher = hashlib.new(name=hash_algorithm, string=salt)
2172 # TODO(zeuthen): might want to read this in chunks to avoid
2173 # memory pressure, then again, this is only supposed to be used
2174 # on kernel/initramfs partitions. Possible optimization.
2175 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002176 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002177 digest = hasher.digest()
2178
2179 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002180 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002181 h_desc.hash_algorithm = hash_algorithm
2182 h_desc.partition_name = partition_name
2183 h_desc.salt = salt
2184 h_desc.digest = digest
2185
David Zeuthenfd41eb92016-11-17 12:24:47 -05002186 # Flags are only allowed on top-level vbmeta struct.
2187 flags = 0
2188
David Zeuthen21e95262016-07-27 17:58:40 -04002189 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002190 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002191 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002192 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002193 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002194 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002195
David Zeuthena4fee8b2016-08-22 15:20:43 -04002196 # If the image isn't sparse, its size might not be a multiple of
2197 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002198 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002199 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002200 padding_needed = image.block_size - (image.image_size%image.block_size)
2201 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002202
David Zeuthena4fee8b2016-08-22 15:20:43 -04002203 # The append_raw() method requires content with size being a
2204 # multiple of |block_size| so add padding as needed. Also record
2205 # where this is written to since we'll need to put that in the
2206 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002207 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002208 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2209 len(vbmeta_blob))
2210 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2211 image.append_raw(vbmeta_blob_with_padding)
2212 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2213
2214 # Now insert a DONT_CARE chunk with enough bytes such that the
2215 # final Footer block is at the end of partition_size..
2216 image.append_dont_care(partition_size - vbmeta_end_offset -
2217 1*image.block_size)
2218
2219 # Generate the Footer that tells where the VBMeta footer
2220 # is. Also put enough padding in the front of the footer since
2221 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002222 footer = AvbFooter()
2223 footer.original_image_size = original_image_size
2224 footer.vbmeta_offset = vbmeta_offset
2225 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002226 footer_blob = footer.encode()
2227 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2228 footer_blob)
2229 image.append_raw(footer_blob_with_padding)
2230
David Zeuthen21e95262016-07-27 17:58:40 -04002231 except:
2232 # Truncate back to original size, then re-raise
2233 image.truncate(original_image_size)
2234 raise
2235
David Zeuthena4fee8b2016-08-22 15:20:43 -04002236 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002237 generate_fec, fec_num_roots, hash_algorithm,
2238 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002239 public_key_metadata_path, rollback_index,
2240 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002241 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002242 include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002243 calc_max_image_size, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04002244 """Implements the 'add_hashtree_footer' command.
2245
2246 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2247 more information about dm-verity and these hashes.
2248
2249 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002250 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002251 partition_size: Size of partition.
2252 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002253 generate_fec: If True, generate FEC codes.
2254 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002255 hash_algorithm: Hash algorithm to use.
2256 block_size: Block size to use.
2257 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2258 algorithm_name: Name of algorithm to use.
2259 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002260 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002261 rollback_index: Rollback index.
2262 props: Properties to insert (List of strings of the form 'key:value').
2263 props_from_file: Properties to insert (List of strings 'key:<path>').
2264 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002265 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002266 dm-verity kernel cmdline from.
2267 include_descriptors_from_image: List of file objects for which
2268 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002269 calc_max_image_size: Don't store the hashtree or footer - instead
2270 calculate the maximum image size leaving enough room for hashtree
2271 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002272 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002273
2274 Raises:
2275 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002276 """
David Zeuthen09692692016-09-30 16:16:40 -04002277 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2278 digest_padding = round_to_pow2(digest_size) - digest_size
2279
2280 # First, calculate the maximum image size such that an image
2281 # this size + the hashtree + metadata (footer + vbmeta struct)
2282 # fits in |partition_size|. We use very conservative figures for
2283 # metadata.
2284 (_, max_tree_size) = calc_hash_level_offsets(
2285 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002286 max_fec_size = 0
2287 if generate_fec:
2288 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2289 max_metadata_size = (max_fec_size + max_tree_size +
2290 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002291 self.MAX_FOOTER_SIZE)
2292 max_image_size = partition_size - max_metadata_size
2293
2294 # If we're asked to only calculate the maximum image size, we're done.
2295 if calc_max_image_size:
2296 print '{}'.format(max_image_size)
2297 return
2298
David Zeuthena4fee8b2016-08-22 15:20:43 -04002299 image = ImageHandler(image_filename)
2300
2301 if partition_size % image.block_size != 0:
2302 raise AvbError('Partition size of {} is not a multiple of the image '
2303 'block size {}.'.format(partition_size,
2304 image.block_size))
2305
David Zeuthen21e95262016-07-27 17:58:40 -04002306 # If there's already a footer, truncate the image to its original
2307 # size. This way 'avbtool add_hashtree_footer' is idempotent
2308 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002309 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002310 try:
2311 footer = AvbFooter(image.read(AvbFooter.SIZE))
2312 # Existing footer found. Just truncate.
2313 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002314 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002315 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002316 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002317
2318 # If anything goes wrong from here-on, restore the image back to
2319 # its original size.
2320 try:
2321 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002322 rounded_image_size = round_to_multiple(image.image_size, block_size)
2323 if rounded_image_size > image.image_size:
2324 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002325
David Zeuthen09692692016-09-30 16:16:40 -04002326 # If image size exceeds the maximum image size, fail.
2327 if image.image_size > max_image_size:
2328 raise AvbError('Image size of {} exceeds maximum image '
2329 'size of {} in order to fit in a partition '
2330 'size of {}.'.format(image.image_size, max_image_size,
2331 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002332
2333 if salt:
2334 salt = salt.decode('hex')
2335 else:
2336 if salt is None:
2337 # If salt is not explicitly specified, choose a hash
2338 # that's the same size as the hash size.
2339 hash_size = digest_size
2340 salt = open('/dev/urandom').read(hash_size)
2341 else:
2342 salt = ''
2343
David Zeuthena4fee8b2016-08-22 15:20:43 -04002344 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002345 # offsets in advance.
2346 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002347 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002348
David Zeuthena4fee8b2016-08-22 15:20:43 -04002349 # If the image isn't sparse, its size might not be a multiple of
2350 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002351 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002352 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002353 padding_needed = image.block_size - (image.image_size%image.block_size)
2354 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002355
David Zeuthena4fee8b2016-08-22 15:20:43 -04002356 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002357 tree_offset = image.image_size
2358 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002359 block_size,
2360 hash_algorithm, salt,
2361 digest_padding,
2362 hash_level_offsets,
2363 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002364
2365 # Generate HashtreeDescriptor with details about the tree we
2366 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002367 ht_desc = AvbHashtreeDescriptor()
2368 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002369 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002370 ht_desc.tree_offset = tree_offset
2371 ht_desc.tree_size = tree_size
2372 ht_desc.data_block_size = block_size
2373 ht_desc.hash_block_size = block_size
2374 ht_desc.hash_algorithm = hash_algorithm
2375 ht_desc.partition_name = partition_name
2376 ht_desc.salt = salt
2377 ht_desc.root_digest = root_digest
2378
David Zeuthen09692692016-09-30 16:16:40 -04002379 # Write the hash tree
2380 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2381 len(hash_tree))
2382 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2383 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002384 len_hashtree_and_fec = len(hash_tree_with_padding)
2385
2386 # Generate FEC codes, if requested.
2387 if generate_fec:
2388 fec_data = generate_fec_data(image_filename, fec_num_roots)
2389 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2390 len(fec_data))
2391 fec_data_with_padding = fec_data + '\0'*padding_needed
2392 fec_offset = image.image_size
2393 image.append_raw(fec_data_with_padding)
2394 len_hashtree_and_fec += len(fec_data_with_padding)
2395 # Update the hashtree descriptor.
2396 ht_desc.fec_num_roots = fec_num_roots
2397 ht_desc.fec_offset = fec_offset
2398 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002399
David Zeuthenfd41eb92016-11-17 12:24:47 -05002400 # Flags are only allowed on top-level vbmeta struct.
2401 flags = 0
2402
David Zeuthena4fee8b2016-08-22 15:20:43 -04002403 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002404 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002405 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002406 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002407 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002408 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002409 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002410 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2411 len(vbmeta_blob))
2412 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2413 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002414
David Zeuthena4fee8b2016-08-22 15:20:43 -04002415 # Now insert a DONT_CARE chunk with enough bytes such that the
2416 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002417 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002418 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002419
David Zeuthena4fee8b2016-08-22 15:20:43 -04002420 # Generate the Footer that tells where the VBMeta footer
2421 # is. Also put enough padding in the front of the footer since
2422 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002423 footer = AvbFooter()
2424 footer.original_image_size = original_image_size
2425 footer.vbmeta_offset = vbmeta_offset
2426 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002427 footer_blob = footer.encode()
2428 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2429 footer_blob)
2430 image.append_raw(footer_blob_with_padding)
2431
David Zeuthen21e95262016-07-27 17:58:40 -04002432 except:
David Zeuthen09692692016-09-30 16:16:40 -04002433 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002434 image.truncate(original_image_size)
2435 raise
2436
Darren Krahn147b08d2016-12-20 16:38:29 -08002437 def make_atx_certificate(self, output, authority_key_path, subject_key,
2438 subject_key_version, subject,
2439 is_intermediate_authority, signing_helper):
2440 """Implements the 'make_atx_certificate' command.
2441
2442 Android Things certificates are required for Android Things public key
2443 metadata. They chain the vbmeta signing key for a particular product back to
2444 a fused, permanent root key. These certificates are fixed-length and fixed-
2445 format with the explicit goal of not parsing ASN.1 in bootloader code.
2446
2447 Arguments:
2448 output: Certificate will be written to this file on success.
2449 authority_key_path: A PEM file path with the authority private key.
2450 If None, then a certificate will be created without a
2451 signature. The signature can be created out-of-band
2452 and appended.
2453 subject_key: A PEM or DER subject public key.
2454 subject_key_version: A 64-bit version value. If this is None, the number
2455 of seconds since the epoch is used.
2456 subject: A subject identifier. For Product Signing Key certificates this
2457 should be the same Product ID found in the permanent attributes.
2458 is_intermediate_authority: True if the certificate is for an intermediate
2459 authority.
2460 signing_helper: Program which signs a hash and returns the signature.
2461 """
2462 signed_data = bytearray()
2463 signed_data.extend(struct.pack('<I', 1)) # Format Version
2464 signed_data.extend(
2465 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2466 hasher = hashlib.sha256()
2467 hasher.update(subject)
2468 signed_data.extend(hasher.digest())
2469 usage = 'com.google.android.things.vboot'
2470 if is_intermediate_authority:
2471 usage += '.ca'
2472 hasher = hashlib.sha256()
2473 hasher.update(usage)
2474 signed_data.extend(hasher.digest())
2475 if not subject_key_version:
2476 subject_key_version = int(time.time())
2477 signed_data.extend(struct.pack('<Q', subject_key_version))
2478 signature = bytearray()
2479 if authority_key_path:
2480 padding_and_hash = bytearray()
2481 algorithm_name = None
2482 hasher = None
2483 if is_intermediate_authority:
2484 hasher = hashlib.sha512()
2485 algorithm_name = 'SHA512_RSA4096'
2486 else:
2487 hasher = hashlib.sha256()
2488 algorithm_name = 'SHA256_RSA2048'
2489 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2490 hasher.update(signed_data)
2491 padding_and_hash.extend(hasher.digest())
2492 signature.extend(raw_sign(signing_helper, algorithm_name,
2493 authority_key_path, padding_and_hash))
2494 output.write(signed_data)
2495 output.write(signature)
2496
2497 def make_atx_permanent_attributes(self, output, root_authority_key,
2498 product_id):
2499 """Implements the 'make_atx_permanent_attributes' command.
2500
2501 Android Things permanent attributes are designed to be permanent for a
2502 particular product and a hash of these attributes should be fused into
2503 hardware to enforce this.
2504
2505 Arguments:
2506 output: Attributes will be written to this file on success.
2507 root_authority_key: A PEM or DER public key for the root authority.
2508 product_id: A 16-byte Product ID.
2509
2510 Raises:
2511 AvbError: If an argument is incorrect.
2512 """
2513 if len(product_id) != 16:
2514 raise AvbError('Invalid Product ID length.')
2515 output.write(struct.pack('<I', 1)) # Format Version
2516 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2517 output.write(product_id)
2518
2519 def make_atx_metadata(self, output, intermediate_key_certificate,
2520 product_key_certificate, google_key_version):
2521 """Implements the 'make_atx_metadata' command.
2522
2523 Android Things metadata are included in vbmeta images to facilitate
2524 verification. The output of this command can be used as the
2525 public_key_metadata argument to other commands.
2526
2527 Arguments:
2528 output: Metadata will be written to this file on success.
2529 intermediate_key_certificate: A certificate file as output by
2530 make_atx_certificate with
2531 is_intermediate_authority set to true.
2532 product_key_certificate: A certificate file as output by
2533 make_atx_certificate with
2534 is_intermediate_authority set to false.
2535 google_key_version: The version of the Google Signing Key used in the
2536 associated vbmeta image.
2537
2538 Raises:
2539 AvbError: If an argument is incorrect.
2540 """
2541 if len(intermediate_key_certificate) != 1108:
2542 raise AvbError('Invalid intermediate key certificate length.')
2543 if len(product_key_certificate) != 852:
2544 raise AvbError('Invalid product key certificate length.')
2545 output.write(struct.pack('<I', 1)) # Format Version
2546 output.write(intermediate_key_certificate)
2547 output.write(product_key_certificate)
2548 output.write(struct.pack('<Q', google_key_version))
2549
David Zeuthen21e95262016-07-27 17:58:40 -04002550
2551def calc_hash_level_offsets(image_size, block_size, digest_size):
2552 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2553
2554 Arguments:
2555 image_size: The size of the image to calculate a Merkle-tree for.
2556 block_size: The block size, e.g. 4096.
2557 digest_size: The size of each hash, e.g. 32 for SHA-256.
2558
2559 Returns:
2560 A tuple where the first argument is an array of offsets and the
2561 second is size of the tree, in bytes.
2562 """
2563 level_offsets = []
2564 level_sizes = []
2565 tree_size = 0
2566
2567 num_levels = 0
2568 size = image_size
2569 while size > block_size:
2570 num_blocks = (size + block_size - 1) / block_size
2571 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2572
2573 level_sizes.append(level_size)
2574 tree_size += level_size
2575 num_levels += 1
2576
2577 size = level_size
2578
2579 for n in range(0, num_levels):
2580 offset = 0
2581 for m in range(n + 1, num_levels):
2582 offset += level_sizes[m]
2583 level_offsets.append(offset)
2584
David Zeuthena4fee8b2016-08-22 15:20:43 -04002585 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002586
2587
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002588# See system/extras/libfec/include/fec/io.h for these definitions.
2589FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2590FEC_MAGIC = 0xfecfecfe
2591
2592
2593def calc_fec_data_size(image_size, num_roots):
2594 """Calculates how much space FEC data will take.
2595
2596 Args:
2597 image_size: The size of the image.
2598 num_roots: Number of roots.
2599
2600 Returns:
2601 The number of bytes needed for FEC for an image of the given size
2602 and with the requested number of FEC roots.
2603
2604 Raises:
2605 ValueError: If output from the 'fec' tool is invalid.
2606
2607 """
2608 p = subprocess.Popen(
2609 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2610 stdout=subprocess.PIPE,
2611 stderr=subprocess.PIPE)
2612 (pout, perr) = p.communicate()
2613 retcode = p.wait()
2614 if retcode != 0:
2615 raise ValueError('Error invoking fec: {}'.format(perr))
2616 return int(pout)
2617
2618
2619def generate_fec_data(image_filename, num_roots):
2620 """Generate FEC codes for an image.
2621
2622 Args:
2623 image_filename: The filename of the image.
2624 num_roots: Number of roots.
2625
2626 Returns:
2627 The FEC data blob.
2628
2629 Raises:
2630 ValueError: If output from the 'fec' tool is invalid.
2631 """
2632 fec_tmpfile = tempfile.NamedTemporaryFile()
2633 subprocess.check_call(
2634 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2635 fec_tmpfile.name],
2636 stderr=open(os.devnull))
2637 fec_data = fec_tmpfile.read()
2638 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2639 footer_data = fec_data[-footer_size:]
2640 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2641 footer_data)
2642 if magic != FEC_MAGIC:
2643 raise ValueError('Unexpected magic in FEC footer')
2644 return fec_data[0:fec_size]
2645
2646
David Zeuthen21e95262016-07-27 17:58:40 -04002647def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002648 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002649 """Generates a Merkle-tree for a file.
2650
2651 Args:
2652 image: The image, as a file.
2653 image_size: The size of the image.
2654 block_size: The block size, e.g. 4096.
2655 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2656 salt: The salt to use.
2657 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002658 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002659 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002660
2661 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002662 A tuple where the first element is the top-level hash and the
2663 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002664 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002665 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002666 hash_src_offset = 0
2667 hash_src_size = image_size
2668 level_num = 0
2669 while hash_src_size > block_size:
2670 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002671 remaining = hash_src_size
2672 while remaining > 0:
2673 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002674 # Only read from the file for the first level - for subsequent
2675 # levels, access the array we're building.
2676 if level_num == 0:
2677 image.seek(hash_src_offset + hash_src_size - remaining)
2678 data = image.read(min(remaining, block_size))
2679 else:
2680 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2681 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002682 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002683
2684 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002685 if len(data) < block_size:
2686 hasher.update('\0' * (block_size - len(data)))
2687 level_output += hasher.digest()
2688 if digest_padding > 0:
2689 level_output += '\0' * digest_padding
2690
2691 padding_needed = (round_to_multiple(
2692 len(level_output), block_size) - len(level_output))
2693 level_output += '\0' * padding_needed
2694
David Zeuthena4fee8b2016-08-22 15:20:43 -04002695 # Copy level-output into resulting tree.
2696 offset = hash_level_offsets[level_num]
2697 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002698
David Zeuthena4fee8b2016-08-22 15:20:43 -04002699 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002700 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002701 level_num += 1
2702
2703 hasher = hashlib.new(name=hash_alg_name, string=salt)
2704 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002705 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002706
2707
2708class AvbTool(object):
2709 """Object for avbtool command-line tool."""
2710
2711 def __init__(self):
2712 """Initializer method."""
2713 self.avb = Avb()
2714
2715 def _add_common_args(self, sub_parser):
2716 """Adds arguments used by several sub-commands.
2717
2718 Arguments:
2719 sub_parser: The parser to add arguments to.
2720 """
2721 sub_parser.add_argument('--algorithm',
2722 help='Algorithm to use (default: NONE)',
2723 metavar='ALGORITHM',
2724 default='NONE')
2725 sub_parser.add_argument('--key',
2726 help='Path to RSA private key file',
2727 metavar='KEY',
2728 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002729 sub_parser.add_argument('--signing_helper',
2730 help='Path to helper used for signing',
2731 metavar='APP',
2732 default=None,
2733 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002734 sub_parser.add_argument('--public_key_metadata',
2735 help='Path to public key metadata file',
2736 metavar='KEY_METADATA',
2737 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002738 sub_parser.add_argument('--rollback_index',
2739 help='Rollback Index',
2740 type=parse_number,
2741 default=0)
2742 sub_parser.add_argument('--prop',
2743 help='Add property',
2744 metavar='KEY:VALUE',
2745 action='append')
2746 sub_parser.add_argument('--prop_from_file',
2747 help='Add property from file',
2748 metavar='KEY:PATH',
2749 action='append')
2750 sub_parser.add_argument('--kernel_cmdline',
2751 help='Add kernel cmdline',
2752 metavar='CMDLINE',
2753 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002754 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2755 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2756 # at some future point.
2757 sub_parser.add_argument('--setup_rootfs_from_kernel',
2758 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002759 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002760 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002761 type=argparse.FileType('rb'))
2762 sub_parser.add_argument('--include_descriptors_from_image',
2763 help='Include descriptors from image',
2764 metavar='IMAGE',
2765 action='append',
2766 type=argparse.FileType('rb'))
2767
2768 def run(self, argv):
2769 """Command-line processor.
2770
2771 Arguments:
2772 argv: Pass sys.argv from main.
2773 """
2774 parser = argparse.ArgumentParser()
2775 subparsers = parser.add_subparsers(title='subcommands')
2776
2777 sub_parser = subparsers.add_parser('version',
2778 help='Prints version of avbtool.')
2779 sub_parser.set_defaults(func=self.version)
2780
2781 sub_parser = subparsers.add_parser('extract_public_key',
2782 help='Extract public key.')
2783 sub_parser.add_argument('--key',
2784 help='Path to RSA private key file',
2785 required=True)
2786 sub_parser.add_argument('--output',
2787 help='Output file name',
2788 type=argparse.FileType('wb'),
2789 required=True)
2790 sub_parser.set_defaults(func=self.extract_public_key)
2791
2792 sub_parser = subparsers.add_parser('make_vbmeta_image',
2793 help='Makes a vbmeta image.')
2794 sub_parser.add_argument('--output',
2795 help='Output file name',
2796 type=argparse.FileType('wb'),
2797 required=True)
2798 self._add_common_args(sub_parser)
2799 sub_parser.add_argument('--chain_partition',
2800 help='Allow signed integrity-data for partition',
2801 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2802 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002803 sub_parser.add_argument('--flags',
2804 help='VBMeta flags',
2805 type=parse_number,
2806 default=0)
David Zeuthen58305522017-01-11 17:42:47 -05002807 sub_parser.add_argument('--set_hashtree_disabled_flag',
2808 help='Set the HASHTREE_DISABLED flag',
2809 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002810 sub_parser.set_defaults(func=self.make_vbmeta_image)
2811
2812 sub_parser = subparsers.add_parser('add_hash_footer',
2813 help='Add hashes and footer to image.')
2814 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002815 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002816 type=argparse.FileType('rab+'))
2817 sub_parser.add_argument('--partition_size',
2818 help='Partition size',
2819 type=parse_number,
2820 required=True)
2821 sub_parser.add_argument('--partition_name',
2822 help='Partition name',
2823 required=True)
2824 sub_parser.add_argument('--hash_algorithm',
2825 help='Hash algorithm to use (default: sha256)',
2826 default='sha256')
2827 sub_parser.add_argument('--salt',
2828 help='Salt in hex (default: /dev/urandom)')
2829 self._add_common_args(sub_parser)
2830 sub_parser.set_defaults(func=self.add_hash_footer)
2831
2832 sub_parser = subparsers.add_parser('add_hashtree_footer',
2833 help='Add hashtree and footer to image.')
2834 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002835 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002836 type=argparse.FileType('rab+'))
2837 sub_parser.add_argument('--partition_size',
2838 help='Partition size',
2839 type=parse_number,
2840 required=True)
2841 sub_parser.add_argument('--partition_name',
2842 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002843 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002844 sub_parser.add_argument('--hash_algorithm',
2845 help='Hash algorithm to use (default: sha1)',
2846 default='sha1')
2847 sub_parser.add_argument('--salt',
2848 help='Salt in hex (default: /dev/urandom)')
2849 sub_parser.add_argument('--block_size',
2850 help='Block size (default: 4096)',
2851 type=parse_number,
2852 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002853 sub_parser.add_argument('--generate_fec',
2854 help='Add forward-error-correction codes',
2855 action='store_true')
2856 sub_parser.add_argument('--fec_num_roots',
2857 help='Number of roots for FEC (default: 2)',
2858 type=parse_number,
2859 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002860 sub_parser.add_argument('--calc_max_image_size',
2861 help=('Don\'t store the hashtree or footer - '
2862 'instead calculate the maximum image size '
2863 'leaving enough room for hashtree '
2864 'and metadata with the given partition '
2865 'size.'),
2866 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002867 self._add_common_args(sub_parser)
2868 sub_parser.set_defaults(func=self.add_hashtree_footer)
2869
2870 sub_parser = subparsers.add_parser('erase_footer',
2871 help='Erase footer from an image.')
2872 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002873 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002874 type=argparse.FileType('rwb+'),
2875 required=True)
2876 sub_parser.add_argument('--keep_hashtree',
2877 help='Keep the hashtree in the image',
2878 action='store_true')
2879 sub_parser.set_defaults(func=self.erase_footer)
2880
2881 sub_parser = subparsers.add_parser(
2882 'info_image',
2883 help='Show information about vbmeta or footer.')
2884 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002885 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002886 type=argparse.FileType('rb'),
2887 required=True)
2888 sub_parser.add_argument('--output',
2889 help='Write info to file',
2890 type=argparse.FileType('wt'),
2891 default=sys.stdout)
2892 sub_parser.set_defaults(func=self.info_image)
2893
David Zeuthen8b6973b2016-09-20 12:39:49 -04002894 sub_parser = subparsers.add_parser('set_ab_metadata',
2895 help='Set A/B metadata.')
2896 sub_parser.add_argument('--misc_image',
2897 help=('The misc image to modify. If the image does '
2898 'not exist, it will be created.'),
2899 type=argparse.FileType('r+b'),
2900 required=True)
2901 sub_parser.add_argument('--slot_data',
2902 help=('Slot data of the form "priority", '
2903 '"tries_remaining", "sucessful_boot" for '
2904 'slot A followed by the same for slot B, '
2905 'separated by colons. The default value '
2906 'is 15:7:0:14:7:0.'),
2907 default='15:7:0:14:7:0')
2908 sub_parser.set_defaults(func=self.set_ab_metadata)
2909
Darren Krahn147b08d2016-12-20 16:38:29 -08002910 sub_parser = subparsers.add_parser(
2911 'make_atx_certificate',
2912 help='Create an Android Things eXtension (ATX) certificate.')
2913 sub_parser.add_argument('--output',
2914 help='Write certificate to file',
2915 type=argparse.FileType('wb'),
2916 default=sys.stdout)
2917 sub_parser.add_argument('--subject',
2918 help=('Path to subject file'),
2919 type=argparse.FileType('rb'),
2920 required=True)
2921 sub_parser.add_argument('--subject_key',
2922 help=('Path to subject RSA public key file'),
2923 type=argparse.FileType('rb'),
2924 required=True)
2925 sub_parser.add_argument('--subject_key_version',
2926 help=('Version of the subject key'),
2927 type=parse_number,
2928 required=False)
2929 sub_parser.add_argument('--subject_is_intermediate_authority',
2930 help=('Generate an intermediate authority '
2931 'certificate'),
2932 action='store_true')
2933 sub_parser.add_argument('--authority_key',
2934 help='Path to authority RSA private key file',
2935 required=False)
2936 sub_parser.add_argument('--signing_helper',
2937 help='Path to helper used for signing',
2938 metavar='APP',
2939 default=None,
2940 required=False)
2941 sub_parser.set_defaults(func=self.make_atx_certificate)
2942
2943 sub_parser = subparsers.add_parser(
2944 'make_atx_permanent_attributes',
2945 help='Create Android Things eXtension (ATX) permanent attributes.')
2946 sub_parser.add_argument('--output',
2947 help='Write attributes to file',
2948 type=argparse.FileType('wb'),
2949 default=sys.stdout)
2950 sub_parser.add_argument('--root_authority_key',
2951 help='Path to authority RSA public key file',
2952 type=argparse.FileType('rb'),
2953 required=True)
2954 sub_parser.add_argument('--product_id',
2955 help=('Path to Product ID file'),
2956 type=argparse.FileType('rb'),
2957 required=True)
2958 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
2959
2960 sub_parser = subparsers.add_parser(
2961 'make_atx_metadata',
2962 help='Create Android Things eXtension (ATX) metadata.')
2963 sub_parser.add_argument('--output',
2964 help='Write metadata to file',
2965 type=argparse.FileType('wb'),
2966 default=sys.stdout)
2967 sub_parser.add_argument('--intermediate_key_certificate',
2968 help='Path to intermediate key certificate file',
2969 type=argparse.FileType('rb'),
2970 required=True)
2971 sub_parser.add_argument('--product_key_certificate',
2972 help='Path to product key certificate file',
2973 type=argparse.FileType('rb'),
2974 required=True)
2975 sub_parser.add_argument('--google_key_version',
2976 help=('Version of the Google signing key'),
2977 type=parse_number,
2978 default=0)
2979 sub_parser.set_defaults(func=self.make_atx_metadata)
2980
David Zeuthen21e95262016-07-27 17:58:40 -04002981 args = parser.parse_args(argv[1:])
2982 try:
2983 args.func(args)
2984 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002985 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002986 sys.exit(1)
2987
2988 def version(self, _):
2989 """Implements the 'version' sub-command."""
2990 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2991
2992 def extract_public_key(self, args):
2993 """Implements the 'extract_public_key' sub-command."""
2994 self.avb.extract_public_key(args.key, args.output)
2995
2996 def make_vbmeta_image(self, args):
2997 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthen58305522017-01-11 17:42:47 -05002998 if args.set_hashtree_disabled_flag:
2999 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
David Zeuthen21e95262016-07-27 17:58:40 -04003000 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003001 args.algorithm, args.key,
3002 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003003 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003004 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003005 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003006 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003007
3008 def add_hash_footer(self, args):
3009 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003010 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003011 args.partition_name, args.hash_algorithm,
3012 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003013 args.public_key_metadata, args.rollback_index,
3014 args.prop, args.prop_from_file,
3015 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003016 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003017 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003018
3019 def add_hashtree_footer(self, args):
3020 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04003021 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3022 args.partition_size,
3023 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003024 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003025 args.hash_algorithm, args.block_size,
3026 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003027 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04003028 args.rollback_index, args.prop,
3029 args.prop_from_file,
3030 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003031 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003032 args.include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003033 args.calc_max_image_size, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003034
3035 def erase_footer(self, args):
3036 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003037 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003038
David Zeuthen8b6973b2016-09-20 12:39:49 -04003039 def set_ab_metadata(self, args):
3040 """Implements the 'set_ab_metadata' sub-command."""
3041 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3042
David Zeuthen21e95262016-07-27 17:58:40 -04003043 def info_image(self, args):
3044 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003045 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003046
Darren Krahn147b08d2016-12-20 16:38:29 -08003047 def make_atx_certificate(self, args):
3048 """Implements the 'make_atx_certificate' sub-command."""
3049 self.avb.make_atx_certificate(args.output, args.authority_key,
3050 args.subject_key.read(),
3051 args.subject_key_version,
3052 args.subject.read(),
3053 args.subject_is_intermediate_authority,
3054 args.signing_helper)
3055
3056 def make_atx_permanent_attributes(self, args):
3057 """Implements the 'make_atx_permanent_attributes' sub-command."""
3058 self.avb.make_atx_permanent_attributes(args.output,
3059 args.root_authority_key.read(),
3060 args.product_id.read())
3061
3062 def make_atx_metadata(self, args):
3063 """Implements the 'make_atx_metadata' sub-command."""
3064 self.avb.make_atx_metadata(args.output,
3065 args.intermediate_key_certificate.read(),
3066 args.product_key_certificate.read(),
3067 args.google_key_version)
3068
David Zeuthen21e95262016-07-27 17:58:40 -04003069
3070if __name__ == '__main__':
3071 tool = AvbTool()
3072 tool.run(sys.argv)