blob: a56a730804b9f87fa88dfb7cb0baa1bbed03d81e [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
Esun Kimff44f232017-03-30 10:34:54 +0900388def raw_sign(signing_helper, algorithm_name, signature_num_bytes, key_path,
389 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800390 """Computes a raw RSA signature using |signing_helper| or openssl.
391
392 Arguments:
393 signing_helper: Program which signs a hash and returns the signature.
394 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900395 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800396 key_path: Path to the private key file. Must be PEM format.
397 raw_data_to_sign: Data to sign (bytearray or str expected).
398
399 Returns:
400 A bytearray containing the signature.
401
402 Raises:
403 Exception: If an error occurs.
404 """
405 p = None
406 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500407 p = subprocess.Popen(
408 [signing_helper, algorithm_name, key_path],
409 stdin=subprocess.PIPE,
410 stdout=subprocess.PIPE,
411 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800412 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500413 p = subprocess.Popen(
414 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
415 stdin=subprocess.PIPE,
416 stdout=subprocess.PIPE,
417 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800418 (pout, perr) = p.communicate(str(raw_data_to_sign))
419 retcode = p.wait()
420 if retcode != 0:
421 raise AvbError('Error signing: {}'.format(perr))
Esun Kimff44f232017-03-30 10:34:54 +0900422 signature = bytearray(pout)
423 if len(signature) != signature_num_bytes:
424 raise AvbError('Error signing: Invalid length of signature')
425 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800426
427
David Zeuthena4fee8b2016-08-22 15:20:43 -0400428class ImageChunk(object):
429 """Data structure used for representing chunks in Android sparse files.
430
431 Attributes:
432 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
433 chunk_offset: Offset in the sparse file where this chunk begins.
434 output_offset: Offset in de-sparsified file where output begins.
435 output_size: Number of bytes in output.
436 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
437 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
438 """
439
440 FORMAT = '<2H2I'
441 TYPE_RAW = 0xcac1
442 TYPE_FILL = 0xcac2
443 TYPE_DONT_CARE = 0xcac3
444 TYPE_CRC32 = 0xcac4
445
446 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
447 input_offset, fill_data):
448 """Initializes an ImageChunk object.
449
450 Arguments:
451 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
452 chunk_offset: Offset in the sparse file where this chunk begins.
453 output_offset: Offset in de-sparsified file.
454 output_size: Number of bytes in output.
455 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
456 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
457
458 Raises:
459 ValueError: If data is not well-formed.
460 """
461 self.chunk_type = chunk_type
462 self.chunk_offset = chunk_offset
463 self.output_offset = output_offset
464 self.output_size = output_size
465 self.input_offset = input_offset
466 self.fill_data = fill_data
467 # Check invariants.
468 if self.chunk_type == self.TYPE_RAW:
469 if self.fill_data is not None:
470 raise ValueError('RAW chunk cannot have fill_data set.')
471 if not self.input_offset:
472 raise ValueError('RAW chunk must have input_offset set.')
473 elif self.chunk_type == self.TYPE_FILL:
474 if self.fill_data is None:
475 raise ValueError('FILL chunk must have fill_data set.')
476 if self.input_offset:
477 raise ValueError('FILL chunk cannot have input_offset set.')
478 elif self.chunk_type == self.TYPE_DONT_CARE:
479 if self.fill_data is not None:
480 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
481 if self.input_offset:
482 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
483 else:
484 raise ValueError('Invalid chunk type')
485
486
487class ImageHandler(object):
488 """Abstraction for image I/O with support for Android sparse images.
489
490 This class provides an interface for working with image files that
491 may be using the Android Sparse Image format. When an instance is
492 constructed, we test whether it's an Android sparse file. If so,
493 operations will be on the sparse file by interpreting the sparse
494 format, otherwise they will be directly on the file. Either way the
495 operations do the same.
496
497 For reading, this interface mimics a file object - it has seek(),
498 tell(), and read() methods. For writing, only truncation
499 (truncate()) and appending is supported (append_raw() and
500 append_dont_care()). Additionally, data can only be written in units
501 of the block size.
502
503 Attributes:
504 is_sparse: Whether the file being operated on is sparse.
505 block_size: The block size, typically 4096.
506 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400507 """
508 # See system/core/libsparse/sparse_format.h for details.
509 MAGIC = 0xed26ff3a
510 HEADER_FORMAT = '<I4H4I'
511
512 # These are formats and offset of just the |total_chunks| and
513 # |total_blocks| fields.
514 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
515 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
516
517 def __init__(self, image_filename):
518 """Initializes an image handler.
519
520 Arguments:
521 image_filename: The name of the file to operate on.
522
523 Raises:
524 ValueError: If data in the file is invalid.
525 """
526 self._image_filename = image_filename
527 self._read_header()
528
529 def _read_header(self):
530 """Initializes internal data structures used for reading file.
531
532 This may be called multiple times and is typically called after
533 modifying the file (e.g. appending, truncation).
534
535 Raises:
536 ValueError: If data in the file is invalid.
537 """
538 self.is_sparse = False
539 self.block_size = 4096
540 self._file_pos = 0
541 self._image = open(self._image_filename, 'r+b')
542 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400543 self.image_size = self._image.tell()
544
545 self._image.seek(0, os.SEEK_SET)
546 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
547 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
548 block_size, self._num_total_blocks, self._num_total_chunks,
549 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
550 if magic != self.MAGIC:
551 # Not a sparse image, our job here is done.
552 return
553 if not (major_version == 1 and minor_version == 0):
554 raise ValueError('Encountered sparse image format version {}.{} but '
555 'only 1.0 is supported'.format(major_version,
556 minor_version))
557 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
558 raise ValueError('Unexpected file_hdr_sz value {}.'.
559 format(file_hdr_sz))
560 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
561 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
562 format(chunk_hdr_sz))
563
564 self.block_size = block_size
565
566 # Build an list of chunks by parsing the file.
567 self._chunks = []
568
569 # Find the smallest offset where only "Don't care" chunks
570 # follow. This will be the size of the content in the sparse
571 # image.
572 offset = 0
573 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400574 for _ in xrange(1, self._num_total_chunks + 1):
575 chunk_offset = self._image.tell()
576
577 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
578 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
579 header_bin)
580 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
581
David Zeuthena4fee8b2016-08-22 15:20:43 -0400582 if chunk_type == ImageChunk.TYPE_RAW:
583 if data_sz != (chunk_sz * self.block_size):
584 raise ValueError('Raw chunk input size ({}) does not match output '
585 'size ({})'.
586 format(data_sz, chunk_sz*self.block_size))
587 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
588 chunk_offset,
589 output_offset,
590 chunk_sz*self.block_size,
591 self._image.tell(),
592 None))
593 self._image.read(data_sz)
594
595 elif chunk_type == ImageChunk.TYPE_FILL:
596 if data_sz != 4:
597 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
598 'has {}'.format(data_sz))
599 fill_data = self._image.read(4)
600 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
601 chunk_offset,
602 output_offset,
603 chunk_sz*self.block_size,
604 None,
605 fill_data))
606 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
607 if data_sz != 0:
608 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
609 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400610 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
611 chunk_offset,
612 output_offset,
613 chunk_sz*self.block_size,
614 None,
615 None))
616 elif chunk_type == ImageChunk.TYPE_CRC32:
617 if data_sz != 4:
618 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
619 'this has {}'.format(data_sz))
620 self._image.read(4)
621 else:
622 raise ValueError('Unknown chunk type {}'.format(chunk_type))
623
624 offset += chunk_sz
625 output_offset += chunk_sz*self.block_size
626
627 # Record where sparse data end.
628 self._sparse_end = self._image.tell()
629
630 # Now that we've traversed all chunks, sanity check.
631 if self._num_total_blocks != offset:
632 raise ValueError('The header said we should have {} output blocks, '
633 'but we saw {}'.format(self._num_total_blocks, offset))
634 junk_len = len(self._image.read())
635 if junk_len > 0:
636 raise ValueError('There were {} bytes of extra data at the end of the '
637 'file.'.format(junk_len))
638
David Zeuthen09692692016-09-30 16:16:40 -0400639 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400640 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400641
642 # This is used when bisecting in read() to find the initial slice.
643 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
644
645 self.is_sparse = True
646
647 def _update_chunks_and_blocks(self):
648 """Helper function to update the image header.
649
650 The the |total_chunks| and |total_blocks| fields in the header
651 will be set to value of the |_num_total_blocks| and
652 |_num_total_chunks| attributes.
653
654 """
655 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
656 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
657 self._num_total_blocks,
658 self._num_total_chunks))
659
660 def append_dont_care(self, num_bytes):
661 """Appends a DONT_CARE chunk to the sparse file.
662
663 The given number of bytes must be a multiple of the block size.
664
665 Arguments:
666 num_bytes: Size in number of bytes of the DONT_CARE chunk.
667 """
668 assert num_bytes % self.block_size == 0
669
670 if not self.is_sparse:
671 self._image.seek(0, os.SEEK_END)
672 # This is more efficient that writing NUL bytes since it'll add
673 # a hole on file systems that support sparse files (native
674 # sparse, not Android sparse).
675 self._image.truncate(self._image.tell() + num_bytes)
676 self._read_header()
677 return
678
679 self._num_total_chunks += 1
680 self._num_total_blocks += num_bytes / self.block_size
681 self._update_chunks_and_blocks()
682
683 self._image.seek(self._sparse_end, os.SEEK_SET)
684 self._image.write(struct.pack(ImageChunk.FORMAT,
685 ImageChunk.TYPE_DONT_CARE,
686 0, # Reserved
687 num_bytes / self.block_size,
688 struct.calcsize(ImageChunk.FORMAT)))
689 self._read_header()
690
691 def append_raw(self, data):
692 """Appends a RAW chunk to the sparse file.
693
694 The length of the given data must be a multiple of the block size.
695
696 Arguments:
697 data: Data to append.
698 """
699 assert len(data) % self.block_size == 0
700
701 if not self.is_sparse:
702 self._image.seek(0, os.SEEK_END)
703 self._image.write(data)
704 self._read_header()
705 return
706
707 self._num_total_chunks += 1
708 self._num_total_blocks += len(data) / self.block_size
709 self._update_chunks_and_blocks()
710
711 self._image.seek(self._sparse_end, os.SEEK_SET)
712 self._image.write(struct.pack(ImageChunk.FORMAT,
713 ImageChunk.TYPE_RAW,
714 0, # Reserved
715 len(data) / self.block_size,
716 len(data) +
717 struct.calcsize(ImageChunk.FORMAT)))
718 self._image.write(data)
719 self._read_header()
720
721 def append_fill(self, fill_data, size):
722 """Appends a fill chunk to the sparse file.
723
724 The total length of the fill data must be a multiple of the block size.
725
726 Arguments:
727 fill_data: Fill data to append - must be four bytes.
728 size: Number of chunk - must be a multiple of four and the block size.
729 """
730 assert len(fill_data) == 4
731 assert size % 4 == 0
732 assert size % self.block_size == 0
733
734 if not self.is_sparse:
735 self._image.seek(0, os.SEEK_END)
736 self._image.write(fill_data * (size/4))
737 self._read_header()
738 return
739
740 self._num_total_chunks += 1
741 self._num_total_blocks += size / self.block_size
742 self._update_chunks_and_blocks()
743
744 self._image.seek(self._sparse_end, os.SEEK_SET)
745 self._image.write(struct.pack(ImageChunk.FORMAT,
746 ImageChunk.TYPE_FILL,
747 0, # Reserved
748 size / self.block_size,
749 4 + struct.calcsize(ImageChunk.FORMAT)))
750 self._image.write(fill_data)
751 self._read_header()
752
753 def seek(self, offset):
754 """Sets the cursor position for reading from unsparsified file.
755
756 Arguments:
757 offset: Offset to seek to from the beginning of the file.
758 """
759 self._file_pos = offset
760
761 def read(self, size):
762 """Reads data from the unsparsified file.
763
764 This method may return fewer than |size| bytes of data if the end
765 of the file was encountered.
766
767 The file cursor for reading is advanced by the number of bytes
768 read.
769
770 Arguments:
771 size: Number of bytes to read.
772
773 Returns:
774 The data.
775
776 """
777 if not self.is_sparse:
778 self._image.seek(self._file_pos)
779 data = self._image.read(size)
780 self._file_pos += len(data)
781 return data
782
783 # Iterate over all chunks.
784 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
785 self._file_pos) - 1
786 data = bytearray()
787 to_go = size
788 while to_go > 0:
789 chunk = self._chunks[chunk_idx]
790 chunk_pos_offset = self._file_pos - chunk.output_offset
791 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
792
793 if chunk.chunk_type == ImageChunk.TYPE_RAW:
794 self._image.seek(chunk.input_offset + chunk_pos_offset)
795 data.extend(self._image.read(chunk_pos_to_go))
796 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
797 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
798 offset_mod = chunk_pos_offset % len(chunk.fill_data)
799 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
800 else:
801 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
802 data.extend('\0' * chunk_pos_to_go)
803
804 to_go -= chunk_pos_to_go
805 self._file_pos += chunk_pos_to_go
806 chunk_idx += 1
807 # Generate partial read in case of EOF.
808 if chunk_idx >= len(self._chunks):
809 break
810
811 return data
812
813 def tell(self):
814 """Returns the file cursor position for reading from unsparsified file.
815
816 Returns:
817 The file cursor position for reading.
818 """
819 return self._file_pos
820
821 def truncate(self, size):
822 """Truncates the unsparsified file.
823
824 Arguments:
825 size: Desired size of unsparsified file.
826
827 Raises:
828 ValueError: If desired size isn't a multiple of the block size.
829 """
830 if not self.is_sparse:
831 self._image.truncate(size)
832 self._read_header()
833 return
834
835 if size % self.block_size != 0:
836 raise ValueError('Cannot truncate to a size which is not a multiple '
837 'of the block size')
838
839 if size == self.image_size:
840 # Trivial where there's nothing to do.
841 return
842 elif size < self.image_size:
843 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
844 chunk = self._chunks[chunk_idx]
845 if chunk.output_offset != size:
846 # Truncation in the middle of a trunk - need to keep the chunk
847 # and modify it.
848 chunk_idx_for_update = chunk_idx + 1
849 num_to_keep = size - chunk.output_offset
850 assert num_to_keep % self.block_size == 0
851 if chunk.chunk_type == ImageChunk.TYPE_RAW:
852 truncate_at = (chunk.chunk_offset +
853 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
854 data_sz = num_to_keep
855 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
856 truncate_at = (chunk.chunk_offset +
857 struct.calcsize(ImageChunk.FORMAT) + 4)
858 data_sz = 4
859 else:
860 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
861 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
862 data_sz = 0
863 chunk_sz = num_to_keep/self.block_size
864 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
865 self._image.seek(chunk.chunk_offset)
866 self._image.write(struct.pack(ImageChunk.FORMAT,
867 chunk.chunk_type,
868 0, # Reserved
869 chunk_sz,
870 total_sz))
871 chunk.output_size = num_to_keep
872 else:
873 # Truncation at trunk boundary.
874 truncate_at = chunk.chunk_offset
875 chunk_idx_for_update = chunk_idx
876
877 self._num_total_chunks = chunk_idx_for_update
878 self._num_total_blocks = 0
879 for i in range(0, chunk_idx_for_update):
880 self._num_total_blocks += self._chunks[i].output_size / self.block_size
881 self._update_chunks_and_blocks()
882 self._image.truncate(truncate_at)
883
884 # We've modified the file so re-read all data.
885 self._read_header()
886 else:
887 # Truncating to grow - just add a DONT_CARE section.
888 self.append_dont_care(size - self.image_size)
889
890
David Zeuthen21e95262016-07-27 17:58:40 -0400891class AvbDescriptor(object):
892 """Class for AVB descriptor.
893
894 See the |AvbDescriptor| C struct for more information.
895
896 Attributes:
897 tag: The tag identifying what kind of descriptor this is.
898 data: The data in the descriptor.
899 """
900
901 SIZE = 16
902 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
903
904 def __init__(self, data):
905 """Initializes a new property descriptor.
906
907 Arguments:
908 data: If not None, must be a bytearray().
909
910 Raises:
911 LookupError: If the given descriptor is malformed.
912 """
913 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
914
915 if data:
916 (self.tag, num_bytes_following) = (
917 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
918 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
919 else:
920 self.tag = None
921 self.data = None
922
923 def print_desc(self, o):
924 """Print the descriptor.
925
926 Arguments:
927 o: The object to write the output to.
928 """
929 o.write(' Unknown descriptor:\n')
930 o.write(' Tag: {}\n'.format(self.tag))
931 if len(self.data) < 256:
932 o.write(' Data: {} ({} bytes)\n'.format(
933 repr(str(self.data)), len(self.data)))
934 else:
935 o.write(' Data: {} bytes\n'.format(len(self.data)))
936
937 def encode(self):
938 """Serializes the descriptor.
939
940 Returns:
941 A bytearray() with the descriptor data.
942 """
943 num_bytes_following = len(self.data)
944 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
945 padding_size = nbf_with_padding - num_bytes_following
946 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
947 padding = struct.pack(str(padding_size) + 'x')
948 ret = desc + self.data + padding
949 return bytearray(ret)
950
951
952class AvbPropertyDescriptor(AvbDescriptor):
953 """A class for property descriptors.
954
955 See the |AvbPropertyDescriptor| C struct for more information.
956
957 Attributes:
958 key: The key.
959 value: The key.
960 """
961
962 TAG = 0
963 SIZE = 32
964 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
965 'Q' # key size (bytes)
966 'Q') # value size (bytes)
967
968 def __init__(self, data=None):
969 """Initializes a new property descriptor.
970
971 Arguments:
972 data: If not None, must be a bytearray of size |SIZE|.
973
974 Raises:
975 LookupError: If the given descriptor is malformed.
976 """
977 AvbDescriptor.__init__(self, None)
978 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
979
980 if data:
981 (tag, num_bytes_following, key_size,
982 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
983 expected_size = round_to_multiple(
984 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
985 if tag != self.TAG or num_bytes_following != expected_size:
986 raise LookupError('Given data does not look like a property '
987 'descriptor.')
988 self.key = data[self.SIZE:(self.SIZE + key_size)]
989 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
990 value_size)]
991 else:
992 self.key = ''
993 self.value = ''
994
995 def print_desc(self, o):
996 """Print the descriptor.
997
998 Arguments:
999 o: The object to write the output to.
1000 """
1001 if len(self.value) < 256:
1002 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1003 else:
1004 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1005
1006 def encode(self):
1007 """Serializes the descriptor.
1008
1009 Returns:
1010 A bytearray() with the descriptor data.
1011 """
1012 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1013 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1014 padding_size = nbf_with_padding - num_bytes_following
1015 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1016 len(self.key), len(self.value))
1017 padding = struct.pack(str(padding_size) + 'x')
1018 ret = desc + self.key + '\0' + self.value + '\0' + padding
1019 return bytearray(ret)
1020
1021
1022class AvbHashtreeDescriptor(AvbDescriptor):
1023 """A class for hashtree descriptors.
1024
1025 See the |AvbHashtreeDescriptor| C struct for more information.
1026
1027 Attributes:
1028 dm_verity_version: dm-verity version used.
1029 image_size: Size of the image, after rounding up to |block_size|.
1030 tree_offset: Offset of the hash tree in the file.
1031 tree_size: Size of the tree.
1032 data_block_size: Data block size
1033 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001034 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1035 fec_offset: Offset of FEC data (0 if FEC is not used).
1036 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001037 hash_algorithm: Hash algorithm used.
1038 partition_name: Partition name.
1039 salt: Salt used.
1040 root_digest: Root digest.
1041 """
1042
1043 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001044 RESERVED = 64
1045 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001046 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1047 'L' # dm-verity version used
1048 'Q' # image size (bytes)
1049 'Q' # tree offset (bytes)
1050 'Q' # tree size (bytes)
1051 'L' # data block size (bytes)
1052 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001053 'L' # FEC number of roots
1054 'Q' # FEC offset (bytes)
1055 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001056 '32s' # hash algorithm used
1057 'L' # partition name (bytes)
1058 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001059 'L' + # root digest length (bytes)
1060 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001061
1062 def __init__(self, data=None):
1063 """Initializes a new hashtree descriptor.
1064
1065 Arguments:
1066 data: If not None, must be a bytearray of size |SIZE|.
1067
1068 Raises:
1069 LookupError: If the given descriptor is malformed.
1070 """
1071 AvbDescriptor.__init__(self, None)
1072 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1073
1074 if data:
1075 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1076 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001077 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1078 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001079 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1080 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001081 expected_size = round_to_multiple(
1082 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1083 if tag != self.TAG or num_bytes_following != expected_size:
1084 raise LookupError('Given data does not look like a hashtree '
1085 'descriptor.')
1086 # Nuke NUL-bytes at the end.
1087 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1088 o = 0
1089 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1090 partition_name_len)])
1091 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1092 self.partition_name.decode('utf-8')
1093 o += partition_name_len
1094 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1095 o += salt_len
1096 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1097 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1098 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1099
1100 else:
1101 self.dm_verity_version = 0
1102 self.image_size = 0
1103 self.tree_offset = 0
1104 self.tree_size = 0
1105 self.data_block_size = 0
1106 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001107 self.fec_num_roots = 0
1108 self.fec_offset = 0
1109 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001110 self.hash_algorithm = ''
1111 self.partition_name = ''
1112 self.salt = bytearray()
1113 self.root_digest = bytearray()
1114
1115 def print_desc(self, o):
1116 """Print the descriptor.
1117
1118 Arguments:
1119 o: The object to write the output to.
1120 """
1121 o.write(' Hashtree descriptor:\n')
1122 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1123 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1124 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1125 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1126 o.write(' Data Block Size: {} bytes\n'.format(
1127 self.data_block_size))
1128 o.write(' Hash Block Size: {} bytes\n'.format(
1129 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001130 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1131 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1132 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001133 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1134 o.write(' Partition Name: {}\n'.format(self.partition_name))
1135 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1136 'hex')))
1137 o.write(' Root Digest: {}\n'.format(str(
1138 self.root_digest).encode('hex')))
1139
1140 def encode(self):
1141 """Serializes the descriptor.
1142
1143 Returns:
1144 A bytearray() with the descriptor data.
1145 """
1146 encoded_name = self.partition_name.encode('utf-8')
1147 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1148 len(self.root_digest) - 16)
1149 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1150 padding_size = nbf_with_padding - num_bytes_following
1151 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1152 self.dm_verity_version, self.image_size,
1153 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001154 self.hash_block_size, self.fec_num_roots,
1155 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001156 len(encoded_name), len(self.salt), len(self.root_digest),
1157 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001158 padding = struct.pack(str(padding_size) + 'x')
1159 ret = desc + encoded_name + self.salt + self.root_digest + padding
1160 return bytearray(ret)
1161
1162
1163class AvbHashDescriptor(AvbDescriptor):
1164 """A class for hash descriptors.
1165
1166 See the |AvbHashDescriptor| C struct for more information.
1167
1168 Attributes:
1169 image_size: Image size, in bytes.
1170 hash_algorithm: Hash algorithm used.
1171 partition_name: Partition name.
1172 salt: Salt used.
1173 digest: The hash value of salt and data combined.
1174 """
1175
1176 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001177 RESERVED = 64
1178 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001179 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1180 'Q' # image size (bytes)
1181 '32s' # hash algorithm used
1182 'L' # partition name (bytes)
1183 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001184 'L' + # digest length (bytes)
1185 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001186
1187 def __init__(self, data=None):
1188 """Initializes a new hash descriptor.
1189
1190 Arguments:
1191 data: If not None, must be a bytearray of size |SIZE|.
1192
1193 Raises:
1194 LookupError: If the given descriptor is malformed.
1195 """
1196 AvbDescriptor.__init__(self, None)
1197 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1198
1199 if data:
1200 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1201 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001202 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001203 expected_size = round_to_multiple(
1204 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1205 if tag != self.TAG or num_bytes_following != expected_size:
1206 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1207 # Nuke NUL-bytes at the end.
1208 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1209 o = 0
1210 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1211 partition_name_len)])
1212 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1213 self.partition_name.decode('utf-8')
1214 o += partition_name_len
1215 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1216 o += salt_len
1217 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1218 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1219 raise LookupError('digest_len doesn\'t match hash algorithm')
1220
1221 else:
1222 self.image_size = 0
1223 self.hash_algorithm = ''
1224 self.partition_name = ''
1225 self.salt = bytearray()
1226 self.digest = bytearray()
1227
1228 def print_desc(self, o):
1229 """Print the descriptor.
1230
1231 Arguments:
1232 o: The object to write the output to.
1233 """
1234 o.write(' Hash descriptor:\n')
1235 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1236 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1237 o.write(' Partition Name: {}\n'.format(self.partition_name))
1238 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1239 'hex')))
1240 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1241 'hex')))
1242
1243 def encode(self):
1244 """Serializes the descriptor.
1245
1246 Returns:
1247 A bytearray() with the descriptor data.
1248 """
1249 encoded_name = self.partition_name.encode('utf-8')
1250 num_bytes_following = (
1251 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1252 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1253 padding_size = nbf_with_padding - num_bytes_following
1254 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1255 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001256 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001257 padding = struct.pack(str(padding_size) + 'x')
1258 ret = desc + encoded_name + self.salt + self.digest + padding
1259 return bytearray(ret)
1260
1261
1262class AvbKernelCmdlineDescriptor(AvbDescriptor):
1263 """A class for kernel command-line descriptors.
1264
1265 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1266
1267 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001268 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001269 kernel_cmdline: The kernel command-line.
1270 """
1271
1272 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001273 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001274 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001275 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001276 'L') # cmdline length (bytes)
1277
David Zeuthenfd41eb92016-11-17 12:24:47 -05001278 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1279 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1280
David Zeuthen21e95262016-07-27 17:58:40 -04001281 def __init__(self, data=None):
1282 """Initializes a new kernel cmdline descriptor.
1283
1284 Arguments:
1285 data: If not None, must be a bytearray of size |SIZE|.
1286
1287 Raises:
1288 LookupError: If the given descriptor is malformed.
1289 """
1290 AvbDescriptor.__init__(self, None)
1291 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1292
1293 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001294 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001295 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1296 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1297 8)
1298 if tag != self.TAG or num_bytes_following != expected_size:
1299 raise LookupError('Given data does not look like a kernel cmdline '
1300 'descriptor.')
1301 # Nuke NUL-bytes at the end.
1302 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1303 kernel_cmdline_length)])
1304 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1305 self.kernel_cmdline.decode('utf-8')
1306 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001307 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001308 self.kernel_cmdline = ''
1309
1310 def print_desc(self, o):
1311 """Print the descriptor.
1312
1313 Arguments:
1314 o: The object to write the output to.
1315 """
1316 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001317 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001318 o.write(' Kernel Cmdline: {}\n'.format(repr(
1319 self.kernel_cmdline)))
1320
1321 def encode(self):
1322 """Serializes the descriptor.
1323
1324 Returns:
1325 A bytearray() with the descriptor data.
1326 """
1327 encoded_str = self.kernel_cmdline.encode('utf-8')
1328 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1329 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1330 padding_size = nbf_with_padding - num_bytes_following
1331 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001332 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001333 padding = struct.pack(str(padding_size) + 'x')
1334 ret = desc + encoded_str + padding
1335 return bytearray(ret)
1336
1337
1338class AvbChainPartitionDescriptor(AvbDescriptor):
1339 """A class for chained partition descriptors.
1340
1341 See the |AvbChainPartitionDescriptor| C struct for more information.
1342
1343 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001344 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001345 partition_name: Partition name.
1346 public_key: Bytes for the public key.
1347 """
1348
1349 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001350 RESERVED = 64
1351 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001352 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001353 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001354 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001355 'L' + # public_key_size (bytes)
1356 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001357
1358 def __init__(self, data=None):
1359 """Initializes a new chain partition descriptor.
1360
1361 Arguments:
1362 data: If not None, must be a bytearray of size |SIZE|.
1363
1364 Raises:
1365 LookupError: If the given descriptor is malformed.
1366 """
1367 AvbDescriptor.__init__(self, None)
1368 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1369
1370 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001371 (tag, num_bytes_following, self.rollback_index_location,
1372 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001373 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001374 expected_size = round_to_multiple(
1375 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1376 if tag != self.TAG or num_bytes_following != expected_size:
1377 raise LookupError('Given data does not look like a chain partition '
1378 'descriptor.')
1379 o = 0
1380 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1381 partition_name_len)])
1382 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1383 self.partition_name.decode('utf-8')
1384 o += partition_name_len
1385 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1386
1387 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001388 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001389 self.partition_name = ''
1390 self.public_key = bytearray()
1391
1392 def print_desc(self, o):
1393 """Print the descriptor.
1394
1395 Arguments:
1396 o: The object to write the output to.
1397 """
1398 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001399 o.write(' Partition Name: {}\n'.format(self.partition_name))
1400 o.write(' Rollback Index Location: {}\n'.format(
1401 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001402 # Just show the SHA1 of the key, for size reasons.
1403 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001404 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001405
1406 def encode(self):
1407 """Serializes the descriptor.
1408
1409 Returns:
1410 A bytearray() with the descriptor data.
1411 """
1412 encoded_name = self.partition_name.encode('utf-8')
1413 num_bytes_following = (
1414 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1415 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1416 padding_size = nbf_with_padding - num_bytes_following
1417 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001418 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001419 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001420 padding = struct.pack(str(padding_size) + 'x')
1421 ret = desc + encoded_name + self.public_key + padding
1422 return bytearray(ret)
1423
1424
1425DESCRIPTOR_CLASSES = [
1426 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1427 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1428]
1429
1430
1431def parse_descriptors(data):
1432 """Parses a blob of data into descriptors.
1433
1434 Arguments:
1435 data: A bytearray() with encoded descriptors.
1436
1437 Returns:
1438 A list of instances of objects derived from AvbDescriptor. For
1439 unknown descriptors, the class AvbDescriptor is used.
1440 """
1441 o = 0
1442 ret = []
1443 while o < len(data):
1444 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1445 if tag < len(DESCRIPTOR_CLASSES):
1446 c = DESCRIPTOR_CLASSES[tag]
1447 else:
1448 c = AvbDescriptor
1449 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1450 o += 16 + nb_following
1451 return ret
1452
1453
1454class AvbFooter(object):
1455 """A class for parsing and writing footers.
1456
1457 Footers are stored at the end of partitions and point to where the
1458 AvbVBMeta blob is located. They also contain the original size of
1459 the image before AVB information was added.
1460
1461 Attributes:
1462 magic: Magic for identifying the footer, see |MAGIC|.
1463 version_major: The major version of avbtool that wrote the footer.
1464 version_minor: The minor version of avbtool that wrote the footer.
1465 original_image_size: Original image size.
1466 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1467 vbmeta_size: Size of the AvbVBMeta blob.
1468 """
1469
1470 MAGIC = 'AVBf'
1471 SIZE = 64
1472 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001473 FOOTER_VERSION_MAJOR = 1
1474 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001475 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1476 'Q' # Original image size.
1477 'Q' # Offset of VBMeta blob.
1478 'Q' + # Size of VBMeta blob.
1479 str(RESERVED) + 'x') # padding for reserved bytes
1480
1481 def __init__(self, data=None):
1482 """Initializes a new footer object.
1483
1484 Arguments:
1485 data: If not None, must be a bytearray of size 4096.
1486
1487 Raises:
1488 LookupError: If the given footer is malformed.
1489 struct.error: If the given data has no footer.
1490 """
1491 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1492
1493 if data:
1494 (self.magic, self.version_major, self.version_minor,
1495 self.original_image_size, self.vbmeta_offset,
1496 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1497 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001498 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001499 else:
1500 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001501 self.version_major = self.FOOTER_VERSION_MAJOR
1502 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001503 self.original_image_size = 0
1504 self.vbmeta_offset = 0
1505 self.vbmeta_size = 0
1506
David Zeuthena4fee8b2016-08-22 15:20:43 -04001507 def encode(self):
1508 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001509
David Zeuthena4fee8b2016-08-22 15:20:43 -04001510 Returns:
1511 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001512 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001513 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1514 self.version_minor, self.original_image_size,
1515 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001516
1517
1518class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001519 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001520
1521 Attributes:
1522 The attributes correspond to the |AvbVBMetaHeader| struct
1523 defined in avb_vbmeta_header.h.
1524 """
1525
1526 SIZE = 256
1527
David Zeuthene3cadca2017-02-22 21:25:46 -05001528 # Keep in sync with |reserved0| and |reserved| field of
1529 # |AvbVBMetaImageHeader|.
1530 RESERVED0 = 4
1531 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001532
1533 # Keep in sync with |AvbVBMetaImageHeader|.
1534 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1535 '2Q' # 2 x block size
1536 'L' # algorithm type
1537 '2Q' # offset, size (hash)
1538 '2Q' # offset, size (signature)
1539 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001540 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001541 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001542 'Q' # rollback_index
1543 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001544 str(RESERVED0) + 'x' + # padding for reserved bytes
1545 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001546 str(RESERVED) + 'x') # padding for reserved bytes
1547
1548 def __init__(self, data=None):
1549 """Initializes a new header object.
1550
1551 Arguments:
1552 data: If not None, must be a bytearray of size 8192.
1553
1554 Raises:
1555 Exception: If the given data is malformed.
1556 """
1557 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1558
1559 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001560 (self.magic, self.required_libavb_version_major,
1561 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001562 self.authentication_data_block_size, self.auxiliary_data_block_size,
1563 self.algorithm_type, self.hash_offset, self.hash_size,
1564 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001565 self.public_key_size, self.public_key_metadata_offset,
1566 self.public_key_metadata_size, self.descriptors_offset,
1567 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001568 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001569 self.flags,
1570 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001571 # Nuke NUL-bytes at the end of the string.
1572 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001573 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001574 else:
1575 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001576 # Start by just requiring version 1.0. Code that adds features
1577 # in a future version can use bump_required_libavb_version_minor() to
1578 # bump the minor.
1579 self.required_libavb_version_major = AVB_VERSION_MAJOR
1580 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001581 self.authentication_data_block_size = 0
1582 self.auxiliary_data_block_size = 0
1583 self.algorithm_type = 0
1584 self.hash_offset = 0
1585 self.hash_size = 0
1586 self.signature_offset = 0
1587 self.signature_size = 0
1588 self.public_key_offset = 0
1589 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001590 self.public_key_metadata_offset = 0
1591 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001592 self.descriptors_offset = 0
1593 self.descriptors_size = 0
1594 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001595 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001596 self.release_string = get_release_string()
1597
1598 def bump_required_libavb_version_minor(self, minor):
1599 """Function to bump required_libavb_version_minor.
1600
1601 Call this when writing data that requires a specific libavb
1602 version to parse it.
1603
1604 Arguments:
1605 minor: The minor version of libavb that has support for the feature.
1606 """
1607 self.required_libavb_version_minor = (
1608 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001609
1610 def save(self, output):
1611 """Serializes the header (256 bytes) to disk.
1612
1613 Arguments:
1614 output: The object to write the output to.
1615 """
1616 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001617 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1618 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001619 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1620 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001621 self.public_key_offset, self.public_key_size,
1622 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001623 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001624 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001625
1626 def encode(self):
1627 """Serializes the header (256) to a bytearray().
1628
1629 Returns:
1630 A bytearray() with the encoded header.
1631 """
1632 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001633 self.required_libavb_version_major,
1634 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001635 self.authentication_data_block_size,
1636 self.auxiliary_data_block_size, self.algorithm_type,
1637 self.hash_offset, self.hash_size, self.signature_offset,
1638 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001639 self.public_key_size, self.public_key_metadata_offset,
1640 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001641 self.descriptors_size, self.rollback_index, self.flags,
1642 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001643
1644
1645class Avb(object):
1646 """Business logic for avbtool command-line tool."""
1647
David Zeuthen8b6973b2016-09-20 12:39:49 -04001648 # Keep in sync with avb_ab_flow.h.
1649 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1650 AB_MAGIC = '\0AB0'
1651 AB_MAJOR_VERSION = 1
1652 AB_MINOR_VERSION = 0
1653 AB_MISC_METADATA_OFFSET = 2048
1654
David Zeuthen09692692016-09-30 16:16:40 -04001655 # Constants for maximum metadata size. These are used to give
1656 # meaningful errors if the value passed in via --partition_size is
1657 # too small and when --calc_max_image_size is used. We use
1658 # conservative figures.
1659 MAX_VBMETA_SIZE = 64 * 1024
1660 MAX_FOOTER_SIZE = 4096
1661
David Zeuthena4fee8b2016-08-22 15:20:43 -04001662 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001663 """Implements the 'erase_footer' command.
1664
1665 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001666 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001667 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001668
1669 Raises:
1670 AvbError: If there's no footer in the image.
1671 """
1672
David Zeuthena4fee8b2016-08-22 15:20:43 -04001673 image = ImageHandler(image_filename)
1674
David Zeuthen21e95262016-07-27 17:58:40 -04001675 (footer, _, descriptors, _) = self._parse_image(image)
1676
1677 if not footer:
1678 raise AvbError('Given image does not have a footer.')
1679
1680 new_image_size = None
1681 if not keep_hashtree:
1682 new_image_size = footer.original_image_size
1683 else:
1684 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001685 # descriptor to figure out the location and size of the hashtree
1686 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001687 for desc in descriptors:
1688 if isinstance(desc, AvbHashtreeDescriptor):
1689 # The hashtree is always just following the main data so the
1690 # new size is easily derived.
1691 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001692 # If the image has FEC codes, also keep those.
1693 if desc.fec_offset > 0:
1694 fec_end = desc.fec_offset + desc.fec_size
1695 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001696 break
1697 if not new_image_size:
1698 raise AvbError('Requested to keep hashtree but no hashtree '
1699 'descriptor was found.')
1700
1701 # And cut...
1702 image.truncate(new_image_size)
1703
David Zeuthen8b6973b2016-09-20 12:39:49 -04001704 def set_ab_metadata(self, misc_image, slot_data):
1705 """Implements the 'set_ab_metadata' command.
1706
1707 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1708 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1709
1710 Arguments:
1711 misc_image: The misc image to write to.
1712 slot_data: Slot data as a string
1713
1714 Raises:
1715 AvbError: If slot data is malformed.
1716 """
1717 tokens = slot_data.split(':')
1718 if len(tokens) != 6:
1719 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1720 a_priority = int(tokens[0])
1721 a_tries_remaining = int(tokens[1])
1722 a_success = True if int(tokens[2]) != 0 else False
1723 b_priority = int(tokens[3])
1724 b_tries_remaining = int(tokens[4])
1725 b_success = True if int(tokens[5]) != 0 else False
1726
1727 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1728 self.AB_MAGIC,
1729 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1730 a_priority, a_tries_remaining, a_success,
1731 b_priority, b_tries_remaining, b_success)
1732 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1733 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1734 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1735 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1736 misc_image.write(ab_data)
1737
David Zeuthena4fee8b2016-08-22 15:20:43 -04001738 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001739 """Implements the 'info_image' command.
1740
1741 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001742 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001743 output: Output file to write human-readable information to (file object).
1744 """
1745
David Zeuthena4fee8b2016-08-22 15:20:43 -04001746 image = ImageHandler(image_filename)
1747
David Zeuthen21e95262016-07-27 17:58:40 -04001748 o = output
1749
1750 (footer, header, descriptors, image_size) = self._parse_image(image)
1751
1752 if footer:
1753 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1754 footer.version_minor))
1755 o.write('Image size: {} bytes\n'.format(image_size))
1756 o.write('Original image size: {} bytes\n'.format(
1757 footer.original_image_size))
1758 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1759 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1760 o.write('--\n')
1761
1762 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1763
David Zeuthene3cadca2017-02-22 21:25:46 -05001764 o.write('Minimum libavb version: {}.{}{}\n'.format(
1765 header.required_libavb_version_major,
1766 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001767 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001768 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1769 o.write('Authentication Block: {} bytes\n'.format(
1770 header.authentication_data_block_size))
1771 o.write('Auxiliary Block: {} bytes\n'.format(
1772 header.auxiliary_data_block_size))
1773 o.write('Algorithm: {}\n'.format(alg_name))
1774 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001775 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001776 o.write('Release String: \'{}\'\n'.format(
1777 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001778
1779 # Print descriptors.
1780 num_printed = 0
1781 o.write('Descriptors:\n')
1782 for desc in descriptors:
1783 desc.print_desc(o)
1784 num_printed += 1
1785 if num_printed == 0:
1786 o.write(' (none)\n')
1787
1788 def _parse_image(self, image):
1789 """Gets information about an image.
1790
1791 The image can either be a vbmeta or an image with a footer.
1792
1793 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001794 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001795
1796 Returns:
1797 A tuple where the first argument is a AvbFooter (None if there
1798 is no footer on the image), the second argument is a
1799 AvbVBMetaHeader, the third argument is a list of
1800 AvbDescriptor-derived instances, and the fourth argument is the
1801 size of |image|.
1802 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001803 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001804 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001805 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001806 try:
1807 footer = AvbFooter(image.read(AvbFooter.SIZE))
1808 except (LookupError, struct.error):
1809 # Nope, just seek back to the start.
1810 image.seek(0)
1811
1812 vbmeta_offset = 0
1813 if footer:
1814 vbmeta_offset = footer.vbmeta_offset
1815
1816 image.seek(vbmeta_offset)
1817 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1818
1819 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1820 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1821 desc_start_offset = aux_block_offset + h.descriptors_offset
1822 image.seek(desc_start_offset)
1823 descriptors = parse_descriptors(image.read(h.descriptors_size))
1824
David Zeuthen09692692016-09-30 16:16:40 -04001825 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001826
David Zeuthenb1b994d2017-03-06 18:01:31 -05001827 def _load_vbmeta_blob(self, image):
1828 """Gets the vbmeta struct and associated sections.
1829
1830 The image can either be a vbmeta.img or an image with a footer.
1831
1832 Arguments:
1833 image: An ImageHandler (vbmeta or footer).
1834
1835 Returns:
1836 A blob with the vbmeta struct and other sections.
1837 """
1838 assert isinstance(image, ImageHandler)
1839 footer = None
1840 image.seek(image.image_size - AvbFooter.SIZE)
1841 try:
1842 footer = AvbFooter(image.read(AvbFooter.SIZE))
1843 except (LookupError, struct.error):
1844 # Nope, just seek back to the start.
1845 image.seek(0)
1846
1847 vbmeta_offset = 0
1848 if footer:
1849 vbmeta_offset = footer.vbmeta_offset
1850
1851 image.seek(vbmeta_offset)
1852 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1853
1854 image.seek(vbmeta_offset)
1855 data_size = AvbVBMetaHeader.SIZE
1856 data_size += h.authentication_data_block_size
1857 data_size += h.auxiliary_data_block_size
1858 return image.read(data_size)
1859
David Zeuthenfd41eb92016-11-17 12:24:47 -05001860 def _get_cmdline_descriptors_for_dm_verity(self, image):
1861 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001862
1863 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001864 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001865
1866 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001867 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1868 instructions. There is one for when hashtree is not disabled and one for
1869 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001870
1871 Raises:
1872 AvbError: If |image| doesn't have a hashtree descriptor.
1873
1874 """
1875
1876 (_, _, descriptors, _) = self._parse_image(image)
1877
1878 ht = None
1879 for desc in descriptors:
1880 if isinstance(desc, AvbHashtreeDescriptor):
1881 ht = desc
1882 break
1883
1884 if not ht:
1885 raise AvbError('No hashtree descriptor in given image')
1886
1887 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001888 c += '0' # start
1889 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1890 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1891 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1892 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1893 c += ' {}'.format(ht.data_block_size) # data_block
1894 c += ' {}'.format(ht.hash_block_size) # hash_block
1895 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1896 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1897 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1898 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1899 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1900 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001901 c += ' 10' # number of optional args
1902 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001903 c += ' ignore_zero_blocks'
1904 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1905 c += ' fec_roots {}'.format(ht.fec_num_roots)
1906 # Note that fec_blocks is the size that FEC covers, *not* the
1907 # size of the FEC data. Since we use FEC for everything up until
1908 # the FEC data, it's the same as the offset.
1909 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1910 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1911 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001912 c += ' 2' # number of optional args
1913 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001914 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04001915 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04001916
David Zeuthenfd41eb92016-11-17 12:24:47 -05001917 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001918 desc = AvbKernelCmdlineDescriptor()
1919 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001920 desc.flags = (
1921 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1922
1923 # The descriptor for when hashtree verification is disabled is a lot
1924 # simpler - we just set the root to the partition.
1925 desc_no_ht = AvbKernelCmdlineDescriptor()
1926 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1927 desc_no_ht.flags = (
1928 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1929
1930 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001931
1932 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001933 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001934 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001935 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001936 include_descriptors_from_image, signing_helper,
1937 release_string,
1938 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001939 """Implements the 'make_vbmeta_image' command.
1940
1941 Arguments:
1942 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001943 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001944 algorithm_name: Name of algorithm to use.
1945 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001946 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001947 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001948 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001949 props: Properties to insert (list of strings of the form 'key:value').
1950 props_from_file: Properties to insert (list of strings 'key:<path>').
1951 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001952 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001953 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001954 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001955 release_string: None or avbtool release string to use instead of default.
1956 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001957
1958 Raises:
1959 AvbError: If a chained partition is malformed.
1960 """
1961
1962 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04001963 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001964 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001965 chain_partitions, rollback_index, flags, props, props_from_file,
1966 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001967 include_descriptors_from_image, signing_helper, release_string,
1968 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001969
1970 # Write entire vbmeta blob (header, authentication, auxiliary).
1971 output.seek(0)
1972 output.write(vbmeta_blob)
1973
David Zeuthen18666ab2016-11-15 11:18:05 -05001974 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1975 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001976 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001977 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001978 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001979 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001980 include_descriptors_from_image, signing_helper,
1981 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001982 """Generates a VBMeta blob.
1983
1984 This blob contains the header (struct AvbVBMetaHeader), the
1985 authentication data block (which contains the hash and signature
1986 for the header and auxiliary block), and the auxiliary block
1987 (which contains descriptors, the public key used, and other data).
1988
1989 The |key| parameter can |None| only if the |algorithm_name| is
1990 'NONE'.
1991
1992 Arguments:
1993 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1994 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001995 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001996 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001997 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001998 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001999 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002000 props: Properties to insert (List of strings of the form 'key:value').
2001 props_from_file: Properties to insert (List of strings 'key:<path>').
2002 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002003 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002004 dm-verity kernel cmdline from.
2005 include_descriptors_from_image: List of file objects for which
2006 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002007 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002008 release_string: None or avbtool release string.
2009 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002010
2011 Returns:
2012 A bytearray() with the VBMeta blob.
2013
2014 Raises:
2015 Exception: If the |algorithm_name| is not found, if no key has
2016 been given and the given algorithm requires one, or the key is
2017 of the wrong size.
2018
2019 """
2020 try:
2021 alg = ALGORITHMS[algorithm_name]
2022 except KeyError:
2023 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2024
David Zeuthena5fd3a42017-02-27 16:38:54 -05002025 if not descriptors:
2026 descriptors = []
2027
2028 # Insert chained partition descriptors, if any
2029 if chain_partitions:
2030 for cp in chain_partitions:
2031 cp_tokens = cp.split(':')
2032 if len(cp_tokens) != 3:
2033 raise AvbError('Malformed chained partition "{}".'.format(cp))
2034 desc = AvbChainPartitionDescriptor()
2035 desc.partition_name = cp_tokens[0]
2036 desc.rollback_index_location = int(cp_tokens[1])
2037 if desc.rollback_index_location < 1:
2038 raise AvbError('Rollback index location must be 1 or larger.')
2039 file_path = cp_tokens[2]
2040 desc.public_key = open(file_path, 'rb').read()
2041 descriptors.append(desc)
2042
David Zeuthen21e95262016-07-27 17:58:40 -04002043 # Descriptors.
2044 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002045 for desc in descriptors:
2046 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002047
2048 # Add properties.
2049 if props:
2050 for prop in props:
2051 idx = prop.find(':')
2052 if idx == -1:
2053 raise AvbError('Malformed property "{}".'.format(prop))
2054 desc = AvbPropertyDescriptor()
2055 desc.key = prop[0:idx]
2056 desc.value = prop[(idx + 1):]
2057 encoded_descriptors.extend(desc.encode())
2058 if props_from_file:
2059 for prop in props_from_file:
2060 idx = prop.find(':')
2061 if idx == -1:
2062 raise AvbError('Malformed property "{}".'.format(prop))
2063 desc = AvbPropertyDescriptor()
2064 desc.key = prop[0:idx]
2065 desc.value = prop[(idx + 1):]
2066 file_path = prop[(idx + 1):]
2067 desc.value = open(file_path, 'rb').read()
2068 encoded_descriptors.extend(desc.encode())
2069
2070 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002071 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002072 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002073 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002074 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2075 encoded_descriptors.extend(cmdline_desc[0].encode())
2076 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002077
2078 # Add kernel command-lines.
2079 if kernel_cmdlines:
2080 for i in kernel_cmdlines:
2081 desc = AvbKernelCmdlineDescriptor()
2082 desc.kernel_cmdline = i
2083 encoded_descriptors.extend(desc.encode())
2084
2085 # Add descriptors from other images.
2086 if include_descriptors_from_image:
2087 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002088 image_handler = ImageHandler(image.name)
2089 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002090 for desc in image_descriptors:
2091 encoded_descriptors.extend(desc.encode())
2092
David Zeuthen18666ab2016-11-15 11:18:05 -05002093 # Load public key metadata blob, if requested.
2094 pkmd_blob = []
2095 if public_key_metadata_path:
2096 with open(public_key_metadata_path) as f:
2097 pkmd_blob = f.read()
2098
David Zeuthen21e95262016-07-27 17:58:40 -04002099 key = None
2100 encoded_key = bytearray()
2101 if alg.public_key_num_bytes > 0:
2102 if not key_path:
2103 raise AvbError('Key is required for algorithm {}'.format(
2104 algorithm_name))
2105 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2106 encoded_key = encode_rsa_key(key)
2107 if len(encoded_key) != alg.public_key_num_bytes:
2108 raise AvbError('Key is wrong size for algorithm {}'.format(
2109 algorithm_name))
2110
2111 h = AvbVBMetaHeader()
2112
David Zeuthene3cadca2017-02-22 21:25:46 -05002113 # Override release string, if requested.
2114 if isinstance(release_string, (str, unicode)):
2115 h.release_string = release_string
2116
2117 # Append to release string, if requested. Also insert a space before.
2118 if isinstance(append_to_release_string, (str, unicode)):
2119 h.release_string += ' ' + append_to_release_string
2120
David Zeuthen18666ab2016-11-15 11:18:05 -05002121 # For the Auxiliary data block, descriptors are stored at offset 0,
2122 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002123 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002124 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002125 h.descriptors_offset = 0
2126 h.descriptors_size = len(encoded_descriptors)
2127 h.public_key_offset = h.descriptors_size
2128 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002129 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2130 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002131
2132 # For the Authentication data block, the hash is first and then
2133 # the signature.
2134 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002135 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002136 h.algorithm_type = alg.algorithm_type
2137 h.hash_offset = 0
2138 h.hash_size = alg.hash_num_bytes
2139 # Signature offset and size - it's stored right after the hash
2140 # (in Authentication data block).
2141 h.signature_offset = alg.hash_num_bytes
2142 h.signature_size = alg.signature_num_bytes
2143
2144 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002145 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002146
2147 # Generate Header data block.
2148 header_data_blob = h.encode()
2149
2150 # Generate Auxiliary data block.
2151 aux_data_blob = bytearray()
2152 aux_data_blob.extend(encoded_descriptors)
2153 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002154 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002155 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2156 aux_data_blob.extend('\0' * padding_bytes)
2157
2158 # Calculate the hash.
2159 binary_hash = bytearray()
2160 binary_signature = bytearray()
2161 if algorithm_name != 'NONE':
2162 if algorithm_name[0:6] == 'SHA256':
2163 ha = hashlib.sha256()
2164 elif algorithm_name[0:6] == 'SHA512':
2165 ha = hashlib.sha512()
2166 else:
2167 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2168 ha.update(header_data_blob)
2169 ha.update(aux_data_blob)
2170 binary_hash.extend(ha.digest())
2171
2172 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002173 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002174 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2175 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002176 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002177
2178 # Generate Authentication data block.
2179 auth_data_blob = bytearray()
2180 auth_data_blob.extend(binary_hash)
2181 auth_data_blob.extend(binary_signature)
2182 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2183 auth_data_blob.extend('\0' * padding_bytes)
2184
2185 return header_data_blob + auth_data_blob + aux_data_blob
2186
2187 def extract_public_key(self, key_path, output):
2188 """Implements the 'extract_public_key' command.
2189
2190 Arguments:
2191 key_path: The path to a RSA private key file.
2192 output: The file to write to.
2193 """
2194 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2195 write_rsa_key(output, key)
2196
David Zeuthenb1b994d2017-03-06 18:01:31 -05002197 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2198 partition_size):
2199 """Implementation of the append_vbmeta_image command.
2200
2201 Arguments:
2202 image_filename: File to add the footer to.
2203 vbmeta_image_filename: File to get vbmeta struct from.
2204 partition_size: Size of partition.
2205
2206 Raises:
2207 AvbError: If an argument is incorrect.
2208 """
2209 image = ImageHandler(image_filename)
2210
2211 if partition_size % image.block_size != 0:
2212 raise AvbError('Partition size of {} is not a multiple of the image '
2213 'block size {}.'.format(partition_size,
2214 image.block_size))
2215
2216 # If there's already a footer, truncate the image to its original
2217 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2218 image.seek(image.image_size - AvbFooter.SIZE)
2219 try:
2220 footer = AvbFooter(image.read(AvbFooter.SIZE))
2221 # Existing footer found. Just truncate.
2222 original_image_size = footer.original_image_size
2223 image.truncate(footer.original_image_size)
2224 except (LookupError, struct.error):
2225 original_image_size = image.image_size
2226
2227 # If anything goes wrong from here-on, restore the image back to
2228 # its original size.
2229 try:
2230 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2231 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2232
2233 # If the image isn't sparse, its size might not be a multiple of
2234 # the block size. This will screw up padding later so just grow it.
2235 if image.image_size % image.block_size != 0:
2236 assert not image.is_sparse
2237 padding_needed = image.block_size - (image.image_size%image.block_size)
2238 image.truncate(image.image_size + padding_needed)
2239
2240 # The append_raw() method requires content with size being a
2241 # multiple of |block_size| so add padding as needed. Also record
2242 # where this is written to since we'll need to put that in the
2243 # footer.
2244 vbmeta_offset = image.image_size
2245 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2246 len(vbmeta_blob))
2247 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2248
2249 # Append vbmeta blob and footer
2250 image.append_raw(vbmeta_blob_with_padding)
2251 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2252
2253 # Now insert a DONT_CARE chunk with enough bytes such that the
2254 # final Footer block is at the end of partition_size..
2255 image.append_dont_care(partition_size - vbmeta_end_offset -
2256 1*image.block_size)
2257
2258 # Generate the Footer that tells where the VBMeta footer
2259 # is. Also put enough padding in the front of the footer since
2260 # we'll write out an entire block.
2261 footer = AvbFooter()
2262 footer.original_image_size = original_image_size
2263 footer.vbmeta_offset = vbmeta_offset
2264 footer.vbmeta_size = len(vbmeta_blob)
2265 footer_blob = footer.encode()
2266 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2267 footer_blob)
2268 image.append_raw(footer_blob_with_padding)
2269
2270 except:
2271 # Truncate back to original size, then re-raise
2272 image.truncate(original_image_size)
2273 raise
2274
David Zeuthena4fee8b2016-08-22 15:20:43 -04002275 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002276 hash_algorithm, salt, chain_partitions, algorithm_name,
2277 key_path,
2278 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002279 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002280 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002281 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002282 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002283 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002284 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002285
2286 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002287 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002288 partition_size: Size of partition.
2289 partition_name: Name of partition (without A/B suffix).
2290 hash_algorithm: Hash algorithm to use.
2291 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002292 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002293 algorithm_name: Name of algorithm to use.
2294 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002295 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002296 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002297 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002298 props: Properties to insert (List of strings of the form 'key:value').
2299 props_from_file: Properties to insert (List of strings 'key:<path>').
2300 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002301 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002302 dm-verity kernel cmdline from.
2303 include_descriptors_from_image: List of file objects for which
2304 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002305 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002306 release_string: None or avbtool release string.
2307 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002308 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2309 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002310
2311 Raises:
2312 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002313 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002314 image = ImageHandler(image_filename)
2315
2316 if partition_size % image.block_size != 0:
2317 raise AvbError('Partition size of {} is not a multiple of the image '
2318 'block size {}.'.format(partition_size,
2319 image.block_size))
2320
David Zeuthen21e95262016-07-27 17:58:40 -04002321 # If there's already a footer, truncate the image to its original
2322 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2323 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002324 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002325 try:
2326 footer = AvbFooter(image.read(AvbFooter.SIZE))
2327 # Existing footer found. Just truncate.
2328 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002329 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002330 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002331 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002332
2333 # If anything goes wrong from here-on, restore the image back to
2334 # its original size.
2335 try:
David Zeuthen09692692016-09-30 16:16:40 -04002336 # First, calculate the maximum image size such that an image
2337 # this size + metadata (footer + vbmeta struct) fits in
2338 # |partition_size|.
2339 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2340 max_image_size = partition_size - max_metadata_size
2341
2342 # If image size exceeds the maximum image size, fail.
2343 if image.image_size > max_image_size:
2344 raise AvbError('Image size of {} exceeds maximum image '
2345 'size of {} in order to fit in a partition '
2346 'size of {}.'.format(image.image_size, max_image_size,
2347 partition_size))
2348
David Zeuthen21e95262016-07-27 17:58:40 -04002349 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2350 if salt:
2351 salt = salt.decode('hex')
2352 else:
2353 if salt is None:
2354 # If salt is not explicitly specified, choose a hash
2355 # that's the same size as the hash size.
2356 hash_size = digest_size
2357 salt = open('/dev/urandom').read(hash_size)
2358 else:
2359 salt = ''
2360
2361 hasher = hashlib.new(name=hash_algorithm, string=salt)
2362 # TODO(zeuthen): might want to read this in chunks to avoid
2363 # memory pressure, then again, this is only supposed to be used
2364 # on kernel/initramfs partitions. Possible optimization.
2365 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002366 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002367 digest = hasher.digest()
2368
2369 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002370 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002371 h_desc.hash_algorithm = hash_algorithm
2372 h_desc.partition_name = partition_name
2373 h_desc.salt = salt
2374 h_desc.digest = digest
2375
2376 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002377 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002378 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002379 chain_partitions, rollback_index, flags, props, props_from_file,
2380 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002381 include_descriptors_from_image, signing_helper, release_string,
2382 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002383
David Zeuthena4fee8b2016-08-22 15:20:43 -04002384 # If the image isn't sparse, its size might not be a multiple of
2385 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002386 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002387 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002388 padding_needed = image.block_size - (image.image_size%image.block_size)
2389 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002390
David Zeuthena4fee8b2016-08-22 15:20:43 -04002391 # The append_raw() method requires content with size being a
2392 # multiple of |block_size| so add padding as needed. Also record
2393 # where this is written to since we'll need to put that in the
2394 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002395 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002396 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2397 len(vbmeta_blob))
2398 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002399
David Zeuthend247fcb2017-02-16 12:09:27 -05002400 # Write vbmeta blob, if requested.
2401 if output_vbmeta_image:
2402 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002403
David Zeuthend247fcb2017-02-16 12:09:27 -05002404 # Append vbmeta blob and footer, unless requested not to.
2405 if not do_not_append_vbmeta_image:
2406 image.append_raw(vbmeta_blob_with_padding)
2407 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2408
2409 # Now insert a DONT_CARE chunk with enough bytes such that the
2410 # final Footer block is at the end of partition_size..
2411 image.append_dont_care(partition_size - vbmeta_end_offset -
2412 1*image.block_size)
2413
2414 # Generate the Footer that tells where the VBMeta footer
2415 # is. Also put enough padding in the front of the footer since
2416 # we'll write out an entire block.
2417 footer = AvbFooter()
2418 footer.original_image_size = original_image_size
2419 footer.vbmeta_offset = vbmeta_offset
2420 footer.vbmeta_size = len(vbmeta_blob)
2421 footer_blob = footer.encode()
2422 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2423 footer_blob)
2424 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002425
David Zeuthen21e95262016-07-27 17:58:40 -04002426 except:
2427 # Truncate back to original size, then re-raise
2428 image.truncate(original_image_size)
2429 raise
2430
David Zeuthena4fee8b2016-08-22 15:20:43 -04002431 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002432 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002433 block_size, salt, chain_partitions, algorithm_name,
2434 key_path,
2435 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002436 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002437 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002438 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002439 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002440 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002441 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002442 """Implements the 'add_hashtree_footer' command.
2443
2444 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2445 more information about dm-verity and these hashes.
2446
2447 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002448 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002449 partition_size: Size of partition.
2450 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002451 generate_fec: If True, generate FEC codes.
2452 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002453 hash_algorithm: Hash algorithm to use.
2454 block_size: Block size to use.
2455 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002456 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002457 algorithm_name: Name of algorithm to use.
2458 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002459 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002460 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002461 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002462 props: Properties to insert (List of strings of the form 'key:value').
2463 props_from_file: Properties to insert (List of strings 'key:<path>').
2464 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002465 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002466 dm-verity kernel cmdline from.
2467 include_descriptors_from_image: List of file objects for which
2468 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002469 calc_max_image_size: Don't store the hashtree or footer - instead
2470 calculate the maximum image size leaving enough room for hashtree
2471 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002472 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002473 release_string: None or avbtool release string.
2474 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002475 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2476 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002477
2478 Raises:
2479 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002480 """
David Zeuthen09692692016-09-30 16:16:40 -04002481 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2482 digest_padding = round_to_pow2(digest_size) - digest_size
2483
2484 # First, calculate the maximum image size such that an image
2485 # this size + the hashtree + metadata (footer + vbmeta struct)
2486 # fits in |partition_size|. We use very conservative figures for
2487 # metadata.
2488 (_, max_tree_size) = calc_hash_level_offsets(
2489 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002490 max_fec_size = 0
2491 if generate_fec:
2492 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2493 max_metadata_size = (max_fec_size + max_tree_size +
2494 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002495 self.MAX_FOOTER_SIZE)
2496 max_image_size = partition_size - max_metadata_size
2497
2498 # If we're asked to only calculate the maximum image size, we're done.
2499 if calc_max_image_size:
2500 print '{}'.format(max_image_size)
2501 return
2502
David Zeuthena4fee8b2016-08-22 15:20:43 -04002503 image = ImageHandler(image_filename)
2504
2505 if partition_size % image.block_size != 0:
2506 raise AvbError('Partition size of {} is not a multiple of the image '
2507 'block size {}.'.format(partition_size,
2508 image.block_size))
2509
David Zeuthen21e95262016-07-27 17:58:40 -04002510 # If there's already a footer, truncate the image to its original
2511 # size. This way 'avbtool add_hashtree_footer' is idempotent
2512 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002513 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002514 try:
2515 footer = AvbFooter(image.read(AvbFooter.SIZE))
2516 # Existing footer found. Just truncate.
2517 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002518 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002519 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002520 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002521
2522 # If anything goes wrong from here-on, restore the image back to
2523 # its original size.
2524 try:
2525 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002526 rounded_image_size = round_to_multiple(image.image_size, block_size)
2527 if rounded_image_size > image.image_size:
2528 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002529
David Zeuthen09692692016-09-30 16:16:40 -04002530 # If image size exceeds the maximum image size, fail.
2531 if image.image_size > max_image_size:
2532 raise AvbError('Image size of {} exceeds maximum image '
2533 'size of {} in order to fit in a partition '
2534 'size of {}.'.format(image.image_size, max_image_size,
2535 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002536
2537 if salt:
2538 salt = salt.decode('hex')
2539 else:
2540 if salt is None:
2541 # If salt is not explicitly specified, choose a hash
2542 # that's the same size as the hash size.
2543 hash_size = digest_size
2544 salt = open('/dev/urandom').read(hash_size)
2545 else:
2546 salt = ''
2547
David Zeuthena4fee8b2016-08-22 15:20:43 -04002548 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002549 # offsets in advance.
2550 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002551 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002552
David Zeuthena4fee8b2016-08-22 15:20:43 -04002553 # If the image isn't sparse, its size might not be a multiple of
2554 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002555 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002556 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002557 padding_needed = image.block_size - (image.image_size%image.block_size)
2558 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002559
David Zeuthena4fee8b2016-08-22 15:20:43 -04002560 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002561 tree_offset = image.image_size
2562 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002563 block_size,
2564 hash_algorithm, salt,
2565 digest_padding,
2566 hash_level_offsets,
2567 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002568
2569 # Generate HashtreeDescriptor with details about the tree we
2570 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002571 ht_desc = AvbHashtreeDescriptor()
2572 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002573 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002574 ht_desc.tree_offset = tree_offset
2575 ht_desc.tree_size = tree_size
2576 ht_desc.data_block_size = block_size
2577 ht_desc.hash_block_size = block_size
2578 ht_desc.hash_algorithm = hash_algorithm
2579 ht_desc.partition_name = partition_name
2580 ht_desc.salt = salt
2581 ht_desc.root_digest = root_digest
2582
David Zeuthen09692692016-09-30 16:16:40 -04002583 # Write the hash tree
2584 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2585 len(hash_tree))
2586 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2587 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002588 len_hashtree_and_fec = len(hash_tree_with_padding)
2589
2590 # Generate FEC codes, if requested.
2591 if generate_fec:
2592 fec_data = generate_fec_data(image_filename, fec_num_roots)
2593 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2594 len(fec_data))
2595 fec_data_with_padding = fec_data + '\0'*padding_needed
2596 fec_offset = image.image_size
2597 image.append_raw(fec_data_with_padding)
2598 len_hashtree_and_fec += len(fec_data_with_padding)
2599 # Update the hashtree descriptor.
2600 ht_desc.fec_num_roots = fec_num_roots
2601 ht_desc.fec_offset = fec_offset
2602 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002603
David Zeuthena4fee8b2016-08-22 15:20:43 -04002604 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002605 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002606 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002607 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002608 chain_partitions, rollback_index, flags, props, props_from_file,
2609 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002610 include_descriptors_from_image, signing_helper, release_string,
2611 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002612 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2613 len(vbmeta_blob))
2614 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002615
David Zeuthend247fcb2017-02-16 12:09:27 -05002616 # Write vbmeta blob, if requested.
2617 if output_vbmeta_image:
2618 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002619
David Zeuthend247fcb2017-02-16 12:09:27 -05002620 # Append vbmeta blob and footer, unless requested not to.
2621 if not do_not_append_vbmeta_image:
2622 image.append_raw(vbmeta_blob_with_padding)
2623
2624 # Now insert a DONT_CARE chunk with enough bytes such that the
2625 # final Footer block is at the end of partition_size..
2626 image.append_dont_care(partition_size - image.image_size -
2627 1*image.block_size)
2628
2629 # Generate the Footer that tells where the VBMeta footer
2630 # is. Also put enough padding in the front of the footer since
2631 # we'll write out an entire block.
2632 footer = AvbFooter()
2633 footer.original_image_size = original_image_size
2634 footer.vbmeta_offset = vbmeta_offset
2635 footer.vbmeta_size = len(vbmeta_blob)
2636 footer_blob = footer.encode()
2637 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2638 footer_blob)
2639 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002640
David Zeuthen21e95262016-07-27 17:58:40 -04002641 except:
David Zeuthen09692692016-09-30 16:16:40 -04002642 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002643 image.truncate(original_image_size)
2644 raise
2645
Darren Krahn147b08d2016-12-20 16:38:29 -08002646 def make_atx_certificate(self, output, authority_key_path, subject_key,
2647 subject_key_version, subject,
2648 is_intermediate_authority, signing_helper):
2649 """Implements the 'make_atx_certificate' command.
2650
2651 Android Things certificates are required for Android Things public key
2652 metadata. They chain the vbmeta signing key for a particular product back to
2653 a fused, permanent root key. These certificates are fixed-length and fixed-
2654 format with the explicit goal of not parsing ASN.1 in bootloader code.
2655
2656 Arguments:
2657 output: Certificate will be written to this file on success.
2658 authority_key_path: A PEM file path with the authority private key.
2659 If None, then a certificate will be created without a
2660 signature. The signature can be created out-of-band
2661 and appended.
2662 subject_key: A PEM or DER subject public key.
2663 subject_key_version: A 64-bit version value. If this is None, the number
2664 of seconds since the epoch is used.
2665 subject: A subject identifier. For Product Signing Key certificates this
2666 should be the same Product ID found in the permanent attributes.
2667 is_intermediate_authority: True if the certificate is for an intermediate
2668 authority.
2669 signing_helper: Program which signs a hash and returns the signature.
2670 """
2671 signed_data = bytearray()
2672 signed_data.extend(struct.pack('<I', 1)) # Format Version
2673 signed_data.extend(
2674 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2675 hasher = hashlib.sha256()
2676 hasher.update(subject)
2677 signed_data.extend(hasher.digest())
2678 usage = 'com.google.android.things.vboot'
2679 if is_intermediate_authority:
2680 usage += '.ca'
2681 hasher = hashlib.sha256()
2682 hasher.update(usage)
2683 signed_data.extend(hasher.digest())
2684 if not subject_key_version:
2685 subject_key_version = int(time.time())
2686 signed_data.extend(struct.pack('<Q', subject_key_version))
2687 signature = bytearray()
2688 if authority_key_path:
2689 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002690 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002691 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002692 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002693 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002694 hasher.update(signed_data)
2695 padding_and_hash.extend(hasher.digest())
2696 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002697 alg.signature_num_bytes, authority_key_path,
2698 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002699 output.write(signed_data)
2700 output.write(signature)
2701
2702 def make_atx_permanent_attributes(self, output, root_authority_key,
2703 product_id):
2704 """Implements the 'make_atx_permanent_attributes' command.
2705
2706 Android Things permanent attributes are designed to be permanent for a
2707 particular product and a hash of these attributes should be fused into
2708 hardware to enforce this.
2709
2710 Arguments:
2711 output: Attributes will be written to this file on success.
2712 root_authority_key: A PEM or DER public key for the root authority.
2713 product_id: A 16-byte Product ID.
2714
2715 Raises:
2716 AvbError: If an argument is incorrect.
2717 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002718 EXPECTED_PRODUCT_ID_SIZE = 16
2719 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002720 raise AvbError('Invalid Product ID length.')
2721 output.write(struct.pack('<I', 1)) # Format Version
2722 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2723 output.write(product_id)
2724
2725 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002726 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002727 """Implements the 'make_atx_metadata' command.
2728
2729 Android Things metadata are included in vbmeta images to facilitate
2730 verification. The output of this command can be used as the
2731 public_key_metadata argument to other commands.
2732
2733 Arguments:
2734 output: Metadata will be written to this file on success.
2735 intermediate_key_certificate: A certificate file as output by
2736 make_atx_certificate with
2737 is_intermediate_authority set to true.
2738 product_key_certificate: A certificate file as output by
2739 make_atx_certificate with
2740 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002741
2742 Raises:
2743 AvbError: If an argument is incorrect.
2744 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002745 EXPECTED_CERTIFICATE_SIZE = 1620
2746 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002747 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002748 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002749 raise AvbError('Invalid product key certificate length.')
2750 output.write(struct.pack('<I', 1)) # Format Version
2751 output.write(intermediate_key_certificate)
2752 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002753
David Zeuthen21e95262016-07-27 17:58:40 -04002754
2755def calc_hash_level_offsets(image_size, block_size, digest_size):
2756 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2757
2758 Arguments:
2759 image_size: The size of the image to calculate a Merkle-tree for.
2760 block_size: The block size, e.g. 4096.
2761 digest_size: The size of each hash, e.g. 32 for SHA-256.
2762
2763 Returns:
2764 A tuple where the first argument is an array of offsets and the
2765 second is size of the tree, in bytes.
2766 """
2767 level_offsets = []
2768 level_sizes = []
2769 tree_size = 0
2770
2771 num_levels = 0
2772 size = image_size
2773 while size > block_size:
2774 num_blocks = (size + block_size - 1) / block_size
2775 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2776
2777 level_sizes.append(level_size)
2778 tree_size += level_size
2779 num_levels += 1
2780
2781 size = level_size
2782
2783 for n in range(0, num_levels):
2784 offset = 0
2785 for m in range(n + 1, num_levels):
2786 offset += level_sizes[m]
2787 level_offsets.append(offset)
2788
David Zeuthena4fee8b2016-08-22 15:20:43 -04002789 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002790
2791
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002792# See system/extras/libfec/include/fec/io.h for these definitions.
2793FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2794FEC_MAGIC = 0xfecfecfe
2795
2796
2797def calc_fec_data_size(image_size, num_roots):
2798 """Calculates how much space FEC data will take.
2799
2800 Args:
2801 image_size: The size of the image.
2802 num_roots: Number of roots.
2803
2804 Returns:
2805 The number of bytes needed for FEC for an image of the given size
2806 and with the requested number of FEC roots.
2807
2808 Raises:
2809 ValueError: If output from the 'fec' tool is invalid.
2810
2811 """
2812 p = subprocess.Popen(
2813 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2814 stdout=subprocess.PIPE,
2815 stderr=subprocess.PIPE)
2816 (pout, perr) = p.communicate()
2817 retcode = p.wait()
2818 if retcode != 0:
2819 raise ValueError('Error invoking fec: {}'.format(perr))
2820 return int(pout)
2821
2822
2823def generate_fec_data(image_filename, num_roots):
2824 """Generate FEC codes for an image.
2825
2826 Args:
2827 image_filename: The filename of the image.
2828 num_roots: Number of roots.
2829
2830 Returns:
2831 The FEC data blob.
2832
2833 Raises:
2834 ValueError: If output from the 'fec' tool is invalid.
2835 """
2836 fec_tmpfile = tempfile.NamedTemporaryFile()
2837 subprocess.check_call(
2838 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2839 fec_tmpfile.name],
2840 stderr=open(os.devnull))
2841 fec_data = fec_tmpfile.read()
2842 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2843 footer_data = fec_data[-footer_size:]
2844 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2845 footer_data)
2846 if magic != FEC_MAGIC:
2847 raise ValueError('Unexpected magic in FEC footer')
2848 return fec_data[0:fec_size]
2849
2850
David Zeuthen21e95262016-07-27 17:58:40 -04002851def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002852 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002853 """Generates a Merkle-tree for a file.
2854
2855 Args:
2856 image: The image, as a file.
2857 image_size: The size of the image.
2858 block_size: The block size, e.g. 4096.
2859 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2860 salt: The salt to use.
2861 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002862 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002863 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002864
2865 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002866 A tuple where the first element is the top-level hash and the
2867 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002868 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002869 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002870 hash_src_offset = 0
2871 hash_src_size = image_size
2872 level_num = 0
2873 while hash_src_size > block_size:
2874 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002875 remaining = hash_src_size
2876 while remaining > 0:
2877 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002878 # Only read from the file for the first level - for subsequent
2879 # levels, access the array we're building.
2880 if level_num == 0:
2881 image.seek(hash_src_offset + hash_src_size - remaining)
2882 data = image.read(min(remaining, block_size))
2883 else:
2884 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2885 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002886 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002887
2888 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002889 if len(data) < block_size:
2890 hasher.update('\0' * (block_size - len(data)))
2891 level_output += hasher.digest()
2892 if digest_padding > 0:
2893 level_output += '\0' * digest_padding
2894
2895 padding_needed = (round_to_multiple(
2896 len(level_output), block_size) - len(level_output))
2897 level_output += '\0' * padding_needed
2898
David Zeuthena4fee8b2016-08-22 15:20:43 -04002899 # Copy level-output into resulting tree.
2900 offset = hash_level_offsets[level_num]
2901 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002902
David Zeuthena4fee8b2016-08-22 15:20:43 -04002903 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002904 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002905 level_num += 1
2906
2907 hasher = hashlib.new(name=hash_alg_name, string=salt)
2908 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002909 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002910
2911
2912class AvbTool(object):
2913 """Object for avbtool command-line tool."""
2914
2915 def __init__(self):
2916 """Initializer method."""
2917 self.avb = Avb()
2918
2919 def _add_common_args(self, sub_parser):
2920 """Adds arguments used by several sub-commands.
2921
2922 Arguments:
2923 sub_parser: The parser to add arguments to.
2924 """
2925 sub_parser.add_argument('--algorithm',
2926 help='Algorithm to use (default: NONE)',
2927 metavar='ALGORITHM',
2928 default='NONE')
2929 sub_parser.add_argument('--key',
2930 help='Path to RSA private key file',
2931 metavar='KEY',
2932 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002933 sub_parser.add_argument('--signing_helper',
2934 help='Path to helper used for signing',
2935 metavar='APP',
2936 default=None,
2937 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002938 sub_parser.add_argument('--public_key_metadata',
2939 help='Path to public key metadata file',
2940 metavar='KEY_METADATA',
2941 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002942 sub_parser.add_argument('--rollback_index',
2943 help='Rollback Index',
2944 type=parse_number,
2945 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05002946 # This is used internally for unit tests. Do not include in --help output.
2947 sub_parser.add_argument('--internal_release_string',
2948 help=argparse.SUPPRESS)
2949 sub_parser.add_argument('--append_to_release_string',
2950 help='Text to append to release string',
2951 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04002952 sub_parser.add_argument('--prop',
2953 help='Add property',
2954 metavar='KEY:VALUE',
2955 action='append')
2956 sub_parser.add_argument('--prop_from_file',
2957 help='Add property from file',
2958 metavar='KEY:PATH',
2959 action='append')
2960 sub_parser.add_argument('--kernel_cmdline',
2961 help='Add kernel cmdline',
2962 metavar='CMDLINE',
2963 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002964 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2965 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2966 # at some future point.
2967 sub_parser.add_argument('--setup_rootfs_from_kernel',
2968 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002969 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002970 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002971 type=argparse.FileType('rb'))
2972 sub_parser.add_argument('--include_descriptors_from_image',
2973 help='Include descriptors from image',
2974 metavar='IMAGE',
2975 action='append',
2976 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05002977 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
2978 sub_parser.add_argument('--chain_partition',
2979 help='Allow signed integrity-data for partition',
2980 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2981 action='append')
2982 sub_parser.add_argument('--flags',
2983 help='VBMeta flags',
2984 type=parse_number,
2985 default=0)
2986 sub_parser.add_argument('--set_hashtree_disabled_flag',
2987 help='Set the HASHTREE_DISABLED flag',
2988 action='store_true')
2989
2990 def _fixup_common_args(self, args):
2991 """Common fixups needed by subcommands.
2992
2993 Arguments:
2994 args: Arguments to modify.
2995
2996 Returns:
2997 The modified arguments.
2998 """
2999 if args.set_hashtree_disabled_flag:
3000 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3001 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003002
3003 def run(self, argv):
3004 """Command-line processor.
3005
3006 Arguments:
3007 argv: Pass sys.argv from main.
3008 """
3009 parser = argparse.ArgumentParser()
3010 subparsers = parser.add_subparsers(title='subcommands')
3011
3012 sub_parser = subparsers.add_parser('version',
3013 help='Prints version of avbtool.')
3014 sub_parser.set_defaults(func=self.version)
3015
3016 sub_parser = subparsers.add_parser('extract_public_key',
3017 help='Extract public key.')
3018 sub_parser.add_argument('--key',
3019 help='Path to RSA private key file',
3020 required=True)
3021 sub_parser.add_argument('--output',
3022 help='Output file name',
3023 type=argparse.FileType('wb'),
3024 required=True)
3025 sub_parser.set_defaults(func=self.extract_public_key)
3026
3027 sub_parser = subparsers.add_parser('make_vbmeta_image',
3028 help='Makes a vbmeta image.')
3029 sub_parser.add_argument('--output',
3030 help='Output file name',
3031 type=argparse.FileType('wb'),
3032 required=True)
3033 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003034 sub_parser.set_defaults(func=self.make_vbmeta_image)
3035
3036 sub_parser = subparsers.add_parser('add_hash_footer',
3037 help='Add hashes and footer to image.')
3038 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003039 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003040 type=argparse.FileType('rab+'))
3041 sub_parser.add_argument('--partition_size',
3042 help='Partition size',
3043 type=parse_number,
3044 required=True)
3045 sub_parser.add_argument('--partition_name',
3046 help='Partition name',
3047 required=True)
3048 sub_parser.add_argument('--hash_algorithm',
3049 help='Hash algorithm to use (default: sha256)',
3050 default='sha256')
3051 sub_parser.add_argument('--salt',
3052 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003053 sub_parser.add_argument('--output_vbmeta_image',
3054 help='Also write vbmeta struct to file',
3055 type=argparse.FileType('wb'))
3056 sub_parser.add_argument('--do_not_append_vbmeta_image',
3057 help=('Do not append vbmeta struct or footer '
3058 'to the image'),
3059 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003060 self._add_common_args(sub_parser)
3061 sub_parser.set_defaults(func=self.add_hash_footer)
3062
David Zeuthenb1b994d2017-03-06 18:01:31 -05003063 sub_parser = subparsers.add_parser('append_vbmeta_image',
3064 help='Append vbmeta image to image.')
3065 sub_parser.add_argument('--image',
3066 help='Image to append vbmeta blob to',
3067 type=argparse.FileType('rab+'))
3068 sub_parser.add_argument('--partition_size',
3069 help='Partition size',
3070 type=parse_number,
3071 required=True)
3072 sub_parser.add_argument('--vbmeta_image',
3073 help='Image with vbmeta blob to append',
3074 type=argparse.FileType('rb'))
3075 sub_parser.set_defaults(func=self.append_vbmeta_image)
3076
David Zeuthen21e95262016-07-27 17:58:40 -04003077 sub_parser = subparsers.add_parser('add_hashtree_footer',
3078 help='Add hashtree and footer to image.')
3079 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003080 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003081 type=argparse.FileType('rab+'))
3082 sub_parser.add_argument('--partition_size',
3083 help='Partition size',
3084 type=parse_number,
3085 required=True)
3086 sub_parser.add_argument('--partition_name',
3087 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003088 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003089 sub_parser.add_argument('--hash_algorithm',
3090 help='Hash algorithm to use (default: sha1)',
3091 default='sha1')
3092 sub_parser.add_argument('--salt',
3093 help='Salt in hex (default: /dev/urandom)')
3094 sub_parser.add_argument('--block_size',
3095 help='Block size (default: 4096)',
3096 type=parse_number,
3097 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003098 sub_parser.add_argument('--generate_fec',
3099 help='Add forward-error-correction codes',
3100 action='store_true')
3101 sub_parser.add_argument('--fec_num_roots',
3102 help='Number of roots for FEC (default: 2)',
3103 type=parse_number,
3104 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003105 sub_parser.add_argument('--calc_max_image_size',
3106 help=('Don\'t store the hashtree or footer - '
3107 'instead calculate the maximum image size '
3108 'leaving enough room for hashtree '
3109 'and metadata with the given partition '
3110 'size.'),
3111 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003112 sub_parser.add_argument('--output_vbmeta_image',
3113 help='Also write vbmeta struct to file',
3114 type=argparse.FileType('wb'))
3115 sub_parser.add_argument('--do_not_append_vbmeta_image',
3116 help=('Do not append vbmeta struct or footer '
3117 'to the image'),
3118 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003119 self._add_common_args(sub_parser)
3120 sub_parser.set_defaults(func=self.add_hashtree_footer)
3121
3122 sub_parser = subparsers.add_parser('erase_footer',
3123 help='Erase footer from an image.')
3124 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003125 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003126 type=argparse.FileType('rwb+'),
3127 required=True)
3128 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003129 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003130 action='store_true')
3131 sub_parser.set_defaults(func=self.erase_footer)
3132
3133 sub_parser = subparsers.add_parser(
3134 'info_image',
3135 help='Show information about vbmeta or footer.')
3136 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003137 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003138 type=argparse.FileType('rb'),
3139 required=True)
3140 sub_parser.add_argument('--output',
3141 help='Write info to file',
3142 type=argparse.FileType('wt'),
3143 default=sys.stdout)
3144 sub_parser.set_defaults(func=self.info_image)
3145
David Zeuthen8b6973b2016-09-20 12:39:49 -04003146 sub_parser = subparsers.add_parser('set_ab_metadata',
3147 help='Set A/B metadata.')
3148 sub_parser.add_argument('--misc_image',
3149 help=('The misc image to modify. If the image does '
3150 'not exist, it will be created.'),
3151 type=argparse.FileType('r+b'),
3152 required=True)
3153 sub_parser.add_argument('--slot_data',
3154 help=('Slot data of the form "priority", '
3155 '"tries_remaining", "sucessful_boot" for '
3156 'slot A followed by the same for slot B, '
3157 'separated by colons. The default value '
3158 'is 15:7:0:14:7:0.'),
3159 default='15:7:0:14:7:0')
3160 sub_parser.set_defaults(func=self.set_ab_metadata)
3161
Darren Krahn147b08d2016-12-20 16:38:29 -08003162 sub_parser = subparsers.add_parser(
3163 'make_atx_certificate',
3164 help='Create an Android Things eXtension (ATX) certificate.')
3165 sub_parser.add_argument('--output',
3166 help='Write certificate to file',
3167 type=argparse.FileType('wb'),
3168 default=sys.stdout)
3169 sub_parser.add_argument('--subject',
3170 help=('Path to subject file'),
3171 type=argparse.FileType('rb'),
3172 required=True)
3173 sub_parser.add_argument('--subject_key',
3174 help=('Path to subject RSA public key file'),
3175 type=argparse.FileType('rb'),
3176 required=True)
3177 sub_parser.add_argument('--subject_key_version',
3178 help=('Version of the subject key'),
3179 type=parse_number,
3180 required=False)
3181 sub_parser.add_argument('--subject_is_intermediate_authority',
3182 help=('Generate an intermediate authority '
3183 'certificate'),
3184 action='store_true')
3185 sub_parser.add_argument('--authority_key',
3186 help='Path to authority RSA private key file',
3187 required=False)
3188 sub_parser.add_argument('--signing_helper',
3189 help='Path to helper used for signing',
3190 metavar='APP',
3191 default=None,
3192 required=False)
3193 sub_parser.set_defaults(func=self.make_atx_certificate)
3194
3195 sub_parser = subparsers.add_parser(
3196 'make_atx_permanent_attributes',
3197 help='Create Android Things eXtension (ATX) permanent attributes.')
3198 sub_parser.add_argument('--output',
3199 help='Write attributes to file',
3200 type=argparse.FileType('wb'),
3201 default=sys.stdout)
3202 sub_parser.add_argument('--root_authority_key',
3203 help='Path to authority RSA public key file',
3204 type=argparse.FileType('rb'),
3205 required=True)
3206 sub_parser.add_argument('--product_id',
3207 help=('Path to Product ID file'),
3208 type=argparse.FileType('rb'),
3209 required=True)
3210 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3211
3212 sub_parser = subparsers.add_parser(
3213 'make_atx_metadata',
3214 help='Create Android Things eXtension (ATX) metadata.')
3215 sub_parser.add_argument('--output',
3216 help='Write metadata to file',
3217 type=argparse.FileType('wb'),
3218 default=sys.stdout)
3219 sub_parser.add_argument('--intermediate_key_certificate',
3220 help='Path to intermediate key certificate file',
3221 type=argparse.FileType('rb'),
3222 required=True)
3223 sub_parser.add_argument('--product_key_certificate',
3224 help='Path to product key certificate file',
3225 type=argparse.FileType('rb'),
3226 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003227 sub_parser.set_defaults(func=self.make_atx_metadata)
3228
David Zeuthen21e95262016-07-27 17:58:40 -04003229 args = parser.parse_args(argv[1:])
3230 try:
3231 args.func(args)
3232 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003233 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003234 sys.exit(1)
3235
3236 def version(self, _):
3237 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003238 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003239
3240 def extract_public_key(self, args):
3241 """Implements the 'extract_public_key' sub-command."""
3242 self.avb.extract_public_key(args.key, args.output)
3243
3244 def make_vbmeta_image(self, args):
3245 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003246 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003247 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003248 args.algorithm, args.key,
3249 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003250 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003251 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003252 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003253 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003254 args.signing_helper,
3255 args.internal_release_string,
3256 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003257
David Zeuthenb1b994d2017-03-06 18:01:31 -05003258 def append_vbmeta_image(self, args):
3259 """Implements the 'append_vbmeta_image' sub-command."""
3260 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3261 args.partition_size)
3262
David Zeuthen21e95262016-07-27 17:58:40 -04003263 def add_hash_footer(self, args):
3264 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003265 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003266 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003267 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003268 args.salt, args.chain_partition, args.algorithm,
3269 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003270 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003271 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003272 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003273 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003274 args.include_descriptors_from_image,
3275 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003276 args.internal_release_string,
3277 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003278 args.output_vbmeta_image,
3279 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003280
3281 def add_hashtree_footer(self, args):
3282 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003283 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003284 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3285 args.partition_size,
3286 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003287 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003288 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003289 args.salt, args.chain_partition, args.algorithm,
3290 args.key, args.public_key_metadata,
3291 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003292 args.prop_from_file,
3293 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003294 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003295 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003296 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003297 args.internal_release_string,
3298 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003299 args.output_vbmeta_image,
3300 args.do_not_append_vbmeta_image)
3301
David Zeuthen21e95262016-07-27 17:58:40 -04003302 def erase_footer(self, args):
3303 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003304 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003305
David Zeuthen8b6973b2016-09-20 12:39:49 -04003306 def set_ab_metadata(self, args):
3307 """Implements the 'set_ab_metadata' sub-command."""
3308 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3309
David Zeuthen21e95262016-07-27 17:58:40 -04003310 def info_image(self, args):
3311 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003312 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003313
Darren Krahn147b08d2016-12-20 16:38:29 -08003314 def make_atx_certificate(self, args):
3315 """Implements the 'make_atx_certificate' sub-command."""
3316 self.avb.make_atx_certificate(args.output, args.authority_key,
3317 args.subject_key.read(),
3318 args.subject_key_version,
3319 args.subject.read(),
3320 args.subject_is_intermediate_authority,
3321 args.signing_helper)
3322
3323 def make_atx_permanent_attributes(self, args):
3324 """Implements the 'make_atx_permanent_attributes' sub-command."""
3325 self.avb.make_atx_permanent_attributes(args.output,
3326 args.root_authority_key.read(),
3327 args.product_id.read())
3328
3329 def make_atx_metadata(self, args):
3330 """Implements the 'make_atx_metadata' sub-command."""
3331 self.avb.make_atx_metadata(args.output,
3332 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003333 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003334
David Zeuthen21e95262016-07-27 17:58:40 -04003335
3336if __name__ == '__main__':
3337 tool = AvbTool()
3338 tool.run(sys.argv)