blob: bb23db296abdb7d19382690f7b6bfb47dd50a6b0 [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
David Zeuthene3cadca2017-02-22 21:25:46 -050040# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040041AVB_VERSION_MAJOR = 1
42AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050043AVB_VERSION_SUB = 0
44
David Zeuthen58305522017-01-11 17:42:47 -050045AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040046
David Zeuthene3cadca2017-02-22 21:25:46 -050047
David Zeuthen21e95262016-07-27 17:58:40 -040048class AvbError(Exception):
49 """Application-specific errors.
50
51 These errors represent issues for which a stack-trace should not be
52 presented.
53
54 Attributes:
55 message: Error message.
56 """
57
58 def __init__(self, message):
59 Exception.__init__(self, message)
60
61
62class Algorithm(object):
63 """Contains details about an algorithm.
64
65 See the avb_vbmeta_header.h file for more details about
66 algorithms.
67
68 The constant |ALGORITHMS| is a dictionary from human-readable
69 names (e.g 'SHA256_RSA2048') to instances of this class.
70
71 Attributes:
72 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
73 hash_num_bytes: Number of bytes used to store the hash.
74 signature_num_bytes: Number of bytes used to store the signature.
75 public_key_num_bytes: Number of bytes used to store the public key.
76 padding: Padding used for signature, if any.
77 """
78
79 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
80 public_key_num_bytes, padding):
81 self.algorithm_type = algorithm_type
82 self.hash_num_bytes = hash_num_bytes
83 self.signature_num_bytes = signature_num_bytes
84 self.public_key_num_bytes = public_key_num_bytes
85 self.padding = padding
86
87# This must be kept in sync with the avb_crypto.h file.
88#
89# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
90# obtained from section 5.2.2 of RFC 4880.
91ALGORITHMS = {
92 'NONE': Algorithm(
93 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
94 hash_num_bytes=0,
95 signature_num_bytes=0,
96 public_key_num_bytes=0,
97 padding=[]),
98 'SHA256_RSA2048': Algorithm(
99 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
100 hash_num_bytes=32,
101 signature_num_bytes=256,
102 public_key_num_bytes=8 + 2*2048/8,
103 padding=[
104 # PKCS1-v1_5 padding
105 0x00, 0x01] + [0xff]*202 + [0x00] + [
106 # ASN.1 header
107 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
108 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
109 0x00, 0x04, 0x20,
110 ]),
111 'SHA256_RSA4096': Algorithm(
112 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
113 hash_num_bytes=32,
114 signature_num_bytes=512,
115 public_key_num_bytes=8 + 2*4096/8,
116 padding=[
117 # PKCS1-v1_5 padding
118 0x00, 0x01] + [0xff]*458 + [0x00] + [
119 # ASN.1 header
120 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
121 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
122 0x00, 0x04, 0x20,
123 ]),
124 'SHA256_RSA8192': Algorithm(
125 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
126 hash_num_bytes=32,
127 signature_num_bytes=1024,
128 public_key_num_bytes=8 + 2*8192/8,
129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*970 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA512_RSA2048': Algorithm(
138 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
139 hash_num_bytes=64,
140 signature_num_bytes=256,
141 public_key_num_bytes=8 + 2*2048/8,
142 padding=[
143 # PKCS1-v1_5 padding
144 0x00, 0x01] + [0xff]*170 + [0x00] + [
145 # ASN.1 header
146 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
147 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
148 0x00, 0x04, 0x40
149 ]),
150 'SHA512_RSA4096': Algorithm(
151 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
152 hash_num_bytes=64,
153 signature_num_bytes=512,
154 public_key_num_bytes=8 + 2*4096/8,
155 padding=[
156 # PKCS1-v1_5 padding
157 0x00, 0x01] + [0xff]*426 + [0x00] + [
158 # ASN.1 header
159 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
160 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
161 0x00, 0x04, 0x40
162 ]),
163 'SHA512_RSA8192': Algorithm(
164 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
165 hash_num_bytes=64,
166 signature_num_bytes=1024,
167 public_key_num_bytes=8 + 2*8192/8,
168 padding=[
169 # PKCS1-v1_5 padding
170 0x00, 0x01] + [0xff]*938 + [0x00] + [
171 # ASN.1 header
172 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
173 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
174 0x00, 0x04, 0x40
175 ]),
176}
177
178
David Zeuthene3cadca2017-02-22 21:25:46 -0500179def get_release_string():
180 """Calculates the release string to use in the VBMeta struct."""
181 # Keep in sync with libavb/avb_version.c:avb_version_string().
182 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
183 AVB_VERSION_MINOR,
184 AVB_VERSION_SUB)
185
186
David Zeuthen21e95262016-07-27 17:58:40 -0400187def round_to_multiple(number, size):
188 """Rounds a number up to nearest multiple of another number.
189
190 Args:
191 number: The number to round up.
192 size: The multiple to round up to.
193
194 Returns:
195 If |number| is a multiple of |size|, returns |number|, otherwise
196 returns |number| + |size|.
197 """
198 remainder = number % size
199 if remainder == 0:
200 return number
201 return number + size - remainder
202
203
204def round_to_pow2(number):
205 """Rounds a number up to the next power of 2.
206
207 Args:
208 number: The number to round up.
209
210 Returns:
211 If |number| is already a power of 2 then |number| is
212 returned. Otherwise the smallest power of 2 greater than |number|
213 is returned.
214 """
215 return 2**((number - 1).bit_length())
216
217
218def write_long(output, num_bits, value):
219 """Writes a long to an output stream using a given amount of bits.
220
221 This number is written big-endian, e.g. with the most significant
222 bit first.
223
224 Arguments:
225 output: The object to write the output to.
226 num_bits: The number of bits to write, e.g. 2048.
227 value: The value to write.
228 """
229 for bit_pos in range(num_bits, 0, -8):
230 octet = (value >> (bit_pos - 8)) & 0xff
231 output.write(struct.pack('!B', octet))
232
233
234def encode_long(num_bits, value):
235 """Encodes a long to a bytearray() using a given amount of bits.
236
237 This number is written big-endian, e.g. with the most significant
238 bit first.
239
240 Arguments:
241 num_bits: The number of bits to write, e.g. 2048.
242 value: The value to write.
243
244 Returns:
245 A bytearray() with the encoded long.
246 """
247 ret = bytearray()
248 for bit_pos in range(num_bits, 0, -8):
249 octet = (value >> (bit_pos - 8)) & 0xff
250 ret.extend(struct.pack('!B', octet))
251 return ret
252
253
254def egcd(a, b):
255 """Calculate greatest common divisor of two numbers.
256
257 This implementation uses a recursive version of the extended
258 Euclidian algorithm.
259
260 Arguments:
261 a: First number.
262 b: Second number.
263
264 Returns:
265 A tuple (gcd, x, y) that where |gcd| is the greatest common
266 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
267 """
268 if a == 0:
269 return (b, 0, 1)
270 else:
271 g, y, x = egcd(b % a, a)
272 return (g, x - (b // a) * y, y)
273
274
275def modinv(a, m):
276 """Calculate modular multiplicative inverse of |a| modulo |m|.
277
278 This calculates the number |x| such that |a| * |x| == 1 (modulo
279 |m|). This number only exists if |a| and |m| are co-prime - |None|
280 is returned if this isn't true.
281
282 Arguments:
283 a: The number to calculate a modular inverse of.
284 m: The modulo to use.
285
286 Returns:
287 The modular multiplicative inverse of |a| and |m| or |None| if
288 these numbers are not co-prime.
289 """
290 gcd, x, _ = egcd(a, m)
291 if gcd != 1:
292 return None # modular inverse does not exist
293 else:
294 return x % m
295
296
297def parse_number(string):
298 """Parse a string as a number.
299
300 This is just a short-hand for int(string, 0) suitable for use in the
301 |type| parameter of |ArgumentParser|'s add_argument() function. An
302 improvement to just using type=int is that this function supports
303 numbers in other bases, e.g. "0x1234".
304
305 Arguments:
306 string: The string to parse.
307
308 Returns:
309 The parsed integer.
310
311 Raises:
312 ValueError: If the number could not be parsed.
313 """
314 return int(string, 0)
315
316
317def write_rsa_key(output, key):
318 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
319
320 This writes the |AvbRSAPublicKeyHeader| as well as the two large
321 numbers (|key_num_bits| bits long) following it.
322
323 Arguments:
324 output: The object to write the output to.
325 key: A Crypto.PublicKey.RSA object.
326 """
327 # key.e is exponent
328 # key.n is modulus
329 key_num_bits = key.size() + 1
330 # Calculate n0inv = -1/n[0] (mod 2^32)
331 b = 2L**32
332 n0inv = b - modinv(key.n, b)
333 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
334 r = 2L**key.n.bit_length()
335 rrmodn = r * r % key.n
336 output.write(struct.pack('!II', key_num_bits, n0inv))
337 write_long(output, key_num_bits, key.n)
338 write_long(output, key_num_bits, rrmodn)
339
340
341def encode_rsa_key(key):
342 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
343
344 This creates a |AvbRSAPublicKeyHeader| as well as the two large
345 numbers (|key_num_bits| bits long) following it.
346
347 Arguments:
348 key: A Crypto.PublicKey.RSA object.
349
350 Returns:
351 A bytearray() with the |AvbRSAPublicKeyHeader|.
352 """
353 ret = bytearray()
354 # key.e is exponent
355 # key.n is modulus
356 key_num_bits = key.size() + 1
357 # Calculate n0inv = -1/n[0] (mod 2^32)
358 b = 2L**32
359 n0inv = b - modinv(key.n, b)
360 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
361 r = 2L**key.n.bit_length()
362 rrmodn = r * r % key.n
363 ret.extend(struct.pack('!II', key_num_bits, n0inv))
364 ret.extend(encode_long(key_num_bits, key.n))
365 ret.extend(encode_long(key_num_bits, rrmodn))
366 return ret
367
368
369def lookup_algorithm_by_type(alg_type):
370 """Looks up algorithm by type.
371
372 Arguments:
373 alg_type: The integer representing the type.
374
375 Returns:
376 A tuple with the algorithm name and an |Algorithm| instance.
377
378 Raises:
379 Exception: If the algorithm cannot be found
380 """
381 for alg_name in ALGORITHMS:
382 alg_data = ALGORITHMS[alg_name]
383 if alg_data.algorithm_type == alg_type:
384 return (alg_name, alg_data)
385 raise AvbError('Unknown algorithm type {}'.format(alg_type))
386
387
Darren Krahn147b08d2016-12-20 16:38:29 -0800388def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
389 """Computes a raw RSA signature using |signing_helper| or openssl.
390
391 Arguments:
392 signing_helper: Program which signs a hash and returns the signature.
393 algorithm_name: The algorithm name as per the ALGORITHMS dict.
394 key_path: Path to the private key file. Must be PEM format.
395 raw_data_to_sign: Data to sign (bytearray or str expected).
396
397 Returns:
398 A bytearray containing the signature.
399
400 Raises:
401 Exception: If an error occurs.
402 """
403 p = None
404 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500405 p = subprocess.Popen(
406 [signing_helper, algorithm_name, key_path],
407 stdin=subprocess.PIPE,
408 stdout=subprocess.PIPE,
409 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800410 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500411 p = subprocess.Popen(
412 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
413 stdin=subprocess.PIPE,
414 stdout=subprocess.PIPE,
415 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800416 (pout, perr) = p.communicate(str(raw_data_to_sign))
417 retcode = p.wait()
418 if retcode != 0:
419 raise AvbError('Error signing: {}'.format(perr))
420 return bytearray(pout)
421
422
David Zeuthena4fee8b2016-08-22 15:20:43 -0400423class ImageChunk(object):
424 """Data structure used for representing chunks in Android sparse files.
425
426 Attributes:
427 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
428 chunk_offset: Offset in the sparse file where this chunk begins.
429 output_offset: Offset in de-sparsified file where output begins.
430 output_size: Number of bytes in output.
431 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
432 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
433 """
434
435 FORMAT = '<2H2I'
436 TYPE_RAW = 0xcac1
437 TYPE_FILL = 0xcac2
438 TYPE_DONT_CARE = 0xcac3
439 TYPE_CRC32 = 0xcac4
440
441 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
442 input_offset, fill_data):
443 """Initializes an ImageChunk object.
444
445 Arguments:
446 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
447 chunk_offset: Offset in the sparse file where this chunk begins.
448 output_offset: Offset in de-sparsified file.
449 output_size: Number of bytes in output.
450 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
451 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
452
453 Raises:
454 ValueError: If data is not well-formed.
455 """
456 self.chunk_type = chunk_type
457 self.chunk_offset = chunk_offset
458 self.output_offset = output_offset
459 self.output_size = output_size
460 self.input_offset = input_offset
461 self.fill_data = fill_data
462 # Check invariants.
463 if self.chunk_type == self.TYPE_RAW:
464 if self.fill_data is not None:
465 raise ValueError('RAW chunk cannot have fill_data set.')
466 if not self.input_offset:
467 raise ValueError('RAW chunk must have input_offset set.')
468 elif self.chunk_type == self.TYPE_FILL:
469 if self.fill_data is None:
470 raise ValueError('FILL chunk must have fill_data set.')
471 if self.input_offset:
472 raise ValueError('FILL chunk cannot have input_offset set.')
473 elif self.chunk_type == self.TYPE_DONT_CARE:
474 if self.fill_data is not None:
475 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
476 if self.input_offset:
477 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
478 else:
479 raise ValueError('Invalid chunk type')
480
481
482class ImageHandler(object):
483 """Abstraction for image I/O with support for Android sparse images.
484
485 This class provides an interface for working with image files that
486 may be using the Android Sparse Image format. When an instance is
487 constructed, we test whether it's an Android sparse file. If so,
488 operations will be on the sparse file by interpreting the sparse
489 format, otherwise they will be directly on the file. Either way the
490 operations do the same.
491
492 For reading, this interface mimics a file object - it has seek(),
493 tell(), and read() methods. For writing, only truncation
494 (truncate()) and appending is supported (append_raw() and
495 append_dont_care()). Additionally, data can only be written in units
496 of the block size.
497
498 Attributes:
499 is_sparse: Whether the file being operated on is sparse.
500 block_size: The block size, typically 4096.
501 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400502 """
503 # See system/core/libsparse/sparse_format.h for details.
504 MAGIC = 0xed26ff3a
505 HEADER_FORMAT = '<I4H4I'
506
507 # These are formats and offset of just the |total_chunks| and
508 # |total_blocks| fields.
509 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
510 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
511
512 def __init__(self, image_filename):
513 """Initializes an image handler.
514
515 Arguments:
516 image_filename: The name of the file to operate on.
517
518 Raises:
519 ValueError: If data in the file is invalid.
520 """
521 self._image_filename = image_filename
522 self._read_header()
523
524 def _read_header(self):
525 """Initializes internal data structures used for reading file.
526
527 This may be called multiple times and is typically called after
528 modifying the file (e.g. appending, truncation).
529
530 Raises:
531 ValueError: If data in the file is invalid.
532 """
533 self.is_sparse = False
534 self.block_size = 4096
535 self._file_pos = 0
536 self._image = open(self._image_filename, 'r+b')
537 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400538 self.image_size = self._image.tell()
539
540 self._image.seek(0, os.SEEK_SET)
541 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
542 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
543 block_size, self._num_total_blocks, self._num_total_chunks,
544 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
545 if magic != self.MAGIC:
546 # Not a sparse image, our job here is done.
547 return
548 if not (major_version == 1 and minor_version == 0):
549 raise ValueError('Encountered sparse image format version {}.{} but '
550 'only 1.0 is supported'.format(major_version,
551 minor_version))
552 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
553 raise ValueError('Unexpected file_hdr_sz value {}.'.
554 format(file_hdr_sz))
555 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
556 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
557 format(chunk_hdr_sz))
558
559 self.block_size = block_size
560
561 # Build an list of chunks by parsing the file.
562 self._chunks = []
563
564 # Find the smallest offset where only "Don't care" chunks
565 # follow. This will be the size of the content in the sparse
566 # image.
567 offset = 0
568 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400569 for _ in xrange(1, self._num_total_chunks + 1):
570 chunk_offset = self._image.tell()
571
572 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
573 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
574 header_bin)
575 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
576
David Zeuthena4fee8b2016-08-22 15:20:43 -0400577 if chunk_type == ImageChunk.TYPE_RAW:
578 if data_sz != (chunk_sz * self.block_size):
579 raise ValueError('Raw chunk input size ({}) does not match output '
580 'size ({})'.
581 format(data_sz, chunk_sz*self.block_size))
582 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
583 chunk_offset,
584 output_offset,
585 chunk_sz*self.block_size,
586 self._image.tell(),
587 None))
588 self._image.read(data_sz)
589
590 elif chunk_type == ImageChunk.TYPE_FILL:
591 if data_sz != 4:
592 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
593 'has {}'.format(data_sz))
594 fill_data = self._image.read(4)
595 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
596 chunk_offset,
597 output_offset,
598 chunk_sz*self.block_size,
599 None,
600 fill_data))
601 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
602 if data_sz != 0:
603 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
604 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400605 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
606 chunk_offset,
607 output_offset,
608 chunk_sz*self.block_size,
609 None,
610 None))
611 elif chunk_type == ImageChunk.TYPE_CRC32:
612 if data_sz != 4:
613 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
614 'this has {}'.format(data_sz))
615 self._image.read(4)
616 else:
617 raise ValueError('Unknown chunk type {}'.format(chunk_type))
618
619 offset += chunk_sz
620 output_offset += chunk_sz*self.block_size
621
622 # Record where sparse data end.
623 self._sparse_end = self._image.tell()
624
625 # Now that we've traversed all chunks, sanity check.
626 if self._num_total_blocks != offset:
627 raise ValueError('The header said we should have {} output blocks, '
628 'but we saw {}'.format(self._num_total_blocks, offset))
629 junk_len = len(self._image.read())
630 if junk_len > 0:
631 raise ValueError('There were {} bytes of extra data at the end of the '
632 'file.'.format(junk_len))
633
David Zeuthen09692692016-09-30 16:16:40 -0400634 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400635 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400636
637 # This is used when bisecting in read() to find the initial slice.
638 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
639
640 self.is_sparse = True
641
642 def _update_chunks_and_blocks(self):
643 """Helper function to update the image header.
644
645 The the |total_chunks| and |total_blocks| fields in the header
646 will be set to value of the |_num_total_blocks| and
647 |_num_total_chunks| attributes.
648
649 """
650 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
651 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
652 self._num_total_blocks,
653 self._num_total_chunks))
654
655 def append_dont_care(self, num_bytes):
656 """Appends a DONT_CARE chunk to the sparse file.
657
658 The given number of bytes must be a multiple of the block size.
659
660 Arguments:
661 num_bytes: Size in number of bytes of the DONT_CARE chunk.
662 """
663 assert num_bytes % self.block_size == 0
664
665 if not self.is_sparse:
666 self._image.seek(0, os.SEEK_END)
667 # This is more efficient that writing NUL bytes since it'll add
668 # a hole on file systems that support sparse files (native
669 # sparse, not Android sparse).
670 self._image.truncate(self._image.tell() + num_bytes)
671 self._read_header()
672 return
673
674 self._num_total_chunks += 1
675 self._num_total_blocks += num_bytes / self.block_size
676 self._update_chunks_and_blocks()
677
678 self._image.seek(self._sparse_end, os.SEEK_SET)
679 self._image.write(struct.pack(ImageChunk.FORMAT,
680 ImageChunk.TYPE_DONT_CARE,
681 0, # Reserved
682 num_bytes / self.block_size,
683 struct.calcsize(ImageChunk.FORMAT)))
684 self._read_header()
685
686 def append_raw(self, data):
687 """Appends a RAW chunk to the sparse file.
688
689 The length of the given data must be a multiple of the block size.
690
691 Arguments:
692 data: Data to append.
693 """
694 assert len(data) % self.block_size == 0
695
696 if not self.is_sparse:
697 self._image.seek(0, os.SEEK_END)
698 self._image.write(data)
699 self._read_header()
700 return
701
702 self._num_total_chunks += 1
703 self._num_total_blocks += len(data) / self.block_size
704 self._update_chunks_and_blocks()
705
706 self._image.seek(self._sparse_end, os.SEEK_SET)
707 self._image.write(struct.pack(ImageChunk.FORMAT,
708 ImageChunk.TYPE_RAW,
709 0, # Reserved
710 len(data) / self.block_size,
711 len(data) +
712 struct.calcsize(ImageChunk.FORMAT)))
713 self._image.write(data)
714 self._read_header()
715
716 def append_fill(self, fill_data, size):
717 """Appends a fill chunk to the sparse file.
718
719 The total length of the fill data must be a multiple of the block size.
720
721 Arguments:
722 fill_data: Fill data to append - must be four bytes.
723 size: Number of chunk - must be a multiple of four and the block size.
724 """
725 assert len(fill_data) == 4
726 assert size % 4 == 0
727 assert size % self.block_size == 0
728
729 if not self.is_sparse:
730 self._image.seek(0, os.SEEK_END)
731 self._image.write(fill_data * (size/4))
732 self._read_header()
733 return
734
735 self._num_total_chunks += 1
736 self._num_total_blocks += size / self.block_size
737 self._update_chunks_and_blocks()
738
739 self._image.seek(self._sparse_end, os.SEEK_SET)
740 self._image.write(struct.pack(ImageChunk.FORMAT,
741 ImageChunk.TYPE_FILL,
742 0, # Reserved
743 size / self.block_size,
744 4 + struct.calcsize(ImageChunk.FORMAT)))
745 self._image.write(fill_data)
746 self._read_header()
747
748 def seek(self, offset):
749 """Sets the cursor position for reading from unsparsified file.
750
751 Arguments:
752 offset: Offset to seek to from the beginning of the file.
753 """
754 self._file_pos = offset
755
756 def read(self, size):
757 """Reads data from the unsparsified file.
758
759 This method may return fewer than |size| bytes of data if the end
760 of the file was encountered.
761
762 The file cursor for reading is advanced by the number of bytes
763 read.
764
765 Arguments:
766 size: Number of bytes to read.
767
768 Returns:
769 The data.
770
771 """
772 if not self.is_sparse:
773 self._image.seek(self._file_pos)
774 data = self._image.read(size)
775 self._file_pos += len(data)
776 return data
777
778 # Iterate over all chunks.
779 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
780 self._file_pos) - 1
781 data = bytearray()
782 to_go = size
783 while to_go > 0:
784 chunk = self._chunks[chunk_idx]
785 chunk_pos_offset = self._file_pos - chunk.output_offset
786 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
787
788 if chunk.chunk_type == ImageChunk.TYPE_RAW:
789 self._image.seek(chunk.input_offset + chunk_pos_offset)
790 data.extend(self._image.read(chunk_pos_to_go))
791 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
792 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
793 offset_mod = chunk_pos_offset % len(chunk.fill_data)
794 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
795 else:
796 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
797 data.extend('\0' * chunk_pos_to_go)
798
799 to_go -= chunk_pos_to_go
800 self._file_pos += chunk_pos_to_go
801 chunk_idx += 1
802 # Generate partial read in case of EOF.
803 if chunk_idx >= len(self._chunks):
804 break
805
806 return data
807
808 def tell(self):
809 """Returns the file cursor position for reading from unsparsified file.
810
811 Returns:
812 The file cursor position for reading.
813 """
814 return self._file_pos
815
816 def truncate(self, size):
817 """Truncates the unsparsified file.
818
819 Arguments:
820 size: Desired size of unsparsified file.
821
822 Raises:
823 ValueError: If desired size isn't a multiple of the block size.
824 """
825 if not self.is_sparse:
826 self._image.truncate(size)
827 self._read_header()
828 return
829
830 if size % self.block_size != 0:
831 raise ValueError('Cannot truncate to a size which is not a multiple '
832 'of the block size')
833
834 if size == self.image_size:
835 # Trivial where there's nothing to do.
836 return
837 elif size < self.image_size:
838 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
839 chunk = self._chunks[chunk_idx]
840 if chunk.output_offset != size:
841 # Truncation in the middle of a trunk - need to keep the chunk
842 # and modify it.
843 chunk_idx_for_update = chunk_idx + 1
844 num_to_keep = size - chunk.output_offset
845 assert num_to_keep % self.block_size == 0
846 if chunk.chunk_type == ImageChunk.TYPE_RAW:
847 truncate_at = (chunk.chunk_offset +
848 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
849 data_sz = num_to_keep
850 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
851 truncate_at = (chunk.chunk_offset +
852 struct.calcsize(ImageChunk.FORMAT) + 4)
853 data_sz = 4
854 else:
855 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
856 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
857 data_sz = 0
858 chunk_sz = num_to_keep/self.block_size
859 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
860 self._image.seek(chunk.chunk_offset)
861 self._image.write(struct.pack(ImageChunk.FORMAT,
862 chunk.chunk_type,
863 0, # Reserved
864 chunk_sz,
865 total_sz))
866 chunk.output_size = num_to_keep
867 else:
868 # Truncation at trunk boundary.
869 truncate_at = chunk.chunk_offset
870 chunk_idx_for_update = chunk_idx
871
872 self._num_total_chunks = chunk_idx_for_update
873 self._num_total_blocks = 0
874 for i in range(0, chunk_idx_for_update):
875 self._num_total_blocks += self._chunks[i].output_size / self.block_size
876 self._update_chunks_and_blocks()
877 self._image.truncate(truncate_at)
878
879 # We've modified the file so re-read all data.
880 self._read_header()
881 else:
882 # Truncating to grow - just add a DONT_CARE section.
883 self.append_dont_care(size - self.image_size)
884
885
David Zeuthen21e95262016-07-27 17:58:40 -0400886class AvbDescriptor(object):
887 """Class for AVB descriptor.
888
889 See the |AvbDescriptor| C struct for more information.
890
891 Attributes:
892 tag: The tag identifying what kind of descriptor this is.
893 data: The data in the descriptor.
894 """
895
896 SIZE = 16
897 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
898
899 def __init__(self, data):
900 """Initializes a new property descriptor.
901
902 Arguments:
903 data: If not None, must be a bytearray().
904
905 Raises:
906 LookupError: If the given descriptor is malformed.
907 """
908 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
909
910 if data:
911 (self.tag, num_bytes_following) = (
912 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
913 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
914 else:
915 self.tag = None
916 self.data = None
917
918 def print_desc(self, o):
919 """Print the descriptor.
920
921 Arguments:
922 o: The object to write the output to.
923 """
924 o.write(' Unknown descriptor:\n')
925 o.write(' Tag: {}\n'.format(self.tag))
926 if len(self.data) < 256:
927 o.write(' Data: {} ({} bytes)\n'.format(
928 repr(str(self.data)), len(self.data)))
929 else:
930 o.write(' Data: {} bytes\n'.format(len(self.data)))
931
932 def encode(self):
933 """Serializes the descriptor.
934
935 Returns:
936 A bytearray() with the descriptor data.
937 """
938 num_bytes_following = len(self.data)
939 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
940 padding_size = nbf_with_padding - num_bytes_following
941 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
942 padding = struct.pack(str(padding_size) + 'x')
943 ret = desc + self.data + padding
944 return bytearray(ret)
945
946
947class AvbPropertyDescriptor(AvbDescriptor):
948 """A class for property descriptors.
949
950 See the |AvbPropertyDescriptor| C struct for more information.
951
952 Attributes:
953 key: The key.
954 value: The key.
955 """
956
957 TAG = 0
958 SIZE = 32
959 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
960 'Q' # key size (bytes)
961 'Q') # value size (bytes)
962
963 def __init__(self, data=None):
964 """Initializes a new property descriptor.
965
966 Arguments:
967 data: If not None, must be a bytearray of size |SIZE|.
968
969 Raises:
970 LookupError: If the given descriptor is malformed.
971 """
972 AvbDescriptor.__init__(self, None)
973 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
974
975 if data:
976 (tag, num_bytes_following, key_size,
977 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
978 expected_size = round_to_multiple(
979 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
980 if tag != self.TAG or num_bytes_following != expected_size:
981 raise LookupError('Given data does not look like a property '
982 'descriptor.')
983 self.key = data[self.SIZE:(self.SIZE + key_size)]
984 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
985 value_size)]
986 else:
987 self.key = ''
988 self.value = ''
989
990 def print_desc(self, o):
991 """Print the descriptor.
992
993 Arguments:
994 o: The object to write the output to.
995 """
996 if len(self.value) < 256:
997 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
998 else:
999 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1000
1001 def encode(self):
1002 """Serializes the descriptor.
1003
1004 Returns:
1005 A bytearray() with the descriptor data.
1006 """
1007 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1008 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1009 padding_size = nbf_with_padding - num_bytes_following
1010 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1011 len(self.key), len(self.value))
1012 padding = struct.pack(str(padding_size) + 'x')
1013 ret = desc + self.key + '\0' + self.value + '\0' + padding
1014 return bytearray(ret)
1015
1016
1017class AvbHashtreeDescriptor(AvbDescriptor):
1018 """A class for hashtree descriptors.
1019
1020 See the |AvbHashtreeDescriptor| C struct for more information.
1021
1022 Attributes:
1023 dm_verity_version: dm-verity version used.
1024 image_size: Size of the image, after rounding up to |block_size|.
1025 tree_offset: Offset of the hash tree in the file.
1026 tree_size: Size of the tree.
1027 data_block_size: Data block size
1028 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001029 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1030 fec_offset: Offset of FEC data (0 if FEC is not used).
1031 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001032 hash_algorithm: Hash algorithm used.
1033 partition_name: Partition name.
1034 salt: Salt used.
1035 root_digest: Root digest.
1036 """
1037
1038 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001039 RESERVED = 64
1040 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001041 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1042 'L' # dm-verity version used
1043 'Q' # image size (bytes)
1044 'Q' # tree offset (bytes)
1045 'Q' # tree size (bytes)
1046 'L' # data block size (bytes)
1047 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001048 'L' # FEC number of roots
1049 'Q' # FEC offset (bytes)
1050 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001051 '32s' # hash algorithm used
1052 'L' # partition name (bytes)
1053 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001054 'L' + # root digest length (bytes)
1055 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001056
1057 def __init__(self, data=None):
1058 """Initializes a new hashtree descriptor.
1059
1060 Arguments:
1061 data: If not None, must be a bytearray of size |SIZE|.
1062
1063 Raises:
1064 LookupError: If the given descriptor is malformed.
1065 """
1066 AvbDescriptor.__init__(self, None)
1067 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1068
1069 if data:
1070 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1071 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001072 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1073 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001074 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1075 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001076 expected_size = round_to_multiple(
1077 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1078 if tag != self.TAG or num_bytes_following != expected_size:
1079 raise LookupError('Given data does not look like a hashtree '
1080 'descriptor.')
1081 # Nuke NUL-bytes at the end.
1082 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1083 o = 0
1084 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1085 partition_name_len)])
1086 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1087 self.partition_name.decode('utf-8')
1088 o += partition_name_len
1089 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1090 o += salt_len
1091 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1092 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1093 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1094
1095 else:
1096 self.dm_verity_version = 0
1097 self.image_size = 0
1098 self.tree_offset = 0
1099 self.tree_size = 0
1100 self.data_block_size = 0
1101 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001102 self.fec_num_roots = 0
1103 self.fec_offset = 0
1104 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001105 self.hash_algorithm = ''
1106 self.partition_name = ''
1107 self.salt = bytearray()
1108 self.root_digest = bytearray()
1109
1110 def print_desc(self, o):
1111 """Print the descriptor.
1112
1113 Arguments:
1114 o: The object to write the output to.
1115 """
1116 o.write(' Hashtree descriptor:\n')
1117 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1118 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1119 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1120 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1121 o.write(' Data Block Size: {} bytes\n'.format(
1122 self.data_block_size))
1123 o.write(' Hash Block Size: {} bytes\n'.format(
1124 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001125 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1126 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1127 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001128 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1129 o.write(' Partition Name: {}\n'.format(self.partition_name))
1130 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1131 'hex')))
1132 o.write(' Root Digest: {}\n'.format(str(
1133 self.root_digest).encode('hex')))
1134
1135 def encode(self):
1136 """Serializes the descriptor.
1137
1138 Returns:
1139 A bytearray() with the descriptor data.
1140 """
1141 encoded_name = self.partition_name.encode('utf-8')
1142 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1143 len(self.root_digest) - 16)
1144 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1145 padding_size = nbf_with_padding - num_bytes_following
1146 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1147 self.dm_verity_version, self.image_size,
1148 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001149 self.hash_block_size, self.fec_num_roots,
1150 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001151 len(encoded_name), len(self.salt), len(self.root_digest),
1152 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001153 padding = struct.pack(str(padding_size) + 'x')
1154 ret = desc + encoded_name + self.salt + self.root_digest + padding
1155 return bytearray(ret)
1156
1157
1158class AvbHashDescriptor(AvbDescriptor):
1159 """A class for hash descriptors.
1160
1161 See the |AvbHashDescriptor| C struct for more information.
1162
1163 Attributes:
1164 image_size: Image size, in bytes.
1165 hash_algorithm: Hash algorithm used.
1166 partition_name: Partition name.
1167 salt: Salt used.
1168 digest: The hash value of salt and data combined.
1169 """
1170
1171 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 RESERVED = 64
1173 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001174 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1175 'Q' # image size (bytes)
1176 '32s' # hash algorithm used
1177 'L' # partition name (bytes)
1178 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001179 'L' + # digest length (bytes)
1180 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001181
1182 def __init__(self, data=None):
1183 """Initializes a new hash descriptor.
1184
1185 Arguments:
1186 data: If not None, must be a bytearray of size |SIZE|.
1187
1188 Raises:
1189 LookupError: If the given descriptor is malformed.
1190 """
1191 AvbDescriptor.__init__(self, None)
1192 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1193
1194 if data:
1195 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1196 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001197 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001198 expected_size = round_to_multiple(
1199 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1200 if tag != self.TAG or num_bytes_following != expected_size:
1201 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1202 # Nuke NUL-bytes at the end.
1203 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1204 o = 0
1205 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1206 partition_name_len)])
1207 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1208 self.partition_name.decode('utf-8')
1209 o += partition_name_len
1210 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1211 o += salt_len
1212 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1213 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1214 raise LookupError('digest_len doesn\'t match hash algorithm')
1215
1216 else:
1217 self.image_size = 0
1218 self.hash_algorithm = ''
1219 self.partition_name = ''
1220 self.salt = bytearray()
1221 self.digest = bytearray()
1222
1223 def print_desc(self, o):
1224 """Print the descriptor.
1225
1226 Arguments:
1227 o: The object to write the output to.
1228 """
1229 o.write(' Hash descriptor:\n')
1230 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1231 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1232 o.write(' Partition Name: {}\n'.format(self.partition_name))
1233 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1234 'hex')))
1235 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1236 'hex')))
1237
1238 def encode(self):
1239 """Serializes the descriptor.
1240
1241 Returns:
1242 A bytearray() with the descriptor data.
1243 """
1244 encoded_name = self.partition_name.encode('utf-8')
1245 num_bytes_following = (
1246 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1247 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1248 padding_size = nbf_with_padding - num_bytes_following
1249 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1250 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001251 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001252 padding = struct.pack(str(padding_size) + 'x')
1253 ret = desc + encoded_name + self.salt + self.digest + padding
1254 return bytearray(ret)
1255
1256
1257class AvbKernelCmdlineDescriptor(AvbDescriptor):
1258 """A class for kernel command-line descriptors.
1259
1260 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1261
1262 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001263 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001264 kernel_cmdline: The kernel command-line.
1265 """
1266
1267 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001268 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001269 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001270 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001271 'L') # cmdline length (bytes)
1272
David Zeuthenfd41eb92016-11-17 12:24:47 -05001273 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1274 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1275
David Zeuthen21e95262016-07-27 17:58:40 -04001276 def __init__(self, data=None):
1277 """Initializes a new kernel cmdline descriptor.
1278
1279 Arguments:
1280 data: If not None, must be a bytearray of size |SIZE|.
1281
1282 Raises:
1283 LookupError: If the given descriptor is malformed.
1284 """
1285 AvbDescriptor.__init__(self, None)
1286 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1287
1288 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001289 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001290 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1291 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1292 8)
1293 if tag != self.TAG or num_bytes_following != expected_size:
1294 raise LookupError('Given data does not look like a kernel cmdline '
1295 'descriptor.')
1296 # Nuke NUL-bytes at the end.
1297 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1298 kernel_cmdline_length)])
1299 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1300 self.kernel_cmdline.decode('utf-8')
1301 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001302 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001303 self.kernel_cmdline = ''
1304
1305 def print_desc(self, o):
1306 """Print the descriptor.
1307
1308 Arguments:
1309 o: The object to write the output to.
1310 """
1311 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001312 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001313 o.write(' Kernel Cmdline: {}\n'.format(repr(
1314 self.kernel_cmdline)))
1315
1316 def encode(self):
1317 """Serializes the descriptor.
1318
1319 Returns:
1320 A bytearray() with the descriptor data.
1321 """
1322 encoded_str = self.kernel_cmdline.encode('utf-8')
1323 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1324 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1325 padding_size = nbf_with_padding - num_bytes_following
1326 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001327 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001328 padding = struct.pack(str(padding_size) + 'x')
1329 ret = desc + encoded_str + padding
1330 return bytearray(ret)
1331
1332
1333class AvbChainPartitionDescriptor(AvbDescriptor):
1334 """A class for chained partition descriptors.
1335
1336 See the |AvbChainPartitionDescriptor| C struct for more information.
1337
1338 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001339 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001340 partition_name: Partition name.
1341 public_key: Bytes for the public key.
1342 """
1343
1344 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001345 RESERVED = 64
1346 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001347 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001348 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001349 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001350 'L' + # public_key_size (bytes)
1351 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001352
1353 def __init__(self, data=None):
1354 """Initializes a new chain partition descriptor.
1355
1356 Arguments:
1357 data: If not None, must be a bytearray of size |SIZE|.
1358
1359 Raises:
1360 LookupError: If the given descriptor is malformed.
1361 """
1362 AvbDescriptor.__init__(self, None)
1363 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1364
1365 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001366 (tag, num_bytes_following, self.rollback_index_location,
1367 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001368 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001369 expected_size = round_to_multiple(
1370 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1371 if tag != self.TAG or num_bytes_following != expected_size:
1372 raise LookupError('Given data does not look like a chain partition '
1373 'descriptor.')
1374 o = 0
1375 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1376 partition_name_len)])
1377 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1378 self.partition_name.decode('utf-8')
1379 o += partition_name_len
1380 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1381
1382 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001383 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001384 self.partition_name = ''
1385 self.public_key = bytearray()
1386
1387 def print_desc(self, o):
1388 """Print the descriptor.
1389
1390 Arguments:
1391 o: The object to write the output to.
1392 """
1393 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001394 o.write(' Partition Name: {}\n'.format(self.partition_name))
1395 o.write(' Rollback Index Location: {}\n'.format(
1396 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001397 # Just show the SHA1 of the key, for size reasons.
1398 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001399 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001400
1401 def encode(self):
1402 """Serializes the descriptor.
1403
1404 Returns:
1405 A bytearray() with the descriptor data.
1406 """
1407 encoded_name = self.partition_name.encode('utf-8')
1408 num_bytes_following = (
1409 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1410 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1411 padding_size = nbf_with_padding - num_bytes_following
1412 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001413 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001414 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001415 padding = struct.pack(str(padding_size) + 'x')
1416 ret = desc + encoded_name + self.public_key + padding
1417 return bytearray(ret)
1418
1419
1420DESCRIPTOR_CLASSES = [
1421 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1422 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1423]
1424
1425
1426def parse_descriptors(data):
1427 """Parses a blob of data into descriptors.
1428
1429 Arguments:
1430 data: A bytearray() with encoded descriptors.
1431
1432 Returns:
1433 A list of instances of objects derived from AvbDescriptor. For
1434 unknown descriptors, the class AvbDescriptor is used.
1435 """
1436 o = 0
1437 ret = []
1438 while o < len(data):
1439 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1440 if tag < len(DESCRIPTOR_CLASSES):
1441 c = DESCRIPTOR_CLASSES[tag]
1442 else:
1443 c = AvbDescriptor
1444 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1445 o += 16 + nb_following
1446 return ret
1447
1448
1449class AvbFooter(object):
1450 """A class for parsing and writing footers.
1451
1452 Footers are stored at the end of partitions and point to where the
1453 AvbVBMeta blob is located. They also contain the original size of
1454 the image before AVB information was added.
1455
1456 Attributes:
1457 magic: Magic for identifying the footer, see |MAGIC|.
1458 version_major: The major version of avbtool that wrote the footer.
1459 version_minor: The minor version of avbtool that wrote the footer.
1460 original_image_size: Original image size.
1461 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1462 vbmeta_size: Size of the AvbVBMeta blob.
1463 """
1464
1465 MAGIC = 'AVBf'
1466 SIZE = 64
1467 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001468 FOOTER_VERSION_MAJOR = 1
1469 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001470 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1471 'Q' # Original image size.
1472 'Q' # Offset of VBMeta blob.
1473 'Q' + # Size of VBMeta blob.
1474 str(RESERVED) + 'x') # padding for reserved bytes
1475
1476 def __init__(self, data=None):
1477 """Initializes a new footer object.
1478
1479 Arguments:
1480 data: If not None, must be a bytearray of size 4096.
1481
1482 Raises:
1483 LookupError: If the given footer is malformed.
1484 struct.error: If the given data has no footer.
1485 """
1486 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1487
1488 if data:
1489 (self.magic, self.version_major, self.version_minor,
1490 self.original_image_size, self.vbmeta_offset,
1491 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1492 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001493 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001494 else:
1495 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001496 self.version_major = self.FOOTER_VERSION_MAJOR
1497 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001498 self.original_image_size = 0
1499 self.vbmeta_offset = 0
1500 self.vbmeta_size = 0
1501
David Zeuthena4fee8b2016-08-22 15:20:43 -04001502 def encode(self):
1503 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001504
David Zeuthena4fee8b2016-08-22 15:20:43 -04001505 Returns:
1506 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001507 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001508 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1509 self.version_minor, self.original_image_size,
1510 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001511
1512
1513class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001514 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001515
1516 Attributes:
1517 The attributes correspond to the |AvbVBMetaHeader| struct
1518 defined in avb_vbmeta_header.h.
1519 """
1520
1521 SIZE = 256
1522
David Zeuthene3cadca2017-02-22 21:25:46 -05001523 # Keep in sync with |reserved0| and |reserved| field of
1524 # |AvbVBMetaImageHeader|.
1525 RESERVED0 = 4
1526 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001527
1528 # Keep in sync with |AvbVBMetaImageHeader|.
1529 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1530 '2Q' # 2 x block size
1531 'L' # algorithm type
1532 '2Q' # offset, size (hash)
1533 '2Q' # offset, size (signature)
1534 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001535 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001536 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001537 'Q' # rollback_index
1538 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001539 str(RESERVED0) + 'x' + # padding for reserved bytes
1540 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001541 str(RESERVED) + 'x') # padding for reserved bytes
1542
1543 def __init__(self, data=None):
1544 """Initializes a new header object.
1545
1546 Arguments:
1547 data: If not None, must be a bytearray of size 8192.
1548
1549 Raises:
1550 Exception: If the given data is malformed.
1551 """
1552 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1553
1554 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001555 (self.magic, self.required_libavb_version_major,
1556 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001557 self.authentication_data_block_size, self.auxiliary_data_block_size,
1558 self.algorithm_type, self.hash_offset, self.hash_size,
1559 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001560 self.public_key_size, self.public_key_metadata_offset,
1561 self.public_key_metadata_size, self.descriptors_offset,
1562 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001563 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001564 self.flags,
1565 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001566 # Nuke NUL-bytes at the end of the string.
1567 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001568 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001569 else:
1570 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001571 # Start by just requiring version 1.0. Code that adds features
1572 # in a future version can use bump_required_libavb_version_minor() to
1573 # bump the minor.
1574 self.required_libavb_version_major = AVB_VERSION_MAJOR
1575 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001576 self.authentication_data_block_size = 0
1577 self.auxiliary_data_block_size = 0
1578 self.algorithm_type = 0
1579 self.hash_offset = 0
1580 self.hash_size = 0
1581 self.signature_offset = 0
1582 self.signature_size = 0
1583 self.public_key_offset = 0
1584 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001585 self.public_key_metadata_offset = 0
1586 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001587 self.descriptors_offset = 0
1588 self.descriptors_size = 0
1589 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001590 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001591 self.release_string = get_release_string()
1592
1593 def bump_required_libavb_version_minor(self, minor):
1594 """Function to bump required_libavb_version_minor.
1595
1596 Call this when writing data that requires a specific libavb
1597 version to parse it.
1598
1599 Arguments:
1600 minor: The minor version of libavb that has support for the feature.
1601 """
1602 self.required_libavb_version_minor = (
1603 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001604
1605 def save(self, output):
1606 """Serializes the header (256 bytes) to disk.
1607
1608 Arguments:
1609 output: The object to write the output to.
1610 """
1611 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001612 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1613 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001614 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1615 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001616 self.public_key_offset, self.public_key_size,
1617 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001618 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001619 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001620
1621 def encode(self):
1622 """Serializes the header (256) to a bytearray().
1623
1624 Returns:
1625 A bytearray() with the encoded header.
1626 """
1627 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001628 self.required_libavb_version_major,
1629 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001630 self.authentication_data_block_size,
1631 self.auxiliary_data_block_size, self.algorithm_type,
1632 self.hash_offset, self.hash_size, self.signature_offset,
1633 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001634 self.public_key_size, self.public_key_metadata_offset,
1635 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001636 self.descriptors_size, self.rollback_index, self.flags,
1637 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001638
1639
1640class Avb(object):
1641 """Business logic for avbtool command-line tool."""
1642
David Zeuthen8b6973b2016-09-20 12:39:49 -04001643 # Keep in sync with avb_ab_flow.h.
1644 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1645 AB_MAGIC = '\0AB0'
1646 AB_MAJOR_VERSION = 1
1647 AB_MINOR_VERSION = 0
1648 AB_MISC_METADATA_OFFSET = 2048
1649
David Zeuthen09692692016-09-30 16:16:40 -04001650 # Constants for maximum metadata size. These are used to give
1651 # meaningful errors if the value passed in via --partition_size is
1652 # too small and when --calc_max_image_size is used. We use
1653 # conservative figures.
1654 MAX_VBMETA_SIZE = 64 * 1024
1655 MAX_FOOTER_SIZE = 4096
1656
David Zeuthena4fee8b2016-08-22 15:20:43 -04001657 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001658 """Implements the 'erase_footer' command.
1659
1660 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001661 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001662 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001663
1664 Raises:
1665 AvbError: If there's no footer in the image.
1666 """
1667
David Zeuthena4fee8b2016-08-22 15:20:43 -04001668 image = ImageHandler(image_filename)
1669
David Zeuthen21e95262016-07-27 17:58:40 -04001670 (footer, _, descriptors, _) = self._parse_image(image)
1671
1672 if not footer:
1673 raise AvbError('Given image does not have a footer.')
1674
1675 new_image_size = None
1676 if not keep_hashtree:
1677 new_image_size = footer.original_image_size
1678 else:
1679 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001680 # descriptor to figure out the location and size of the hashtree
1681 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001682 for desc in descriptors:
1683 if isinstance(desc, AvbHashtreeDescriptor):
1684 # The hashtree is always just following the main data so the
1685 # new size is easily derived.
1686 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001687 # If the image has FEC codes, also keep those.
1688 if desc.fec_offset > 0:
1689 fec_end = desc.fec_offset + desc.fec_size
1690 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001691 break
1692 if not new_image_size:
1693 raise AvbError('Requested to keep hashtree but no hashtree '
1694 'descriptor was found.')
1695
1696 # And cut...
1697 image.truncate(new_image_size)
1698
David Zeuthen8b6973b2016-09-20 12:39:49 -04001699 def set_ab_metadata(self, misc_image, slot_data):
1700 """Implements the 'set_ab_metadata' command.
1701
1702 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1703 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1704
1705 Arguments:
1706 misc_image: The misc image to write to.
1707 slot_data: Slot data as a string
1708
1709 Raises:
1710 AvbError: If slot data is malformed.
1711 """
1712 tokens = slot_data.split(':')
1713 if len(tokens) != 6:
1714 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1715 a_priority = int(tokens[0])
1716 a_tries_remaining = int(tokens[1])
1717 a_success = True if int(tokens[2]) != 0 else False
1718 b_priority = int(tokens[3])
1719 b_tries_remaining = int(tokens[4])
1720 b_success = True if int(tokens[5]) != 0 else False
1721
1722 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1723 self.AB_MAGIC,
1724 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1725 a_priority, a_tries_remaining, a_success,
1726 b_priority, b_tries_remaining, b_success)
1727 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1728 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1729 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1730 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1731 misc_image.write(ab_data)
1732
David Zeuthena4fee8b2016-08-22 15:20:43 -04001733 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001734 """Implements the 'info_image' command.
1735
1736 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001737 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001738 output: Output file to write human-readable information to (file object).
1739 """
1740
David Zeuthena4fee8b2016-08-22 15:20:43 -04001741 image = ImageHandler(image_filename)
1742
David Zeuthen21e95262016-07-27 17:58:40 -04001743 o = output
1744
1745 (footer, header, descriptors, image_size) = self._parse_image(image)
1746
1747 if footer:
1748 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1749 footer.version_minor))
1750 o.write('Image size: {} bytes\n'.format(image_size))
1751 o.write('Original image size: {} bytes\n'.format(
1752 footer.original_image_size))
1753 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1754 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1755 o.write('--\n')
1756
1757 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1758
David Zeuthene3cadca2017-02-22 21:25:46 -05001759 o.write('Minimum libavb version: {}.{}{}\n'.format(
1760 header.required_libavb_version_major,
1761 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001762 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001763 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1764 o.write('Authentication Block: {} bytes\n'.format(
1765 header.authentication_data_block_size))
1766 o.write('Auxiliary Block: {} bytes\n'.format(
1767 header.auxiliary_data_block_size))
1768 o.write('Algorithm: {}\n'.format(alg_name))
1769 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001770 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001771 o.write('Release String: \'{}\'\n'.format(
1772 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001773
1774 # Print descriptors.
1775 num_printed = 0
1776 o.write('Descriptors:\n')
1777 for desc in descriptors:
1778 desc.print_desc(o)
1779 num_printed += 1
1780 if num_printed == 0:
1781 o.write(' (none)\n')
1782
1783 def _parse_image(self, image):
1784 """Gets information about an image.
1785
1786 The image can either be a vbmeta or an image with a footer.
1787
1788 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001789 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001790
1791 Returns:
1792 A tuple where the first argument is a AvbFooter (None if there
1793 is no footer on the image), the second argument is a
1794 AvbVBMetaHeader, the third argument is a list of
1795 AvbDescriptor-derived instances, and the fourth argument is the
1796 size of |image|.
1797 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001798 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001799 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001800 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001801 try:
1802 footer = AvbFooter(image.read(AvbFooter.SIZE))
1803 except (LookupError, struct.error):
1804 # Nope, just seek back to the start.
1805 image.seek(0)
1806
1807 vbmeta_offset = 0
1808 if footer:
1809 vbmeta_offset = footer.vbmeta_offset
1810
1811 image.seek(vbmeta_offset)
1812 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1813
1814 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1815 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1816 desc_start_offset = aux_block_offset + h.descriptors_offset
1817 image.seek(desc_start_offset)
1818 descriptors = parse_descriptors(image.read(h.descriptors_size))
1819
David Zeuthen09692692016-09-30 16:16:40 -04001820 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001821
David Zeuthenb1b994d2017-03-06 18:01:31 -05001822 def _load_vbmeta_blob(self, image):
1823 """Gets the vbmeta struct and associated sections.
1824
1825 The image can either be a vbmeta.img or an image with a footer.
1826
1827 Arguments:
1828 image: An ImageHandler (vbmeta or footer).
1829
1830 Returns:
1831 A blob with the vbmeta struct and other sections.
1832 """
1833 assert isinstance(image, ImageHandler)
1834 footer = None
1835 image.seek(image.image_size - AvbFooter.SIZE)
1836 try:
1837 footer = AvbFooter(image.read(AvbFooter.SIZE))
1838 except (LookupError, struct.error):
1839 # Nope, just seek back to the start.
1840 image.seek(0)
1841
1842 vbmeta_offset = 0
1843 if footer:
1844 vbmeta_offset = footer.vbmeta_offset
1845
1846 image.seek(vbmeta_offset)
1847 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1848
1849 image.seek(vbmeta_offset)
1850 data_size = AvbVBMetaHeader.SIZE
1851 data_size += h.authentication_data_block_size
1852 data_size += h.auxiliary_data_block_size
1853 return image.read(data_size)
1854
David Zeuthenfd41eb92016-11-17 12:24:47 -05001855 def _get_cmdline_descriptors_for_dm_verity(self, image):
1856 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001857
1858 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001859 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001860
1861 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001862 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1863 instructions. There is one for when hashtree is not disabled and one for
1864 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001865
1866 Raises:
1867 AvbError: If |image| doesn't have a hashtree descriptor.
1868
1869 """
1870
1871 (_, _, descriptors, _) = self._parse_image(image)
1872
1873 ht = None
1874 for desc in descriptors:
1875 if isinstance(desc, AvbHashtreeDescriptor):
1876 ht = desc
1877 break
1878
1879 if not ht:
1880 raise AvbError('No hashtree descriptor in given image')
1881
1882 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001883 c += '0' # start
1884 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1885 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1886 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1887 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1888 c += ' {}'.format(ht.data_block_size) # data_block
1889 c += ' {}'.format(ht.hash_block_size) # hash_block
1890 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1891 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1892 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1893 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1894 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1895 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001896 c += ' 10' # number of optional args
1897 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001898 c += ' ignore_zero_blocks'
1899 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1900 c += ' fec_roots {}'.format(ht.fec_num_roots)
1901 # Note that fec_blocks is the size that FEC covers, *not* the
1902 # size of the FEC data. Since we use FEC for everything up until
1903 # the FEC data, it's the same as the offset.
1904 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1905 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1906 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001907 c += ' 2' # number of optional args
1908 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001909 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001910 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001911
David Zeuthenfd41eb92016-11-17 12:24:47 -05001912 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001913 desc = AvbKernelCmdlineDescriptor()
1914 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001915 desc.flags = (
1916 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1917
1918 # The descriptor for when hashtree verification is disabled is a lot
1919 # simpler - we just set the root to the partition.
1920 desc_no_ht = AvbKernelCmdlineDescriptor()
1921 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1922 desc_no_ht.flags = (
1923 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1924
1925 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001926
1927 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001928 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001929 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001930 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001931 include_descriptors_from_image, signing_helper,
1932 release_string,
1933 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001934 """Implements the 'make_vbmeta_image' command.
1935
1936 Arguments:
1937 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001938 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001939 algorithm_name: Name of algorithm to use.
1940 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001941 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001942 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001943 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001944 props: Properties to insert (list of strings of the form 'key:value').
1945 props_from_file: Properties to insert (list of strings 'key:<path>').
1946 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001947 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001948 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001949 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001950 release_string: None or avbtool release string to use instead of default.
1951 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001952
1953 Raises:
1954 AvbError: If a chained partition is malformed.
1955 """
1956
1957 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04001958 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001959 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001960 chain_partitions, rollback_index, flags, props, props_from_file,
1961 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001962 include_descriptors_from_image, signing_helper, release_string,
1963 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001964
1965 # Write entire vbmeta blob (header, authentication, auxiliary).
1966 output.seek(0)
1967 output.write(vbmeta_blob)
1968
David Zeuthen18666ab2016-11-15 11:18:05 -05001969 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1970 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001971 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001972 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001973 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001974 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001975 include_descriptors_from_image, signing_helper,
1976 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001977 """Generates a VBMeta blob.
1978
1979 This blob contains the header (struct AvbVBMetaHeader), the
1980 authentication data block (which contains the hash and signature
1981 for the header and auxiliary block), and the auxiliary block
1982 (which contains descriptors, the public key used, and other data).
1983
1984 The |key| parameter can |None| only if the |algorithm_name| is
1985 'NONE'.
1986
1987 Arguments:
1988 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1989 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001990 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001991 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001992 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001993 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001994 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001995 props: Properties to insert (List of strings of the form 'key:value').
1996 props_from_file: Properties to insert (List of strings 'key:<path>').
1997 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001998 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001999 dm-verity kernel cmdline from.
2000 include_descriptors_from_image: List of file objects for which
2001 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002002 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002003 release_string: None or avbtool release string.
2004 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002005
2006 Returns:
2007 A bytearray() with the VBMeta blob.
2008
2009 Raises:
2010 Exception: If the |algorithm_name| is not found, if no key has
2011 been given and the given algorithm requires one, or the key is
2012 of the wrong size.
2013
2014 """
2015 try:
2016 alg = ALGORITHMS[algorithm_name]
2017 except KeyError:
2018 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2019
David Zeuthena5fd3a42017-02-27 16:38:54 -05002020 if not descriptors:
2021 descriptors = []
2022
2023 # Insert chained partition descriptors, if any
2024 if chain_partitions:
2025 for cp in chain_partitions:
2026 cp_tokens = cp.split(':')
2027 if len(cp_tokens) != 3:
2028 raise AvbError('Malformed chained partition "{}".'.format(cp))
2029 desc = AvbChainPartitionDescriptor()
2030 desc.partition_name = cp_tokens[0]
2031 desc.rollback_index_location = int(cp_tokens[1])
2032 if desc.rollback_index_location < 1:
2033 raise AvbError('Rollback index location must be 1 or larger.')
2034 file_path = cp_tokens[2]
2035 desc.public_key = open(file_path, 'rb').read()
2036 descriptors.append(desc)
2037
David Zeuthen21e95262016-07-27 17:58:40 -04002038 # Descriptors.
2039 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002040 for desc in descriptors:
2041 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002042
2043 # Add properties.
2044 if props:
2045 for prop in props:
2046 idx = prop.find(':')
2047 if idx == -1:
2048 raise AvbError('Malformed property "{}".'.format(prop))
2049 desc = AvbPropertyDescriptor()
2050 desc.key = prop[0:idx]
2051 desc.value = prop[(idx + 1):]
2052 encoded_descriptors.extend(desc.encode())
2053 if props_from_file:
2054 for prop in props_from_file:
2055 idx = prop.find(':')
2056 if idx == -1:
2057 raise AvbError('Malformed property "{}".'.format(prop))
2058 desc = AvbPropertyDescriptor()
2059 desc.key = prop[0:idx]
2060 desc.value = prop[(idx + 1):]
2061 file_path = prop[(idx + 1):]
2062 desc.value = open(file_path, 'rb').read()
2063 encoded_descriptors.extend(desc.encode())
2064
2065 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002066 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002067 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002068 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002069 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2070 encoded_descriptors.extend(cmdline_desc[0].encode())
2071 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002072
2073 # Add kernel command-lines.
2074 if kernel_cmdlines:
2075 for i in kernel_cmdlines:
2076 desc = AvbKernelCmdlineDescriptor()
2077 desc.kernel_cmdline = i
2078 encoded_descriptors.extend(desc.encode())
2079
2080 # Add descriptors from other images.
2081 if include_descriptors_from_image:
2082 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002083 image_handler = ImageHandler(image.name)
2084 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002085 for desc in image_descriptors:
2086 encoded_descriptors.extend(desc.encode())
2087
David Zeuthen18666ab2016-11-15 11:18:05 -05002088 # Load public key metadata blob, if requested.
2089 pkmd_blob = []
2090 if public_key_metadata_path:
2091 with open(public_key_metadata_path) as f:
2092 pkmd_blob = f.read()
2093
David Zeuthen21e95262016-07-27 17:58:40 -04002094 key = None
2095 encoded_key = bytearray()
2096 if alg.public_key_num_bytes > 0:
2097 if not key_path:
2098 raise AvbError('Key is required for algorithm {}'.format(
2099 algorithm_name))
2100 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2101 encoded_key = encode_rsa_key(key)
2102 if len(encoded_key) != alg.public_key_num_bytes:
2103 raise AvbError('Key is wrong size for algorithm {}'.format(
2104 algorithm_name))
2105
2106 h = AvbVBMetaHeader()
2107
David Zeuthene3cadca2017-02-22 21:25:46 -05002108 # Override release string, if requested.
2109 if isinstance(release_string, (str, unicode)):
2110 h.release_string = release_string
2111
2112 # Append to release string, if requested. Also insert a space before.
2113 if isinstance(append_to_release_string, (str, unicode)):
2114 h.release_string += ' ' + append_to_release_string
2115
David Zeuthen18666ab2016-11-15 11:18:05 -05002116 # For the Auxiliary data block, descriptors are stored at offset 0,
2117 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002118 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002119 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002120 h.descriptors_offset = 0
2121 h.descriptors_size = len(encoded_descriptors)
2122 h.public_key_offset = h.descriptors_size
2123 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002124 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2125 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002126
2127 # For the Authentication data block, the hash is first and then
2128 # the signature.
2129 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002130 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002131 h.algorithm_type = alg.algorithm_type
2132 h.hash_offset = 0
2133 h.hash_size = alg.hash_num_bytes
2134 # Signature offset and size - it's stored right after the hash
2135 # (in Authentication data block).
2136 h.signature_offset = alg.hash_num_bytes
2137 h.signature_size = alg.signature_num_bytes
2138
2139 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002140 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002141
2142 # Generate Header data block.
2143 header_data_blob = h.encode()
2144
2145 # Generate Auxiliary data block.
2146 aux_data_blob = bytearray()
2147 aux_data_blob.extend(encoded_descriptors)
2148 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002149 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002150 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2151 aux_data_blob.extend('\0' * padding_bytes)
2152
2153 # Calculate the hash.
2154 binary_hash = bytearray()
2155 binary_signature = bytearray()
2156 if algorithm_name != 'NONE':
2157 if algorithm_name[0:6] == 'SHA256':
2158 ha = hashlib.sha256()
2159 elif algorithm_name[0:6] == 'SHA512':
2160 ha = hashlib.sha512()
2161 else:
2162 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2163 ha.update(header_data_blob)
2164 ha.update(aux_data_blob)
2165 binary_hash.extend(ha.digest())
2166
2167 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002168 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002169 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2170 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002171
2172 # Generate Authentication data block.
2173 auth_data_blob = bytearray()
2174 auth_data_blob.extend(binary_hash)
2175 auth_data_blob.extend(binary_signature)
2176 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2177 auth_data_blob.extend('\0' * padding_bytes)
2178
2179 return header_data_blob + auth_data_blob + aux_data_blob
2180
2181 def extract_public_key(self, key_path, output):
2182 """Implements the 'extract_public_key' command.
2183
2184 Arguments:
2185 key_path: The path to a RSA private key file.
2186 output: The file to write to.
2187 """
2188 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2189 write_rsa_key(output, key)
2190
David Zeuthenb1b994d2017-03-06 18:01:31 -05002191 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2192 partition_size):
2193 """Implementation of the append_vbmeta_image command.
2194
2195 Arguments:
2196 image_filename: File to add the footer to.
2197 vbmeta_image_filename: File to get vbmeta struct from.
2198 partition_size: Size of partition.
2199
2200 Raises:
2201 AvbError: If an argument is incorrect.
2202 """
2203 image = ImageHandler(image_filename)
2204
2205 if partition_size % image.block_size != 0:
2206 raise AvbError('Partition size of {} is not a multiple of the image '
2207 'block size {}.'.format(partition_size,
2208 image.block_size))
2209
2210 # If there's already a footer, truncate the image to its original
2211 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2212 image.seek(image.image_size - AvbFooter.SIZE)
2213 try:
2214 footer = AvbFooter(image.read(AvbFooter.SIZE))
2215 # Existing footer found. Just truncate.
2216 original_image_size = footer.original_image_size
2217 image.truncate(footer.original_image_size)
2218 except (LookupError, struct.error):
2219 original_image_size = image.image_size
2220
2221 # If anything goes wrong from here-on, restore the image back to
2222 # its original size.
2223 try:
2224 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2225 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2226
2227 # If the image isn't sparse, its size might not be a multiple of
2228 # the block size. This will screw up padding later so just grow it.
2229 if image.image_size % image.block_size != 0:
2230 assert not image.is_sparse
2231 padding_needed = image.block_size - (image.image_size%image.block_size)
2232 image.truncate(image.image_size + padding_needed)
2233
2234 # The append_raw() method requires content with size being a
2235 # multiple of |block_size| so add padding as needed. Also record
2236 # where this is written to since we'll need to put that in the
2237 # footer.
2238 vbmeta_offset = image.image_size
2239 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2240 len(vbmeta_blob))
2241 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2242
2243 # Append vbmeta blob and footer
2244 image.append_raw(vbmeta_blob_with_padding)
2245 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2246
2247 # Now insert a DONT_CARE chunk with enough bytes such that the
2248 # final Footer block is at the end of partition_size..
2249 image.append_dont_care(partition_size - vbmeta_end_offset -
2250 1*image.block_size)
2251
2252 # Generate the Footer that tells where the VBMeta footer
2253 # is. Also put enough padding in the front of the footer since
2254 # we'll write out an entire block.
2255 footer = AvbFooter()
2256 footer.original_image_size = original_image_size
2257 footer.vbmeta_offset = vbmeta_offset
2258 footer.vbmeta_size = len(vbmeta_blob)
2259 footer_blob = footer.encode()
2260 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2261 footer_blob)
2262 image.append_raw(footer_blob_with_padding)
2263
2264 except:
2265 # Truncate back to original size, then re-raise
2266 image.truncate(original_image_size)
2267 raise
2268
David Zeuthena4fee8b2016-08-22 15:20:43 -04002269 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002270 hash_algorithm, salt, chain_partitions, algorithm_name,
2271 key_path,
2272 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002273 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002274 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002275 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002276 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002277 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002278 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002279
2280 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002281 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002282 partition_size: Size of partition.
2283 partition_name: Name of partition (without A/B suffix).
2284 hash_algorithm: Hash algorithm to use.
2285 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002286 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002287 algorithm_name: Name of algorithm to use.
2288 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002289 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002290 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002291 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002292 props: Properties to insert (List of strings of the form 'key:value').
2293 props_from_file: Properties to insert (List of strings 'key:<path>').
2294 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002295 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002296 dm-verity kernel cmdline from.
2297 include_descriptors_from_image: List of file objects for which
2298 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002299 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002300 release_string: None or avbtool release string.
2301 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002302 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2303 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002304
2305 Raises:
2306 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002307 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002308 image = ImageHandler(image_filename)
2309
2310 if partition_size % image.block_size != 0:
2311 raise AvbError('Partition size of {} is not a multiple of the image '
2312 'block size {}.'.format(partition_size,
2313 image.block_size))
2314
David Zeuthen21e95262016-07-27 17:58:40 -04002315 # If there's already a footer, truncate the image to its original
2316 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2317 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002318 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002319 try:
2320 footer = AvbFooter(image.read(AvbFooter.SIZE))
2321 # Existing footer found. Just truncate.
2322 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002323 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002324 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002325 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002326
2327 # If anything goes wrong from here-on, restore the image back to
2328 # its original size.
2329 try:
David Zeuthen09692692016-09-30 16:16:40 -04002330 # First, calculate the maximum image size such that an image
2331 # this size + metadata (footer + vbmeta struct) fits in
2332 # |partition_size|.
2333 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2334 max_image_size = partition_size - max_metadata_size
2335
2336 # If image size exceeds the maximum image size, fail.
2337 if image.image_size > max_image_size:
2338 raise AvbError('Image size of {} exceeds maximum image '
2339 'size of {} in order to fit in a partition '
2340 'size of {}.'.format(image.image_size, max_image_size,
2341 partition_size))
2342
David Zeuthen21e95262016-07-27 17:58:40 -04002343 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2344 if salt:
2345 salt = salt.decode('hex')
2346 else:
2347 if salt is None:
2348 # If salt is not explicitly specified, choose a hash
2349 # that's the same size as the hash size.
2350 hash_size = digest_size
2351 salt = open('/dev/urandom').read(hash_size)
2352 else:
2353 salt = ''
2354
2355 hasher = hashlib.new(name=hash_algorithm, string=salt)
2356 # TODO(zeuthen): might want to read this in chunks to avoid
2357 # memory pressure, then again, this is only supposed to be used
2358 # on kernel/initramfs partitions. Possible optimization.
2359 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002360 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002361 digest = hasher.digest()
2362
2363 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002364 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002365 h_desc.hash_algorithm = hash_algorithm
2366 h_desc.partition_name = partition_name
2367 h_desc.salt = salt
2368 h_desc.digest = digest
2369
2370 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002371 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002372 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002373 chain_partitions, rollback_index, flags, props, props_from_file,
2374 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002375 include_descriptors_from_image, signing_helper, release_string,
2376 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002377
David Zeuthena4fee8b2016-08-22 15:20:43 -04002378 # If the image isn't sparse, its size might not be a multiple of
2379 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002380 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002381 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002382 padding_needed = image.block_size - (image.image_size%image.block_size)
2383 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002384
David Zeuthena4fee8b2016-08-22 15:20:43 -04002385 # The append_raw() method requires content with size being a
2386 # multiple of |block_size| so add padding as needed. Also record
2387 # where this is written to since we'll need to put that in the
2388 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002389 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002390 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2391 len(vbmeta_blob))
2392 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002393
David Zeuthend247fcb2017-02-16 12:09:27 -05002394 # Write vbmeta blob, if requested.
2395 if output_vbmeta_image:
2396 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002397
David Zeuthend247fcb2017-02-16 12:09:27 -05002398 # Append vbmeta blob and footer, unless requested not to.
2399 if not do_not_append_vbmeta_image:
2400 image.append_raw(vbmeta_blob_with_padding)
2401 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2402
2403 # Now insert a DONT_CARE chunk with enough bytes such that the
2404 # final Footer block is at the end of partition_size..
2405 image.append_dont_care(partition_size - vbmeta_end_offset -
2406 1*image.block_size)
2407
2408 # Generate the Footer that tells where the VBMeta footer
2409 # is. Also put enough padding in the front of the footer since
2410 # we'll write out an entire block.
2411 footer = AvbFooter()
2412 footer.original_image_size = original_image_size
2413 footer.vbmeta_offset = vbmeta_offset
2414 footer.vbmeta_size = len(vbmeta_blob)
2415 footer_blob = footer.encode()
2416 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2417 footer_blob)
2418 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002419
David Zeuthen21e95262016-07-27 17:58:40 -04002420 except:
2421 # Truncate back to original size, then re-raise
2422 image.truncate(original_image_size)
2423 raise
2424
David Zeuthena4fee8b2016-08-22 15:20:43 -04002425 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002426 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002427 block_size, salt, chain_partitions, algorithm_name,
2428 key_path,
2429 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002430 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002431 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002432 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002433 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002434 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002435 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002436 """Implements the 'add_hashtree_footer' command.
2437
2438 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2439 more information about dm-verity and these hashes.
2440
2441 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002442 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002443 partition_size: Size of partition.
2444 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002445 generate_fec: If True, generate FEC codes.
2446 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002447 hash_algorithm: Hash algorithm to use.
2448 block_size: Block size to use.
2449 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002450 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002451 algorithm_name: Name of algorithm to use.
2452 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002453 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002454 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002455 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002456 props: Properties to insert (List of strings of the form 'key:value').
2457 props_from_file: Properties to insert (List of strings 'key:<path>').
2458 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002459 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002460 dm-verity kernel cmdline from.
2461 include_descriptors_from_image: List of file objects for which
2462 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002463 calc_max_image_size: Don't store the hashtree or footer - instead
2464 calculate the maximum image size leaving enough room for hashtree
2465 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002466 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002467 release_string: None or avbtool release string.
2468 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002469 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2470 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002471
2472 Raises:
2473 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002474 """
David Zeuthen09692692016-09-30 16:16:40 -04002475 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2476 digest_padding = round_to_pow2(digest_size) - digest_size
2477
2478 # First, calculate the maximum image size such that an image
2479 # this size + the hashtree + metadata (footer + vbmeta struct)
2480 # fits in |partition_size|. We use very conservative figures for
2481 # metadata.
2482 (_, max_tree_size) = calc_hash_level_offsets(
2483 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002484 max_fec_size = 0
2485 if generate_fec:
2486 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2487 max_metadata_size = (max_fec_size + max_tree_size +
2488 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002489 self.MAX_FOOTER_SIZE)
2490 max_image_size = partition_size - max_metadata_size
2491
2492 # If we're asked to only calculate the maximum image size, we're done.
2493 if calc_max_image_size:
2494 print '{}'.format(max_image_size)
2495 return
2496
David Zeuthena4fee8b2016-08-22 15:20:43 -04002497 image = ImageHandler(image_filename)
2498
2499 if partition_size % image.block_size != 0:
2500 raise AvbError('Partition size of {} is not a multiple of the image '
2501 'block size {}.'.format(partition_size,
2502 image.block_size))
2503
David Zeuthen21e95262016-07-27 17:58:40 -04002504 # If there's already a footer, truncate the image to its original
2505 # size. This way 'avbtool add_hashtree_footer' is idempotent
2506 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002507 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002508 try:
2509 footer = AvbFooter(image.read(AvbFooter.SIZE))
2510 # Existing footer found. Just truncate.
2511 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002512 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002513 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002514 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002515
2516 # If anything goes wrong from here-on, restore the image back to
2517 # its original size.
2518 try:
2519 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002520 rounded_image_size = round_to_multiple(image.image_size, block_size)
2521 if rounded_image_size > image.image_size:
2522 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002523
David Zeuthen09692692016-09-30 16:16:40 -04002524 # If image size exceeds the maximum image size, fail.
2525 if image.image_size > max_image_size:
2526 raise AvbError('Image size of {} exceeds maximum image '
2527 'size of {} in order to fit in a partition '
2528 'size of {}.'.format(image.image_size, max_image_size,
2529 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002530
2531 if salt:
2532 salt = salt.decode('hex')
2533 else:
2534 if salt is None:
2535 # If salt is not explicitly specified, choose a hash
2536 # that's the same size as the hash size.
2537 hash_size = digest_size
2538 salt = open('/dev/urandom').read(hash_size)
2539 else:
2540 salt = ''
2541
David Zeuthena4fee8b2016-08-22 15:20:43 -04002542 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002543 # offsets in advance.
2544 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002545 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002546
David Zeuthena4fee8b2016-08-22 15:20:43 -04002547 # If the image isn't sparse, its size might not be a multiple of
2548 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002549 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002550 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002551 padding_needed = image.block_size - (image.image_size%image.block_size)
2552 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002553
David Zeuthena4fee8b2016-08-22 15:20:43 -04002554 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002555 tree_offset = image.image_size
2556 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002557 block_size,
2558 hash_algorithm, salt,
2559 digest_padding,
2560 hash_level_offsets,
2561 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002562
2563 # Generate HashtreeDescriptor with details about the tree we
2564 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002565 ht_desc = AvbHashtreeDescriptor()
2566 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002567 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002568 ht_desc.tree_offset = tree_offset
2569 ht_desc.tree_size = tree_size
2570 ht_desc.data_block_size = block_size
2571 ht_desc.hash_block_size = block_size
2572 ht_desc.hash_algorithm = hash_algorithm
2573 ht_desc.partition_name = partition_name
2574 ht_desc.salt = salt
2575 ht_desc.root_digest = root_digest
2576
David Zeuthen09692692016-09-30 16:16:40 -04002577 # Write the hash tree
2578 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2579 len(hash_tree))
2580 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2581 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002582 len_hashtree_and_fec = len(hash_tree_with_padding)
2583
2584 # Generate FEC codes, if requested.
2585 if generate_fec:
2586 fec_data = generate_fec_data(image_filename, fec_num_roots)
2587 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2588 len(fec_data))
2589 fec_data_with_padding = fec_data + '\0'*padding_needed
2590 fec_offset = image.image_size
2591 image.append_raw(fec_data_with_padding)
2592 len_hashtree_and_fec += len(fec_data_with_padding)
2593 # Update the hashtree descriptor.
2594 ht_desc.fec_num_roots = fec_num_roots
2595 ht_desc.fec_offset = fec_offset
2596 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002597
David Zeuthena4fee8b2016-08-22 15:20:43 -04002598 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002599 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002600 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002601 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002602 chain_partitions, rollback_index, flags, props, props_from_file,
2603 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002604 include_descriptors_from_image, signing_helper, release_string,
2605 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002606 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2607 len(vbmeta_blob))
2608 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002609
David Zeuthend247fcb2017-02-16 12:09:27 -05002610 # Write vbmeta blob, if requested.
2611 if output_vbmeta_image:
2612 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002613
David Zeuthend247fcb2017-02-16 12:09:27 -05002614 # Append vbmeta blob and footer, unless requested not to.
2615 if not do_not_append_vbmeta_image:
2616 image.append_raw(vbmeta_blob_with_padding)
2617
2618 # Now insert a DONT_CARE chunk with enough bytes such that the
2619 # final Footer block is at the end of partition_size..
2620 image.append_dont_care(partition_size - image.image_size -
2621 1*image.block_size)
2622
2623 # Generate the Footer that tells where the VBMeta footer
2624 # is. Also put enough padding in the front of the footer since
2625 # we'll write out an entire block.
2626 footer = AvbFooter()
2627 footer.original_image_size = original_image_size
2628 footer.vbmeta_offset = vbmeta_offset
2629 footer.vbmeta_size = len(vbmeta_blob)
2630 footer_blob = footer.encode()
2631 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2632 footer_blob)
2633 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002634
David Zeuthen21e95262016-07-27 17:58:40 -04002635 except:
David Zeuthen09692692016-09-30 16:16:40 -04002636 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002637 image.truncate(original_image_size)
2638 raise
2639
Darren Krahn147b08d2016-12-20 16:38:29 -08002640 def make_atx_certificate(self, output, authority_key_path, subject_key,
2641 subject_key_version, subject,
2642 is_intermediate_authority, signing_helper):
2643 """Implements the 'make_atx_certificate' command.
2644
2645 Android Things certificates are required for Android Things public key
2646 metadata. They chain the vbmeta signing key for a particular product back to
2647 a fused, permanent root key. These certificates are fixed-length and fixed-
2648 format with the explicit goal of not parsing ASN.1 in bootloader code.
2649
2650 Arguments:
2651 output: Certificate will be written to this file on success.
2652 authority_key_path: A PEM file path with the authority private key.
2653 If None, then a certificate will be created without a
2654 signature. The signature can be created out-of-band
2655 and appended.
2656 subject_key: A PEM or DER subject public key.
2657 subject_key_version: A 64-bit version value. If this is None, the number
2658 of seconds since the epoch is used.
2659 subject: A subject identifier. For Product Signing Key certificates this
2660 should be the same Product ID found in the permanent attributes.
2661 is_intermediate_authority: True if the certificate is for an intermediate
2662 authority.
2663 signing_helper: Program which signs a hash and returns the signature.
2664 """
2665 signed_data = bytearray()
2666 signed_data.extend(struct.pack('<I', 1)) # Format Version
2667 signed_data.extend(
2668 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2669 hasher = hashlib.sha256()
2670 hasher.update(subject)
2671 signed_data.extend(hasher.digest())
2672 usage = 'com.google.android.things.vboot'
2673 if is_intermediate_authority:
2674 usage += '.ca'
2675 hasher = hashlib.sha256()
2676 hasher.update(usage)
2677 signed_data.extend(hasher.digest())
2678 if not subject_key_version:
2679 subject_key_version = int(time.time())
2680 signed_data.extend(struct.pack('<Q', subject_key_version))
2681 signature = bytearray()
2682 if authority_key_path:
2683 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002684 algorithm_name = 'SHA512_RSA4096'
2685 hasher = hashlib.sha512()
Darren Krahn147b08d2016-12-20 16:38:29 -08002686 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2687 hasher.update(signed_data)
2688 padding_and_hash.extend(hasher.digest())
2689 signature.extend(raw_sign(signing_helper, algorithm_name,
2690 authority_key_path, padding_and_hash))
2691 output.write(signed_data)
2692 output.write(signature)
2693
2694 def make_atx_permanent_attributes(self, output, root_authority_key,
2695 product_id):
2696 """Implements the 'make_atx_permanent_attributes' command.
2697
2698 Android Things permanent attributes are designed to be permanent for a
2699 particular product and a hash of these attributes should be fused into
2700 hardware to enforce this.
2701
2702 Arguments:
2703 output: Attributes will be written to this file on success.
2704 root_authority_key: A PEM or DER public key for the root authority.
2705 product_id: A 16-byte Product ID.
2706
2707 Raises:
2708 AvbError: If an argument is incorrect.
2709 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002710 EXPECTED_PRODUCT_ID_SIZE = 16
2711 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002712 raise AvbError('Invalid Product ID length.')
2713 output.write(struct.pack('<I', 1)) # Format Version
2714 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2715 output.write(product_id)
2716
2717 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002718 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002719 """Implements the 'make_atx_metadata' command.
2720
2721 Android Things metadata are included in vbmeta images to facilitate
2722 verification. The output of this command can be used as the
2723 public_key_metadata argument to other commands.
2724
2725 Arguments:
2726 output: Metadata will be written to this file on success.
2727 intermediate_key_certificate: A certificate file as output by
2728 make_atx_certificate with
2729 is_intermediate_authority set to true.
2730 product_key_certificate: A certificate file as output by
2731 make_atx_certificate with
2732 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002733
2734 Raises:
2735 AvbError: If an argument is incorrect.
2736 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002737 EXPECTED_CERTIFICATE_SIZE = 1620
2738 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002739 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002740 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002741 raise AvbError('Invalid product key certificate length.')
2742 output.write(struct.pack('<I', 1)) # Format Version
2743 output.write(intermediate_key_certificate)
2744 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002745
David Zeuthen21e95262016-07-27 17:58:40 -04002746
2747def calc_hash_level_offsets(image_size, block_size, digest_size):
2748 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2749
2750 Arguments:
2751 image_size: The size of the image to calculate a Merkle-tree for.
2752 block_size: The block size, e.g. 4096.
2753 digest_size: The size of each hash, e.g. 32 for SHA-256.
2754
2755 Returns:
2756 A tuple where the first argument is an array of offsets and the
2757 second is size of the tree, in bytes.
2758 """
2759 level_offsets = []
2760 level_sizes = []
2761 tree_size = 0
2762
2763 num_levels = 0
2764 size = image_size
2765 while size > block_size:
2766 num_blocks = (size + block_size - 1) / block_size
2767 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2768
2769 level_sizes.append(level_size)
2770 tree_size += level_size
2771 num_levels += 1
2772
2773 size = level_size
2774
2775 for n in range(0, num_levels):
2776 offset = 0
2777 for m in range(n + 1, num_levels):
2778 offset += level_sizes[m]
2779 level_offsets.append(offset)
2780
David Zeuthena4fee8b2016-08-22 15:20:43 -04002781 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002782
2783
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002784# See system/extras/libfec/include/fec/io.h for these definitions.
2785FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2786FEC_MAGIC = 0xfecfecfe
2787
2788
2789def calc_fec_data_size(image_size, num_roots):
2790 """Calculates how much space FEC data will take.
2791
2792 Args:
2793 image_size: The size of the image.
2794 num_roots: Number of roots.
2795
2796 Returns:
2797 The number of bytes needed for FEC for an image of the given size
2798 and with the requested number of FEC roots.
2799
2800 Raises:
2801 ValueError: If output from the 'fec' tool is invalid.
2802
2803 """
2804 p = subprocess.Popen(
2805 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2806 stdout=subprocess.PIPE,
2807 stderr=subprocess.PIPE)
2808 (pout, perr) = p.communicate()
2809 retcode = p.wait()
2810 if retcode != 0:
2811 raise ValueError('Error invoking fec: {}'.format(perr))
2812 return int(pout)
2813
2814
2815def generate_fec_data(image_filename, num_roots):
2816 """Generate FEC codes for an image.
2817
2818 Args:
2819 image_filename: The filename of the image.
2820 num_roots: Number of roots.
2821
2822 Returns:
2823 The FEC data blob.
2824
2825 Raises:
2826 ValueError: If output from the 'fec' tool is invalid.
2827 """
2828 fec_tmpfile = tempfile.NamedTemporaryFile()
2829 subprocess.check_call(
2830 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2831 fec_tmpfile.name],
2832 stderr=open(os.devnull))
2833 fec_data = fec_tmpfile.read()
2834 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2835 footer_data = fec_data[-footer_size:]
2836 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2837 footer_data)
2838 if magic != FEC_MAGIC:
2839 raise ValueError('Unexpected magic in FEC footer')
2840 return fec_data[0:fec_size]
2841
2842
David Zeuthen21e95262016-07-27 17:58:40 -04002843def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002844 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002845 """Generates a Merkle-tree for a file.
2846
2847 Args:
2848 image: The image, as a file.
2849 image_size: The size of the image.
2850 block_size: The block size, e.g. 4096.
2851 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2852 salt: The salt to use.
2853 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002854 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002855 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002856
2857 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002858 A tuple where the first element is the top-level hash and the
2859 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002860 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002861 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002862 hash_src_offset = 0
2863 hash_src_size = image_size
2864 level_num = 0
2865 while hash_src_size > block_size:
2866 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002867 remaining = hash_src_size
2868 while remaining > 0:
2869 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002870 # Only read from the file for the first level - for subsequent
2871 # levels, access the array we're building.
2872 if level_num == 0:
2873 image.seek(hash_src_offset + hash_src_size - remaining)
2874 data = image.read(min(remaining, block_size))
2875 else:
2876 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2877 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002878 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002879
2880 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002881 if len(data) < block_size:
2882 hasher.update('\0' * (block_size - len(data)))
2883 level_output += hasher.digest()
2884 if digest_padding > 0:
2885 level_output += '\0' * digest_padding
2886
2887 padding_needed = (round_to_multiple(
2888 len(level_output), block_size) - len(level_output))
2889 level_output += '\0' * padding_needed
2890
David Zeuthena4fee8b2016-08-22 15:20:43 -04002891 # Copy level-output into resulting tree.
2892 offset = hash_level_offsets[level_num]
2893 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002894
David Zeuthena4fee8b2016-08-22 15:20:43 -04002895 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002896 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002897 level_num += 1
2898
2899 hasher = hashlib.new(name=hash_alg_name, string=salt)
2900 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002901 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002902
2903
2904class AvbTool(object):
2905 """Object for avbtool command-line tool."""
2906
2907 def __init__(self):
2908 """Initializer method."""
2909 self.avb = Avb()
2910
2911 def _add_common_args(self, sub_parser):
2912 """Adds arguments used by several sub-commands.
2913
2914 Arguments:
2915 sub_parser: The parser to add arguments to.
2916 """
2917 sub_parser.add_argument('--algorithm',
2918 help='Algorithm to use (default: NONE)',
2919 metavar='ALGORITHM',
2920 default='NONE')
2921 sub_parser.add_argument('--key',
2922 help='Path to RSA private key file',
2923 metavar='KEY',
2924 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002925 sub_parser.add_argument('--signing_helper',
2926 help='Path to helper used for signing',
2927 metavar='APP',
2928 default=None,
2929 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002930 sub_parser.add_argument('--public_key_metadata',
2931 help='Path to public key metadata file',
2932 metavar='KEY_METADATA',
2933 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002934 sub_parser.add_argument('--rollback_index',
2935 help='Rollback Index',
2936 type=parse_number,
2937 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05002938 # This is used internally for unit tests. Do not include in --help output.
2939 sub_parser.add_argument('--internal_release_string',
2940 help=argparse.SUPPRESS)
2941 sub_parser.add_argument('--append_to_release_string',
2942 help='Text to append to release string',
2943 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04002944 sub_parser.add_argument('--prop',
2945 help='Add property',
2946 metavar='KEY:VALUE',
2947 action='append')
2948 sub_parser.add_argument('--prop_from_file',
2949 help='Add property from file',
2950 metavar='KEY:PATH',
2951 action='append')
2952 sub_parser.add_argument('--kernel_cmdline',
2953 help='Add kernel cmdline',
2954 metavar='CMDLINE',
2955 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002956 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2957 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2958 # at some future point.
2959 sub_parser.add_argument('--setup_rootfs_from_kernel',
2960 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002961 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002962 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002963 type=argparse.FileType('rb'))
2964 sub_parser.add_argument('--include_descriptors_from_image',
2965 help='Include descriptors from image',
2966 metavar='IMAGE',
2967 action='append',
2968 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05002969 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
2970 sub_parser.add_argument('--chain_partition',
2971 help='Allow signed integrity-data for partition',
2972 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2973 action='append')
2974 sub_parser.add_argument('--flags',
2975 help='VBMeta flags',
2976 type=parse_number,
2977 default=0)
2978 sub_parser.add_argument('--set_hashtree_disabled_flag',
2979 help='Set the HASHTREE_DISABLED flag',
2980 action='store_true')
2981
2982 def _fixup_common_args(self, args):
2983 """Common fixups needed by subcommands.
2984
2985 Arguments:
2986 args: Arguments to modify.
2987
2988 Returns:
2989 The modified arguments.
2990 """
2991 if args.set_hashtree_disabled_flag:
2992 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
2993 return args
David Zeuthen21e95262016-07-27 17:58:40 -04002994
2995 def run(self, argv):
2996 """Command-line processor.
2997
2998 Arguments:
2999 argv: Pass sys.argv from main.
3000 """
3001 parser = argparse.ArgumentParser()
3002 subparsers = parser.add_subparsers(title='subcommands')
3003
3004 sub_parser = subparsers.add_parser('version',
3005 help='Prints version of avbtool.')
3006 sub_parser.set_defaults(func=self.version)
3007
3008 sub_parser = subparsers.add_parser('extract_public_key',
3009 help='Extract public key.')
3010 sub_parser.add_argument('--key',
3011 help='Path to RSA private key file',
3012 required=True)
3013 sub_parser.add_argument('--output',
3014 help='Output file name',
3015 type=argparse.FileType('wb'),
3016 required=True)
3017 sub_parser.set_defaults(func=self.extract_public_key)
3018
3019 sub_parser = subparsers.add_parser('make_vbmeta_image',
3020 help='Makes a vbmeta image.')
3021 sub_parser.add_argument('--output',
3022 help='Output file name',
3023 type=argparse.FileType('wb'),
3024 required=True)
3025 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003026 sub_parser.set_defaults(func=self.make_vbmeta_image)
3027
3028 sub_parser = subparsers.add_parser('add_hash_footer',
3029 help='Add hashes and footer to image.')
3030 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003031 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003032 type=argparse.FileType('rab+'))
3033 sub_parser.add_argument('--partition_size',
3034 help='Partition size',
3035 type=parse_number,
3036 required=True)
3037 sub_parser.add_argument('--partition_name',
3038 help='Partition name',
3039 required=True)
3040 sub_parser.add_argument('--hash_algorithm',
3041 help='Hash algorithm to use (default: sha256)',
3042 default='sha256')
3043 sub_parser.add_argument('--salt',
3044 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003045 sub_parser.add_argument('--output_vbmeta_image',
3046 help='Also write vbmeta struct to file',
3047 type=argparse.FileType('wb'))
3048 sub_parser.add_argument('--do_not_append_vbmeta_image',
3049 help=('Do not append vbmeta struct or footer '
3050 'to the image'),
3051 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003052 self._add_common_args(sub_parser)
3053 sub_parser.set_defaults(func=self.add_hash_footer)
3054
David Zeuthenb1b994d2017-03-06 18:01:31 -05003055 sub_parser = subparsers.add_parser('append_vbmeta_image',
3056 help='Append vbmeta image to image.')
3057 sub_parser.add_argument('--image',
3058 help='Image to append vbmeta blob to',
3059 type=argparse.FileType('rab+'))
3060 sub_parser.add_argument('--partition_size',
3061 help='Partition size',
3062 type=parse_number,
3063 required=True)
3064 sub_parser.add_argument('--vbmeta_image',
3065 help='Image with vbmeta blob to append',
3066 type=argparse.FileType('rb'))
3067 sub_parser.set_defaults(func=self.append_vbmeta_image)
3068
David Zeuthen21e95262016-07-27 17:58:40 -04003069 sub_parser = subparsers.add_parser('add_hashtree_footer',
3070 help='Add hashtree and footer to image.')
3071 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003072 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003073 type=argparse.FileType('rab+'))
3074 sub_parser.add_argument('--partition_size',
3075 help='Partition size',
3076 type=parse_number,
3077 required=True)
3078 sub_parser.add_argument('--partition_name',
3079 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003080 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003081 sub_parser.add_argument('--hash_algorithm',
3082 help='Hash algorithm to use (default: sha1)',
3083 default='sha1')
3084 sub_parser.add_argument('--salt',
3085 help='Salt in hex (default: /dev/urandom)')
3086 sub_parser.add_argument('--block_size',
3087 help='Block size (default: 4096)',
3088 type=parse_number,
3089 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003090 sub_parser.add_argument('--generate_fec',
3091 help='Add forward-error-correction codes',
3092 action='store_true')
3093 sub_parser.add_argument('--fec_num_roots',
3094 help='Number of roots for FEC (default: 2)',
3095 type=parse_number,
3096 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003097 sub_parser.add_argument('--calc_max_image_size',
3098 help=('Don\'t store the hashtree or footer - '
3099 'instead calculate the maximum image size '
3100 'leaving enough room for hashtree '
3101 'and metadata with the given partition '
3102 'size.'),
3103 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003104 sub_parser.add_argument('--output_vbmeta_image',
3105 help='Also write vbmeta struct to file',
3106 type=argparse.FileType('wb'))
3107 sub_parser.add_argument('--do_not_append_vbmeta_image',
3108 help=('Do not append vbmeta struct or footer '
3109 'to the image'),
3110 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003111 self._add_common_args(sub_parser)
3112 sub_parser.set_defaults(func=self.add_hashtree_footer)
3113
3114 sub_parser = subparsers.add_parser('erase_footer',
3115 help='Erase footer from an image.')
3116 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003117 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003118 type=argparse.FileType('rwb+'),
3119 required=True)
3120 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003121 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003122 action='store_true')
3123 sub_parser.set_defaults(func=self.erase_footer)
3124
3125 sub_parser = subparsers.add_parser(
3126 'info_image',
3127 help='Show information about vbmeta or footer.')
3128 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003129 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003130 type=argparse.FileType('rb'),
3131 required=True)
3132 sub_parser.add_argument('--output',
3133 help='Write info to file',
3134 type=argparse.FileType('wt'),
3135 default=sys.stdout)
3136 sub_parser.set_defaults(func=self.info_image)
3137
David Zeuthen8b6973b2016-09-20 12:39:49 -04003138 sub_parser = subparsers.add_parser('set_ab_metadata',
3139 help='Set A/B metadata.')
3140 sub_parser.add_argument('--misc_image',
3141 help=('The misc image to modify. If the image does '
3142 'not exist, it will be created.'),
3143 type=argparse.FileType('r+b'),
3144 required=True)
3145 sub_parser.add_argument('--slot_data',
3146 help=('Slot data of the form "priority", '
3147 '"tries_remaining", "sucessful_boot" for '
3148 'slot A followed by the same for slot B, '
3149 'separated by colons. The default value '
3150 'is 15:7:0:14:7:0.'),
3151 default='15:7:0:14:7:0')
3152 sub_parser.set_defaults(func=self.set_ab_metadata)
3153
Darren Krahn147b08d2016-12-20 16:38:29 -08003154 sub_parser = subparsers.add_parser(
3155 'make_atx_certificate',
3156 help='Create an Android Things eXtension (ATX) certificate.')
3157 sub_parser.add_argument('--output',
3158 help='Write certificate to file',
3159 type=argparse.FileType('wb'),
3160 default=sys.stdout)
3161 sub_parser.add_argument('--subject',
3162 help=('Path to subject file'),
3163 type=argparse.FileType('rb'),
3164 required=True)
3165 sub_parser.add_argument('--subject_key',
3166 help=('Path to subject RSA public key file'),
3167 type=argparse.FileType('rb'),
3168 required=True)
3169 sub_parser.add_argument('--subject_key_version',
3170 help=('Version of the subject key'),
3171 type=parse_number,
3172 required=False)
3173 sub_parser.add_argument('--subject_is_intermediate_authority',
3174 help=('Generate an intermediate authority '
3175 'certificate'),
3176 action='store_true')
3177 sub_parser.add_argument('--authority_key',
3178 help='Path to authority RSA private key file',
3179 required=False)
3180 sub_parser.add_argument('--signing_helper',
3181 help='Path to helper used for signing',
3182 metavar='APP',
3183 default=None,
3184 required=False)
3185 sub_parser.set_defaults(func=self.make_atx_certificate)
3186
3187 sub_parser = subparsers.add_parser(
3188 'make_atx_permanent_attributes',
3189 help='Create Android Things eXtension (ATX) permanent attributes.')
3190 sub_parser.add_argument('--output',
3191 help='Write attributes to file',
3192 type=argparse.FileType('wb'),
3193 default=sys.stdout)
3194 sub_parser.add_argument('--root_authority_key',
3195 help='Path to authority RSA public key file',
3196 type=argparse.FileType('rb'),
3197 required=True)
3198 sub_parser.add_argument('--product_id',
3199 help=('Path to Product ID file'),
3200 type=argparse.FileType('rb'),
3201 required=True)
3202 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3203
3204 sub_parser = subparsers.add_parser(
3205 'make_atx_metadata',
3206 help='Create Android Things eXtension (ATX) metadata.')
3207 sub_parser.add_argument('--output',
3208 help='Write metadata to file',
3209 type=argparse.FileType('wb'),
3210 default=sys.stdout)
3211 sub_parser.add_argument('--intermediate_key_certificate',
3212 help='Path to intermediate key certificate file',
3213 type=argparse.FileType('rb'),
3214 required=True)
3215 sub_parser.add_argument('--product_key_certificate',
3216 help='Path to product key certificate file',
3217 type=argparse.FileType('rb'),
3218 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003219 sub_parser.set_defaults(func=self.make_atx_metadata)
3220
David Zeuthen21e95262016-07-27 17:58:40 -04003221 args = parser.parse_args(argv[1:])
3222 try:
3223 args.func(args)
3224 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003225 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003226 sys.exit(1)
3227
3228 def version(self, _):
3229 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003230 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003231
3232 def extract_public_key(self, args):
3233 """Implements the 'extract_public_key' sub-command."""
3234 self.avb.extract_public_key(args.key, args.output)
3235
3236 def make_vbmeta_image(self, args):
3237 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003238 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003239 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003240 args.algorithm, args.key,
3241 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003242 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003243 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003244 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003245 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003246 args.signing_helper,
3247 args.internal_release_string,
3248 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003249
David Zeuthenb1b994d2017-03-06 18:01:31 -05003250 def append_vbmeta_image(self, args):
3251 """Implements the 'append_vbmeta_image' sub-command."""
3252 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3253 args.partition_size)
3254
David Zeuthen21e95262016-07-27 17:58:40 -04003255 def add_hash_footer(self, args):
3256 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003257 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003258 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003259 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003260 args.salt, args.chain_partition, args.algorithm,
3261 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003262 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003263 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003264 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003265 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003266 args.include_descriptors_from_image,
3267 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003268 args.internal_release_string,
3269 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003270 args.output_vbmeta_image,
3271 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003272
3273 def add_hashtree_footer(self, args):
3274 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003275 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003276 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3277 args.partition_size,
3278 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003279 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003280 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003281 args.salt, args.chain_partition, args.algorithm,
3282 args.key, args.public_key_metadata,
3283 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003284 args.prop_from_file,
3285 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003286 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003287 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003288 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003289 args.internal_release_string,
3290 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003291 args.output_vbmeta_image,
3292 args.do_not_append_vbmeta_image)
3293
David Zeuthen21e95262016-07-27 17:58:40 -04003294 def erase_footer(self, args):
3295 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003296 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003297
David Zeuthen8b6973b2016-09-20 12:39:49 -04003298 def set_ab_metadata(self, args):
3299 """Implements the 'set_ab_metadata' sub-command."""
3300 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3301
David Zeuthen21e95262016-07-27 17:58:40 -04003302 def info_image(self, args):
3303 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003304 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003305
Darren Krahn147b08d2016-12-20 16:38:29 -08003306 def make_atx_certificate(self, args):
3307 """Implements the 'make_atx_certificate' sub-command."""
3308 self.avb.make_atx_certificate(args.output, args.authority_key,
3309 args.subject_key.read(),
3310 args.subject_key_version,
3311 args.subject.read(),
3312 args.subject_is_intermediate_authority,
3313 args.signing_helper)
3314
3315 def make_atx_permanent_attributes(self, args):
3316 """Implements the 'make_atx_permanent_attributes' sub-command."""
3317 self.avb.make_atx_permanent_attributes(args.output,
3318 args.root_authority_key.read(),
3319 args.product_id.read())
3320
3321 def make_atx_metadata(self, args):
3322 """Implements the 'make_atx_metadata' sub-command."""
3323 self.avb.make_atx_metadata(args.output,
3324 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003325 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003326
David Zeuthen21e95262016-07-27 17:58:40 -04003327
3328if __name__ == '__main__':
3329 tool = AvbTool()
3330 tool.run(sys.argv)