blob: a855e844c99a850e5db93e3b1cdb9ea30d6601ab [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 Zeuthenfd41eb92016-11-17 12:24:47 -05001822 def _get_cmdline_descriptors_for_dm_verity(self, image):
1823 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001824
1825 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001826 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001827
1828 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001829 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1830 instructions. There is one for when hashtree is not disabled and one for
1831 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001832
1833 Raises:
1834 AvbError: If |image| doesn't have a hashtree descriptor.
1835
1836 """
1837
1838 (_, _, descriptors, _) = self._parse_image(image)
1839
1840 ht = None
1841 for desc in descriptors:
1842 if isinstance(desc, AvbHashtreeDescriptor):
1843 ht = desc
1844 break
1845
1846 if not ht:
1847 raise AvbError('No hashtree descriptor in given image')
1848
1849 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001850 c += '0' # start
1851 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1852 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1853 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1854 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1855 c += ' {}'.format(ht.data_block_size) # data_block
1856 c += ' {}'.format(ht.hash_block_size) # hash_block
1857 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1858 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1859 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1860 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1861 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1862 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001863 c += ' 10' # number of optional args
1864 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001865 c += ' ignore_zero_blocks'
1866 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1867 c += ' fec_roots {}'.format(ht.fec_num_roots)
1868 # Note that fec_blocks is the size that FEC covers, *not* the
1869 # size of the FEC data. Since we use FEC for everything up until
1870 # the FEC data, it's the same as the offset.
1871 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1872 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1873 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001874 c += ' 2' # number of optional args
1875 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001876 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001877 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001878
David Zeuthenfd41eb92016-11-17 12:24:47 -05001879 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001880 desc = AvbKernelCmdlineDescriptor()
1881 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001882 desc.flags = (
1883 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1884
1885 # The descriptor for when hashtree verification is disabled is a lot
1886 # simpler - we just set the root to the partition.
1887 desc_no_ht = AvbKernelCmdlineDescriptor()
1888 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1889 desc_no_ht.flags = (
1890 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1891
1892 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001893
1894 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001895 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001896 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001897 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001898 include_descriptors_from_image, signing_helper,
1899 release_string,
1900 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001901 """Implements the 'make_vbmeta_image' command.
1902
1903 Arguments:
1904 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001905 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001906 algorithm_name: Name of algorithm to use.
1907 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001908 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001909 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001910 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001911 props: Properties to insert (list of strings of the form 'key:value').
1912 props_from_file: Properties to insert (list of strings 'key:<path>').
1913 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001914 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001915 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001916 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001917 release_string: None or avbtool release string to use instead of default.
1918 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001919
1920 Raises:
1921 AvbError: If a chained partition is malformed.
1922 """
1923
1924 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04001925 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001926 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001927 chain_partitions, rollback_index, flags, props, props_from_file,
1928 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001929 include_descriptors_from_image, signing_helper, release_string,
1930 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001931
1932 # Write entire vbmeta blob (header, authentication, auxiliary).
1933 output.seek(0)
1934 output.write(vbmeta_blob)
1935
David Zeuthen18666ab2016-11-15 11:18:05 -05001936 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1937 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001938 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001939 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001940 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001941 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001942 include_descriptors_from_image, signing_helper,
1943 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001944 """Generates a VBMeta blob.
1945
1946 This blob contains the header (struct AvbVBMetaHeader), the
1947 authentication data block (which contains the hash and signature
1948 for the header and auxiliary block), and the auxiliary block
1949 (which contains descriptors, the public key used, and other data).
1950
1951 The |key| parameter can |None| only if the |algorithm_name| is
1952 'NONE'.
1953
1954 Arguments:
1955 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1956 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001957 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001958 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001959 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001960 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001961 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001962 props: Properties to insert (List of strings of the form 'key:value').
1963 props_from_file: Properties to insert (List of strings 'key:<path>').
1964 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001965 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001966 dm-verity kernel cmdline from.
1967 include_descriptors_from_image: List of file objects for which
1968 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001969 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001970 release_string: None or avbtool release string.
1971 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001972
1973 Returns:
1974 A bytearray() with the VBMeta blob.
1975
1976 Raises:
1977 Exception: If the |algorithm_name| is not found, if no key has
1978 been given and the given algorithm requires one, or the key is
1979 of the wrong size.
1980
1981 """
1982 try:
1983 alg = ALGORITHMS[algorithm_name]
1984 except KeyError:
1985 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1986
David Zeuthena5fd3a42017-02-27 16:38:54 -05001987 if not descriptors:
1988 descriptors = []
1989
1990 # Insert chained partition descriptors, if any
1991 if chain_partitions:
1992 for cp in chain_partitions:
1993 cp_tokens = cp.split(':')
1994 if len(cp_tokens) != 3:
1995 raise AvbError('Malformed chained partition "{}".'.format(cp))
1996 desc = AvbChainPartitionDescriptor()
1997 desc.partition_name = cp_tokens[0]
1998 desc.rollback_index_location = int(cp_tokens[1])
1999 if desc.rollback_index_location < 1:
2000 raise AvbError('Rollback index location must be 1 or larger.')
2001 file_path = cp_tokens[2]
2002 desc.public_key = open(file_path, 'rb').read()
2003 descriptors.append(desc)
2004
David Zeuthen21e95262016-07-27 17:58:40 -04002005 # Descriptors.
2006 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002007 for desc in descriptors:
2008 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002009
2010 # Add properties.
2011 if props:
2012 for prop in props:
2013 idx = prop.find(':')
2014 if idx == -1:
2015 raise AvbError('Malformed property "{}".'.format(prop))
2016 desc = AvbPropertyDescriptor()
2017 desc.key = prop[0:idx]
2018 desc.value = prop[(idx + 1):]
2019 encoded_descriptors.extend(desc.encode())
2020 if props_from_file:
2021 for prop in props_from_file:
2022 idx = prop.find(':')
2023 if idx == -1:
2024 raise AvbError('Malformed property "{}".'.format(prop))
2025 desc = AvbPropertyDescriptor()
2026 desc.key = prop[0:idx]
2027 desc.value = prop[(idx + 1):]
2028 file_path = prop[(idx + 1):]
2029 desc.value = open(file_path, 'rb').read()
2030 encoded_descriptors.extend(desc.encode())
2031
2032 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002033 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002034 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002035 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002036 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2037 encoded_descriptors.extend(cmdline_desc[0].encode())
2038 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002039
2040 # Add kernel command-lines.
2041 if kernel_cmdlines:
2042 for i in kernel_cmdlines:
2043 desc = AvbKernelCmdlineDescriptor()
2044 desc.kernel_cmdline = i
2045 encoded_descriptors.extend(desc.encode())
2046
2047 # Add descriptors from other images.
2048 if include_descriptors_from_image:
2049 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002050 image_handler = ImageHandler(image.name)
2051 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002052 for desc in image_descriptors:
2053 encoded_descriptors.extend(desc.encode())
2054
David Zeuthen18666ab2016-11-15 11:18:05 -05002055 # Load public key metadata blob, if requested.
2056 pkmd_blob = []
2057 if public_key_metadata_path:
2058 with open(public_key_metadata_path) as f:
2059 pkmd_blob = f.read()
2060
David Zeuthen21e95262016-07-27 17:58:40 -04002061 key = None
2062 encoded_key = bytearray()
2063 if alg.public_key_num_bytes > 0:
2064 if not key_path:
2065 raise AvbError('Key is required for algorithm {}'.format(
2066 algorithm_name))
2067 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2068 encoded_key = encode_rsa_key(key)
2069 if len(encoded_key) != alg.public_key_num_bytes:
2070 raise AvbError('Key is wrong size for algorithm {}'.format(
2071 algorithm_name))
2072
2073 h = AvbVBMetaHeader()
2074
David Zeuthene3cadca2017-02-22 21:25:46 -05002075 # Override release string, if requested.
2076 if isinstance(release_string, (str, unicode)):
2077 h.release_string = release_string
2078
2079 # Append to release string, if requested. Also insert a space before.
2080 if isinstance(append_to_release_string, (str, unicode)):
2081 h.release_string += ' ' + append_to_release_string
2082
David Zeuthen18666ab2016-11-15 11:18:05 -05002083 # For the Auxiliary data block, descriptors are stored at offset 0,
2084 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002085 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002086 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002087 h.descriptors_offset = 0
2088 h.descriptors_size = len(encoded_descriptors)
2089 h.public_key_offset = h.descriptors_size
2090 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002091 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2092 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002093
2094 # For the Authentication data block, the hash is first and then
2095 # the signature.
2096 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002097 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002098 h.algorithm_type = alg.algorithm_type
2099 h.hash_offset = 0
2100 h.hash_size = alg.hash_num_bytes
2101 # Signature offset and size - it's stored right after the hash
2102 # (in Authentication data block).
2103 h.signature_offset = alg.hash_num_bytes
2104 h.signature_size = alg.signature_num_bytes
2105
2106 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002107 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002108
2109 # Generate Header data block.
2110 header_data_blob = h.encode()
2111
2112 # Generate Auxiliary data block.
2113 aux_data_blob = bytearray()
2114 aux_data_blob.extend(encoded_descriptors)
2115 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002116 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002117 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2118 aux_data_blob.extend('\0' * padding_bytes)
2119
2120 # Calculate the hash.
2121 binary_hash = bytearray()
2122 binary_signature = bytearray()
2123 if algorithm_name != 'NONE':
2124 if algorithm_name[0:6] == 'SHA256':
2125 ha = hashlib.sha256()
2126 elif algorithm_name[0:6] == 'SHA512':
2127 ha = hashlib.sha512()
2128 else:
2129 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2130 ha.update(header_data_blob)
2131 ha.update(aux_data_blob)
2132 binary_hash.extend(ha.digest())
2133
2134 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002135 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002136 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2137 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002138
2139 # Generate Authentication data block.
2140 auth_data_blob = bytearray()
2141 auth_data_blob.extend(binary_hash)
2142 auth_data_blob.extend(binary_signature)
2143 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2144 auth_data_blob.extend('\0' * padding_bytes)
2145
2146 return header_data_blob + auth_data_blob + aux_data_blob
2147
2148 def extract_public_key(self, key_path, output):
2149 """Implements the 'extract_public_key' command.
2150
2151 Arguments:
2152 key_path: The path to a RSA private key file.
2153 output: The file to write to.
2154 """
2155 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2156 write_rsa_key(output, key)
2157
David Zeuthena4fee8b2016-08-22 15:20:43 -04002158 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002159 hash_algorithm, salt, chain_partitions, algorithm_name,
2160 key_path,
2161 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002162 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002163 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002164 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002165 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002166 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002167 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002168
2169 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002170 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002171 partition_size: Size of partition.
2172 partition_name: Name of partition (without A/B suffix).
2173 hash_algorithm: Hash algorithm to use.
2174 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002175 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002176 algorithm_name: Name of algorithm to use.
2177 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002178 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002179 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002180 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002181 props: Properties to insert (List of strings of the form 'key:value').
2182 props_from_file: Properties to insert (List of strings 'key:<path>').
2183 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002184 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002185 dm-verity kernel cmdline from.
2186 include_descriptors_from_image: List of file objects for which
2187 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002188 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002189 release_string: None or avbtool release string.
2190 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002191 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2192 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002193
2194 Raises:
2195 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002196 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002197 image = ImageHandler(image_filename)
2198
2199 if partition_size % image.block_size != 0:
2200 raise AvbError('Partition size of {} is not a multiple of the image '
2201 'block size {}.'.format(partition_size,
2202 image.block_size))
2203
David Zeuthen21e95262016-07-27 17:58:40 -04002204 # If there's already a footer, truncate the image to its original
2205 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2206 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002207 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002208 try:
2209 footer = AvbFooter(image.read(AvbFooter.SIZE))
2210 # Existing footer found. Just truncate.
2211 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002212 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002213 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002214 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002215
2216 # If anything goes wrong from here-on, restore the image back to
2217 # its original size.
2218 try:
David Zeuthen09692692016-09-30 16:16:40 -04002219 # First, calculate the maximum image size such that an image
2220 # this size + metadata (footer + vbmeta struct) fits in
2221 # |partition_size|.
2222 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2223 max_image_size = partition_size - max_metadata_size
2224
2225 # If image size exceeds the maximum image size, fail.
2226 if image.image_size > max_image_size:
2227 raise AvbError('Image size of {} exceeds maximum image '
2228 'size of {} in order to fit in a partition '
2229 'size of {}.'.format(image.image_size, max_image_size,
2230 partition_size))
2231
David Zeuthen21e95262016-07-27 17:58:40 -04002232 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2233 if salt:
2234 salt = salt.decode('hex')
2235 else:
2236 if salt is None:
2237 # If salt is not explicitly specified, choose a hash
2238 # that's the same size as the hash size.
2239 hash_size = digest_size
2240 salt = open('/dev/urandom').read(hash_size)
2241 else:
2242 salt = ''
2243
2244 hasher = hashlib.new(name=hash_algorithm, string=salt)
2245 # TODO(zeuthen): might want to read this in chunks to avoid
2246 # memory pressure, then again, this is only supposed to be used
2247 # on kernel/initramfs partitions. Possible optimization.
2248 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002249 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002250 digest = hasher.digest()
2251
2252 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002253 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002254 h_desc.hash_algorithm = hash_algorithm
2255 h_desc.partition_name = partition_name
2256 h_desc.salt = salt
2257 h_desc.digest = digest
2258
2259 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002260 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002261 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002262 chain_partitions, rollback_index, flags, props, props_from_file,
2263 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002264 include_descriptors_from_image, signing_helper, release_string,
2265 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002266
David Zeuthena4fee8b2016-08-22 15:20:43 -04002267 # If the image isn't sparse, its size might not be a multiple of
2268 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002269 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002270 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002271 padding_needed = image.block_size - (image.image_size%image.block_size)
2272 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002273
David Zeuthena4fee8b2016-08-22 15:20:43 -04002274 # The append_raw() method requires content with size being a
2275 # multiple of |block_size| so add padding as needed. Also record
2276 # where this is written to since we'll need to put that in the
2277 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002278 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002279 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2280 len(vbmeta_blob))
2281 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002282
David Zeuthend247fcb2017-02-16 12:09:27 -05002283 # Write vbmeta blob, if requested.
2284 if output_vbmeta_image:
2285 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002286
David Zeuthend247fcb2017-02-16 12:09:27 -05002287 # Append vbmeta blob and footer, unless requested not to.
2288 if not do_not_append_vbmeta_image:
2289 image.append_raw(vbmeta_blob_with_padding)
2290 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2291
2292 # Now insert a DONT_CARE chunk with enough bytes such that the
2293 # final Footer block is at the end of partition_size..
2294 image.append_dont_care(partition_size - vbmeta_end_offset -
2295 1*image.block_size)
2296
2297 # Generate the Footer that tells where the VBMeta footer
2298 # is. Also put enough padding in the front of the footer since
2299 # we'll write out an entire block.
2300 footer = AvbFooter()
2301 footer.original_image_size = original_image_size
2302 footer.vbmeta_offset = vbmeta_offset
2303 footer.vbmeta_size = len(vbmeta_blob)
2304 footer_blob = footer.encode()
2305 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2306 footer_blob)
2307 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002308
David Zeuthen21e95262016-07-27 17:58:40 -04002309 except:
2310 # Truncate back to original size, then re-raise
2311 image.truncate(original_image_size)
2312 raise
2313
David Zeuthena4fee8b2016-08-22 15:20:43 -04002314 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002315 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002316 block_size, salt, chain_partitions, algorithm_name,
2317 key_path,
2318 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002319 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002320 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002321 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002322 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002323 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002324 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002325 """Implements the 'add_hashtree_footer' command.
2326
2327 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2328 more information about dm-verity and these hashes.
2329
2330 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002331 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002332 partition_size: Size of partition.
2333 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002334 generate_fec: If True, generate FEC codes.
2335 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002336 hash_algorithm: Hash algorithm to use.
2337 block_size: Block size to use.
2338 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002339 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002340 algorithm_name: Name of algorithm to use.
2341 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002342 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002343 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002344 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002345 props: Properties to insert (List of strings of the form 'key:value').
2346 props_from_file: Properties to insert (List of strings 'key:<path>').
2347 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002348 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002349 dm-verity kernel cmdline from.
2350 include_descriptors_from_image: List of file objects for which
2351 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002352 calc_max_image_size: Don't store the hashtree or footer - instead
2353 calculate the maximum image size leaving enough room for hashtree
2354 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002355 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002356 release_string: None or avbtool release string.
2357 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002358 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2359 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002360
2361 Raises:
2362 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002363 """
David Zeuthen09692692016-09-30 16:16:40 -04002364 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2365 digest_padding = round_to_pow2(digest_size) - digest_size
2366
2367 # First, calculate the maximum image size such that an image
2368 # this size + the hashtree + metadata (footer + vbmeta struct)
2369 # fits in |partition_size|. We use very conservative figures for
2370 # metadata.
2371 (_, max_tree_size) = calc_hash_level_offsets(
2372 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002373 max_fec_size = 0
2374 if generate_fec:
2375 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2376 max_metadata_size = (max_fec_size + max_tree_size +
2377 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002378 self.MAX_FOOTER_SIZE)
2379 max_image_size = partition_size - max_metadata_size
2380
2381 # If we're asked to only calculate the maximum image size, we're done.
2382 if calc_max_image_size:
2383 print '{}'.format(max_image_size)
2384 return
2385
David Zeuthena4fee8b2016-08-22 15:20:43 -04002386 image = ImageHandler(image_filename)
2387
2388 if partition_size % image.block_size != 0:
2389 raise AvbError('Partition size of {} is not a multiple of the image '
2390 'block size {}.'.format(partition_size,
2391 image.block_size))
2392
David Zeuthen21e95262016-07-27 17:58:40 -04002393 # If there's already a footer, truncate the image to its original
2394 # size. This way 'avbtool add_hashtree_footer' is idempotent
2395 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002396 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002397 try:
2398 footer = AvbFooter(image.read(AvbFooter.SIZE))
2399 # Existing footer found. Just truncate.
2400 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002401 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002402 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002403 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002404
2405 # If anything goes wrong from here-on, restore the image back to
2406 # its original size.
2407 try:
2408 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002409 rounded_image_size = round_to_multiple(image.image_size, block_size)
2410 if rounded_image_size > image.image_size:
2411 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002412
David Zeuthen09692692016-09-30 16:16:40 -04002413 # If image size exceeds the maximum image size, fail.
2414 if image.image_size > max_image_size:
2415 raise AvbError('Image size of {} exceeds maximum image '
2416 'size of {} in order to fit in a partition '
2417 'size of {}.'.format(image.image_size, max_image_size,
2418 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002419
2420 if salt:
2421 salt = salt.decode('hex')
2422 else:
2423 if salt is None:
2424 # If salt is not explicitly specified, choose a hash
2425 # that's the same size as the hash size.
2426 hash_size = digest_size
2427 salt = open('/dev/urandom').read(hash_size)
2428 else:
2429 salt = ''
2430
David Zeuthena4fee8b2016-08-22 15:20:43 -04002431 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002432 # offsets in advance.
2433 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002434 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002435
David Zeuthena4fee8b2016-08-22 15:20:43 -04002436 # If the image isn't sparse, its size might not be a multiple of
2437 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002438 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002439 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002440 padding_needed = image.block_size - (image.image_size%image.block_size)
2441 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002442
David Zeuthena4fee8b2016-08-22 15:20:43 -04002443 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002444 tree_offset = image.image_size
2445 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002446 block_size,
2447 hash_algorithm, salt,
2448 digest_padding,
2449 hash_level_offsets,
2450 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002451
2452 # Generate HashtreeDescriptor with details about the tree we
2453 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002454 ht_desc = AvbHashtreeDescriptor()
2455 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002456 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002457 ht_desc.tree_offset = tree_offset
2458 ht_desc.tree_size = tree_size
2459 ht_desc.data_block_size = block_size
2460 ht_desc.hash_block_size = block_size
2461 ht_desc.hash_algorithm = hash_algorithm
2462 ht_desc.partition_name = partition_name
2463 ht_desc.salt = salt
2464 ht_desc.root_digest = root_digest
2465
David Zeuthen09692692016-09-30 16:16:40 -04002466 # Write the hash tree
2467 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2468 len(hash_tree))
2469 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2470 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002471 len_hashtree_and_fec = len(hash_tree_with_padding)
2472
2473 # Generate FEC codes, if requested.
2474 if generate_fec:
2475 fec_data = generate_fec_data(image_filename, fec_num_roots)
2476 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2477 len(fec_data))
2478 fec_data_with_padding = fec_data + '\0'*padding_needed
2479 fec_offset = image.image_size
2480 image.append_raw(fec_data_with_padding)
2481 len_hashtree_and_fec += len(fec_data_with_padding)
2482 # Update the hashtree descriptor.
2483 ht_desc.fec_num_roots = fec_num_roots
2484 ht_desc.fec_offset = fec_offset
2485 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002486
David Zeuthena4fee8b2016-08-22 15:20:43 -04002487 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002488 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002489 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002490 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002491 chain_partitions, rollback_index, flags, props, props_from_file,
2492 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002493 include_descriptors_from_image, signing_helper, release_string,
2494 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002495 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2496 len(vbmeta_blob))
2497 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002498
David Zeuthend247fcb2017-02-16 12:09:27 -05002499 # Write vbmeta blob, if requested.
2500 if output_vbmeta_image:
2501 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002502
David Zeuthend247fcb2017-02-16 12:09:27 -05002503 # Append vbmeta blob and footer, unless requested not to.
2504 if not do_not_append_vbmeta_image:
2505 image.append_raw(vbmeta_blob_with_padding)
2506
2507 # Now insert a DONT_CARE chunk with enough bytes such that the
2508 # final Footer block is at the end of partition_size..
2509 image.append_dont_care(partition_size - image.image_size -
2510 1*image.block_size)
2511
2512 # Generate the Footer that tells where the VBMeta footer
2513 # is. Also put enough padding in the front of the footer since
2514 # we'll write out an entire block.
2515 footer = AvbFooter()
2516 footer.original_image_size = original_image_size
2517 footer.vbmeta_offset = vbmeta_offset
2518 footer.vbmeta_size = len(vbmeta_blob)
2519 footer_blob = footer.encode()
2520 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2521 footer_blob)
2522 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002523
David Zeuthen21e95262016-07-27 17:58:40 -04002524 except:
David Zeuthen09692692016-09-30 16:16:40 -04002525 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002526 image.truncate(original_image_size)
2527 raise
2528
Darren Krahn147b08d2016-12-20 16:38:29 -08002529 def make_atx_certificate(self, output, authority_key_path, subject_key,
2530 subject_key_version, subject,
2531 is_intermediate_authority, signing_helper):
2532 """Implements the 'make_atx_certificate' command.
2533
2534 Android Things certificates are required for Android Things public key
2535 metadata. They chain the vbmeta signing key for a particular product back to
2536 a fused, permanent root key. These certificates are fixed-length and fixed-
2537 format with the explicit goal of not parsing ASN.1 in bootloader code.
2538
2539 Arguments:
2540 output: Certificate will be written to this file on success.
2541 authority_key_path: A PEM file path with the authority private key.
2542 If None, then a certificate will be created without a
2543 signature. The signature can be created out-of-band
2544 and appended.
2545 subject_key: A PEM or DER subject public key.
2546 subject_key_version: A 64-bit version value. If this is None, the number
2547 of seconds since the epoch is used.
2548 subject: A subject identifier. For Product Signing Key certificates this
2549 should be the same Product ID found in the permanent attributes.
2550 is_intermediate_authority: True if the certificate is for an intermediate
2551 authority.
2552 signing_helper: Program which signs a hash and returns the signature.
2553 """
2554 signed_data = bytearray()
2555 signed_data.extend(struct.pack('<I', 1)) # Format Version
2556 signed_data.extend(
2557 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2558 hasher = hashlib.sha256()
2559 hasher.update(subject)
2560 signed_data.extend(hasher.digest())
2561 usage = 'com.google.android.things.vboot'
2562 if is_intermediate_authority:
2563 usage += '.ca'
2564 hasher = hashlib.sha256()
2565 hasher.update(usage)
2566 signed_data.extend(hasher.digest())
2567 if not subject_key_version:
2568 subject_key_version = int(time.time())
2569 signed_data.extend(struct.pack('<Q', subject_key_version))
2570 signature = bytearray()
2571 if authority_key_path:
2572 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002573 algorithm_name = 'SHA512_RSA4096'
2574 hasher = hashlib.sha512()
Darren Krahn147b08d2016-12-20 16:38:29 -08002575 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2576 hasher.update(signed_data)
2577 padding_and_hash.extend(hasher.digest())
2578 signature.extend(raw_sign(signing_helper, algorithm_name,
2579 authority_key_path, padding_and_hash))
2580 output.write(signed_data)
2581 output.write(signature)
2582
2583 def make_atx_permanent_attributes(self, output, root_authority_key,
2584 product_id):
2585 """Implements the 'make_atx_permanent_attributes' command.
2586
2587 Android Things permanent attributes are designed to be permanent for a
2588 particular product and a hash of these attributes should be fused into
2589 hardware to enforce this.
2590
2591 Arguments:
2592 output: Attributes will be written to this file on success.
2593 root_authority_key: A PEM or DER public key for the root authority.
2594 product_id: A 16-byte Product ID.
2595
2596 Raises:
2597 AvbError: If an argument is incorrect.
2598 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002599 EXPECTED_PRODUCT_ID_SIZE = 16
2600 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002601 raise AvbError('Invalid Product ID length.')
2602 output.write(struct.pack('<I', 1)) # Format Version
2603 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2604 output.write(product_id)
2605
2606 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002607 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002608 """Implements the 'make_atx_metadata' command.
2609
2610 Android Things metadata are included in vbmeta images to facilitate
2611 verification. The output of this command can be used as the
2612 public_key_metadata argument to other commands.
2613
2614 Arguments:
2615 output: Metadata will be written to this file on success.
2616 intermediate_key_certificate: A certificate file as output by
2617 make_atx_certificate with
2618 is_intermediate_authority set to true.
2619 product_key_certificate: A certificate file as output by
2620 make_atx_certificate with
2621 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002622
2623 Raises:
2624 AvbError: If an argument is incorrect.
2625 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002626 EXPECTED_CERTIFICATE_SIZE = 1620
2627 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002628 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002629 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002630 raise AvbError('Invalid product key certificate length.')
2631 output.write(struct.pack('<I', 1)) # Format Version
2632 output.write(intermediate_key_certificate)
2633 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002634
David Zeuthen21e95262016-07-27 17:58:40 -04002635
2636def calc_hash_level_offsets(image_size, block_size, digest_size):
2637 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2638
2639 Arguments:
2640 image_size: The size of the image to calculate a Merkle-tree for.
2641 block_size: The block size, e.g. 4096.
2642 digest_size: The size of each hash, e.g. 32 for SHA-256.
2643
2644 Returns:
2645 A tuple where the first argument is an array of offsets and the
2646 second is size of the tree, in bytes.
2647 """
2648 level_offsets = []
2649 level_sizes = []
2650 tree_size = 0
2651
2652 num_levels = 0
2653 size = image_size
2654 while size > block_size:
2655 num_blocks = (size + block_size - 1) / block_size
2656 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2657
2658 level_sizes.append(level_size)
2659 tree_size += level_size
2660 num_levels += 1
2661
2662 size = level_size
2663
2664 for n in range(0, num_levels):
2665 offset = 0
2666 for m in range(n + 1, num_levels):
2667 offset += level_sizes[m]
2668 level_offsets.append(offset)
2669
David Zeuthena4fee8b2016-08-22 15:20:43 -04002670 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002671
2672
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002673# See system/extras/libfec/include/fec/io.h for these definitions.
2674FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2675FEC_MAGIC = 0xfecfecfe
2676
2677
2678def calc_fec_data_size(image_size, num_roots):
2679 """Calculates how much space FEC data will take.
2680
2681 Args:
2682 image_size: The size of the image.
2683 num_roots: Number of roots.
2684
2685 Returns:
2686 The number of bytes needed for FEC for an image of the given size
2687 and with the requested number of FEC roots.
2688
2689 Raises:
2690 ValueError: If output from the 'fec' tool is invalid.
2691
2692 """
2693 p = subprocess.Popen(
2694 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2695 stdout=subprocess.PIPE,
2696 stderr=subprocess.PIPE)
2697 (pout, perr) = p.communicate()
2698 retcode = p.wait()
2699 if retcode != 0:
2700 raise ValueError('Error invoking fec: {}'.format(perr))
2701 return int(pout)
2702
2703
2704def generate_fec_data(image_filename, num_roots):
2705 """Generate FEC codes for an image.
2706
2707 Args:
2708 image_filename: The filename of the image.
2709 num_roots: Number of roots.
2710
2711 Returns:
2712 The FEC data blob.
2713
2714 Raises:
2715 ValueError: If output from the 'fec' tool is invalid.
2716 """
2717 fec_tmpfile = tempfile.NamedTemporaryFile()
2718 subprocess.check_call(
2719 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2720 fec_tmpfile.name],
2721 stderr=open(os.devnull))
2722 fec_data = fec_tmpfile.read()
2723 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2724 footer_data = fec_data[-footer_size:]
2725 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2726 footer_data)
2727 if magic != FEC_MAGIC:
2728 raise ValueError('Unexpected magic in FEC footer')
2729 return fec_data[0:fec_size]
2730
2731
David Zeuthen21e95262016-07-27 17:58:40 -04002732def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002733 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002734 """Generates a Merkle-tree for a file.
2735
2736 Args:
2737 image: The image, as a file.
2738 image_size: The size of the image.
2739 block_size: The block size, e.g. 4096.
2740 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2741 salt: The salt to use.
2742 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002743 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002744 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002745
2746 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002747 A tuple where the first element is the top-level hash and the
2748 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002749 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002750 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002751 hash_src_offset = 0
2752 hash_src_size = image_size
2753 level_num = 0
2754 while hash_src_size > block_size:
2755 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002756 remaining = hash_src_size
2757 while remaining > 0:
2758 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002759 # Only read from the file for the first level - for subsequent
2760 # levels, access the array we're building.
2761 if level_num == 0:
2762 image.seek(hash_src_offset + hash_src_size - remaining)
2763 data = image.read(min(remaining, block_size))
2764 else:
2765 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2766 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002767 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002768
2769 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002770 if len(data) < block_size:
2771 hasher.update('\0' * (block_size - len(data)))
2772 level_output += hasher.digest()
2773 if digest_padding > 0:
2774 level_output += '\0' * digest_padding
2775
2776 padding_needed = (round_to_multiple(
2777 len(level_output), block_size) - len(level_output))
2778 level_output += '\0' * padding_needed
2779
David Zeuthena4fee8b2016-08-22 15:20:43 -04002780 # Copy level-output into resulting tree.
2781 offset = hash_level_offsets[level_num]
2782 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002783
David Zeuthena4fee8b2016-08-22 15:20:43 -04002784 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002785 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002786 level_num += 1
2787
2788 hasher = hashlib.new(name=hash_alg_name, string=salt)
2789 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002790 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002791
2792
2793class AvbTool(object):
2794 """Object for avbtool command-line tool."""
2795
2796 def __init__(self):
2797 """Initializer method."""
2798 self.avb = Avb()
2799
2800 def _add_common_args(self, sub_parser):
2801 """Adds arguments used by several sub-commands.
2802
2803 Arguments:
2804 sub_parser: The parser to add arguments to.
2805 """
2806 sub_parser.add_argument('--algorithm',
2807 help='Algorithm to use (default: NONE)',
2808 metavar='ALGORITHM',
2809 default='NONE')
2810 sub_parser.add_argument('--key',
2811 help='Path to RSA private key file',
2812 metavar='KEY',
2813 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002814 sub_parser.add_argument('--signing_helper',
2815 help='Path to helper used for signing',
2816 metavar='APP',
2817 default=None,
2818 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002819 sub_parser.add_argument('--public_key_metadata',
2820 help='Path to public key metadata file',
2821 metavar='KEY_METADATA',
2822 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002823 sub_parser.add_argument('--rollback_index',
2824 help='Rollback Index',
2825 type=parse_number,
2826 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05002827 # This is used internally for unit tests. Do not include in --help output.
2828 sub_parser.add_argument('--internal_release_string',
2829 help=argparse.SUPPRESS)
2830 sub_parser.add_argument('--append_to_release_string',
2831 help='Text to append to release string',
2832 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04002833 sub_parser.add_argument('--prop',
2834 help='Add property',
2835 metavar='KEY:VALUE',
2836 action='append')
2837 sub_parser.add_argument('--prop_from_file',
2838 help='Add property from file',
2839 metavar='KEY:PATH',
2840 action='append')
2841 sub_parser.add_argument('--kernel_cmdline',
2842 help='Add kernel cmdline',
2843 metavar='CMDLINE',
2844 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002845 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2846 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2847 # at some future point.
2848 sub_parser.add_argument('--setup_rootfs_from_kernel',
2849 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002850 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002851 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002852 type=argparse.FileType('rb'))
2853 sub_parser.add_argument('--include_descriptors_from_image',
2854 help='Include descriptors from image',
2855 metavar='IMAGE',
2856 action='append',
2857 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05002858 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
2859 sub_parser.add_argument('--chain_partition',
2860 help='Allow signed integrity-data for partition',
2861 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2862 action='append')
2863 sub_parser.add_argument('--flags',
2864 help='VBMeta flags',
2865 type=parse_number,
2866 default=0)
2867 sub_parser.add_argument('--set_hashtree_disabled_flag',
2868 help='Set the HASHTREE_DISABLED flag',
2869 action='store_true')
2870
2871 def _fixup_common_args(self, args):
2872 """Common fixups needed by subcommands.
2873
2874 Arguments:
2875 args: Arguments to modify.
2876
2877 Returns:
2878 The modified arguments.
2879 """
2880 if args.set_hashtree_disabled_flag:
2881 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
2882 return args
David Zeuthen21e95262016-07-27 17:58:40 -04002883
2884 def run(self, argv):
2885 """Command-line processor.
2886
2887 Arguments:
2888 argv: Pass sys.argv from main.
2889 """
2890 parser = argparse.ArgumentParser()
2891 subparsers = parser.add_subparsers(title='subcommands')
2892
2893 sub_parser = subparsers.add_parser('version',
2894 help='Prints version of avbtool.')
2895 sub_parser.set_defaults(func=self.version)
2896
2897 sub_parser = subparsers.add_parser('extract_public_key',
2898 help='Extract public key.')
2899 sub_parser.add_argument('--key',
2900 help='Path to RSA private key file',
2901 required=True)
2902 sub_parser.add_argument('--output',
2903 help='Output file name',
2904 type=argparse.FileType('wb'),
2905 required=True)
2906 sub_parser.set_defaults(func=self.extract_public_key)
2907
2908 sub_parser = subparsers.add_parser('make_vbmeta_image',
2909 help='Makes a vbmeta image.')
2910 sub_parser.add_argument('--output',
2911 help='Output file name',
2912 type=argparse.FileType('wb'),
2913 required=True)
2914 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04002915 sub_parser.set_defaults(func=self.make_vbmeta_image)
2916
2917 sub_parser = subparsers.add_parser('add_hash_footer',
2918 help='Add hashes and footer to image.')
2919 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002920 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002921 type=argparse.FileType('rab+'))
2922 sub_parser.add_argument('--partition_size',
2923 help='Partition size',
2924 type=parse_number,
2925 required=True)
2926 sub_parser.add_argument('--partition_name',
2927 help='Partition name',
2928 required=True)
2929 sub_parser.add_argument('--hash_algorithm',
2930 help='Hash algorithm to use (default: sha256)',
2931 default='sha256')
2932 sub_parser.add_argument('--salt',
2933 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05002934 sub_parser.add_argument('--output_vbmeta_image',
2935 help='Also write vbmeta struct to file',
2936 type=argparse.FileType('wb'))
2937 sub_parser.add_argument('--do_not_append_vbmeta_image',
2938 help=('Do not append vbmeta struct or footer '
2939 'to the image'),
2940 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002941 self._add_common_args(sub_parser)
2942 sub_parser.set_defaults(func=self.add_hash_footer)
2943
2944 sub_parser = subparsers.add_parser('add_hashtree_footer',
2945 help='Add hashtree and footer to image.')
2946 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002947 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002948 type=argparse.FileType('rab+'))
2949 sub_parser.add_argument('--partition_size',
2950 help='Partition size',
2951 type=parse_number,
2952 required=True)
2953 sub_parser.add_argument('--partition_name',
2954 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002955 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002956 sub_parser.add_argument('--hash_algorithm',
2957 help='Hash algorithm to use (default: sha1)',
2958 default='sha1')
2959 sub_parser.add_argument('--salt',
2960 help='Salt in hex (default: /dev/urandom)')
2961 sub_parser.add_argument('--block_size',
2962 help='Block size (default: 4096)',
2963 type=parse_number,
2964 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002965 sub_parser.add_argument('--generate_fec',
2966 help='Add forward-error-correction codes',
2967 action='store_true')
2968 sub_parser.add_argument('--fec_num_roots',
2969 help='Number of roots for FEC (default: 2)',
2970 type=parse_number,
2971 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002972 sub_parser.add_argument('--calc_max_image_size',
2973 help=('Don\'t store the hashtree or footer - '
2974 'instead calculate the maximum image size '
2975 'leaving enough room for hashtree '
2976 'and metadata with the given partition '
2977 'size.'),
2978 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05002979 sub_parser.add_argument('--output_vbmeta_image',
2980 help='Also write vbmeta struct to file',
2981 type=argparse.FileType('wb'))
2982 sub_parser.add_argument('--do_not_append_vbmeta_image',
2983 help=('Do not append vbmeta struct or footer '
2984 'to the image'),
2985 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002986 self._add_common_args(sub_parser)
2987 sub_parser.set_defaults(func=self.add_hashtree_footer)
2988
2989 sub_parser = subparsers.add_parser('erase_footer',
2990 help='Erase footer from an image.')
2991 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002992 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002993 type=argparse.FileType('rwb+'),
2994 required=True)
2995 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002996 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04002997 action='store_true')
2998 sub_parser.set_defaults(func=self.erase_footer)
2999
3000 sub_parser = subparsers.add_parser(
3001 'info_image',
3002 help='Show information about vbmeta or footer.')
3003 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003004 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003005 type=argparse.FileType('rb'),
3006 required=True)
3007 sub_parser.add_argument('--output',
3008 help='Write info to file',
3009 type=argparse.FileType('wt'),
3010 default=sys.stdout)
3011 sub_parser.set_defaults(func=self.info_image)
3012
David Zeuthen8b6973b2016-09-20 12:39:49 -04003013 sub_parser = subparsers.add_parser('set_ab_metadata',
3014 help='Set A/B metadata.')
3015 sub_parser.add_argument('--misc_image',
3016 help=('The misc image to modify. If the image does '
3017 'not exist, it will be created.'),
3018 type=argparse.FileType('r+b'),
3019 required=True)
3020 sub_parser.add_argument('--slot_data',
3021 help=('Slot data of the form "priority", '
3022 '"tries_remaining", "sucessful_boot" for '
3023 'slot A followed by the same for slot B, '
3024 'separated by colons. The default value '
3025 'is 15:7:0:14:7:0.'),
3026 default='15:7:0:14:7:0')
3027 sub_parser.set_defaults(func=self.set_ab_metadata)
3028
Darren Krahn147b08d2016-12-20 16:38:29 -08003029 sub_parser = subparsers.add_parser(
3030 'make_atx_certificate',
3031 help='Create an Android Things eXtension (ATX) certificate.')
3032 sub_parser.add_argument('--output',
3033 help='Write certificate to file',
3034 type=argparse.FileType('wb'),
3035 default=sys.stdout)
3036 sub_parser.add_argument('--subject',
3037 help=('Path to subject file'),
3038 type=argparse.FileType('rb'),
3039 required=True)
3040 sub_parser.add_argument('--subject_key',
3041 help=('Path to subject RSA public key file'),
3042 type=argparse.FileType('rb'),
3043 required=True)
3044 sub_parser.add_argument('--subject_key_version',
3045 help=('Version of the subject key'),
3046 type=parse_number,
3047 required=False)
3048 sub_parser.add_argument('--subject_is_intermediate_authority',
3049 help=('Generate an intermediate authority '
3050 'certificate'),
3051 action='store_true')
3052 sub_parser.add_argument('--authority_key',
3053 help='Path to authority RSA private key file',
3054 required=False)
3055 sub_parser.add_argument('--signing_helper',
3056 help='Path to helper used for signing',
3057 metavar='APP',
3058 default=None,
3059 required=False)
3060 sub_parser.set_defaults(func=self.make_atx_certificate)
3061
3062 sub_parser = subparsers.add_parser(
3063 'make_atx_permanent_attributes',
3064 help='Create Android Things eXtension (ATX) permanent attributes.')
3065 sub_parser.add_argument('--output',
3066 help='Write attributes to file',
3067 type=argparse.FileType('wb'),
3068 default=sys.stdout)
3069 sub_parser.add_argument('--root_authority_key',
3070 help='Path to authority RSA public key file',
3071 type=argparse.FileType('rb'),
3072 required=True)
3073 sub_parser.add_argument('--product_id',
3074 help=('Path to Product ID file'),
3075 type=argparse.FileType('rb'),
3076 required=True)
3077 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3078
3079 sub_parser = subparsers.add_parser(
3080 'make_atx_metadata',
3081 help='Create Android Things eXtension (ATX) metadata.')
3082 sub_parser.add_argument('--output',
3083 help='Write metadata to file',
3084 type=argparse.FileType('wb'),
3085 default=sys.stdout)
3086 sub_parser.add_argument('--intermediate_key_certificate',
3087 help='Path to intermediate key certificate file',
3088 type=argparse.FileType('rb'),
3089 required=True)
3090 sub_parser.add_argument('--product_key_certificate',
3091 help='Path to product key certificate file',
3092 type=argparse.FileType('rb'),
3093 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003094 sub_parser.set_defaults(func=self.make_atx_metadata)
3095
David Zeuthen21e95262016-07-27 17:58:40 -04003096 args = parser.parse_args(argv[1:])
3097 try:
3098 args.func(args)
3099 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003100 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003101 sys.exit(1)
3102
3103 def version(self, _):
3104 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003105 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003106
3107 def extract_public_key(self, args):
3108 """Implements the 'extract_public_key' sub-command."""
3109 self.avb.extract_public_key(args.key, args.output)
3110
3111 def make_vbmeta_image(self, args):
3112 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003113 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003114 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003115 args.algorithm, args.key,
3116 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003117 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003118 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003119 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003120 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003121 args.signing_helper,
3122 args.internal_release_string,
3123 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003124
3125 def add_hash_footer(self, args):
3126 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003127 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003128 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003129 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003130 args.salt, args.chain_partition, args.algorithm,
3131 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003132 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003133 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003134 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003135 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003136 args.include_descriptors_from_image,
3137 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003138 args.internal_release_string,
3139 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003140 args.output_vbmeta_image,
3141 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003142
3143 def add_hashtree_footer(self, args):
3144 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003145 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003146 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3147 args.partition_size,
3148 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003149 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003150 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003151 args.salt, args.chain_partition, args.algorithm,
3152 args.key, args.public_key_metadata,
3153 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003154 args.prop_from_file,
3155 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003156 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003157 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003158 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003159 args.internal_release_string,
3160 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003161 args.output_vbmeta_image,
3162 args.do_not_append_vbmeta_image)
3163
David Zeuthen21e95262016-07-27 17:58:40 -04003164 def erase_footer(self, args):
3165 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003166 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003167
David Zeuthen8b6973b2016-09-20 12:39:49 -04003168 def set_ab_metadata(self, args):
3169 """Implements the 'set_ab_metadata' sub-command."""
3170 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3171
David Zeuthen21e95262016-07-27 17:58:40 -04003172 def info_image(self, args):
3173 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003174 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003175
Darren Krahn147b08d2016-12-20 16:38:29 -08003176 def make_atx_certificate(self, args):
3177 """Implements the 'make_atx_certificate' sub-command."""
3178 self.avb.make_atx_certificate(args.output, args.authority_key,
3179 args.subject_key.read(),
3180 args.subject_key_version,
3181 args.subject.read(),
3182 args.subject_is_intermediate_authority,
3183 args.signing_helper)
3184
3185 def make_atx_permanent_attributes(self, args):
3186 """Implements the 'make_atx_permanent_attributes' sub-command."""
3187 self.avb.make_atx_permanent_attributes(args.output,
3188 args.root_authority_key.read(),
3189 args.product_id.read())
3190
3191 def make_atx_metadata(self, args):
3192 """Implements the 'make_atx_metadata' sub-command."""
3193 self.avb.make_atx_metadata(args.output,
3194 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003195 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003196
David Zeuthen21e95262016-07-27 17:58:40 -04003197
3198if __name__ == '__main__':
3199 tool = AvbTool()
3200 tool.run(sys.argv)