blob: 9850634b47d08a09d68725ee92fd0774f6f7d22d [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
31import os
32import struct
33import subprocess
34import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040035import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080036import time
David Zeuthen21e95262016-07-27 17:58:40 -040037
38import Crypto.PublicKey.RSA
39
David Zeuthene3cadca2017-02-22 21:25:46 -050040# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040041AVB_VERSION_MAJOR = 1
42AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050043AVB_VERSION_SUB = 0
44
David Zeuthen58305522017-01-11 17:42:47 -050045AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040046
David Zeuthene3cadca2017-02-22 21:25:46 -050047
David Zeuthen21e95262016-07-27 17:58:40 -040048class AvbError(Exception):
49 """Application-specific errors.
50
51 These errors represent issues for which a stack-trace should not be
52 presented.
53
54 Attributes:
55 message: Error message.
56 """
57
58 def __init__(self, message):
59 Exception.__init__(self, message)
60
61
62class Algorithm(object):
63 """Contains details about an algorithm.
64
65 See the avb_vbmeta_header.h file for more details about
66 algorithms.
67
68 The constant |ALGORITHMS| is a dictionary from human-readable
69 names (e.g 'SHA256_RSA2048') to instances of this class.
70
71 Attributes:
72 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
73 hash_num_bytes: Number of bytes used to store the hash.
74 signature_num_bytes: Number of bytes used to store the signature.
75 public_key_num_bytes: Number of bytes used to store the public key.
76 padding: Padding used for signature, if any.
77 """
78
79 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
80 public_key_num_bytes, padding):
81 self.algorithm_type = algorithm_type
82 self.hash_num_bytes = hash_num_bytes
83 self.signature_num_bytes = signature_num_bytes
84 self.public_key_num_bytes = public_key_num_bytes
85 self.padding = padding
86
87# This must be kept in sync with the avb_crypto.h file.
88#
89# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
90# obtained from section 5.2.2 of RFC 4880.
91ALGORITHMS = {
92 'NONE': Algorithm(
93 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
94 hash_num_bytes=0,
95 signature_num_bytes=0,
96 public_key_num_bytes=0,
97 padding=[]),
98 'SHA256_RSA2048': Algorithm(
99 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
100 hash_num_bytes=32,
101 signature_num_bytes=256,
102 public_key_num_bytes=8 + 2*2048/8,
103 padding=[
104 # PKCS1-v1_5 padding
105 0x00, 0x01] + [0xff]*202 + [0x00] + [
106 # ASN.1 header
107 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
108 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
109 0x00, 0x04, 0x20,
110 ]),
111 'SHA256_RSA4096': Algorithm(
112 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
113 hash_num_bytes=32,
114 signature_num_bytes=512,
115 public_key_num_bytes=8 + 2*4096/8,
116 padding=[
117 # PKCS1-v1_5 padding
118 0x00, 0x01] + [0xff]*458 + [0x00] + [
119 # ASN.1 header
120 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
121 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
122 0x00, 0x04, 0x20,
123 ]),
124 'SHA256_RSA8192': Algorithm(
125 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
126 hash_num_bytes=32,
127 signature_num_bytes=1024,
128 public_key_num_bytes=8 + 2*8192/8,
129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*970 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA512_RSA2048': Algorithm(
138 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
139 hash_num_bytes=64,
140 signature_num_bytes=256,
141 public_key_num_bytes=8 + 2*2048/8,
142 padding=[
143 # PKCS1-v1_5 padding
144 0x00, 0x01] + [0xff]*170 + [0x00] + [
145 # ASN.1 header
146 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
147 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
148 0x00, 0x04, 0x40
149 ]),
150 'SHA512_RSA4096': Algorithm(
151 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
152 hash_num_bytes=64,
153 signature_num_bytes=512,
154 public_key_num_bytes=8 + 2*4096/8,
155 padding=[
156 # PKCS1-v1_5 padding
157 0x00, 0x01] + [0xff]*426 + [0x00] + [
158 # ASN.1 header
159 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
160 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
161 0x00, 0x04, 0x40
162 ]),
163 'SHA512_RSA8192': Algorithm(
164 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
165 hash_num_bytes=64,
166 signature_num_bytes=1024,
167 public_key_num_bytes=8 + 2*8192/8,
168 padding=[
169 # PKCS1-v1_5 padding
170 0x00, 0x01] + [0xff]*938 + [0x00] + [
171 # ASN.1 header
172 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
173 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
174 0x00, 0x04, 0x40
175 ]),
176}
177
178
David Zeuthene3cadca2017-02-22 21:25:46 -0500179def get_release_string():
180 """Calculates the release string to use in the VBMeta struct."""
181 # Keep in sync with libavb/avb_version.c:avb_version_string().
182 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
183 AVB_VERSION_MINOR,
184 AVB_VERSION_SUB)
185
186
David Zeuthen21e95262016-07-27 17:58:40 -0400187def round_to_multiple(number, size):
188 """Rounds a number up to nearest multiple of another number.
189
190 Args:
191 number: The number to round up.
192 size: The multiple to round up to.
193
194 Returns:
195 If |number| is a multiple of |size|, returns |number|, otherwise
196 returns |number| + |size|.
197 """
198 remainder = number % size
199 if remainder == 0:
200 return number
201 return number + size - remainder
202
203
204def round_to_pow2(number):
205 """Rounds a number up to the next power of 2.
206
207 Args:
208 number: The number to round up.
209
210 Returns:
211 If |number| is already a power of 2 then |number| is
212 returned. Otherwise the smallest power of 2 greater than |number|
213 is returned.
214 """
215 return 2**((number - 1).bit_length())
216
217
218def write_long(output, num_bits, value):
219 """Writes a long to an output stream using a given amount of bits.
220
221 This number is written big-endian, e.g. with the most significant
222 bit first.
223
224 Arguments:
225 output: The object to write the output to.
226 num_bits: The number of bits to write, e.g. 2048.
227 value: The value to write.
228 """
229 for bit_pos in range(num_bits, 0, -8):
230 octet = (value >> (bit_pos - 8)) & 0xff
231 output.write(struct.pack('!B', octet))
232
233
234def encode_long(num_bits, value):
235 """Encodes a long to a bytearray() using a given amount of bits.
236
237 This number is written big-endian, e.g. with the most significant
238 bit first.
239
240 Arguments:
241 num_bits: The number of bits to write, e.g. 2048.
242 value: The value to write.
243
244 Returns:
245 A bytearray() with the encoded long.
246 """
247 ret = bytearray()
248 for bit_pos in range(num_bits, 0, -8):
249 octet = (value >> (bit_pos - 8)) & 0xff
250 ret.extend(struct.pack('!B', octet))
251 return ret
252
253
254def egcd(a, b):
255 """Calculate greatest common divisor of two numbers.
256
257 This implementation uses a recursive version of the extended
258 Euclidian algorithm.
259
260 Arguments:
261 a: First number.
262 b: Second number.
263
264 Returns:
265 A tuple (gcd, x, y) that where |gcd| is the greatest common
266 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
267 """
268 if a == 0:
269 return (b, 0, 1)
270 else:
271 g, y, x = egcd(b % a, a)
272 return (g, x - (b // a) * y, y)
273
274
275def modinv(a, m):
276 """Calculate modular multiplicative inverse of |a| modulo |m|.
277
278 This calculates the number |x| such that |a| * |x| == 1 (modulo
279 |m|). This number only exists if |a| and |m| are co-prime - |None|
280 is returned if this isn't true.
281
282 Arguments:
283 a: The number to calculate a modular inverse of.
284 m: The modulo to use.
285
286 Returns:
287 The modular multiplicative inverse of |a| and |m| or |None| if
288 these numbers are not co-prime.
289 """
290 gcd, x, _ = egcd(a, m)
291 if gcd != 1:
292 return None # modular inverse does not exist
293 else:
294 return x % m
295
296
297def parse_number(string):
298 """Parse a string as a number.
299
300 This is just a short-hand for int(string, 0) suitable for use in the
301 |type| parameter of |ArgumentParser|'s add_argument() function. An
302 improvement to just using type=int is that this function supports
303 numbers in other bases, e.g. "0x1234".
304
305 Arguments:
306 string: The string to parse.
307
308 Returns:
309 The parsed integer.
310
311 Raises:
312 ValueError: If the number could not be parsed.
313 """
314 return int(string, 0)
315
316
317def write_rsa_key(output, key):
318 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
319
320 This writes the |AvbRSAPublicKeyHeader| as well as the two large
321 numbers (|key_num_bits| bits long) following it.
322
323 Arguments:
324 output: The object to write the output to.
325 key: A Crypto.PublicKey.RSA object.
326 """
327 # key.e is exponent
328 # key.n is modulus
329 key_num_bits = key.size() + 1
330 # Calculate n0inv = -1/n[0] (mod 2^32)
331 b = 2L**32
332 n0inv = b - modinv(key.n, b)
333 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
334 r = 2L**key.n.bit_length()
335 rrmodn = r * r % key.n
336 output.write(struct.pack('!II', key_num_bits, n0inv))
337 write_long(output, key_num_bits, key.n)
338 write_long(output, key_num_bits, rrmodn)
339
340
341def encode_rsa_key(key):
342 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
343
344 This creates a |AvbRSAPublicKeyHeader| as well as the two large
345 numbers (|key_num_bits| bits long) following it.
346
347 Arguments:
348 key: A Crypto.PublicKey.RSA object.
349
350 Returns:
351 A bytearray() with the |AvbRSAPublicKeyHeader|.
352 """
353 ret = bytearray()
354 # key.e is exponent
355 # key.n is modulus
356 key_num_bits = key.size() + 1
357 # Calculate n0inv = -1/n[0] (mod 2^32)
358 b = 2L**32
359 n0inv = b - modinv(key.n, b)
360 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
361 r = 2L**key.n.bit_length()
362 rrmodn = r * r % key.n
363 ret.extend(struct.pack('!II', key_num_bits, n0inv))
364 ret.extend(encode_long(key_num_bits, key.n))
365 ret.extend(encode_long(key_num_bits, rrmodn))
366 return ret
367
368
369def lookup_algorithm_by_type(alg_type):
370 """Looks up algorithm by type.
371
372 Arguments:
373 alg_type: The integer representing the type.
374
375 Returns:
376 A tuple with the algorithm name and an |Algorithm| instance.
377
378 Raises:
379 Exception: If the algorithm cannot be found
380 """
381 for alg_name in ALGORITHMS:
382 alg_data = ALGORITHMS[alg_name]
383 if alg_data.algorithm_type == alg_type:
384 return (alg_name, alg_data)
385 raise AvbError('Unknown algorithm type {}'.format(alg_type))
386
387
Darren Krahn147b08d2016-12-20 16:38:29 -0800388def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
389 """Computes a raw RSA signature using |signing_helper| or openssl.
390
391 Arguments:
392 signing_helper: Program which signs a hash and returns the signature.
393 algorithm_name: The algorithm name as per the ALGORITHMS dict.
394 key_path: Path to the private key file. Must be PEM format.
395 raw_data_to_sign: Data to sign (bytearray or str expected).
396
397 Returns:
398 A bytearray containing the signature.
399
400 Raises:
401 Exception: If an error occurs.
402 """
403 p = None
404 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500405 p = subprocess.Popen(
406 [signing_helper, algorithm_name, key_path],
407 stdin=subprocess.PIPE,
408 stdout=subprocess.PIPE,
409 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800410 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500411 p = subprocess.Popen(
412 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
413 stdin=subprocess.PIPE,
414 stdout=subprocess.PIPE,
415 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800416 (pout, perr) = p.communicate(str(raw_data_to_sign))
417 retcode = p.wait()
418 if retcode != 0:
419 raise AvbError('Error signing: {}'.format(perr))
420 return bytearray(pout)
421
422
David Zeuthena4fee8b2016-08-22 15:20:43 -0400423class ImageChunk(object):
424 """Data structure used for representing chunks in Android sparse files.
425
426 Attributes:
427 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
428 chunk_offset: Offset in the sparse file where this chunk begins.
429 output_offset: Offset in de-sparsified file where output begins.
430 output_size: Number of bytes in output.
431 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
432 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
433 """
434
435 FORMAT = '<2H2I'
436 TYPE_RAW = 0xcac1
437 TYPE_FILL = 0xcac2
438 TYPE_DONT_CARE = 0xcac3
439 TYPE_CRC32 = 0xcac4
440
441 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
442 input_offset, fill_data):
443 """Initializes an ImageChunk object.
444
445 Arguments:
446 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
447 chunk_offset: Offset in the sparse file where this chunk begins.
448 output_offset: Offset in de-sparsified file.
449 output_size: Number of bytes in output.
450 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
451 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
452
453 Raises:
454 ValueError: If data is not well-formed.
455 """
456 self.chunk_type = chunk_type
457 self.chunk_offset = chunk_offset
458 self.output_offset = output_offset
459 self.output_size = output_size
460 self.input_offset = input_offset
461 self.fill_data = fill_data
462 # Check invariants.
463 if self.chunk_type == self.TYPE_RAW:
464 if self.fill_data is not None:
465 raise ValueError('RAW chunk cannot have fill_data set.')
466 if not self.input_offset:
467 raise ValueError('RAW chunk must have input_offset set.')
468 elif self.chunk_type == self.TYPE_FILL:
469 if self.fill_data is None:
470 raise ValueError('FILL chunk must have fill_data set.')
471 if self.input_offset:
472 raise ValueError('FILL chunk cannot have input_offset set.')
473 elif self.chunk_type == self.TYPE_DONT_CARE:
474 if self.fill_data is not None:
475 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
476 if self.input_offset:
477 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
478 else:
479 raise ValueError('Invalid chunk type')
480
481
482class ImageHandler(object):
483 """Abstraction for image I/O with support for Android sparse images.
484
485 This class provides an interface for working with image files that
486 may be using the Android Sparse Image format. When an instance is
487 constructed, we test whether it's an Android sparse file. If so,
488 operations will be on the sparse file by interpreting the sparse
489 format, otherwise they will be directly on the file. Either way the
490 operations do the same.
491
492 For reading, this interface mimics a file object - it has seek(),
493 tell(), and read() methods. For writing, only truncation
494 (truncate()) and appending is supported (append_raw() and
495 append_dont_care()). Additionally, data can only be written in units
496 of the block size.
497
498 Attributes:
499 is_sparse: Whether the file being operated on is sparse.
500 block_size: The block size, typically 4096.
501 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400502 """
503 # See system/core/libsparse/sparse_format.h for details.
504 MAGIC = 0xed26ff3a
505 HEADER_FORMAT = '<I4H4I'
506
507 # These are formats and offset of just the |total_chunks| and
508 # |total_blocks| fields.
509 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
510 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
511
512 def __init__(self, image_filename):
513 """Initializes an image handler.
514
515 Arguments:
516 image_filename: The name of the file to operate on.
517
518 Raises:
519 ValueError: If data in the file is invalid.
520 """
521 self._image_filename = image_filename
522 self._read_header()
523
524 def _read_header(self):
525 """Initializes internal data structures used for reading file.
526
527 This may be called multiple times and is typically called after
528 modifying the file (e.g. appending, truncation).
529
530 Raises:
531 ValueError: If data in the file is invalid.
532 """
533 self.is_sparse = False
534 self.block_size = 4096
535 self._file_pos = 0
536 self._image = open(self._image_filename, 'r+b')
537 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400538 self.image_size = self._image.tell()
539
540 self._image.seek(0, os.SEEK_SET)
541 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
542 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
543 block_size, self._num_total_blocks, self._num_total_chunks,
544 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
545 if magic != self.MAGIC:
546 # Not a sparse image, our job here is done.
547 return
548 if not (major_version == 1 and minor_version == 0):
549 raise ValueError('Encountered sparse image format version {}.{} but '
550 'only 1.0 is supported'.format(major_version,
551 minor_version))
552 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
553 raise ValueError('Unexpected file_hdr_sz value {}.'.
554 format(file_hdr_sz))
555 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
556 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
557 format(chunk_hdr_sz))
558
559 self.block_size = block_size
560
561 # Build an list of chunks by parsing the file.
562 self._chunks = []
563
564 # Find the smallest offset where only "Don't care" chunks
565 # follow. This will be the size of the content in the sparse
566 # image.
567 offset = 0
568 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400569 for _ in xrange(1, self._num_total_chunks + 1):
570 chunk_offset = self._image.tell()
571
572 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
573 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
574 header_bin)
575 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
576
David Zeuthena4fee8b2016-08-22 15:20:43 -0400577 if chunk_type == ImageChunk.TYPE_RAW:
578 if data_sz != (chunk_sz * self.block_size):
579 raise ValueError('Raw chunk input size ({}) does not match output '
580 'size ({})'.
581 format(data_sz, chunk_sz*self.block_size))
582 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
583 chunk_offset,
584 output_offset,
585 chunk_sz*self.block_size,
586 self._image.tell(),
587 None))
588 self._image.read(data_sz)
589
590 elif chunk_type == ImageChunk.TYPE_FILL:
591 if data_sz != 4:
592 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
593 'has {}'.format(data_sz))
594 fill_data = self._image.read(4)
595 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
596 chunk_offset,
597 output_offset,
598 chunk_sz*self.block_size,
599 None,
600 fill_data))
601 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
602 if data_sz != 0:
603 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
604 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400605 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
606 chunk_offset,
607 output_offset,
608 chunk_sz*self.block_size,
609 None,
610 None))
611 elif chunk_type == ImageChunk.TYPE_CRC32:
612 if data_sz != 4:
613 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
614 'this has {}'.format(data_sz))
615 self._image.read(4)
616 else:
617 raise ValueError('Unknown chunk type {}'.format(chunk_type))
618
619 offset += chunk_sz
620 output_offset += chunk_sz*self.block_size
621
622 # Record where sparse data end.
623 self._sparse_end = self._image.tell()
624
625 # Now that we've traversed all chunks, sanity check.
626 if self._num_total_blocks != offset:
627 raise ValueError('The header said we should have {} output blocks, '
628 'but we saw {}'.format(self._num_total_blocks, offset))
629 junk_len = len(self._image.read())
630 if junk_len > 0:
631 raise ValueError('There were {} bytes of extra data at the end of the '
632 'file.'.format(junk_len))
633
David Zeuthen09692692016-09-30 16:16:40 -0400634 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400635 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400636
637 # This is used when bisecting in read() to find the initial slice.
638 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
639
640 self.is_sparse = True
641
642 def _update_chunks_and_blocks(self):
643 """Helper function to update the image header.
644
645 The the |total_chunks| and |total_blocks| fields in the header
646 will be set to value of the |_num_total_blocks| and
647 |_num_total_chunks| attributes.
648
649 """
650 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
651 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
652 self._num_total_blocks,
653 self._num_total_chunks))
654
655 def append_dont_care(self, num_bytes):
656 """Appends a DONT_CARE chunk to the sparse file.
657
658 The given number of bytes must be a multiple of the block size.
659
660 Arguments:
661 num_bytes: Size in number of bytes of the DONT_CARE chunk.
662 """
663 assert num_bytes % self.block_size == 0
664
665 if not self.is_sparse:
666 self._image.seek(0, os.SEEK_END)
667 # This is more efficient that writing NUL bytes since it'll add
668 # a hole on file systems that support sparse files (native
669 # sparse, not Android sparse).
670 self._image.truncate(self._image.tell() + num_bytes)
671 self._read_header()
672 return
673
674 self._num_total_chunks += 1
675 self._num_total_blocks += num_bytes / self.block_size
676 self._update_chunks_and_blocks()
677
678 self._image.seek(self._sparse_end, os.SEEK_SET)
679 self._image.write(struct.pack(ImageChunk.FORMAT,
680 ImageChunk.TYPE_DONT_CARE,
681 0, # Reserved
682 num_bytes / self.block_size,
683 struct.calcsize(ImageChunk.FORMAT)))
684 self._read_header()
685
686 def append_raw(self, data):
687 """Appends a RAW chunk to the sparse file.
688
689 The length of the given data must be a multiple of the block size.
690
691 Arguments:
692 data: Data to append.
693 """
694 assert len(data) % self.block_size == 0
695
696 if not self.is_sparse:
697 self._image.seek(0, os.SEEK_END)
698 self._image.write(data)
699 self._read_header()
700 return
701
702 self._num_total_chunks += 1
703 self._num_total_blocks += len(data) / self.block_size
704 self._update_chunks_and_blocks()
705
706 self._image.seek(self._sparse_end, os.SEEK_SET)
707 self._image.write(struct.pack(ImageChunk.FORMAT,
708 ImageChunk.TYPE_RAW,
709 0, # Reserved
710 len(data) / self.block_size,
711 len(data) +
712 struct.calcsize(ImageChunk.FORMAT)))
713 self._image.write(data)
714 self._read_header()
715
716 def append_fill(self, fill_data, size):
717 """Appends a fill chunk to the sparse file.
718
719 The total length of the fill data must be a multiple of the block size.
720
721 Arguments:
722 fill_data: Fill data to append - must be four bytes.
723 size: Number of chunk - must be a multiple of four and the block size.
724 """
725 assert len(fill_data) == 4
726 assert size % 4 == 0
727 assert size % self.block_size == 0
728
729 if not self.is_sparse:
730 self._image.seek(0, os.SEEK_END)
731 self._image.write(fill_data * (size/4))
732 self._read_header()
733 return
734
735 self._num_total_chunks += 1
736 self._num_total_blocks += size / self.block_size
737 self._update_chunks_and_blocks()
738
739 self._image.seek(self._sparse_end, os.SEEK_SET)
740 self._image.write(struct.pack(ImageChunk.FORMAT,
741 ImageChunk.TYPE_FILL,
742 0, # Reserved
743 size / self.block_size,
744 4 + struct.calcsize(ImageChunk.FORMAT)))
745 self._image.write(fill_data)
746 self._read_header()
747
748 def seek(self, offset):
749 """Sets the cursor position for reading from unsparsified file.
750
751 Arguments:
752 offset: Offset to seek to from the beginning of the file.
753 """
754 self._file_pos = offset
755
756 def read(self, size):
757 """Reads data from the unsparsified file.
758
759 This method may return fewer than |size| bytes of data if the end
760 of the file was encountered.
761
762 The file cursor for reading is advanced by the number of bytes
763 read.
764
765 Arguments:
766 size: Number of bytes to read.
767
768 Returns:
769 The data.
770
771 """
772 if not self.is_sparse:
773 self._image.seek(self._file_pos)
774 data = self._image.read(size)
775 self._file_pos += len(data)
776 return data
777
778 # Iterate over all chunks.
779 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
780 self._file_pos) - 1
781 data = bytearray()
782 to_go = size
783 while to_go > 0:
784 chunk = self._chunks[chunk_idx]
785 chunk_pos_offset = self._file_pos - chunk.output_offset
786 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
787
788 if chunk.chunk_type == ImageChunk.TYPE_RAW:
789 self._image.seek(chunk.input_offset + chunk_pos_offset)
790 data.extend(self._image.read(chunk_pos_to_go))
791 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
792 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
793 offset_mod = chunk_pos_offset % len(chunk.fill_data)
794 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
795 else:
796 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
797 data.extend('\0' * chunk_pos_to_go)
798
799 to_go -= chunk_pos_to_go
800 self._file_pos += chunk_pos_to_go
801 chunk_idx += 1
802 # Generate partial read in case of EOF.
803 if chunk_idx >= len(self._chunks):
804 break
805
806 return data
807
808 def tell(self):
809 """Returns the file cursor position for reading from unsparsified file.
810
811 Returns:
812 The file cursor position for reading.
813 """
814 return self._file_pos
815
816 def truncate(self, size):
817 """Truncates the unsparsified file.
818
819 Arguments:
820 size: Desired size of unsparsified file.
821
822 Raises:
823 ValueError: If desired size isn't a multiple of the block size.
824 """
825 if not self.is_sparse:
826 self._image.truncate(size)
827 self._read_header()
828 return
829
830 if size % self.block_size != 0:
831 raise ValueError('Cannot truncate to a size which is not a multiple '
832 'of the block size')
833
834 if size == self.image_size:
835 # Trivial where there's nothing to do.
836 return
837 elif size < self.image_size:
838 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
839 chunk = self._chunks[chunk_idx]
840 if chunk.output_offset != size:
841 # Truncation in the middle of a trunk - need to keep the chunk
842 # and modify it.
843 chunk_idx_for_update = chunk_idx + 1
844 num_to_keep = size - chunk.output_offset
845 assert num_to_keep % self.block_size == 0
846 if chunk.chunk_type == ImageChunk.TYPE_RAW:
847 truncate_at = (chunk.chunk_offset +
848 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
849 data_sz = num_to_keep
850 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
851 truncate_at = (chunk.chunk_offset +
852 struct.calcsize(ImageChunk.FORMAT) + 4)
853 data_sz = 4
854 else:
855 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
856 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
857 data_sz = 0
858 chunk_sz = num_to_keep/self.block_size
859 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
860 self._image.seek(chunk.chunk_offset)
861 self._image.write(struct.pack(ImageChunk.FORMAT,
862 chunk.chunk_type,
863 0, # Reserved
864 chunk_sz,
865 total_sz))
866 chunk.output_size = num_to_keep
867 else:
868 # Truncation at trunk boundary.
869 truncate_at = chunk.chunk_offset
870 chunk_idx_for_update = chunk_idx
871
872 self._num_total_chunks = chunk_idx_for_update
873 self._num_total_blocks = 0
874 for i in range(0, chunk_idx_for_update):
875 self._num_total_blocks += self._chunks[i].output_size / self.block_size
876 self._update_chunks_and_blocks()
877 self._image.truncate(truncate_at)
878
879 # We've modified the file so re-read all data.
880 self._read_header()
881 else:
882 # Truncating to grow - just add a DONT_CARE section.
883 self.append_dont_care(size - self.image_size)
884
885
David Zeuthen21e95262016-07-27 17:58:40 -0400886class AvbDescriptor(object):
887 """Class for AVB descriptor.
888
889 See the |AvbDescriptor| C struct for more information.
890
891 Attributes:
892 tag: The tag identifying what kind of descriptor this is.
893 data: The data in the descriptor.
894 """
895
896 SIZE = 16
897 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
898
899 def __init__(self, data):
900 """Initializes a new property descriptor.
901
902 Arguments:
903 data: If not None, must be a bytearray().
904
905 Raises:
906 LookupError: If the given descriptor is malformed.
907 """
908 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
909
910 if data:
911 (self.tag, num_bytes_following) = (
912 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
913 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
914 else:
915 self.tag = None
916 self.data = None
917
918 def print_desc(self, o):
919 """Print the descriptor.
920
921 Arguments:
922 o: The object to write the output to.
923 """
924 o.write(' Unknown descriptor:\n')
925 o.write(' Tag: {}\n'.format(self.tag))
926 if len(self.data) < 256:
927 o.write(' Data: {} ({} bytes)\n'.format(
928 repr(str(self.data)), len(self.data)))
929 else:
930 o.write(' Data: {} bytes\n'.format(len(self.data)))
931
932 def encode(self):
933 """Serializes the descriptor.
934
935 Returns:
936 A bytearray() with the descriptor data.
937 """
938 num_bytes_following = len(self.data)
939 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
940 padding_size = nbf_with_padding - num_bytes_following
941 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
942 padding = struct.pack(str(padding_size) + 'x')
943 ret = desc + self.data + padding
944 return bytearray(ret)
945
946
947class AvbPropertyDescriptor(AvbDescriptor):
948 """A class for property descriptors.
949
950 See the |AvbPropertyDescriptor| C struct for more information.
951
952 Attributes:
953 key: The key.
954 value: The key.
955 """
956
957 TAG = 0
958 SIZE = 32
959 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
960 'Q' # key size (bytes)
961 'Q') # value size (bytes)
962
963 def __init__(self, data=None):
964 """Initializes a new property descriptor.
965
966 Arguments:
967 data: If not None, must be a bytearray of size |SIZE|.
968
969 Raises:
970 LookupError: If the given descriptor is malformed.
971 """
972 AvbDescriptor.__init__(self, None)
973 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
974
975 if data:
976 (tag, num_bytes_following, key_size,
977 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
978 expected_size = round_to_multiple(
979 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
980 if tag != self.TAG or num_bytes_following != expected_size:
981 raise LookupError('Given data does not look like a property '
982 'descriptor.')
983 self.key = data[self.SIZE:(self.SIZE + key_size)]
984 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
985 value_size)]
986 else:
987 self.key = ''
988 self.value = ''
989
990 def print_desc(self, o):
991 """Print the descriptor.
992
993 Arguments:
994 o: The object to write the output to.
995 """
996 if len(self.value) < 256:
997 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
998 else:
999 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1000
1001 def encode(self):
1002 """Serializes the descriptor.
1003
1004 Returns:
1005 A bytearray() with the descriptor data.
1006 """
1007 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1008 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1009 padding_size = nbf_with_padding - num_bytes_following
1010 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1011 len(self.key), len(self.value))
1012 padding = struct.pack(str(padding_size) + 'x')
1013 ret = desc + self.key + '\0' + self.value + '\0' + padding
1014 return bytearray(ret)
1015
1016
1017class AvbHashtreeDescriptor(AvbDescriptor):
1018 """A class for hashtree descriptors.
1019
1020 See the |AvbHashtreeDescriptor| C struct for more information.
1021
1022 Attributes:
1023 dm_verity_version: dm-verity version used.
1024 image_size: Size of the image, after rounding up to |block_size|.
1025 tree_offset: Offset of the hash tree in the file.
1026 tree_size: Size of the tree.
1027 data_block_size: Data block size
1028 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001029 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1030 fec_offset: Offset of FEC data (0 if FEC is not used).
1031 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001032 hash_algorithm: Hash algorithm used.
1033 partition_name: Partition name.
1034 salt: Salt used.
1035 root_digest: Root digest.
1036 """
1037
1038 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001039 RESERVED = 64
1040 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001041 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1042 'L' # dm-verity version used
1043 'Q' # image size (bytes)
1044 'Q' # tree offset (bytes)
1045 'Q' # tree size (bytes)
1046 'L' # data block size (bytes)
1047 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001048 'L' # FEC number of roots
1049 'Q' # FEC offset (bytes)
1050 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001051 '32s' # hash algorithm used
1052 'L' # partition name (bytes)
1053 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001054 'L' + # root digest length (bytes)
1055 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001056
1057 def __init__(self, data=None):
1058 """Initializes a new hashtree descriptor.
1059
1060 Arguments:
1061 data: If not None, must be a bytearray of size |SIZE|.
1062
1063 Raises:
1064 LookupError: If the given descriptor is malformed.
1065 """
1066 AvbDescriptor.__init__(self, None)
1067 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1068
1069 if data:
1070 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1071 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001072 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1073 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001074 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1075 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001076 expected_size = round_to_multiple(
1077 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1078 if tag != self.TAG or num_bytes_following != expected_size:
1079 raise LookupError('Given data does not look like a hashtree '
1080 'descriptor.')
1081 # Nuke NUL-bytes at the end.
1082 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1083 o = 0
1084 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1085 partition_name_len)])
1086 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1087 self.partition_name.decode('utf-8')
1088 o += partition_name_len
1089 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1090 o += salt_len
1091 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1092 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1093 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1094
1095 else:
1096 self.dm_verity_version = 0
1097 self.image_size = 0
1098 self.tree_offset = 0
1099 self.tree_size = 0
1100 self.data_block_size = 0
1101 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001102 self.fec_num_roots = 0
1103 self.fec_offset = 0
1104 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001105 self.hash_algorithm = ''
1106 self.partition_name = ''
1107 self.salt = bytearray()
1108 self.root_digest = bytearray()
1109
1110 def print_desc(self, o):
1111 """Print the descriptor.
1112
1113 Arguments:
1114 o: The object to write the output to.
1115 """
1116 o.write(' Hashtree descriptor:\n')
1117 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1118 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1119 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1120 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1121 o.write(' Data Block Size: {} bytes\n'.format(
1122 self.data_block_size))
1123 o.write(' Hash Block Size: {} bytes\n'.format(
1124 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001125 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1126 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1127 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001128 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1129 o.write(' Partition Name: {}\n'.format(self.partition_name))
1130 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1131 'hex')))
1132 o.write(' Root Digest: {}\n'.format(str(
1133 self.root_digest).encode('hex')))
1134
1135 def encode(self):
1136 """Serializes the descriptor.
1137
1138 Returns:
1139 A bytearray() with the descriptor data.
1140 """
1141 encoded_name = self.partition_name.encode('utf-8')
1142 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1143 len(self.root_digest) - 16)
1144 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1145 padding_size = nbf_with_padding - num_bytes_following
1146 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1147 self.dm_verity_version, self.image_size,
1148 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001149 self.hash_block_size, self.fec_num_roots,
1150 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001151 len(encoded_name), len(self.salt), len(self.root_digest),
1152 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001153 padding = struct.pack(str(padding_size) + 'x')
1154 ret = desc + encoded_name + self.salt + self.root_digest + padding
1155 return bytearray(ret)
1156
1157
1158class AvbHashDescriptor(AvbDescriptor):
1159 """A class for hash descriptors.
1160
1161 See the |AvbHashDescriptor| C struct for more information.
1162
1163 Attributes:
1164 image_size: Image size, in bytes.
1165 hash_algorithm: Hash algorithm used.
1166 partition_name: Partition name.
1167 salt: Salt used.
1168 digest: The hash value of salt and data combined.
1169 """
1170
1171 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 RESERVED = 64
1173 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001174 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1175 'Q' # image size (bytes)
1176 '32s' # hash algorithm used
1177 'L' # partition name (bytes)
1178 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001179 'L' + # digest length (bytes)
1180 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001181
1182 def __init__(self, data=None):
1183 """Initializes a new hash descriptor.
1184
1185 Arguments:
1186 data: If not None, must be a bytearray of size |SIZE|.
1187
1188 Raises:
1189 LookupError: If the given descriptor is malformed.
1190 """
1191 AvbDescriptor.__init__(self, None)
1192 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1193
1194 if data:
1195 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1196 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001197 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001198 expected_size = round_to_multiple(
1199 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1200 if tag != self.TAG or num_bytes_following != expected_size:
1201 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1202 # Nuke NUL-bytes at the end.
1203 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1204 o = 0
1205 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1206 partition_name_len)])
1207 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1208 self.partition_name.decode('utf-8')
1209 o += partition_name_len
1210 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1211 o += salt_len
1212 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1213 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1214 raise LookupError('digest_len doesn\'t match hash algorithm')
1215
1216 else:
1217 self.image_size = 0
1218 self.hash_algorithm = ''
1219 self.partition_name = ''
1220 self.salt = bytearray()
1221 self.digest = bytearray()
1222
1223 def print_desc(self, o):
1224 """Print the descriptor.
1225
1226 Arguments:
1227 o: The object to write the output to.
1228 """
1229 o.write(' Hash descriptor:\n')
1230 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1231 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1232 o.write(' Partition Name: {}\n'.format(self.partition_name))
1233 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1234 'hex')))
1235 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1236 'hex')))
1237
1238 def encode(self):
1239 """Serializes the descriptor.
1240
1241 Returns:
1242 A bytearray() with the descriptor data.
1243 """
1244 encoded_name = self.partition_name.encode('utf-8')
1245 num_bytes_following = (
1246 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1247 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1248 padding_size = nbf_with_padding - num_bytes_following
1249 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1250 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001251 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001252 padding = struct.pack(str(padding_size) + 'x')
1253 ret = desc + encoded_name + self.salt + self.digest + padding
1254 return bytearray(ret)
1255
1256
1257class AvbKernelCmdlineDescriptor(AvbDescriptor):
1258 """A class for kernel command-line descriptors.
1259
1260 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1261
1262 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001263 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001264 kernel_cmdline: The kernel command-line.
1265 """
1266
1267 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001268 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001269 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001270 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001271 'L') # cmdline length (bytes)
1272
David Zeuthenfd41eb92016-11-17 12:24:47 -05001273 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1274 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1275
David Zeuthen21e95262016-07-27 17:58:40 -04001276 def __init__(self, data=None):
1277 """Initializes a new kernel cmdline descriptor.
1278
1279 Arguments:
1280 data: If not None, must be a bytearray of size |SIZE|.
1281
1282 Raises:
1283 LookupError: If the given descriptor is malformed.
1284 """
1285 AvbDescriptor.__init__(self, None)
1286 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1287
1288 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001289 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001290 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1291 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1292 8)
1293 if tag != self.TAG or num_bytes_following != expected_size:
1294 raise LookupError('Given data does not look like a kernel cmdline '
1295 'descriptor.')
1296 # Nuke NUL-bytes at the end.
1297 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1298 kernel_cmdline_length)])
1299 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1300 self.kernel_cmdline.decode('utf-8')
1301 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001302 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001303 self.kernel_cmdline = ''
1304
1305 def print_desc(self, o):
1306 """Print the descriptor.
1307
1308 Arguments:
1309 o: The object to write the output to.
1310 """
1311 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001312 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001313 o.write(' Kernel Cmdline: {}\n'.format(repr(
1314 self.kernel_cmdline)))
1315
1316 def encode(self):
1317 """Serializes the descriptor.
1318
1319 Returns:
1320 A bytearray() with the descriptor data.
1321 """
1322 encoded_str = self.kernel_cmdline.encode('utf-8')
1323 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1324 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1325 padding_size = nbf_with_padding - num_bytes_following
1326 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001327 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001328 padding = struct.pack(str(padding_size) + 'x')
1329 ret = desc + encoded_str + padding
1330 return bytearray(ret)
1331
1332
1333class AvbChainPartitionDescriptor(AvbDescriptor):
1334 """A class for chained partition descriptors.
1335
1336 See the |AvbChainPartitionDescriptor| C struct for more information.
1337
1338 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001339 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001340 partition_name: Partition name.
1341 public_key: Bytes for the public key.
1342 """
1343
1344 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001345 RESERVED = 64
1346 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001347 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001348 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001349 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001350 'L' + # public_key_size (bytes)
1351 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001352
1353 def __init__(self, data=None):
1354 """Initializes a new chain partition descriptor.
1355
1356 Arguments:
1357 data: If not None, must be a bytearray of size |SIZE|.
1358
1359 Raises:
1360 LookupError: If the given descriptor is malformed.
1361 """
1362 AvbDescriptor.__init__(self, None)
1363 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1364
1365 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001366 (tag, num_bytes_following, self.rollback_index_location,
1367 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001368 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001369 expected_size = round_to_multiple(
1370 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1371 if tag != self.TAG or num_bytes_following != expected_size:
1372 raise LookupError('Given data does not look like a chain partition '
1373 'descriptor.')
1374 o = 0
1375 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1376 partition_name_len)])
1377 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1378 self.partition_name.decode('utf-8')
1379 o += partition_name_len
1380 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1381
1382 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001383 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001384 self.partition_name = ''
1385 self.public_key = bytearray()
1386
1387 def print_desc(self, o):
1388 """Print the descriptor.
1389
1390 Arguments:
1391 o: The object to write the output to.
1392 """
1393 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001394 o.write(' Partition Name: {}\n'.format(self.partition_name))
1395 o.write(' Rollback Index Location: {}\n'.format(
1396 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001397 # Just show the SHA1 of the key, for size reasons.
1398 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001399 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001400
1401 def encode(self):
1402 """Serializes the descriptor.
1403
1404 Returns:
1405 A bytearray() with the descriptor data.
1406 """
1407 encoded_name = self.partition_name.encode('utf-8')
1408 num_bytes_following = (
1409 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1410 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1411 padding_size = nbf_with_padding - num_bytes_following
1412 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001413 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001414 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001415 padding = struct.pack(str(padding_size) + 'x')
1416 ret = desc + encoded_name + self.public_key + padding
1417 return bytearray(ret)
1418
1419
1420DESCRIPTOR_CLASSES = [
1421 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1422 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1423]
1424
1425
1426def parse_descriptors(data):
1427 """Parses a blob of data into descriptors.
1428
1429 Arguments:
1430 data: A bytearray() with encoded descriptors.
1431
1432 Returns:
1433 A list of instances of objects derived from AvbDescriptor. For
1434 unknown descriptors, the class AvbDescriptor is used.
1435 """
1436 o = 0
1437 ret = []
1438 while o < len(data):
1439 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1440 if tag < len(DESCRIPTOR_CLASSES):
1441 c = DESCRIPTOR_CLASSES[tag]
1442 else:
1443 c = AvbDescriptor
1444 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1445 o += 16 + nb_following
1446 return ret
1447
1448
1449class AvbFooter(object):
1450 """A class for parsing and writing footers.
1451
1452 Footers are stored at the end of partitions and point to where the
1453 AvbVBMeta blob is located. They also contain the original size of
1454 the image before AVB information was added.
1455
1456 Attributes:
1457 magic: Magic for identifying the footer, see |MAGIC|.
1458 version_major: The major version of avbtool that wrote the footer.
1459 version_minor: The minor version of avbtool that wrote the footer.
1460 original_image_size: Original image size.
1461 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1462 vbmeta_size: Size of the AvbVBMeta blob.
1463 """
1464
1465 MAGIC = 'AVBf'
1466 SIZE = 64
1467 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001468 FOOTER_VERSION_MAJOR = 1
1469 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001470 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1471 'Q' # Original image size.
1472 'Q' # Offset of VBMeta blob.
1473 'Q' + # Size of VBMeta blob.
1474 str(RESERVED) + 'x') # padding for reserved bytes
1475
1476 def __init__(self, data=None):
1477 """Initializes a new footer object.
1478
1479 Arguments:
1480 data: If not None, must be a bytearray of size 4096.
1481
1482 Raises:
1483 LookupError: If the given footer is malformed.
1484 struct.error: If the given data has no footer.
1485 """
1486 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1487
1488 if data:
1489 (self.magic, self.version_major, self.version_minor,
1490 self.original_image_size, self.vbmeta_offset,
1491 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1492 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001493 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001494 else:
1495 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001496 self.version_major = self.FOOTER_VERSION_MAJOR
1497 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001498 self.original_image_size = 0
1499 self.vbmeta_offset = 0
1500 self.vbmeta_size = 0
1501
David Zeuthena4fee8b2016-08-22 15:20:43 -04001502 def encode(self):
1503 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001504
David Zeuthena4fee8b2016-08-22 15:20:43 -04001505 Returns:
1506 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001507 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001508 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1509 self.version_minor, self.original_image_size,
1510 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001511
1512
1513class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001514 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001515
1516 Attributes:
1517 The attributes correspond to the |AvbVBMetaHeader| struct
1518 defined in avb_vbmeta_header.h.
1519 """
1520
1521 SIZE = 256
1522
David Zeuthene3cadca2017-02-22 21:25:46 -05001523 # Keep in sync with |reserved0| and |reserved| field of
1524 # |AvbVBMetaImageHeader|.
1525 RESERVED0 = 4
1526 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001527
1528 # Keep in sync with |AvbVBMetaImageHeader|.
1529 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1530 '2Q' # 2 x block size
1531 'L' # algorithm type
1532 '2Q' # offset, size (hash)
1533 '2Q' # offset, size (signature)
1534 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001535 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001536 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001537 'Q' # rollback_index
1538 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001539 str(RESERVED0) + 'x' + # padding for reserved bytes
1540 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001541 str(RESERVED) + 'x') # padding for reserved bytes
1542
1543 def __init__(self, data=None):
1544 """Initializes a new header object.
1545
1546 Arguments:
1547 data: If not None, must be a bytearray of size 8192.
1548
1549 Raises:
1550 Exception: If the given data is malformed.
1551 """
1552 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1553
1554 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001555 (self.magic, self.required_libavb_version_major,
1556 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001557 self.authentication_data_block_size, self.auxiliary_data_block_size,
1558 self.algorithm_type, self.hash_offset, self.hash_size,
1559 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001560 self.public_key_size, self.public_key_metadata_offset,
1561 self.public_key_metadata_size, self.descriptors_offset,
1562 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001563 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001564 self.flags,
1565 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001566 # Nuke NUL-bytes at the end of the string.
1567 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001568 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001569 else:
1570 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001571 # Start by just requiring version 1.0. Code that adds features
1572 # in a future version can use bump_required_libavb_version_minor() to
1573 # bump the minor.
1574 self.required_libavb_version_major = AVB_VERSION_MAJOR
1575 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001576 self.authentication_data_block_size = 0
1577 self.auxiliary_data_block_size = 0
1578 self.algorithm_type = 0
1579 self.hash_offset = 0
1580 self.hash_size = 0
1581 self.signature_offset = 0
1582 self.signature_size = 0
1583 self.public_key_offset = 0
1584 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001585 self.public_key_metadata_offset = 0
1586 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001587 self.descriptors_offset = 0
1588 self.descriptors_size = 0
1589 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001590 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001591 self.release_string = get_release_string()
1592
1593 def bump_required_libavb_version_minor(self, minor):
1594 """Function to bump required_libavb_version_minor.
1595
1596 Call this when writing data that requires a specific libavb
1597 version to parse it.
1598
1599 Arguments:
1600 minor: The minor version of libavb that has support for the feature.
1601 """
1602 self.required_libavb_version_minor = (
1603 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001604
1605 def save(self, output):
1606 """Serializes the header (256 bytes) to disk.
1607
1608 Arguments:
1609 output: The object to write the output to.
1610 """
1611 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001612 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1613 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001614 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1615 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001616 self.public_key_offset, self.public_key_size,
1617 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001618 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001619 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001620
1621 def encode(self):
1622 """Serializes the header (256) to a bytearray().
1623
1624 Returns:
1625 A bytearray() with the encoded header.
1626 """
1627 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001628 self.required_libavb_version_major,
1629 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001630 self.authentication_data_block_size,
1631 self.auxiliary_data_block_size, self.algorithm_type,
1632 self.hash_offset, self.hash_size, self.signature_offset,
1633 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001634 self.public_key_size, self.public_key_metadata_offset,
1635 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001636 self.descriptors_size, self.rollback_index, self.flags,
1637 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001638
1639
1640class Avb(object):
1641 """Business logic for avbtool command-line tool."""
1642
David Zeuthen8b6973b2016-09-20 12:39:49 -04001643 # Keep in sync with avb_ab_flow.h.
1644 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1645 AB_MAGIC = '\0AB0'
1646 AB_MAJOR_VERSION = 1
1647 AB_MINOR_VERSION = 0
1648 AB_MISC_METADATA_OFFSET = 2048
1649
David Zeuthen09692692016-09-30 16:16:40 -04001650 # Constants for maximum metadata size. These are used to give
1651 # meaningful errors if the value passed in via --partition_size is
1652 # too small and when --calc_max_image_size is used. We use
1653 # conservative figures.
1654 MAX_VBMETA_SIZE = 64 * 1024
1655 MAX_FOOTER_SIZE = 4096
1656
David Zeuthena4fee8b2016-08-22 15:20:43 -04001657 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001658 """Implements the 'erase_footer' command.
1659
1660 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001661 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001662 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001663
1664 Raises:
1665 AvbError: If there's no footer in the image.
1666 """
1667
David Zeuthena4fee8b2016-08-22 15:20:43 -04001668 image = ImageHandler(image_filename)
1669
David Zeuthen21e95262016-07-27 17:58:40 -04001670 (footer, _, descriptors, _) = self._parse_image(image)
1671
1672 if not footer:
1673 raise AvbError('Given image does not have a footer.')
1674
1675 new_image_size = None
1676 if not keep_hashtree:
1677 new_image_size = footer.original_image_size
1678 else:
1679 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001680 # descriptor to figure out the location and size of the hashtree
1681 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001682 for desc in descriptors:
1683 if isinstance(desc, AvbHashtreeDescriptor):
1684 # The hashtree is always just following the main data so the
1685 # new size is easily derived.
1686 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001687 # If the image has FEC codes, also keep those.
1688 if desc.fec_offset > 0:
1689 fec_end = desc.fec_offset + desc.fec_size
1690 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001691 break
1692 if not new_image_size:
1693 raise AvbError('Requested to keep hashtree but no hashtree '
1694 'descriptor was found.')
1695
1696 # And cut...
1697 image.truncate(new_image_size)
1698
David Zeuthen8b6973b2016-09-20 12:39:49 -04001699 def set_ab_metadata(self, misc_image, slot_data):
1700 """Implements the 'set_ab_metadata' command.
1701
1702 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1703 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1704
1705 Arguments:
1706 misc_image: The misc image to write to.
1707 slot_data: Slot data as a string
1708
1709 Raises:
1710 AvbError: If slot data is malformed.
1711 """
1712 tokens = slot_data.split(':')
1713 if len(tokens) != 6:
1714 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1715 a_priority = int(tokens[0])
1716 a_tries_remaining = int(tokens[1])
1717 a_success = True if int(tokens[2]) != 0 else False
1718 b_priority = int(tokens[3])
1719 b_tries_remaining = int(tokens[4])
1720 b_success = True if int(tokens[5]) != 0 else False
1721
1722 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1723 self.AB_MAGIC,
1724 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1725 a_priority, a_tries_remaining, a_success,
1726 b_priority, b_tries_remaining, b_success)
1727 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1728 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1729 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1730 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1731 misc_image.write(ab_data)
1732
David Zeuthena4fee8b2016-08-22 15:20:43 -04001733 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001734 """Implements the 'info_image' command.
1735
1736 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001737 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001738 output: Output file to write human-readable information to (file object).
1739 """
1740
David Zeuthena4fee8b2016-08-22 15:20:43 -04001741 image = ImageHandler(image_filename)
1742
David Zeuthen21e95262016-07-27 17:58:40 -04001743 o = output
1744
1745 (footer, header, descriptors, image_size) = self._parse_image(image)
1746
1747 if footer:
1748 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1749 footer.version_minor))
1750 o.write('Image size: {} bytes\n'.format(image_size))
1751 o.write('Original image size: {} bytes\n'.format(
1752 footer.original_image_size))
1753 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1754 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1755 o.write('--\n')
1756
1757 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1758
David Zeuthene3cadca2017-02-22 21:25:46 -05001759 o.write('Minimum libavb version: {}.{}{}\n'.format(
1760 header.required_libavb_version_major,
1761 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001762 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001763 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1764 o.write('Authentication Block: {} bytes\n'.format(
1765 header.authentication_data_block_size))
1766 o.write('Auxiliary Block: {} bytes\n'.format(
1767 header.auxiliary_data_block_size))
1768 o.write('Algorithm: {}\n'.format(alg_name))
1769 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001770 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001771 o.write('Release String: \'{}\'\n'.format(
1772 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001773
1774 # Print descriptors.
1775 num_printed = 0
1776 o.write('Descriptors:\n')
1777 for desc in descriptors:
1778 desc.print_desc(o)
1779 num_printed += 1
1780 if num_printed == 0:
1781 o.write(' (none)\n')
1782
1783 def _parse_image(self, image):
1784 """Gets information about an image.
1785
1786 The image can either be a vbmeta or an image with a footer.
1787
1788 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001789 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001790
1791 Returns:
1792 A tuple where the first argument is a AvbFooter (None if there
1793 is no footer on the image), the second argument is a
1794 AvbVBMetaHeader, the third argument is a list of
1795 AvbDescriptor-derived instances, and the fourth argument is the
1796 size of |image|.
1797 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001798 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001799 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001800 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001801 try:
1802 footer = AvbFooter(image.read(AvbFooter.SIZE))
1803 except (LookupError, struct.error):
1804 # Nope, just seek back to the start.
1805 image.seek(0)
1806
1807 vbmeta_offset = 0
1808 if footer:
1809 vbmeta_offset = footer.vbmeta_offset
1810
1811 image.seek(vbmeta_offset)
1812 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1813
1814 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1815 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1816 desc_start_offset = aux_block_offset + h.descriptors_offset
1817 image.seek(desc_start_offset)
1818 descriptors = parse_descriptors(image.read(h.descriptors_size))
1819
David Zeuthen09692692016-09-30 16:16:40 -04001820 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001821
David Zeuthenfd41eb92016-11-17 12:24:47 -05001822 def _get_cmdline_descriptors_for_dm_verity(self, image):
1823 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001824
1825 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001826 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001827
1828 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001829 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1830 instructions. There is one for when hashtree is not disabled and one for
1831 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001832
1833 Raises:
1834 AvbError: If |image| doesn't have a hashtree descriptor.
1835
1836 """
1837
1838 (_, _, descriptors, _) = self._parse_image(image)
1839
1840 ht = None
1841 for desc in descriptors:
1842 if isinstance(desc, AvbHashtreeDescriptor):
1843 ht = desc
1844 break
1845
1846 if not ht:
1847 raise AvbError('No hashtree descriptor in given image')
1848
1849 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001850 c += '0' # start
1851 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1852 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1853 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1854 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1855 c += ' {}'.format(ht.data_block_size) # data_block
1856 c += ' {}'.format(ht.hash_block_size) # hash_block
1857 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1858 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1859 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1860 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1861 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1862 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001863 c += ' 10' # number of optional args
1864 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001865 c += ' ignore_zero_blocks'
1866 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1867 c += ' fec_roots {}'.format(ht.fec_num_roots)
1868 # Note that fec_blocks is the size that FEC covers, *not* the
1869 # size of the FEC data. Since we use FEC for everything up until
1870 # the FEC data, it's the same as the offset.
1871 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1872 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1873 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001874 c += ' 2' # number of optional args
1875 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001876 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001877 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001878
David Zeuthenfd41eb92016-11-17 12:24:47 -05001879 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001880 desc = AvbKernelCmdlineDescriptor()
1881 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001882 desc.flags = (
1883 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1884
1885 # The descriptor for when hashtree verification is disabled is a lot
1886 # simpler - we just set the root to the partition.
1887 desc_no_ht = AvbKernelCmdlineDescriptor()
1888 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1889 desc_no_ht.flags = (
1890 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1891
1892 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001893
1894 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001895 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001896 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001897 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001898 include_descriptors_from_image, signing_helper,
1899 release_string,
1900 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001901 """Implements the 'make_vbmeta_image' command.
1902
1903 Arguments:
1904 output: File to write the image to.
1905 chain_partitions: List of partitions to chain.
1906 algorithm_name: Name of algorithm to use.
1907 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001908 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001909 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001910 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001911 props: Properties to insert (list of strings of the form 'key:value').
1912 props_from_file: Properties to insert (list of strings 'key:<path>').
1913 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001914 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001915 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001916 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001917 release_string: None or avbtool release string to use instead of default.
1918 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001919
1920 Raises:
1921 AvbError: If a chained partition is malformed.
1922 """
1923
1924 descriptors = []
1925
1926 # Insert chained partition descriptors.
1927 if chain_partitions:
1928 for cp in chain_partitions:
1929 cp_tokens = cp.split(':')
1930 if len(cp_tokens) != 3:
1931 raise AvbError('Malformed chained partition "{}".'.format(cp))
1932 desc = AvbChainPartitionDescriptor()
1933 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001934 desc.rollback_index_location = int(cp_tokens[1])
1935 if desc.rollback_index_location < 1:
1936 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001937 file_path = cp_tokens[2]
1938 desc.public_key = open(file_path, 'rb').read()
1939 descriptors.append(desc)
1940
1941 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001942 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001943 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001944 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001945 include_descriptors_from_image, signing_helper, release_string,
1946 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001947
1948 # Write entire vbmeta blob (header, authentication, auxiliary).
1949 output.seek(0)
1950 output.write(vbmeta_blob)
1951
David Zeuthen18666ab2016-11-15 11:18:05 -05001952 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1953 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001954 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001955 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001956 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001957 include_descriptors_from_image, signing_helper,
1958 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001959 """Generates a VBMeta blob.
1960
1961 This blob contains the header (struct AvbVBMetaHeader), the
1962 authentication data block (which contains the hash and signature
1963 for the header and auxiliary block), and the auxiliary block
1964 (which contains descriptors, the public key used, and other data).
1965
1966 The |key| parameter can |None| only if the |algorithm_name| is
1967 'NONE'.
1968
1969 Arguments:
1970 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1971 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001972 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001973 descriptors: A list of descriptors to insert or None.
1974 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001975 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001976 props: Properties to insert (List of strings of the form 'key:value').
1977 props_from_file: Properties to insert (List of strings 'key:<path>').
1978 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001979 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001980 dm-verity kernel cmdline from.
1981 include_descriptors_from_image: List of file objects for which
1982 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001983 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001984 release_string: None or avbtool release string.
1985 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001986
1987 Returns:
1988 A bytearray() with the VBMeta blob.
1989
1990 Raises:
1991 Exception: If the |algorithm_name| is not found, if no key has
1992 been given and the given algorithm requires one, or the key is
1993 of the wrong size.
1994
1995 """
1996 try:
1997 alg = ALGORITHMS[algorithm_name]
1998 except KeyError:
1999 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2000
2001 # Descriptors.
2002 encoded_descriptors = bytearray()
2003 if descriptors:
2004 for desc in descriptors:
2005 encoded_descriptors.extend(desc.encode())
2006
2007 # Add properties.
2008 if props:
2009 for prop in props:
2010 idx = prop.find(':')
2011 if idx == -1:
2012 raise AvbError('Malformed property "{}".'.format(prop))
2013 desc = AvbPropertyDescriptor()
2014 desc.key = prop[0:idx]
2015 desc.value = prop[(idx + 1):]
2016 encoded_descriptors.extend(desc.encode())
2017 if props_from_file:
2018 for prop in props_from_file:
2019 idx = prop.find(':')
2020 if idx == -1:
2021 raise AvbError('Malformed property "{}".'.format(prop))
2022 desc = AvbPropertyDescriptor()
2023 desc.key = prop[0:idx]
2024 desc.value = prop[(idx + 1):]
2025 file_path = prop[(idx + 1):]
2026 desc.value = open(file_path, 'rb').read()
2027 encoded_descriptors.extend(desc.encode())
2028
2029 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002030 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002031 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002032 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002033 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2034 encoded_descriptors.extend(cmdline_desc[0].encode())
2035 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002036
2037 # Add kernel command-lines.
2038 if kernel_cmdlines:
2039 for i in kernel_cmdlines:
2040 desc = AvbKernelCmdlineDescriptor()
2041 desc.kernel_cmdline = i
2042 encoded_descriptors.extend(desc.encode())
2043
2044 # Add descriptors from other images.
2045 if include_descriptors_from_image:
2046 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002047 image_handler = ImageHandler(image.name)
2048 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002049 for desc in image_descriptors:
2050 encoded_descriptors.extend(desc.encode())
2051
David Zeuthen18666ab2016-11-15 11:18:05 -05002052 # Load public key metadata blob, if requested.
2053 pkmd_blob = []
2054 if public_key_metadata_path:
2055 with open(public_key_metadata_path) as f:
2056 pkmd_blob = f.read()
2057
David Zeuthen21e95262016-07-27 17:58:40 -04002058 key = None
2059 encoded_key = bytearray()
2060 if alg.public_key_num_bytes > 0:
2061 if not key_path:
2062 raise AvbError('Key is required for algorithm {}'.format(
2063 algorithm_name))
2064 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2065 encoded_key = encode_rsa_key(key)
2066 if len(encoded_key) != alg.public_key_num_bytes:
2067 raise AvbError('Key is wrong size for algorithm {}'.format(
2068 algorithm_name))
2069
2070 h = AvbVBMetaHeader()
2071
David Zeuthene3cadca2017-02-22 21:25:46 -05002072 # Override release string, if requested.
2073 if isinstance(release_string, (str, unicode)):
2074 h.release_string = release_string
2075
2076 # Append to release string, if requested. Also insert a space before.
2077 if isinstance(append_to_release_string, (str, unicode)):
2078 h.release_string += ' ' + append_to_release_string
2079
David Zeuthen18666ab2016-11-15 11:18:05 -05002080 # For the Auxiliary data block, descriptors are stored at offset 0,
2081 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002082 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002083 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002084 h.descriptors_offset = 0
2085 h.descriptors_size = len(encoded_descriptors)
2086 h.public_key_offset = h.descriptors_size
2087 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002088 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2089 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002090
2091 # For the Authentication data block, the hash is first and then
2092 # the signature.
2093 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002094 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002095 h.algorithm_type = alg.algorithm_type
2096 h.hash_offset = 0
2097 h.hash_size = alg.hash_num_bytes
2098 # Signature offset and size - it's stored right after the hash
2099 # (in Authentication data block).
2100 h.signature_offset = alg.hash_num_bytes
2101 h.signature_size = alg.signature_num_bytes
2102
2103 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002104 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002105
2106 # Generate Header data block.
2107 header_data_blob = h.encode()
2108
2109 # Generate Auxiliary data block.
2110 aux_data_blob = bytearray()
2111 aux_data_blob.extend(encoded_descriptors)
2112 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002113 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002114 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2115 aux_data_blob.extend('\0' * padding_bytes)
2116
2117 # Calculate the hash.
2118 binary_hash = bytearray()
2119 binary_signature = bytearray()
2120 if algorithm_name != 'NONE':
2121 if algorithm_name[0:6] == 'SHA256':
2122 ha = hashlib.sha256()
2123 elif algorithm_name[0:6] == 'SHA512':
2124 ha = hashlib.sha512()
2125 else:
2126 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2127 ha.update(header_data_blob)
2128 ha.update(aux_data_blob)
2129 binary_hash.extend(ha.digest())
2130
2131 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002132 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002133 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2134 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002135
2136 # Generate Authentication data block.
2137 auth_data_blob = bytearray()
2138 auth_data_blob.extend(binary_hash)
2139 auth_data_blob.extend(binary_signature)
2140 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2141 auth_data_blob.extend('\0' * padding_bytes)
2142
2143 return header_data_blob + auth_data_blob + aux_data_blob
2144
2145 def extract_public_key(self, key_path, output):
2146 """Implements the 'extract_public_key' command.
2147
2148 Arguments:
2149 key_path: The path to a RSA private key file.
2150 output: The file to write to.
2151 """
2152 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2153 write_rsa_key(output, key)
2154
David Zeuthena4fee8b2016-08-22 15:20:43 -04002155 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002156 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002157 public_key_metadata_path, rollback_index, props,
2158 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002159 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002160 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002161 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002162 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002163 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002164
2165 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002166 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002167 partition_size: Size of partition.
2168 partition_name: Name of partition (without A/B suffix).
2169 hash_algorithm: Hash algorithm to use.
2170 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2171 algorithm_name: Name of algorithm to use.
2172 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002173 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002174 rollback_index: Rollback index.
2175 props: Properties to insert (List of strings of the form 'key:value').
2176 props_from_file: Properties to insert (List of strings 'key:<path>').
2177 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002178 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002179 dm-verity kernel cmdline from.
2180 include_descriptors_from_image: List of file objects for which
2181 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002182 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002183 release_string: None or avbtool release string.
2184 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002185 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2186 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002187
2188 Raises:
2189 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002190 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002191 image = ImageHandler(image_filename)
2192
2193 if partition_size % image.block_size != 0:
2194 raise AvbError('Partition size of {} is not a multiple of the image '
2195 'block size {}.'.format(partition_size,
2196 image.block_size))
2197
David Zeuthen21e95262016-07-27 17:58:40 -04002198 # If there's already a footer, truncate the image to its original
2199 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2200 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002201 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002202 try:
2203 footer = AvbFooter(image.read(AvbFooter.SIZE))
2204 # Existing footer found. Just truncate.
2205 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002206 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002207 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002208 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002209
2210 # If anything goes wrong from here-on, restore the image back to
2211 # its original size.
2212 try:
David Zeuthen09692692016-09-30 16:16:40 -04002213 # First, calculate the maximum image size such that an image
2214 # this size + metadata (footer + vbmeta struct) fits in
2215 # |partition_size|.
2216 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2217 max_image_size = partition_size - max_metadata_size
2218
2219 # If image size exceeds the maximum image size, fail.
2220 if image.image_size > max_image_size:
2221 raise AvbError('Image size of {} exceeds maximum image '
2222 'size of {} in order to fit in a partition '
2223 'size of {}.'.format(image.image_size, max_image_size,
2224 partition_size))
2225
David Zeuthen21e95262016-07-27 17:58:40 -04002226 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2227 if salt:
2228 salt = salt.decode('hex')
2229 else:
2230 if salt is None:
2231 # If salt is not explicitly specified, choose a hash
2232 # that's the same size as the hash size.
2233 hash_size = digest_size
2234 salt = open('/dev/urandom').read(hash_size)
2235 else:
2236 salt = ''
2237
2238 hasher = hashlib.new(name=hash_algorithm, string=salt)
2239 # TODO(zeuthen): might want to read this in chunks to avoid
2240 # memory pressure, then again, this is only supposed to be used
2241 # on kernel/initramfs partitions. Possible optimization.
2242 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002243 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002244 digest = hasher.digest()
2245
2246 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002247 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002248 h_desc.hash_algorithm = hash_algorithm
2249 h_desc.partition_name = partition_name
2250 h_desc.salt = salt
2251 h_desc.digest = digest
2252
David Zeuthenfd41eb92016-11-17 12:24:47 -05002253 # Flags are only allowed on top-level vbmeta struct.
2254 flags = 0
2255
David Zeuthen21e95262016-07-27 17:58:40 -04002256 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002257 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002258 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002259 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002260 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002261 include_descriptors_from_image, signing_helper, release_string,
2262 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002263
David Zeuthena4fee8b2016-08-22 15:20:43 -04002264 # If the image isn't sparse, its size might not be a multiple of
2265 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002266 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002267 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002268 padding_needed = image.block_size - (image.image_size%image.block_size)
2269 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002270
David Zeuthena4fee8b2016-08-22 15:20:43 -04002271 # The append_raw() method requires content with size being a
2272 # multiple of |block_size| so add padding as needed. Also record
2273 # where this is written to since we'll need to put that in the
2274 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002275 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002276 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2277 len(vbmeta_blob))
2278 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002279
David Zeuthend247fcb2017-02-16 12:09:27 -05002280 # Write vbmeta blob, if requested.
2281 if output_vbmeta_image:
2282 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002283
David Zeuthend247fcb2017-02-16 12:09:27 -05002284 # Append vbmeta blob and footer, unless requested not to.
2285 if not do_not_append_vbmeta_image:
2286 image.append_raw(vbmeta_blob_with_padding)
2287 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2288
2289 # Now insert a DONT_CARE chunk with enough bytes such that the
2290 # final Footer block is at the end of partition_size..
2291 image.append_dont_care(partition_size - vbmeta_end_offset -
2292 1*image.block_size)
2293
2294 # Generate the Footer that tells where the VBMeta footer
2295 # is. Also put enough padding in the front of the footer since
2296 # we'll write out an entire block.
2297 footer = AvbFooter()
2298 footer.original_image_size = original_image_size
2299 footer.vbmeta_offset = vbmeta_offset
2300 footer.vbmeta_size = len(vbmeta_blob)
2301 footer_blob = footer.encode()
2302 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2303 footer_blob)
2304 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002305
David Zeuthen21e95262016-07-27 17:58:40 -04002306 except:
2307 # Truncate back to original size, then re-raise
2308 image.truncate(original_image_size)
2309 raise
2310
David Zeuthena4fee8b2016-08-22 15:20:43 -04002311 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002312 generate_fec, fec_num_roots, hash_algorithm,
2313 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002314 public_key_metadata_path, rollback_index,
2315 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002316 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002317 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002318 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002319 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002320 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002321 """Implements the 'add_hashtree_footer' command.
2322
2323 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2324 more information about dm-verity and these hashes.
2325
2326 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002327 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002328 partition_size: Size of partition.
2329 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002330 generate_fec: If True, generate FEC codes.
2331 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002332 hash_algorithm: Hash algorithm to use.
2333 block_size: Block size to use.
2334 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2335 algorithm_name: Name of algorithm to use.
2336 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002337 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002338 rollback_index: Rollback index.
2339 props: Properties to insert (List of strings of the form 'key:value').
2340 props_from_file: Properties to insert (List of strings 'key:<path>').
2341 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002342 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002343 dm-verity kernel cmdline from.
2344 include_descriptors_from_image: List of file objects for which
2345 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002346 calc_max_image_size: Don't store the hashtree or footer - instead
2347 calculate the maximum image size leaving enough room for hashtree
2348 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002349 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002350 release_string: None or avbtool release string.
2351 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002352 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2353 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002354
2355 Raises:
2356 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002357 """
David Zeuthen09692692016-09-30 16:16:40 -04002358 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2359 digest_padding = round_to_pow2(digest_size) - digest_size
2360
2361 # First, calculate the maximum image size such that an image
2362 # this size + the hashtree + metadata (footer + vbmeta struct)
2363 # fits in |partition_size|. We use very conservative figures for
2364 # metadata.
2365 (_, max_tree_size) = calc_hash_level_offsets(
2366 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002367 max_fec_size = 0
2368 if generate_fec:
2369 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2370 max_metadata_size = (max_fec_size + max_tree_size +
2371 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002372 self.MAX_FOOTER_SIZE)
2373 max_image_size = partition_size - max_metadata_size
2374
2375 # If we're asked to only calculate the maximum image size, we're done.
2376 if calc_max_image_size:
2377 print '{}'.format(max_image_size)
2378 return
2379
David Zeuthena4fee8b2016-08-22 15:20:43 -04002380 image = ImageHandler(image_filename)
2381
2382 if partition_size % image.block_size != 0:
2383 raise AvbError('Partition size of {} is not a multiple of the image '
2384 'block size {}.'.format(partition_size,
2385 image.block_size))
2386
David Zeuthen21e95262016-07-27 17:58:40 -04002387 # If there's already a footer, truncate the image to its original
2388 # size. This way 'avbtool add_hashtree_footer' is idempotent
2389 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002390 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002391 try:
2392 footer = AvbFooter(image.read(AvbFooter.SIZE))
2393 # Existing footer found. Just truncate.
2394 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002395 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002396 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002397 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002398
2399 # If anything goes wrong from here-on, restore the image back to
2400 # its original size.
2401 try:
2402 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002403 rounded_image_size = round_to_multiple(image.image_size, block_size)
2404 if rounded_image_size > image.image_size:
2405 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002406
David Zeuthen09692692016-09-30 16:16:40 -04002407 # If image size exceeds the maximum image size, fail.
2408 if image.image_size > max_image_size:
2409 raise AvbError('Image size of {} exceeds maximum image '
2410 'size of {} in order to fit in a partition '
2411 'size of {}.'.format(image.image_size, max_image_size,
2412 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002413
2414 if salt:
2415 salt = salt.decode('hex')
2416 else:
2417 if salt is None:
2418 # If salt is not explicitly specified, choose a hash
2419 # that's the same size as the hash size.
2420 hash_size = digest_size
2421 salt = open('/dev/urandom').read(hash_size)
2422 else:
2423 salt = ''
2424
David Zeuthena4fee8b2016-08-22 15:20:43 -04002425 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002426 # offsets in advance.
2427 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002428 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002429
David Zeuthena4fee8b2016-08-22 15:20:43 -04002430 # If the image isn't sparse, its size might not be a multiple of
2431 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002432 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002433 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002434 padding_needed = image.block_size - (image.image_size%image.block_size)
2435 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002436
David Zeuthena4fee8b2016-08-22 15:20:43 -04002437 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002438 tree_offset = image.image_size
2439 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002440 block_size,
2441 hash_algorithm, salt,
2442 digest_padding,
2443 hash_level_offsets,
2444 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002445
2446 # Generate HashtreeDescriptor with details about the tree we
2447 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002448 ht_desc = AvbHashtreeDescriptor()
2449 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002450 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002451 ht_desc.tree_offset = tree_offset
2452 ht_desc.tree_size = tree_size
2453 ht_desc.data_block_size = block_size
2454 ht_desc.hash_block_size = block_size
2455 ht_desc.hash_algorithm = hash_algorithm
2456 ht_desc.partition_name = partition_name
2457 ht_desc.salt = salt
2458 ht_desc.root_digest = root_digest
2459
David Zeuthen09692692016-09-30 16:16:40 -04002460 # Write the hash tree
2461 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2462 len(hash_tree))
2463 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2464 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002465 len_hashtree_and_fec = len(hash_tree_with_padding)
2466
2467 # Generate FEC codes, if requested.
2468 if generate_fec:
2469 fec_data = generate_fec_data(image_filename, fec_num_roots)
2470 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2471 len(fec_data))
2472 fec_data_with_padding = fec_data + '\0'*padding_needed
2473 fec_offset = image.image_size
2474 image.append_raw(fec_data_with_padding)
2475 len_hashtree_and_fec += len(fec_data_with_padding)
2476 # Update the hashtree descriptor.
2477 ht_desc.fec_num_roots = fec_num_roots
2478 ht_desc.fec_offset = fec_offset
2479 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002480
David Zeuthenfd41eb92016-11-17 12:24:47 -05002481 # Flags are only allowed on top-level vbmeta struct.
2482 flags = 0
2483
David Zeuthena4fee8b2016-08-22 15:20:43 -04002484 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002485 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002486 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002487 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002488 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002489 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002490 include_descriptors_from_image, signing_helper, release_string,
2491 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002492 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2493 len(vbmeta_blob))
2494 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002495
David Zeuthend247fcb2017-02-16 12:09:27 -05002496 # Write vbmeta blob, if requested.
2497 if output_vbmeta_image:
2498 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002499
David Zeuthend247fcb2017-02-16 12:09:27 -05002500 # Append vbmeta blob and footer, unless requested not to.
2501 if not do_not_append_vbmeta_image:
2502 image.append_raw(vbmeta_blob_with_padding)
2503
2504 # Now insert a DONT_CARE chunk with enough bytes such that the
2505 # final Footer block is at the end of partition_size..
2506 image.append_dont_care(partition_size - image.image_size -
2507 1*image.block_size)
2508
2509 # Generate the Footer that tells where the VBMeta footer
2510 # is. Also put enough padding in the front of the footer since
2511 # we'll write out an entire block.
2512 footer = AvbFooter()
2513 footer.original_image_size = original_image_size
2514 footer.vbmeta_offset = vbmeta_offset
2515 footer.vbmeta_size = len(vbmeta_blob)
2516 footer_blob = footer.encode()
2517 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2518 footer_blob)
2519 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002520
David Zeuthen21e95262016-07-27 17:58:40 -04002521 except:
David Zeuthen09692692016-09-30 16:16:40 -04002522 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002523 image.truncate(original_image_size)
2524 raise
2525
Darren Krahn147b08d2016-12-20 16:38:29 -08002526 def make_atx_certificate(self, output, authority_key_path, subject_key,
2527 subject_key_version, subject,
2528 is_intermediate_authority, signing_helper):
2529 """Implements the 'make_atx_certificate' command.
2530
2531 Android Things certificates are required for Android Things public key
2532 metadata. They chain the vbmeta signing key for a particular product back to
2533 a fused, permanent root key. These certificates are fixed-length and fixed-
2534 format with the explicit goal of not parsing ASN.1 in bootloader code.
2535
2536 Arguments:
2537 output: Certificate will be written to this file on success.
2538 authority_key_path: A PEM file path with the authority private key.
2539 If None, then a certificate will be created without a
2540 signature. The signature can be created out-of-band
2541 and appended.
2542 subject_key: A PEM or DER subject public key.
2543 subject_key_version: A 64-bit version value. If this is None, the number
2544 of seconds since the epoch is used.
2545 subject: A subject identifier. For Product Signing Key certificates this
2546 should be the same Product ID found in the permanent attributes.
2547 is_intermediate_authority: True if the certificate is for an intermediate
2548 authority.
2549 signing_helper: Program which signs a hash and returns the signature.
2550 """
2551 signed_data = bytearray()
2552 signed_data.extend(struct.pack('<I', 1)) # Format Version
2553 signed_data.extend(
2554 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2555 hasher = hashlib.sha256()
2556 hasher.update(subject)
2557 signed_data.extend(hasher.digest())
2558 usage = 'com.google.android.things.vboot'
2559 if is_intermediate_authority:
2560 usage += '.ca'
2561 hasher = hashlib.sha256()
2562 hasher.update(usage)
2563 signed_data.extend(hasher.digest())
2564 if not subject_key_version:
2565 subject_key_version = int(time.time())
2566 signed_data.extend(struct.pack('<Q', subject_key_version))
2567 signature = bytearray()
2568 if authority_key_path:
2569 padding_and_hash = bytearray()
2570 algorithm_name = None
2571 hasher = None
2572 if is_intermediate_authority:
2573 hasher = hashlib.sha512()
2574 algorithm_name = 'SHA512_RSA4096'
2575 else:
2576 hasher = hashlib.sha256()
2577 algorithm_name = 'SHA256_RSA2048'
2578 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2579 hasher.update(signed_data)
2580 padding_and_hash.extend(hasher.digest())
2581 signature.extend(raw_sign(signing_helper, algorithm_name,
2582 authority_key_path, padding_and_hash))
2583 output.write(signed_data)
2584 output.write(signature)
2585
2586 def make_atx_permanent_attributes(self, output, root_authority_key,
2587 product_id):
2588 """Implements the 'make_atx_permanent_attributes' command.
2589
2590 Android Things permanent attributes are designed to be permanent for a
2591 particular product and a hash of these attributes should be fused into
2592 hardware to enforce this.
2593
2594 Arguments:
2595 output: Attributes will be written to this file on success.
2596 root_authority_key: A PEM or DER public key for the root authority.
2597 product_id: A 16-byte Product ID.
2598
2599 Raises:
2600 AvbError: If an argument is incorrect.
2601 """
2602 if len(product_id) != 16:
2603 raise AvbError('Invalid Product ID length.')
2604 output.write(struct.pack('<I', 1)) # Format Version
2605 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2606 output.write(product_id)
2607
2608 def make_atx_metadata(self, output, intermediate_key_certificate,
2609 product_key_certificate, google_key_version):
2610 """Implements the 'make_atx_metadata' command.
2611
2612 Android Things metadata are included in vbmeta images to facilitate
2613 verification. The output of this command can be used as the
2614 public_key_metadata argument to other commands.
2615
2616 Arguments:
2617 output: Metadata will be written to this file on success.
2618 intermediate_key_certificate: A certificate file as output by
2619 make_atx_certificate with
2620 is_intermediate_authority set to true.
2621 product_key_certificate: A certificate file as output by
2622 make_atx_certificate with
2623 is_intermediate_authority set to false.
2624 google_key_version: The version of the Google Signing Key used in the
2625 associated vbmeta image.
2626
2627 Raises:
2628 AvbError: If an argument is incorrect.
2629 """
2630 if len(intermediate_key_certificate) != 1108:
2631 raise AvbError('Invalid intermediate key certificate length.')
2632 if len(product_key_certificate) != 852:
2633 raise AvbError('Invalid product key certificate length.')
2634 output.write(struct.pack('<I', 1)) # Format Version
2635 output.write(intermediate_key_certificate)
2636 output.write(product_key_certificate)
2637 output.write(struct.pack('<Q', google_key_version))
2638
David Zeuthen21e95262016-07-27 17:58:40 -04002639
2640def calc_hash_level_offsets(image_size, block_size, digest_size):
2641 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2642
2643 Arguments:
2644 image_size: The size of the image to calculate a Merkle-tree for.
2645 block_size: The block size, e.g. 4096.
2646 digest_size: The size of each hash, e.g. 32 for SHA-256.
2647
2648 Returns:
2649 A tuple where the first argument is an array of offsets and the
2650 second is size of the tree, in bytes.
2651 """
2652 level_offsets = []
2653 level_sizes = []
2654 tree_size = 0
2655
2656 num_levels = 0
2657 size = image_size
2658 while size > block_size:
2659 num_blocks = (size + block_size - 1) / block_size
2660 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2661
2662 level_sizes.append(level_size)
2663 tree_size += level_size
2664 num_levels += 1
2665
2666 size = level_size
2667
2668 for n in range(0, num_levels):
2669 offset = 0
2670 for m in range(n + 1, num_levels):
2671 offset += level_sizes[m]
2672 level_offsets.append(offset)
2673
David Zeuthena4fee8b2016-08-22 15:20:43 -04002674 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002675
2676
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002677# See system/extras/libfec/include/fec/io.h for these definitions.
2678FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2679FEC_MAGIC = 0xfecfecfe
2680
2681
2682def calc_fec_data_size(image_size, num_roots):
2683 """Calculates how much space FEC data will take.
2684
2685 Args:
2686 image_size: The size of the image.
2687 num_roots: Number of roots.
2688
2689 Returns:
2690 The number of bytes needed for FEC for an image of the given size
2691 and with the requested number of FEC roots.
2692
2693 Raises:
2694 ValueError: If output from the 'fec' tool is invalid.
2695
2696 """
2697 p = subprocess.Popen(
2698 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2699 stdout=subprocess.PIPE,
2700 stderr=subprocess.PIPE)
2701 (pout, perr) = p.communicate()
2702 retcode = p.wait()
2703 if retcode != 0:
2704 raise ValueError('Error invoking fec: {}'.format(perr))
2705 return int(pout)
2706
2707
2708def generate_fec_data(image_filename, num_roots):
2709 """Generate FEC codes for an image.
2710
2711 Args:
2712 image_filename: The filename of the image.
2713 num_roots: Number of roots.
2714
2715 Returns:
2716 The FEC data blob.
2717
2718 Raises:
2719 ValueError: If output from the 'fec' tool is invalid.
2720 """
2721 fec_tmpfile = tempfile.NamedTemporaryFile()
2722 subprocess.check_call(
2723 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2724 fec_tmpfile.name],
2725 stderr=open(os.devnull))
2726 fec_data = fec_tmpfile.read()
2727 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2728 footer_data = fec_data[-footer_size:]
2729 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2730 footer_data)
2731 if magic != FEC_MAGIC:
2732 raise ValueError('Unexpected magic in FEC footer')
2733 return fec_data[0:fec_size]
2734
2735
David Zeuthen21e95262016-07-27 17:58:40 -04002736def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002737 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002738 """Generates a Merkle-tree for a file.
2739
2740 Args:
2741 image: The image, as a file.
2742 image_size: The size of the image.
2743 block_size: The block size, e.g. 4096.
2744 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2745 salt: The salt to use.
2746 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002747 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002748 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002749
2750 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002751 A tuple where the first element is the top-level hash and the
2752 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002753 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002754 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002755 hash_src_offset = 0
2756 hash_src_size = image_size
2757 level_num = 0
2758 while hash_src_size > block_size:
2759 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002760 remaining = hash_src_size
2761 while remaining > 0:
2762 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002763 # Only read from the file for the first level - for subsequent
2764 # levels, access the array we're building.
2765 if level_num == 0:
2766 image.seek(hash_src_offset + hash_src_size - remaining)
2767 data = image.read(min(remaining, block_size))
2768 else:
2769 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2770 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002771 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002772
2773 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002774 if len(data) < block_size:
2775 hasher.update('\0' * (block_size - len(data)))
2776 level_output += hasher.digest()
2777 if digest_padding > 0:
2778 level_output += '\0' * digest_padding
2779
2780 padding_needed = (round_to_multiple(
2781 len(level_output), block_size) - len(level_output))
2782 level_output += '\0' * padding_needed
2783
David Zeuthena4fee8b2016-08-22 15:20:43 -04002784 # Copy level-output into resulting tree.
2785 offset = hash_level_offsets[level_num]
2786 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002787
David Zeuthena4fee8b2016-08-22 15:20:43 -04002788 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002789 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002790 level_num += 1
2791
2792 hasher = hashlib.new(name=hash_alg_name, string=salt)
2793 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002794 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002795
2796
2797class AvbTool(object):
2798 """Object for avbtool command-line tool."""
2799
2800 def __init__(self):
2801 """Initializer method."""
2802 self.avb = Avb()
2803
2804 def _add_common_args(self, sub_parser):
2805 """Adds arguments used by several sub-commands.
2806
2807 Arguments:
2808 sub_parser: The parser to add arguments to.
2809 """
2810 sub_parser.add_argument('--algorithm',
2811 help='Algorithm to use (default: NONE)',
2812 metavar='ALGORITHM',
2813 default='NONE')
2814 sub_parser.add_argument('--key',
2815 help='Path to RSA private key file',
2816 metavar='KEY',
2817 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002818 sub_parser.add_argument('--signing_helper',
2819 help='Path to helper used for signing',
2820 metavar='APP',
2821 default=None,
2822 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002823 sub_parser.add_argument('--public_key_metadata',
2824 help='Path to public key metadata file',
2825 metavar='KEY_METADATA',
2826 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002827 sub_parser.add_argument('--rollback_index',
2828 help='Rollback Index',
2829 type=parse_number,
2830 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05002831 # This is used internally for unit tests. Do not include in --help output.
2832 sub_parser.add_argument('--internal_release_string',
2833 help=argparse.SUPPRESS)
2834 sub_parser.add_argument('--append_to_release_string',
2835 help='Text to append to release string',
2836 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04002837 sub_parser.add_argument('--prop',
2838 help='Add property',
2839 metavar='KEY:VALUE',
2840 action='append')
2841 sub_parser.add_argument('--prop_from_file',
2842 help='Add property from file',
2843 metavar='KEY:PATH',
2844 action='append')
2845 sub_parser.add_argument('--kernel_cmdline',
2846 help='Add kernel cmdline',
2847 metavar='CMDLINE',
2848 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002849 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2850 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2851 # at some future point.
2852 sub_parser.add_argument('--setup_rootfs_from_kernel',
2853 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002854 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002855 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002856 type=argparse.FileType('rb'))
2857 sub_parser.add_argument('--include_descriptors_from_image',
2858 help='Include descriptors from image',
2859 metavar='IMAGE',
2860 action='append',
2861 type=argparse.FileType('rb'))
2862
2863 def run(self, argv):
2864 """Command-line processor.
2865
2866 Arguments:
2867 argv: Pass sys.argv from main.
2868 """
2869 parser = argparse.ArgumentParser()
2870 subparsers = parser.add_subparsers(title='subcommands')
2871
2872 sub_parser = subparsers.add_parser('version',
2873 help='Prints version of avbtool.')
2874 sub_parser.set_defaults(func=self.version)
2875
2876 sub_parser = subparsers.add_parser('extract_public_key',
2877 help='Extract public key.')
2878 sub_parser.add_argument('--key',
2879 help='Path to RSA private key file',
2880 required=True)
2881 sub_parser.add_argument('--output',
2882 help='Output file name',
2883 type=argparse.FileType('wb'),
2884 required=True)
2885 sub_parser.set_defaults(func=self.extract_public_key)
2886
2887 sub_parser = subparsers.add_parser('make_vbmeta_image',
2888 help='Makes a vbmeta image.')
2889 sub_parser.add_argument('--output',
2890 help='Output file name',
2891 type=argparse.FileType('wb'),
2892 required=True)
2893 self._add_common_args(sub_parser)
2894 sub_parser.add_argument('--chain_partition',
2895 help='Allow signed integrity-data for partition',
2896 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2897 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002898 sub_parser.add_argument('--flags',
2899 help='VBMeta flags',
2900 type=parse_number,
2901 default=0)
David Zeuthen58305522017-01-11 17:42:47 -05002902 sub_parser.add_argument('--set_hashtree_disabled_flag',
2903 help='Set the HASHTREE_DISABLED flag',
2904 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002905 sub_parser.set_defaults(func=self.make_vbmeta_image)
2906
2907 sub_parser = subparsers.add_parser('add_hash_footer',
2908 help='Add hashes and footer to image.')
2909 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002910 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002911 type=argparse.FileType('rab+'))
2912 sub_parser.add_argument('--partition_size',
2913 help='Partition size',
2914 type=parse_number,
2915 required=True)
2916 sub_parser.add_argument('--partition_name',
2917 help='Partition name',
2918 required=True)
2919 sub_parser.add_argument('--hash_algorithm',
2920 help='Hash algorithm to use (default: sha256)',
2921 default='sha256')
2922 sub_parser.add_argument('--salt',
2923 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05002924 sub_parser.add_argument('--output_vbmeta_image',
2925 help='Also write vbmeta struct to file',
2926 type=argparse.FileType('wb'))
2927 sub_parser.add_argument('--do_not_append_vbmeta_image',
2928 help=('Do not append vbmeta struct or footer '
2929 'to the image'),
2930 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002931 self._add_common_args(sub_parser)
2932 sub_parser.set_defaults(func=self.add_hash_footer)
2933
2934 sub_parser = subparsers.add_parser('add_hashtree_footer',
2935 help='Add hashtree and footer to image.')
2936 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002937 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002938 type=argparse.FileType('rab+'))
2939 sub_parser.add_argument('--partition_size',
2940 help='Partition size',
2941 type=parse_number,
2942 required=True)
2943 sub_parser.add_argument('--partition_name',
2944 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002945 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002946 sub_parser.add_argument('--hash_algorithm',
2947 help='Hash algorithm to use (default: sha1)',
2948 default='sha1')
2949 sub_parser.add_argument('--salt',
2950 help='Salt in hex (default: /dev/urandom)')
2951 sub_parser.add_argument('--block_size',
2952 help='Block size (default: 4096)',
2953 type=parse_number,
2954 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002955 sub_parser.add_argument('--generate_fec',
2956 help='Add forward-error-correction codes',
2957 action='store_true')
2958 sub_parser.add_argument('--fec_num_roots',
2959 help='Number of roots for FEC (default: 2)',
2960 type=parse_number,
2961 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002962 sub_parser.add_argument('--calc_max_image_size',
2963 help=('Don\'t store the hashtree or footer - '
2964 'instead calculate the maximum image size '
2965 'leaving enough room for hashtree '
2966 'and metadata with the given partition '
2967 'size.'),
2968 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05002969 sub_parser.add_argument('--output_vbmeta_image',
2970 help='Also write vbmeta struct to file',
2971 type=argparse.FileType('wb'))
2972 sub_parser.add_argument('--do_not_append_vbmeta_image',
2973 help=('Do not append vbmeta struct or footer '
2974 'to the image'),
2975 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002976 self._add_common_args(sub_parser)
2977 sub_parser.set_defaults(func=self.add_hashtree_footer)
2978
2979 sub_parser = subparsers.add_parser('erase_footer',
2980 help='Erase footer from an image.')
2981 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002982 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002983 type=argparse.FileType('rwb+'),
2984 required=True)
2985 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002986 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04002987 action='store_true')
2988 sub_parser.set_defaults(func=self.erase_footer)
2989
2990 sub_parser = subparsers.add_parser(
2991 'info_image',
2992 help='Show information about vbmeta or footer.')
2993 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002994 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002995 type=argparse.FileType('rb'),
2996 required=True)
2997 sub_parser.add_argument('--output',
2998 help='Write info to file',
2999 type=argparse.FileType('wt'),
3000 default=sys.stdout)
3001 sub_parser.set_defaults(func=self.info_image)
3002
David Zeuthen8b6973b2016-09-20 12:39:49 -04003003 sub_parser = subparsers.add_parser('set_ab_metadata',
3004 help='Set A/B metadata.')
3005 sub_parser.add_argument('--misc_image',
3006 help=('The misc image to modify. If the image does '
3007 'not exist, it will be created.'),
3008 type=argparse.FileType('r+b'),
3009 required=True)
3010 sub_parser.add_argument('--slot_data',
3011 help=('Slot data of the form "priority", '
3012 '"tries_remaining", "sucessful_boot" for '
3013 'slot A followed by the same for slot B, '
3014 'separated by colons. The default value '
3015 'is 15:7:0:14:7:0.'),
3016 default='15:7:0:14:7:0')
3017 sub_parser.set_defaults(func=self.set_ab_metadata)
3018
Darren Krahn147b08d2016-12-20 16:38:29 -08003019 sub_parser = subparsers.add_parser(
3020 'make_atx_certificate',
3021 help='Create an Android Things eXtension (ATX) certificate.')
3022 sub_parser.add_argument('--output',
3023 help='Write certificate to file',
3024 type=argparse.FileType('wb'),
3025 default=sys.stdout)
3026 sub_parser.add_argument('--subject',
3027 help=('Path to subject file'),
3028 type=argparse.FileType('rb'),
3029 required=True)
3030 sub_parser.add_argument('--subject_key',
3031 help=('Path to subject RSA public key file'),
3032 type=argparse.FileType('rb'),
3033 required=True)
3034 sub_parser.add_argument('--subject_key_version',
3035 help=('Version of the subject key'),
3036 type=parse_number,
3037 required=False)
3038 sub_parser.add_argument('--subject_is_intermediate_authority',
3039 help=('Generate an intermediate authority '
3040 'certificate'),
3041 action='store_true')
3042 sub_parser.add_argument('--authority_key',
3043 help='Path to authority RSA private key file',
3044 required=False)
3045 sub_parser.add_argument('--signing_helper',
3046 help='Path to helper used for signing',
3047 metavar='APP',
3048 default=None,
3049 required=False)
3050 sub_parser.set_defaults(func=self.make_atx_certificate)
3051
3052 sub_parser = subparsers.add_parser(
3053 'make_atx_permanent_attributes',
3054 help='Create Android Things eXtension (ATX) permanent attributes.')
3055 sub_parser.add_argument('--output',
3056 help='Write attributes to file',
3057 type=argparse.FileType('wb'),
3058 default=sys.stdout)
3059 sub_parser.add_argument('--root_authority_key',
3060 help='Path to authority RSA public key file',
3061 type=argparse.FileType('rb'),
3062 required=True)
3063 sub_parser.add_argument('--product_id',
3064 help=('Path to Product ID file'),
3065 type=argparse.FileType('rb'),
3066 required=True)
3067 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3068
3069 sub_parser = subparsers.add_parser(
3070 'make_atx_metadata',
3071 help='Create Android Things eXtension (ATX) metadata.')
3072 sub_parser.add_argument('--output',
3073 help='Write metadata to file',
3074 type=argparse.FileType('wb'),
3075 default=sys.stdout)
3076 sub_parser.add_argument('--intermediate_key_certificate',
3077 help='Path to intermediate key certificate file',
3078 type=argparse.FileType('rb'),
3079 required=True)
3080 sub_parser.add_argument('--product_key_certificate',
3081 help='Path to product key certificate file',
3082 type=argparse.FileType('rb'),
3083 required=True)
3084 sub_parser.add_argument('--google_key_version',
3085 help=('Version of the Google signing key'),
3086 type=parse_number,
3087 default=0)
3088 sub_parser.set_defaults(func=self.make_atx_metadata)
3089
David Zeuthen21e95262016-07-27 17:58:40 -04003090 args = parser.parse_args(argv[1:])
3091 try:
3092 args.func(args)
3093 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003094 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003095 sys.exit(1)
3096
3097 def version(self, _):
3098 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003099 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003100
3101 def extract_public_key(self, args):
3102 """Implements the 'extract_public_key' sub-command."""
3103 self.avb.extract_public_key(args.key, args.output)
3104
3105 def make_vbmeta_image(self, args):
3106 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthen58305522017-01-11 17:42:47 -05003107 if args.set_hashtree_disabled_flag:
3108 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
David Zeuthen21e95262016-07-27 17:58:40 -04003109 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003110 args.algorithm, args.key,
3111 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003112 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003113 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003114 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003115 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003116 args.signing_helper,
3117 args.internal_release_string,
3118 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003119
3120 def add_hash_footer(self, args):
3121 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003122 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003123 args.partition_name, args.hash_algorithm,
3124 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003125 args.public_key_metadata, args.rollback_index,
3126 args.prop, args.prop_from_file,
3127 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003128 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003129 args.include_descriptors_from_image,
3130 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003131 args.internal_release_string,
3132 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003133 args.output_vbmeta_image,
3134 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003135
3136 def add_hashtree_footer(self, args):
3137 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04003138 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3139 args.partition_size,
3140 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003141 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003142 args.hash_algorithm, args.block_size,
3143 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003144 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04003145 args.rollback_index, args.prop,
3146 args.prop_from_file,
3147 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003148 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003149 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003150 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003151 args.internal_release_string,
3152 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003153 args.output_vbmeta_image,
3154 args.do_not_append_vbmeta_image)
3155
David Zeuthen21e95262016-07-27 17:58:40 -04003156 def erase_footer(self, args):
3157 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003158 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003159
David Zeuthen8b6973b2016-09-20 12:39:49 -04003160 def set_ab_metadata(self, args):
3161 """Implements the 'set_ab_metadata' sub-command."""
3162 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3163
David Zeuthen21e95262016-07-27 17:58:40 -04003164 def info_image(self, args):
3165 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003166 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003167
Darren Krahn147b08d2016-12-20 16:38:29 -08003168 def make_atx_certificate(self, args):
3169 """Implements the 'make_atx_certificate' sub-command."""
3170 self.avb.make_atx_certificate(args.output, args.authority_key,
3171 args.subject_key.read(),
3172 args.subject_key_version,
3173 args.subject.read(),
3174 args.subject_is_intermediate_authority,
3175 args.signing_helper)
3176
3177 def make_atx_permanent_attributes(self, args):
3178 """Implements the 'make_atx_permanent_attributes' sub-command."""
3179 self.avb.make_atx_permanent_attributes(args.output,
3180 args.root_authority_key.read(),
3181 args.product_id.read())
3182
3183 def make_atx_metadata(self, args):
3184 """Implements the 'make_atx_metadata' sub-command."""
3185 self.avb.make_atx_metadata(args.output,
3186 args.intermediate_key_certificate.read(),
3187 args.product_key_certificate.read(),
3188 args.google_key_version)
3189
David Zeuthen21e95262016-07-27 17:58:40 -04003190
3191if __name__ == '__main__':
3192 tool = AvbTool()
3193 tool.run(sys.argv)