blob: 10ff39ad4543b04005fa3ee95d7d0595ff3521da [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
David Zeuthenc68f0822017-03-31 17:22:35 -040031import math
David Zeuthen21e95262016-07-27 17:58:40 -040032import os
33import struct
34import subprocess
35import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040036import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080037import time
David Zeuthen21e95262016-07-27 17:58:40 -040038
David Zeuthene3cadca2017-02-22 21:25:46 -050039# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040040AVB_VERSION_MAJOR = 1
41AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
David Zeuthen58305522017-01-11 17:42:47 -050044AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040045
David Zeuthene3cadca2017-02-22 21:25:46 -050046
David Zeuthen21e95262016-07-27 17:58:40 -040047class AvbError(Exception):
48 """Application-specific errors.
49
50 These errors represent issues for which a stack-trace should not be
51 presented.
52
53 Attributes:
54 message: Error message.
55 """
56
57 def __init__(self, message):
58 Exception.__init__(self, message)
59
60
61class Algorithm(object):
62 """Contains details about an algorithm.
63
64 See the avb_vbmeta_header.h file for more details about
65 algorithms.
66
67 The constant |ALGORITHMS| is a dictionary from human-readable
68 names (e.g 'SHA256_RSA2048') to instances of this class.
69
70 Attributes:
71 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
72 hash_num_bytes: Number of bytes used to store the hash.
73 signature_num_bytes: Number of bytes used to store the signature.
74 public_key_num_bytes: Number of bytes used to store the public key.
75 padding: Padding used for signature, if any.
76 """
77
78 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
79 public_key_num_bytes, padding):
80 self.algorithm_type = algorithm_type
81 self.hash_num_bytes = hash_num_bytes
82 self.signature_num_bytes = signature_num_bytes
83 self.public_key_num_bytes = public_key_num_bytes
84 self.padding = padding
85
86# This must be kept in sync with the avb_crypto.h file.
87#
88# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
89# obtained from section 5.2.2 of RFC 4880.
90ALGORITHMS = {
91 'NONE': Algorithm(
92 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
93 hash_num_bytes=0,
94 signature_num_bytes=0,
95 public_key_num_bytes=0,
96 padding=[]),
97 'SHA256_RSA2048': Algorithm(
98 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
99 hash_num_bytes=32,
100 signature_num_bytes=256,
101 public_key_num_bytes=8 + 2*2048/8,
102 padding=[
103 # PKCS1-v1_5 padding
104 0x00, 0x01] + [0xff]*202 + [0x00] + [
105 # ASN.1 header
106 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
107 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
108 0x00, 0x04, 0x20,
109 ]),
110 'SHA256_RSA4096': Algorithm(
111 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
112 hash_num_bytes=32,
113 signature_num_bytes=512,
114 public_key_num_bytes=8 + 2*4096/8,
115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*458 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA8192': Algorithm(
124 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
125 hash_num_bytes=32,
126 signature_num_bytes=1024,
127 public_key_num_bytes=8 + 2*8192/8,
128 padding=[
129 # PKCS1-v1_5 padding
130 0x00, 0x01] + [0xff]*970 + [0x00] + [
131 # ASN.1 header
132 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
133 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
134 0x00, 0x04, 0x20,
135 ]),
136 'SHA512_RSA2048': Algorithm(
137 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
138 hash_num_bytes=64,
139 signature_num_bytes=256,
140 public_key_num_bytes=8 + 2*2048/8,
141 padding=[
142 # PKCS1-v1_5 padding
143 0x00, 0x01] + [0xff]*170 + [0x00] + [
144 # ASN.1 header
145 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
146 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
147 0x00, 0x04, 0x40
148 ]),
149 'SHA512_RSA4096': Algorithm(
150 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
151 hash_num_bytes=64,
152 signature_num_bytes=512,
153 public_key_num_bytes=8 + 2*4096/8,
154 padding=[
155 # PKCS1-v1_5 padding
156 0x00, 0x01] + [0xff]*426 + [0x00] + [
157 # ASN.1 header
158 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
159 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
160 0x00, 0x04, 0x40
161 ]),
162 'SHA512_RSA8192': Algorithm(
163 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
164 hash_num_bytes=64,
165 signature_num_bytes=1024,
166 public_key_num_bytes=8 + 2*8192/8,
167 padding=[
168 # PKCS1-v1_5 padding
169 0x00, 0x01] + [0xff]*938 + [0x00] + [
170 # ASN.1 header
171 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
172 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
173 0x00, 0x04, 0x40
174 ]),
175}
176
177
David Zeuthene3cadca2017-02-22 21:25:46 -0500178def get_release_string():
179 """Calculates the release string to use in the VBMeta struct."""
180 # Keep in sync with libavb/avb_version.c:avb_version_string().
181 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
182 AVB_VERSION_MINOR,
183 AVB_VERSION_SUB)
184
185
David Zeuthen21e95262016-07-27 17:58:40 -0400186def round_to_multiple(number, size):
187 """Rounds a number up to nearest multiple of another number.
188
189 Args:
190 number: The number to round up.
191 size: The multiple to round up to.
192
193 Returns:
194 If |number| is a multiple of |size|, returns |number|, otherwise
195 returns |number| + |size|.
196 """
197 remainder = number % size
198 if remainder == 0:
199 return number
200 return number + size - remainder
201
202
203def round_to_pow2(number):
204 """Rounds a number up to the next power of 2.
205
206 Args:
207 number: The number to round up.
208
209 Returns:
210 If |number| is already a power of 2 then |number| is
211 returned. Otherwise the smallest power of 2 greater than |number|
212 is returned.
213 """
214 return 2**((number - 1).bit_length())
215
216
David Zeuthen21e95262016-07-27 17:58:40 -0400217def encode_long(num_bits, value):
218 """Encodes a long to a bytearray() using a given amount of bits.
219
220 This number is written big-endian, e.g. with the most significant
221 bit first.
222
223 Arguments:
224 num_bits: The number of bits to write, e.g. 2048.
225 value: The value to write.
226
227 Returns:
228 A bytearray() with the encoded long.
229 """
230 ret = bytearray()
231 for bit_pos in range(num_bits, 0, -8):
232 octet = (value >> (bit_pos - 8)) & 0xff
233 ret.extend(struct.pack('!B', octet))
234 return ret
235
236
237def egcd(a, b):
238 """Calculate greatest common divisor of two numbers.
239
240 This implementation uses a recursive version of the extended
241 Euclidian algorithm.
242
243 Arguments:
244 a: First number.
245 b: Second number.
246
247 Returns:
248 A tuple (gcd, x, y) that where |gcd| is the greatest common
249 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
250 """
251 if a == 0:
252 return (b, 0, 1)
253 else:
254 g, y, x = egcd(b % a, a)
255 return (g, x - (b // a) * y, y)
256
257
258def modinv(a, m):
259 """Calculate modular multiplicative inverse of |a| modulo |m|.
260
261 This calculates the number |x| such that |a| * |x| == 1 (modulo
262 |m|). This number only exists if |a| and |m| are co-prime - |None|
263 is returned if this isn't true.
264
265 Arguments:
266 a: The number to calculate a modular inverse of.
267 m: The modulo to use.
268
269 Returns:
270 The modular multiplicative inverse of |a| and |m| or |None| if
271 these numbers are not co-prime.
272 """
273 gcd, x, _ = egcd(a, m)
274 if gcd != 1:
275 return None # modular inverse does not exist
276 else:
277 return x % m
278
279
280def parse_number(string):
281 """Parse a string as a number.
282
283 This is just a short-hand for int(string, 0) suitable for use in the
284 |type| parameter of |ArgumentParser|'s add_argument() function. An
285 improvement to just using type=int is that this function supports
286 numbers in other bases, e.g. "0x1234".
287
288 Arguments:
289 string: The string to parse.
290
291 Returns:
292 The parsed integer.
293
294 Raises:
295 ValueError: If the number could not be parsed.
296 """
297 return int(string, 0)
298
299
David Zeuthenc68f0822017-03-31 17:22:35 -0400300class RSAPublicKey(object):
301 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400302
David Zeuthenc68f0822017-03-31 17:22:35 -0400303 Attributes:
304 exponent: The key exponent.
305 modulus: The key modulus.
306 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400307 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400308
309 MODULUS_PREFIX = 'modulus='
310
311 def __init__(self, key_path):
312 """Loads and parses an RSA key from either a private or public key file.
313
314 Arguments:
315 key_path: The path to a key file.
316 """
317 # We used to have something as simple as this:
318 #
319 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
320 # self.exponent = key.e
321 # self.modulus = key.n
322 # self.num_bits = key.size() + 1
323 #
324 # but unfortunately PyCrypto is not available in the builder. So
325 # instead just parse openssl(1) output to get this
326 # information. It's ugly but...
327 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
328 p = subprocess.Popen(args,
329 stdin=subprocess.PIPE,
330 stdout=subprocess.PIPE,
331 stderr=subprocess.PIPE)
332 (pout, perr) = p.communicate()
333 if p.wait() != 0:
334 # Could be just a public key is passed, try that.
335 args.append('-pubin')
336 p = subprocess.Popen(args,
337 stdin=subprocess.PIPE,
338 stdout=subprocess.PIPE,
339 stderr=subprocess.PIPE)
340 (pout, perr) = p.communicate()
341 if p.wait() != 0:
342 raise AvbError('Error getting public key: {}'.format(perr))
343
344 if not pout.lower().startswith(self.MODULUS_PREFIX):
345 raise AvbError('Unexpected modulus output')
346
347 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
348
349 # The exponent is assumed to always be 65537 and the number of
350 # bits can be derived from the modulus by rounding up to the
351 # nearest power of 2.
352 self.modulus = int(modulus_hexstr, 16)
353 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
354 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400355
356
David Zeuthenc68f0822017-03-31 17:22:35 -0400357def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400358 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
359
360 This creates a |AvbRSAPublicKeyHeader| as well as the two large
361 numbers (|key_num_bits| bits long) following it.
362
363 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400364 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400365
366 Returns:
367 A bytearray() with the |AvbRSAPublicKeyHeader|.
368 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400369 key = RSAPublicKey(key_path)
370 if key.exponent != 65537:
371 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400372 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400373 # Calculate n0inv = -1/n[0] (mod 2^32)
374 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400375 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400376 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400377 r = 2L**key.modulus.bit_length()
378 rrmodn = r * r % key.modulus
379 ret.extend(struct.pack('!II', key.num_bits, n0inv))
380 ret.extend(encode_long(key.num_bits, key.modulus))
381 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400382 return ret
383
384
385def lookup_algorithm_by_type(alg_type):
386 """Looks up algorithm by type.
387
388 Arguments:
389 alg_type: The integer representing the type.
390
391 Returns:
392 A tuple with the algorithm name and an |Algorithm| instance.
393
394 Raises:
395 Exception: If the algorithm cannot be found
396 """
397 for alg_name in ALGORITHMS:
398 alg_data = ALGORITHMS[alg_name]
399 if alg_data.algorithm_type == alg_type:
400 return (alg_name, alg_data)
401 raise AvbError('Unknown algorithm type {}'.format(alg_type))
402
403
Esun Kimff44f232017-03-30 10:34:54 +0900404def raw_sign(signing_helper, algorithm_name, signature_num_bytes, key_path,
405 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800406 """Computes a raw RSA signature using |signing_helper| or openssl.
407
408 Arguments:
409 signing_helper: Program which signs a hash and returns the signature.
410 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900411 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800412 key_path: Path to the private key file. Must be PEM format.
413 raw_data_to_sign: Data to sign (bytearray or str expected).
414
415 Returns:
416 A bytearray containing the signature.
417
418 Raises:
419 Exception: If an error occurs.
420 """
421 p = None
422 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500423 p = subprocess.Popen(
424 [signing_helper, algorithm_name, key_path],
425 stdin=subprocess.PIPE,
426 stdout=subprocess.PIPE,
427 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800428 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500429 p = subprocess.Popen(
430 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
431 stdin=subprocess.PIPE,
432 stdout=subprocess.PIPE,
433 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800434 (pout, perr) = p.communicate(str(raw_data_to_sign))
435 retcode = p.wait()
436 if retcode != 0:
437 raise AvbError('Error signing: {}'.format(perr))
Esun Kimff44f232017-03-30 10:34:54 +0900438 signature = bytearray(pout)
439 if len(signature) != signature_num_bytes:
440 raise AvbError('Error signing: Invalid length of signature')
441 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800442
443
David Zeuthena4fee8b2016-08-22 15:20:43 -0400444class ImageChunk(object):
445 """Data structure used for representing chunks in Android sparse files.
446
447 Attributes:
448 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
449 chunk_offset: Offset in the sparse file where this chunk begins.
450 output_offset: Offset in de-sparsified file where output begins.
451 output_size: Number of bytes in output.
452 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
453 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
454 """
455
456 FORMAT = '<2H2I'
457 TYPE_RAW = 0xcac1
458 TYPE_FILL = 0xcac2
459 TYPE_DONT_CARE = 0xcac3
460 TYPE_CRC32 = 0xcac4
461
462 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
463 input_offset, fill_data):
464 """Initializes an ImageChunk object.
465
466 Arguments:
467 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
468 chunk_offset: Offset in the sparse file where this chunk begins.
469 output_offset: Offset in de-sparsified file.
470 output_size: Number of bytes in output.
471 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
472 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
473
474 Raises:
475 ValueError: If data is not well-formed.
476 """
477 self.chunk_type = chunk_type
478 self.chunk_offset = chunk_offset
479 self.output_offset = output_offset
480 self.output_size = output_size
481 self.input_offset = input_offset
482 self.fill_data = fill_data
483 # Check invariants.
484 if self.chunk_type == self.TYPE_RAW:
485 if self.fill_data is not None:
486 raise ValueError('RAW chunk cannot have fill_data set.')
487 if not self.input_offset:
488 raise ValueError('RAW chunk must have input_offset set.')
489 elif self.chunk_type == self.TYPE_FILL:
490 if self.fill_data is None:
491 raise ValueError('FILL chunk must have fill_data set.')
492 if self.input_offset:
493 raise ValueError('FILL chunk cannot have input_offset set.')
494 elif self.chunk_type == self.TYPE_DONT_CARE:
495 if self.fill_data is not None:
496 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
497 if self.input_offset:
498 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
499 else:
500 raise ValueError('Invalid chunk type')
501
502
503class ImageHandler(object):
504 """Abstraction for image I/O with support for Android sparse images.
505
506 This class provides an interface for working with image files that
507 may be using the Android Sparse Image format. When an instance is
508 constructed, we test whether it's an Android sparse file. If so,
509 operations will be on the sparse file by interpreting the sparse
510 format, otherwise they will be directly on the file. Either way the
511 operations do the same.
512
513 For reading, this interface mimics a file object - it has seek(),
514 tell(), and read() methods. For writing, only truncation
515 (truncate()) and appending is supported (append_raw() and
516 append_dont_care()). Additionally, data can only be written in units
517 of the block size.
518
519 Attributes:
520 is_sparse: Whether the file being operated on is sparse.
521 block_size: The block size, typically 4096.
522 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400523 """
524 # See system/core/libsparse/sparse_format.h for details.
525 MAGIC = 0xed26ff3a
526 HEADER_FORMAT = '<I4H4I'
527
528 # These are formats and offset of just the |total_chunks| and
529 # |total_blocks| fields.
530 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
531 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
532
533 def __init__(self, image_filename):
534 """Initializes an image handler.
535
536 Arguments:
537 image_filename: The name of the file to operate on.
538
539 Raises:
540 ValueError: If data in the file is invalid.
541 """
542 self._image_filename = image_filename
543 self._read_header()
544
545 def _read_header(self):
546 """Initializes internal data structures used for reading file.
547
548 This may be called multiple times and is typically called after
549 modifying the file (e.g. appending, truncation).
550
551 Raises:
552 ValueError: If data in the file is invalid.
553 """
554 self.is_sparse = False
555 self.block_size = 4096
556 self._file_pos = 0
557 self._image = open(self._image_filename, 'r+b')
558 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400559 self.image_size = self._image.tell()
560
561 self._image.seek(0, os.SEEK_SET)
562 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
563 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
564 block_size, self._num_total_blocks, self._num_total_chunks,
565 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
566 if magic != self.MAGIC:
567 # Not a sparse image, our job here is done.
568 return
569 if not (major_version == 1 and minor_version == 0):
570 raise ValueError('Encountered sparse image format version {}.{} but '
571 'only 1.0 is supported'.format(major_version,
572 minor_version))
573 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
574 raise ValueError('Unexpected file_hdr_sz value {}.'.
575 format(file_hdr_sz))
576 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
577 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
578 format(chunk_hdr_sz))
579
580 self.block_size = block_size
581
582 # Build an list of chunks by parsing the file.
583 self._chunks = []
584
585 # Find the smallest offset where only "Don't care" chunks
586 # follow. This will be the size of the content in the sparse
587 # image.
588 offset = 0
589 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400590 for _ in xrange(1, self._num_total_chunks + 1):
591 chunk_offset = self._image.tell()
592
593 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
594 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
595 header_bin)
596 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
597
David Zeuthena4fee8b2016-08-22 15:20:43 -0400598 if chunk_type == ImageChunk.TYPE_RAW:
599 if data_sz != (chunk_sz * self.block_size):
600 raise ValueError('Raw chunk input size ({}) does not match output '
601 'size ({})'.
602 format(data_sz, chunk_sz*self.block_size))
603 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
604 chunk_offset,
605 output_offset,
606 chunk_sz*self.block_size,
607 self._image.tell(),
608 None))
609 self._image.read(data_sz)
610
611 elif chunk_type == ImageChunk.TYPE_FILL:
612 if data_sz != 4:
613 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
614 'has {}'.format(data_sz))
615 fill_data = self._image.read(4)
616 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
617 chunk_offset,
618 output_offset,
619 chunk_sz*self.block_size,
620 None,
621 fill_data))
622 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
623 if data_sz != 0:
624 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
625 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400626 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
627 chunk_offset,
628 output_offset,
629 chunk_sz*self.block_size,
630 None,
631 None))
632 elif chunk_type == ImageChunk.TYPE_CRC32:
633 if data_sz != 4:
634 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
635 'this has {}'.format(data_sz))
636 self._image.read(4)
637 else:
638 raise ValueError('Unknown chunk type {}'.format(chunk_type))
639
640 offset += chunk_sz
641 output_offset += chunk_sz*self.block_size
642
643 # Record where sparse data end.
644 self._sparse_end = self._image.tell()
645
646 # Now that we've traversed all chunks, sanity check.
647 if self._num_total_blocks != offset:
648 raise ValueError('The header said we should have {} output blocks, '
649 'but we saw {}'.format(self._num_total_blocks, offset))
650 junk_len = len(self._image.read())
651 if junk_len > 0:
652 raise ValueError('There were {} bytes of extra data at the end of the '
653 'file.'.format(junk_len))
654
David Zeuthen09692692016-09-30 16:16:40 -0400655 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400656 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400657
658 # This is used when bisecting in read() to find the initial slice.
659 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
660
661 self.is_sparse = True
662
663 def _update_chunks_and_blocks(self):
664 """Helper function to update the image header.
665
666 The the |total_chunks| and |total_blocks| fields in the header
667 will be set to value of the |_num_total_blocks| and
668 |_num_total_chunks| attributes.
669
670 """
671 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
672 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
673 self._num_total_blocks,
674 self._num_total_chunks))
675
676 def append_dont_care(self, num_bytes):
677 """Appends a DONT_CARE chunk to the sparse file.
678
679 The given number of bytes must be a multiple of the block size.
680
681 Arguments:
682 num_bytes: Size in number of bytes of the DONT_CARE chunk.
683 """
684 assert num_bytes % self.block_size == 0
685
686 if not self.is_sparse:
687 self._image.seek(0, os.SEEK_END)
688 # This is more efficient that writing NUL bytes since it'll add
689 # a hole on file systems that support sparse files (native
690 # sparse, not Android sparse).
691 self._image.truncate(self._image.tell() + num_bytes)
692 self._read_header()
693 return
694
695 self._num_total_chunks += 1
696 self._num_total_blocks += num_bytes / self.block_size
697 self._update_chunks_and_blocks()
698
699 self._image.seek(self._sparse_end, os.SEEK_SET)
700 self._image.write(struct.pack(ImageChunk.FORMAT,
701 ImageChunk.TYPE_DONT_CARE,
702 0, # Reserved
703 num_bytes / self.block_size,
704 struct.calcsize(ImageChunk.FORMAT)))
705 self._read_header()
706
707 def append_raw(self, data):
708 """Appends a RAW chunk to the sparse file.
709
710 The length of the given data must be a multiple of the block size.
711
712 Arguments:
713 data: Data to append.
714 """
715 assert len(data) % self.block_size == 0
716
717 if not self.is_sparse:
718 self._image.seek(0, os.SEEK_END)
719 self._image.write(data)
720 self._read_header()
721 return
722
723 self._num_total_chunks += 1
724 self._num_total_blocks += len(data) / self.block_size
725 self._update_chunks_and_blocks()
726
727 self._image.seek(self._sparse_end, os.SEEK_SET)
728 self._image.write(struct.pack(ImageChunk.FORMAT,
729 ImageChunk.TYPE_RAW,
730 0, # Reserved
731 len(data) / self.block_size,
732 len(data) +
733 struct.calcsize(ImageChunk.FORMAT)))
734 self._image.write(data)
735 self._read_header()
736
737 def append_fill(self, fill_data, size):
738 """Appends a fill chunk to the sparse file.
739
740 The total length of the fill data must be a multiple of the block size.
741
742 Arguments:
743 fill_data: Fill data to append - must be four bytes.
744 size: Number of chunk - must be a multiple of four and the block size.
745 """
746 assert len(fill_data) == 4
747 assert size % 4 == 0
748 assert size % self.block_size == 0
749
750 if not self.is_sparse:
751 self._image.seek(0, os.SEEK_END)
752 self._image.write(fill_data * (size/4))
753 self._read_header()
754 return
755
756 self._num_total_chunks += 1
757 self._num_total_blocks += size / self.block_size
758 self._update_chunks_and_blocks()
759
760 self._image.seek(self._sparse_end, os.SEEK_SET)
761 self._image.write(struct.pack(ImageChunk.FORMAT,
762 ImageChunk.TYPE_FILL,
763 0, # Reserved
764 size / self.block_size,
765 4 + struct.calcsize(ImageChunk.FORMAT)))
766 self._image.write(fill_data)
767 self._read_header()
768
769 def seek(self, offset):
770 """Sets the cursor position for reading from unsparsified file.
771
772 Arguments:
773 offset: Offset to seek to from the beginning of the file.
774 """
775 self._file_pos = offset
776
777 def read(self, size):
778 """Reads data from the unsparsified file.
779
780 This method may return fewer than |size| bytes of data if the end
781 of the file was encountered.
782
783 The file cursor for reading is advanced by the number of bytes
784 read.
785
786 Arguments:
787 size: Number of bytes to read.
788
789 Returns:
790 The data.
791
792 """
793 if not self.is_sparse:
794 self._image.seek(self._file_pos)
795 data = self._image.read(size)
796 self._file_pos += len(data)
797 return data
798
799 # Iterate over all chunks.
800 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
801 self._file_pos) - 1
802 data = bytearray()
803 to_go = size
804 while to_go > 0:
805 chunk = self._chunks[chunk_idx]
806 chunk_pos_offset = self._file_pos - chunk.output_offset
807 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
808
809 if chunk.chunk_type == ImageChunk.TYPE_RAW:
810 self._image.seek(chunk.input_offset + chunk_pos_offset)
811 data.extend(self._image.read(chunk_pos_to_go))
812 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
813 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
814 offset_mod = chunk_pos_offset % len(chunk.fill_data)
815 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
816 else:
817 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
818 data.extend('\0' * chunk_pos_to_go)
819
820 to_go -= chunk_pos_to_go
821 self._file_pos += chunk_pos_to_go
822 chunk_idx += 1
823 # Generate partial read in case of EOF.
824 if chunk_idx >= len(self._chunks):
825 break
826
827 return data
828
829 def tell(self):
830 """Returns the file cursor position for reading from unsparsified file.
831
832 Returns:
833 The file cursor position for reading.
834 """
835 return self._file_pos
836
837 def truncate(self, size):
838 """Truncates the unsparsified file.
839
840 Arguments:
841 size: Desired size of unsparsified file.
842
843 Raises:
844 ValueError: If desired size isn't a multiple of the block size.
845 """
846 if not self.is_sparse:
847 self._image.truncate(size)
848 self._read_header()
849 return
850
851 if size % self.block_size != 0:
852 raise ValueError('Cannot truncate to a size which is not a multiple '
853 'of the block size')
854
855 if size == self.image_size:
856 # Trivial where there's nothing to do.
857 return
858 elif size < self.image_size:
859 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
860 chunk = self._chunks[chunk_idx]
861 if chunk.output_offset != size:
862 # Truncation in the middle of a trunk - need to keep the chunk
863 # and modify it.
864 chunk_idx_for_update = chunk_idx + 1
865 num_to_keep = size - chunk.output_offset
866 assert num_to_keep % self.block_size == 0
867 if chunk.chunk_type == ImageChunk.TYPE_RAW:
868 truncate_at = (chunk.chunk_offset +
869 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
870 data_sz = num_to_keep
871 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
872 truncate_at = (chunk.chunk_offset +
873 struct.calcsize(ImageChunk.FORMAT) + 4)
874 data_sz = 4
875 else:
876 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
877 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
878 data_sz = 0
879 chunk_sz = num_to_keep/self.block_size
880 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
881 self._image.seek(chunk.chunk_offset)
882 self._image.write(struct.pack(ImageChunk.FORMAT,
883 chunk.chunk_type,
884 0, # Reserved
885 chunk_sz,
886 total_sz))
887 chunk.output_size = num_to_keep
888 else:
889 # Truncation at trunk boundary.
890 truncate_at = chunk.chunk_offset
891 chunk_idx_for_update = chunk_idx
892
893 self._num_total_chunks = chunk_idx_for_update
894 self._num_total_blocks = 0
895 for i in range(0, chunk_idx_for_update):
896 self._num_total_blocks += self._chunks[i].output_size / self.block_size
897 self._update_chunks_and_blocks()
898 self._image.truncate(truncate_at)
899
900 # We've modified the file so re-read all data.
901 self._read_header()
902 else:
903 # Truncating to grow - just add a DONT_CARE section.
904 self.append_dont_care(size - self.image_size)
905
906
David Zeuthen21e95262016-07-27 17:58:40 -0400907class AvbDescriptor(object):
908 """Class for AVB descriptor.
909
910 See the |AvbDescriptor| C struct for more information.
911
912 Attributes:
913 tag: The tag identifying what kind of descriptor this is.
914 data: The data in the descriptor.
915 """
916
917 SIZE = 16
918 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
919
920 def __init__(self, data):
921 """Initializes a new property descriptor.
922
923 Arguments:
924 data: If not None, must be a bytearray().
925
926 Raises:
927 LookupError: If the given descriptor is malformed.
928 """
929 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
930
931 if data:
932 (self.tag, num_bytes_following) = (
933 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
934 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
935 else:
936 self.tag = None
937 self.data = None
938
939 def print_desc(self, o):
940 """Print the descriptor.
941
942 Arguments:
943 o: The object to write the output to.
944 """
945 o.write(' Unknown descriptor:\n')
946 o.write(' Tag: {}\n'.format(self.tag))
947 if len(self.data) < 256:
948 o.write(' Data: {} ({} bytes)\n'.format(
949 repr(str(self.data)), len(self.data)))
950 else:
951 o.write(' Data: {} bytes\n'.format(len(self.data)))
952
953 def encode(self):
954 """Serializes the descriptor.
955
956 Returns:
957 A bytearray() with the descriptor data.
958 """
959 num_bytes_following = len(self.data)
960 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
961 padding_size = nbf_with_padding - num_bytes_following
962 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
963 padding = struct.pack(str(padding_size) + 'x')
964 ret = desc + self.data + padding
965 return bytearray(ret)
966
967
968class AvbPropertyDescriptor(AvbDescriptor):
969 """A class for property descriptors.
970
971 See the |AvbPropertyDescriptor| C struct for more information.
972
973 Attributes:
974 key: The key.
975 value: The key.
976 """
977
978 TAG = 0
979 SIZE = 32
980 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
981 'Q' # key size (bytes)
982 'Q') # value size (bytes)
983
984 def __init__(self, data=None):
985 """Initializes a new property descriptor.
986
987 Arguments:
988 data: If not None, must be a bytearray of size |SIZE|.
989
990 Raises:
991 LookupError: If the given descriptor is malformed.
992 """
993 AvbDescriptor.__init__(self, None)
994 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
995
996 if data:
997 (tag, num_bytes_following, key_size,
998 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
999 expected_size = round_to_multiple(
1000 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1001 if tag != self.TAG or num_bytes_following != expected_size:
1002 raise LookupError('Given data does not look like a property '
1003 'descriptor.')
1004 self.key = data[self.SIZE:(self.SIZE + key_size)]
1005 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1006 value_size)]
1007 else:
1008 self.key = ''
1009 self.value = ''
1010
1011 def print_desc(self, o):
1012 """Print the descriptor.
1013
1014 Arguments:
1015 o: The object to write the output to.
1016 """
1017 if len(self.value) < 256:
1018 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1019 else:
1020 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1021
1022 def encode(self):
1023 """Serializes the descriptor.
1024
1025 Returns:
1026 A bytearray() with the descriptor data.
1027 """
1028 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1029 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1030 padding_size = nbf_with_padding - num_bytes_following
1031 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1032 len(self.key), len(self.value))
1033 padding = struct.pack(str(padding_size) + 'x')
1034 ret = desc + self.key + '\0' + self.value + '\0' + padding
1035 return bytearray(ret)
1036
1037
1038class AvbHashtreeDescriptor(AvbDescriptor):
1039 """A class for hashtree descriptors.
1040
1041 See the |AvbHashtreeDescriptor| C struct for more information.
1042
1043 Attributes:
1044 dm_verity_version: dm-verity version used.
1045 image_size: Size of the image, after rounding up to |block_size|.
1046 tree_offset: Offset of the hash tree in the file.
1047 tree_size: Size of the tree.
1048 data_block_size: Data block size
1049 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001050 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1051 fec_offset: Offset of FEC data (0 if FEC is not used).
1052 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001053 hash_algorithm: Hash algorithm used.
1054 partition_name: Partition name.
1055 salt: Salt used.
1056 root_digest: Root digest.
1057 """
1058
1059 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001060 RESERVED = 64
1061 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001062 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1063 'L' # dm-verity version used
1064 'Q' # image size (bytes)
1065 'Q' # tree offset (bytes)
1066 'Q' # tree size (bytes)
1067 'L' # data block size (bytes)
1068 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001069 'L' # FEC number of roots
1070 'Q' # FEC offset (bytes)
1071 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001072 '32s' # hash algorithm used
1073 'L' # partition name (bytes)
1074 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001075 'L' + # root digest length (bytes)
1076 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001077
1078 def __init__(self, data=None):
1079 """Initializes a new hashtree descriptor.
1080
1081 Arguments:
1082 data: If not None, must be a bytearray of size |SIZE|.
1083
1084 Raises:
1085 LookupError: If the given descriptor is malformed.
1086 """
1087 AvbDescriptor.__init__(self, None)
1088 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1089
1090 if data:
1091 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1092 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001093 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1094 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001095 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1096 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001097 expected_size = round_to_multiple(
1098 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1099 if tag != self.TAG or num_bytes_following != expected_size:
1100 raise LookupError('Given data does not look like a hashtree '
1101 'descriptor.')
1102 # Nuke NUL-bytes at the end.
1103 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1104 o = 0
1105 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1106 partition_name_len)])
1107 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1108 self.partition_name.decode('utf-8')
1109 o += partition_name_len
1110 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1111 o += salt_len
1112 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1113 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1114 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1115
1116 else:
1117 self.dm_verity_version = 0
1118 self.image_size = 0
1119 self.tree_offset = 0
1120 self.tree_size = 0
1121 self.data_block_size = 0
1122 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001123 self.fec_num_roots = 0
1124 self.fec_offset = 0
1125 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001126 self.hash_algorithm = ''
1127 self.partition_name = ''
1128 self.salt = bytearray()
1129 self.root_digest = bytearray()
1130
1131 def print_desc(self, o):
1132 """Print the descriptor.
1133
1134 Arguments:
1135 o: The object to write the output to.
1136 """
1137 o.write(' Hashtree descriptor:\n')
1138 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1139 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1140 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1141 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1142 o.write(' Data Block Size: {} bytes\n'.format(
1143 self.data_block_size))
1144 o.write(' Hash Block Size: {} bytes\n'.format(
1145 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001146 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1147 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1148 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001149 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1150 o.write(' Partition Name: {}\n'.format(self.partition_name))
1151 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1152 'hex')))
1153 o.write(' Root Digest: {}\n'.format(str(
1154 self.root_digest).encode('hex')))
1155
1156 def encode(self):
1157 """Serializes the descriptor.
1158
1159 Returns:
1160 A bytearray() with the descriptor data.
1161 """
1162 encoded_name = self.partition_name.encode('utf-8')
1163 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1164 len(self.root_digest) - 16)
1165 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1166 padding_size = nbf_with_padding - num_bytes_following
1167 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1168 self.dm_verity_version, self.image_size,
1169 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001170 self.hash_block_size, self.fec_num_roots,
1171 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 len(encoded_name), len(self.salt), len(self.root_digest),
1173 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001174 padding = struct.pack(str(padding_size) + 'x')
1175 ret = desc + encoded_name + self.salt + self.root_digest + padding
1176 return bytearray(ret)
1177
1178
1179class AvbHashDescriptor(AvbDescriptor):
1180 """A class for hash descriptors.
1181
1182 See the |AvbHashDescriptor| C struct for more information.
1183
1184 Attributes:
1185 image_size: Image size, in bytes.
1186 hash_algorithm: Hash algorithm used.
1187 partition_name: Partition name.
1188 salt: Salt used.
1189 digest: The hash value of salt and data combined.
1190 """
1191
1192 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001193 RESERVED = 64
1194 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001195 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1196 'Q' # image size (bytes)
1197 '32s' # hash algorithm used
1198 'L' # partition name (bytes)
1199 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001200 'L' + # digest length (bytes)
1201 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001202
1203 def __init__(self, data=None):
1204 """Initializes a new hash descriptor.
1205
1206 Arguments:
1207 data: If not None, must be a bytearray of size |SIZE|.
1208
1209 Raises:
1210 LookupError: If the given descriptor is malformed.
1211 """
1212 AvbDescriptor.__init__(self, None)
1213 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1214
1215 if data:
1216 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1217 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001218 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001219 expected_size = round_to_multiple(
1220 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1221 if tag != self.TAG or num_bytes_following != expected_size:
1222 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1223 # Nuke NUL-bytes at the end.
1224 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1225 o = 0
1226 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1227 partition_name_len)])
1228 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1229 self.partition_name.decode('utf-8')
1230 o += partition_name_len
1231 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1232 o += salt_len
1233 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1234 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1235 raise LookupError('digest_len doesn\'t match hash algorithm')
1236
1237 else:
1238 self.image_size = 0
1239 self.hash_algorithm = ''
1240 self.partition_name = ''
1241 self.salt = bytearray()
1242 self.digest = bytearray()
1243
1244 def print_desc(self, o):
1245 """Print the descriptor.
1246
1247 Arguments:
1248 o: The object to write the output to.
1249 """
1250 o.write(' Hash descriptor:\n')
1251 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1252 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1253 o.write(' Partition Name: {}\n'.format(self.partition_name))
1254 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1255 'hex')))
1256 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1257 'hex')))
1258
1259 def encode(self):
1260 """Serializes the descriptor.
1261
1262 Returns:
1263 A bytearray() with the descriptor data.
1264 """
1265 encoded_name = self.partition_name.encode('utf-8')
1266 num_bytes_following = (
1267 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1268 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1269 padding_size = nbf_with_padding - num_bytes_following
1270 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1271 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001272 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001273 padding = struct.pack(str(padding_size) + 'x')
1274 ret = desc + encoded_name + self.salt + self.digest + padding
1275 return bytearray(ret)
1276
1277
1278class AvbKernelCmdlineDescriptor(AvbDescriptor):
1279 """A class for kernel command-line descriptors.
1280
1281 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1282
1283 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001284 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001285 kernel_cmdline: The kernel command-line.
1286 """
1287
1288 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001289 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001290 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001291 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001292 'L') # cmdline length (bytes)
1293
David Zeuthenfd41eb92016-11-17 12:24:47 -05001294 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1295 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1296
David Zeuthen21e95262016-07-27 17:58:40 -04001297 def __init__(self, data=None):
1298 """Initializes a new kernel cmdline descriptor.
1299
1300 Arguments:
1301 data: If not None, must be a bytearray of size |SIZE|.
1302
1303 Raises:
1304 LookupError: If the given descriptor is malformed.
1305 """
1306 AvbDescriptor.__init__(self, None)
1307 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1308
1309 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001310 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001311 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1312 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1313 8)
1314 if tag != self.TAG or num_bytes_following != expected_size:
1315 raise LookupError('Given data does not look like a kernel cmdline '
1316 'descriptor.')
1317 # Nuke NUL-bytes at the end.
1318 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1319 kernel_cmdline_length)])
1320 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1321 self.kernel_cmdline.decode('utf-8')
1322 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001323 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001324 self.kernel_cmdline = ''
1325
1326 def print_desc(self, o):
1327 """Print the descriptor.
1328
1329 Arguments:
1330 o: The object to write the output to.
1331 """
1332 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001333 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001334 o.write(' Kernel Cmdline: {}\n'.format(repr(
1335 self.kernel_cmdline)))
1336
1337 def encode(self):
1338 """Serializes the descriptor.
1339
1340 Returns:
1341 A bytearray() with the descriptor data.
1342 """
1343 encoded_str = self.kernel_cmdline.encode('utf-8')
1344 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1345 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1346 padding_size = nbf_with_padding - num_bytes_following
1347 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001348 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001349 padding = struct.pack(str(padding_size) + 'x')
1350 ret = desc + encoded_str + padding
1351 return bytearray(ret)
1352
1353
1354class AvbChainPartitionDescriptor(AvbDescriptor):
1355 """A class for chained partition descriptors.
1356
1357 See the |AvbChainPartitionDescriptor| C struct for more information.
1358
1359 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001360 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001361 partition_name: Partition name.
1362 public_key: Bytes for the public key.
1363 """
1364
1365 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001366 RESERVED = 64
1367 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001368 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001369 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001370 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001371 'L' + # public_key_size (bytes)
1372 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001373
1374 def __init__(self, data=None):
1375 """Initializes a new chain partition descriptor.
1376
1377 Arguments:
1378 data: If not None, must be a bytearray of size |SIZE|.
1379
1380 Raises:
1381 LookupError: If the given descriptor is malformed.
1382 """
1383 AvbDescriptor.__init__(self, None)
1384 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1385
1386 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001387 (tag, num_bytes_following, self.rollback_index_location,
1388 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001389 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001390 expected_size = round_to_multiple(
1391 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1392 if tag != self.TAG or num_bytes_following != expected_size:
1393 raise LookupError('Given data does not look like a chain partition '
1394 'descriptor.')
1395 o = 0
1396 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1397 partition_name_len)])
1398 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1399 self.partition_name.decode('utf-8')
1400 o += partition_name_len
1401 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1402
1403 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001404 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001405 self.partition_name = ''
1406 self.public_key = bytearray()
1407
1408 def print_desc(self, o):
1409 """Print the descriptor.
1410
1411 Arguments:
1412 o: The object to write the output to.
1413 """
1414 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001415 o.write(' Partition Name: {}\n'.format(self.partition_name))
1416 o.write(' Rollback Index Location: {}\n'.format(
1417 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001418 # Just show the SHA1 of the key, for size reasons.
1419 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001420 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001421
1422 def encode(self):
1423 """Serializes the descriptor.
1424
1425 Returns:
1426 A bytearray() with the descriptor data.
1427 """
1428 encoded_name = self.partition_name.encode('utf-8')
1429 num_bytes_following = (
1430 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1431 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1432 padding_size = nbf_with_padding - num_bytes_following
1433 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001434 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001435 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001436 padding = struct.pack(str(padding_size) + 'x')
1437 ret = desc + encoded_name + self.public_key + padding
1438 return bytearray(ret)
1439
1440
1441DESCRIPTOR_CLASSES = [
1442 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1443 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1444]
1445
1446
1447def parse_descriptors(data):
1448 """Parses a blob of data into descriptors.
1449
1450 Arguments:
1451 data: A bytearray() with encoded descriptors.
1452
1453 Returns:
1454 A list of instances of objects derived from AvbDescriptor. For
1455 unknown descriptors, the class AvbDescriptor is used.
1456 """
1457 o = 0
1458 ret = []
1459 while o < len(data):
1460 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1461 if tag < len(DESCRIPTOR_CLASSES):
1462 c = DESCRIPTOR_CLASSES[tag]
1463 else:
1464 c = AvbDescriptor
1465 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1466 o += 16 + nb_following
1467 return ret
1468
1469
1470class AvbFooter(object):
1471 """A class for parsing and writing footers.
1472
1473 Footers are stored at the end of partitions and point to where the
1474 AvbVBMeta blob is located. They also contain the original size of
1475 the image before AVB information was added.
1476
1477 Attributes:
1478 magic: Magic for identifying the footer, see |MAGIC|.
1479 version_major: The major version of avbtool that wrote the footer.
1480 version_minor: The minor version of avbtool that wrote the footer.
1481 original_image_size: Original image size.
1482 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1483 vbmeta_size: Size of the AvbVBMeta blob.
1484 """
1485
1486 MAGIC = 'AVBf'
1487 SIZE = 64
1488 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001489 FOOTER_VERSION_MAJOR = 1
1490 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001491 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1492 'Q' # Original image size.
1493 'Q' # Offset of VBMeta blob.
1494 'Q' + # Size of VBMeta blob.
1495 str(RESERVED) + 'x') # padding for reserved bytes
1496
1497 def __init__(self, data=None):
1498 """Initializes a new footer object.
1499
1500 Arguments:
1501 data: If not None, must be a bytearray of size 4096.
1502
1503 Raises:
1504 LookupError: If the given footer is malformed.
1505 struct.error: If the given data has no footer.
1506 """
1507 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1508
1509 if data:
1510 (self.magic, self.version_major, self.version_minor,
1511 self.original_image_size, self.vbmeta_offset,
1512 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1513 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001514 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001515 else:
1516 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001517 self.version_major = self.FOOTER_VERSION_MAJOR
1518 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001519 self.original_image_size = 0
1520 self.vbmeta_offset = 0
1521 self.vbmeta_size = 0
1522
David Zeuthena4fee8b2016-08-22 15:20:43 -04001523 def encode(self):
1524 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001525
David Zeuthena4fee8b2016-08-22 15:20:43 -04001526 Returns:
1527 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001528 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001529 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1530 self.version_minor, self.original_image_size,
1531 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001532
1533
1534class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001535 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001536
1537 Attributes:
1538 The attributes correspond to the |AvbVBMetaHeader| struct
1539 defined in avb_vbmeta_header.h.
1540 """
1541
1542 SIZE = 256
1543
David Zeuthene3cadca2017-02-22 21:25:46 -05001544 # Keep in sync with |reserved0| and |reserved| field of
1545 # |AvbVBMetaImageHeader|.
1546 RESERVED0 = 4
1547 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001548
1549 # Keep in sync with |AvbVBMetaImageHeader|.
1550 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1551 '2Q' # 2 x block size
1552 'L' # algorithm type
1553 '2Q' # offset, size (hash)
1554 '2Q' # offset, size (signature)
1555 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001556 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001557 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001558 'Q' # rollback_index
1559 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001560 str(RESERVED0) + 'x' + # padding for reserved bytes
1561 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001562 str(RESERVED) + 'x') # padding for reserved bytes
1563
1564 def __init__(self, data=None):
1565 """Initializes a new header object.
1566
1567 Arguments:
1568 data: If not None, must be a bytearray of size 8192.
1569
1570 Raises:
1571 Exception: If the given data is malformed.
1572 """
1573 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1574
1575 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001576 (self.magic, self.required_libavb_version_major,
1577 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001578 self.authentication_data_block_size, self.auxiliary_data_block_size,
1579 self.algorithm_type, self.hash_offset, self.hash_size,
1580 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001581 self.public_key_size, self.public_key_metadata_offset,
1582 self.public_key_metadata_size, self.descriptors_offset,
1583 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001584 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001585 self.flags,
1586 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001587 # Nuke NUL-bytes at the end of the string.
1588 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001589 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001590 else:
1591 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001592 # Start by just requiring version 1.0. Code that adds features
1593 # in a future version can use bump_required_libavb_version_minor() to
1594 # bump the minor.
1595 self.required_libavb_version_major = AVB_VERSION_MAJOR
1596 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001597 self.authentication_data_block_size = 0
1598 self.auxiliary_data_block_size = 0
1599 self.algorithm_type = 0
1600 self.hash_offset = 0
1601 self.hash_size = 0
1602 self.signature_offset = 0
1603 self.signature_size = 0
1604 self.public_key_offset = 0
1605 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001606 self.public_key_metadata_offset = 0
1607 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001608 self.descriptors_offset = 0
1609 self.descriptors_size = 0
1610 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001611 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001612 self.release_string = get_release_string()
1613
1614 def bump_required_libavb_version_minor(self, minor):
1615 """Function to bump required_libavb_version_minor.
1616
1617 Call this when writing data that requires a specific libavb
1618 version to parse it.
1619
1620 Arguments:
1621 minor: The minor version of libavb that has support for the feature.
1622 """
1623 self.required_libavb_version_minor = (
1624 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001625
1626 def save(self, output):
1627 """Serializes the header (256 bytes) to disk.
1628
1629 Arguments:
1630 output: The object to write the output to.
1631 """
1632 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001633 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1634 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001635 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1636 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001637 self.public_key_offset, self.public_key_size,
1638 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001639 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001640 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001641
1642 def encode(self):
1643 """Serializes the header (256) to a bytearray().
1644
1645 Returns:
1646 A bytearray() with the encoded header.
1647 """
1648 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001649 self.required_libavb_version_major,
1650 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001651 self.authentication_data_block_size,
1652 self.auxiliary_data_block_size, self.algorithm_type,
1653 self.hash_offset, self.hash_size, self.signature_offset,
1654 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001655 self.public_key_size, self.public_key_metadata_offset,
1656 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001657 self.descriptors_size, self.rollback_index, self.flags,
1658 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001659
1660
1661class Avb(object):
1662 """Business logic for avbtool command-line tool."""
1663
David Zeuthen8b6973b2016-09-20 12:39:49 -04001664 # Keep in sync with avb_ab_flow.h.
1665 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1666 AB_MAGIC = '\0AB0'
1667 AB_MAJOR_VERSION = 1
1668 AB_MINOR_VERSION = 0
1669 AB_MISC_METADATA_OFFSET = 2048
1670
David Zeuthen09692692016-09-30 16:16:40 -04001671 # Constants for maximum metadata size. These are used to give
1672 # meaningful errors if the value passed in via --partition_size is
1673 # too small and when --calc_max_image_size is used. We use
1674 # conservative figures.
1675 MAX_VBMETA_SIZE = 64 * 1024
1676 MAX_FOOTER_SIZE = 4096
1677
David Zeuthena4fee8b2016-08-22 15:20:43 -04001678 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001679 """Implements the 'erase_footer' command.
1680
1681 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001682 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001683 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001684
1685 Raises:
1686 AvbError: If there's no footer in the image.
1687 """
1688
David Zeuthena4fee8b2016-08-22 15:20:43 -04001689 image = ImageHandler(image_filename)
1690
David Zeuthen21e95262016-07-27 17:58:40 -04001691 (footer, _, descriptors, _) = self._parse_image(image)
1692
1693 if not footer:
1694 raise AvbError('Given image does not have a footer.')
1695
1696 new_image_size = None
1697 if not keep_hashtree:
1698 new_image_size = footer.original_image_size
1699 else:
1700 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001701 # descriptor to figure out the location and size of the hashtree
1702 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001703 for desc in descriptors:
1704 if isinstance(desc, AvbHashtreeDescriptor):
1705 # The hashtree is always just following the main data so the
1706 # new size is easily derived.
1707 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001708 # If the image has FEC codes, also keep those.
1709 if desc.fec_offset > 0:
1710 fec_end = desc.fec_offset + desc.fec_size
1711 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001712 break
1713 if not new_image_size:
1714 raise AvbError('Requested to keep hashtree but no hashtree '
1715 'descriptor was found.')
1716
1717 # And cut...
1718 image.truncate(new_image_size)
1719
David Zeuthen8b6973b2016-09-20 12:39:49 -04001720 def set_ab_metadata(self, misc_image, slot_data):
1721 """Implements the 'set_ab_metadata' command.
1722
1723 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1724 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1725
1726 Arguments:
1727 misc_image: The misc image to write to.
1728 slot_data: Slot data as a string
1729
1730 Raises:
1731 AvbError: If slot data is malformed.
1732 """
1733 tokens = slot_data.split(':')
1734 if len(tokens) != 6:
1735 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1736 a_priority = int(tokens[0])
1737 a_tries_remaining = int(tokens[1])
1738 a_success = True if int(tokens[2]) != 0 else False
1739 b_priority = int(tokens[3])
1740 b_tries_remaining = int(tokens[4])
1741 b_success = True if int(tokens[5]) != 0 else False
1742
1743 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1744 self.AB_MAGIC,
1745 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1746 a_priority, a_tries_remaining, a_success,
1747 b_priority, b_tries_remaining, b_success)
1748 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1749 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1750 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1751 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1752 misc_image.write(ab_data)
1753
David Zeuthena4fee8b2016-08-22 15:20:43 -04001754 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001755 """Implements the 'info_image' command.
1756
1757 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001758 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001759 output: Output file to write human-readable information to (file object).
1760 """
1761
David Zeuthena4fee8b2016-08-22 15:20:43 -04001762 image = ImageHandler(image_filename)
1763
David Zeuthen21e95262016-07-27 17:58:40 -04001764 o = output
1765
1766 (footer, header, descriptors, image_size) = self._parse_image(image)
1767
1768 if footer:
1769 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1770 footer.version_minor))
1771 o.write('Image size: {} bytes\n'.format(image_size))
1772 o.write('Original image size: {} bytes\n'.format(
1773 footer.original_image_size))
1774 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1775 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1776 o.write('--\n')
1777
1778 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1779
David Zeuthene3cadca2017-02-22 21:25:46 -05001780 o.write('Minimum libavb version: {}.{}{}\n'.format(
1781 header.required_libavb_version_major,
1782 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001783 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001784 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1785 o.write('Authentication Block: {} bytes\n'.format(
1786 header.authentication_data_block_size))
1787 o.write('Auxiliary Block: {} bytes\n'.format(
1788 header.auxiliary_data_block_size))
1789 o.write('Algorithm: {}\n'.format(alg_name))
1790 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001791 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001792 o.write('Release String: \'{}\'\n'.format(
1793 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001794
1795 # Print descriptors.
1796 num_printed = 0
1797 o.write('Descriptors:\n')
1798 for desc in descriptors:
1799 desc.print_desc(o)
1800 num_printed += 1
1801 if num_printed == 0:
1802 o.write(' (none)\n')
1803
1804 def _parse_image(self, image):
1805 """Gets information about an image.
1806
1807 The image can either be a vbmeta or an image with a footer.
1808
1809 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001810 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 Returns:
1813 A tuple where the first argument is a AvbFooter (None if there
1814 is no footer on the image), the second argument is a
1815 AvbVBMetaHeader, the third argument is a list of
1816 AvbDescriptor-derived instances, and the fourth argument is the
1817 size of |image|.
1818 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001819 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001820 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001821 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001822 try:
1823 footer = AvbFooter(image.read(AvbFooter.SIZE))
1824 except (LookupError, struct.error):
1825 # Nope, just seek back to the start.
1826 image.seek(0)
1827
1828 vbmeta_offset = 0
1829 if footer:
1830 vbmeta_offset = footer.vbmeta_offset
1831
1832 image.seek(vbmeta_offset)
1833 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1834
1835 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1836 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1837 desc_start_offset = aux_block_offset + h.descriptors_offset
1838 image.seek(desc_start_offset)
1839 descriptors = parse_descriptors(image.read(h.descriptors_size))
1840
David Zeuthen09692692016-09-30 16:16:40 -04001841 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001842
David Zeuthenb1b994d2017-03-06 18:01:31 -05001843 def _load_vbmeta_blob(self, image):
1844 """Gets the vbmeta struct and associated sections.
1845
1846 The image can either be a vbmeta.img or an image with a footer.
1847
1848 Arguments:
1849 image: An ImageHandler (vbmeta or footer).
1850
1851 Returns:
1852 A blob with the vbmeta struct and other sections.
1853 """
1854 assert isinstance(image, ImageHandler)
1855 footer = None
1856 image.seek(image.image_size - AvbFooter.SIZE)
1857 try:
1858 footer = AvbFooter(image.read(AvbFooter.SIZE))
1859 except (LookupError, struct.error):
1860 # Nope, just seek back to the start.
1861 image.seek(0)
1862
1863 vbmeta_offset = 0
1864 if footer:
1865 vbmeta_offset = footer.vbmeta_offset
1866
1867 image.seek(vbmeta_offset)
1868 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1869
1870 image.seek(vbmeta_offset)
1871 data_size = AvbVBMetaHeader.SIZE
1872 data_size += h.authentication_data_block_size
1873 data_size += h.auxiliary_data_block_size
1874 return image.read(data_size)
1875
David Zeuthenfd41eb92016-11-17 12:24:47 -05001876 def _get_cmdline_descriptors_for_dm_verity(self, image):
1877 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001878
1879 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001880 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001881
1882 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001883 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1884 instructions. There is one for when hashtree is not disabled and one for
1885 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001886
1887 Raises:
1888 AvbError: If |image| doesn't have a hashtree descriptor.
1889
1890 """
1891
1892 (_, _, descriptors, _) = self._parse_image(image)
1893
1894 ht = None
1895 for desc in descriptors:
1896 if isinstance(desc, AvbHashtreeDescriptor):
1897 ht = desc
1898 break
1899
1900 if not ht:
1901 raise AvbError('No hashtree descriptor in given image')
1902
1903 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001904 c += '0' # start
1905 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1906 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1907 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1908 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1909 c += ' {}'.format(ht.data_block_size) # data_block
1910 c += ' {}'.format(ht.hash_block_size) # hash_block
1911 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1912 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1913 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1914 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1915 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1916 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05001917 c += ' 10' # number of optional args
1918 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001919 c += ' ignore_zero_blocks'
1920 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1921 c += ' fec_roots {}'.format(ht.fec_num_roots)
1922 # Note that fec_blocks is the size that FEC covers, *not* the
1923 # size of the FEC data. Since we use FEC for everything up until
1924 # the FEC data, it's the same as the offset.
1925 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1926 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1927 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05001928 c += ' 2' # number of optional args
1929 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001930 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04001931 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04001932
David Zeuthenfd41eb92016-11-17 12:24:47 -05001933 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001934 desc = AvbKernelCmdlineDescriptor()
1935 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001936 desc.flags = (
1937 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1938
1939 # The descriptor for when hashtree verification is disabled is a lot
1940 # simpler - we just set the root to the partition.
1941 desc_no_ht = AvbKernelCmdlineDescriptor()
1942 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1943 desc_no_ht.flags = (
1944 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1945
1946 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001947
1948 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001949 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001950 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001951 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001952 include_descriptors_from_image, signing_helper,
1953 release_string,
1954 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001955 """Implements the 'make_vbmeta_image' command.
1956
1957 Arguments:
1958 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05001959 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001960 algorithm_name: Name of algorithm to use.
1961 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001962 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001963 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001964 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001965 props: Properties to insert (list of strings of the form 'key:value').
1966 props_from_file: Properties to insert (list of strings 'key:<path>').
1967 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001968 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001969 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001970 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05001971 release_string: None or avbtool release string to use instead of default.
1972 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04001973
1974 Raises:
1975 AvbError: If a chained partition is malformed.
1976 """
1977
1978 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04001979 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001980 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001981 chain_partitions, rollback_index, flags, props, props_from_file,
1982 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001983 include_descriptors_from_image, signing_helper, release_string,
1984 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001985
1986 # Write entire vbmeta blob (header, authentication, auxiliary).
1987 output.seek(0)
1988 output.write(vbmeta_blob)
1989
David Zeuthen18666ab2016-11-15 11:18:05 -05001990 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1991 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05001992 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001993 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001994 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001995 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05001996 include_descriptors_from_image, signing_helper,
1997 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04001998 """Generates a VBMeta blob.
1999
2000 This blob contains the header (struct AvbVBMetaHeader), the
2001 authentication data block (which contains the hash and signature
2002 for the header and auxiliary block), and the auxiliary block
2003 (which contains descriptors, the public key used, and other data).
2004
2005 The |key| parameter can |None| only if the |algorithm_name| is
2006 'NONE'.
2007
2008 Arguments:
2009 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2010 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002011 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002012 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002013 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002014 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002015 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002016 props: Properties to insert (List of strings of the form 'key:value').
2017 props_from_file: Properties to insert (List of strings 'key:<path>').
2018 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002019 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002020 dm-verity kernel cmdline from.
2021 include_descriptors_from_image: List of file objects for which
2022 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002023 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002024 release_string: None or avbtool release string.
2025 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002026
2027 Returns:
2028 A bytearray() with the VBMeta blob.
2029
2030 Raises:
2031 Exception: If the |algorithm_name| is not found, if no key has
2032 been given and the given algorithm requires one, or the key is
2033 of the wrong size.
2034
2035 """
2036 try:
2037 alg = ALGORITHMS[algorithm_name]
2038 except KeyError:
2039 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2040
David Zeuthena5fd3a42017-02-27 16:38:54 -05002041 if not descriptors:
2042 descriptors = []
2043
2044 # Insert chained partition descriptors, if any
2045 if chain_partitions:
2046 for cp in chain_partitions:
2047 cp_tokens = cp.split(':')
2048 if len(cp_tokens) != 3:
2049 raise AvbError('Malformed chained partition "{}".'.format(cp))
2050 desc = AvbChainPartitionDescriptor()
2051 desc.partition_name = cp_tokens[0]
2052 desc.rollback_index_location = int(cp_tokens[1])
2053 if desc.rollback_index_location < 1:
2054 raise AvbError('Rollback index location must be 1 or larger.')
2055 file_path = cp_tokens[2]
2056 desc.public_key = open(file_path, 'rb').read()
2057 descriptors.append(desc)
2058
David Zeuthen21e95262016-07-27 17:58:40 -04002059 # Descriptors.
2060 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002061 for desc in descriptors:
2062 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002063
2064 # Add properties.
2065 if props:
2066 for prop in props:
2067 idx = prop.find(':')
2068 if idx == -1:
2069 raise AvbError('Malformed property "{}".'.format(prop))
2070 desc = AvbPropertyDescriptor()
2071 desc.key = prop[0:idx]
2072 desc.value = prop[(idx + 1):]
2073 encoded_descriptors.extend(desc.encode())
2074 if props_from_file:
2075 for prop in props_from_file:
2076 idx = prop.find(':')
2077 if idx == -1:
2078 raise AvbError('Malformed property "{}".'.format(prop))
2079 desc = AvbPropertyDescriptor()
2080 desc.key = prop[0:idx]
2081 desc.value = prop[(idx + 1):]
2082 file_path = prop[(idx + 1):]
2083 desc.value = open(file_path, 'rb').read()
2084 encoded_descriptors.extend(desc.encode())
2085
2086 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002087 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002088 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002089 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002090 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2091 encoded_descriptors.extend(cmdline_desc[0].encode())
2092 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002093
2094 # Add kernel command-lines.
2095 if kernel_cmdlines:
2096 for i in kernel_cmdlines:
2097 desc = AvbKernelCmdlineDescriptor()
2098 desc.kernel_cmdline = i
2099 encoded_descriptors.extend(desc.encode())
2100
2101 # Add descriptors from other images.
2102 if include_descriptors_from_image:
2103 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002104 image_handler = ImageHandler(image.name)
2105 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002106 for desc in image_descriptors:
2107 encoded_descriptors.extend(desc.encode())
2108
David Zeuthen18666ab2016-11-15 11:18:05 -05002109 # Load public key metadata blob, if requested.
2110 pkmd_blob = []
2111 if public_key_metadata_path:
2112 with open(public_key_metadata_path) as f:
2113 pkmd_blob = f.read()
2114
David Zeuthen21e95262016-07-27 17:58:40 -04002115 key = None
2116 encoded_key = bytearray()
2117 if alg.public_key_num_bytes > 0:
2118 if not key_path:
2119 raise AvbError('Key is required for algorithm {}'.format(
2120 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002121 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002122 if len(encoded_key) != alg.public_key_num_bytes:
2123 raise AvbError('Key is wrong size for algorithm {}'.format(
2124 algorithm_name))
2125
2126 h = AvbVBMetaHeader()
2127
David Zeuthene3cadca2017-02-22 21:25:46 -05002128 # Override release string, if requested.
2129 if isinstance(release_string, (str, unicode)):
2130 h.release_string = release_string
2131
2132 # Append to release string, if requested. Also insert a space before.
2133 if isinstance(append_to_release_string, (str, unicode)):
2134 h.release_string += ' ' + append_to_release_string
2135
David Zeuthen18666ab2016-11-15 11:18:05 -05002136 # For the Auxiliary data block, descriptors are stored at offset 0,
2137 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002138 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002139 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002140 h.descriptors_offset = 0
2141 h.descriptors_size = len(encoded_descriptors)
2142 h.public_key_offset = h.descriptors_size
2143 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002144 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2145 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002146
2147 # For the Authentication data block, the hash is first and then
2148 # the signature.
2149 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002150 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002151 h.algorithm_type = alg.algorithm_type
2152 h.hash_offset = 0
2153 h.hash_size = alg.hash_num_bytes
2154 # Signature offset and size - it's stored right after the hash
2155 # (in Authentication data block).
2156 h.signature_offset = alg.hash_num_bytes
2157 h.signature_size = alg.signature_num_bytes
2158
2159 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002160 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002161
2162 # Generate Header data block.
2163 header_data_blob = h.encode()
2164
2165 # Generate Auxiliary data block.
2166 aux_data_blob = bytearray()
2167 aux_data_blob.extend(encoded_descriptors)
2168 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002169 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002170 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2171 aux_data_blob.extend('\0' * padding_bytes)
2172
2173 # Calculate the hash.
2174 binary_hash = bytearray()
2175 binary_signature = bytearray()
2176 if algorithm_name != 'NONE':
2177 if algorithm_name[0:6] == 'SHA256':
2178 ha = hashlib.sha256()
2179 elif algorithm_name[0:6] == 'SHA512':
2180 ha = hashlib.sha512()
2181 else:
2182 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2183 ha.update(header_data_blob)
2184 ha.update(aux_data_blob)
2185 binary_hash.extend(ha.digest())
2186
2187 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002188 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002189 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2190 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002191 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002192
2193 # Generate Authentication data block.
2194 auth_data_blob = bytearray()
2195 auth_data_blob.extend(binary_hash)
2196 auth_data_blob.extend(binary_signature)
2197 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2198 auth_data_blob.extend('\0' * padding_bytes)
2199
2200 return header_data_blob + auth_data_blob + aux_data_blob
2201
2202 def extract_public_key(self, key_path, output):
2203 """Implements the 'extract_public_key' command.
2204
2205 Arguments:
2206 key_path: The path to a RSA private key file.
2207 output: The file to write to.
2208 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002209 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002210
David Zeuthenb1b994d2017-03-06 18:01:31 -05002211 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2212 partition_size):
2213 """Implementation of the append_vbmeta_image command.
2214
2215 Arguments:
2216 image_filename: File to add the footer to.
2217 vbmeta_image_filename: File to get vbmeta struct from.
2218 partition_size: Size of partition.
2219
2220 Raises:
2221 AvbError: If an argument is incorrect.
2222 """
2223 image = ImageHandler(image_filename)
2224
2225 if partition_size % image.block_size != 0:
2226 raise AvbError('Partition size of {} is not a multiple of the image '
2227 'block size {}.'.format(partition_size,
2228 image.block_size))
2229
2230 # If there's already a footer, truncate the image to its original
2231 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2232 image.seek(image.image_size - AvbFooter.SIZE)
2233 try:
2234 footer = AvbFooter(image.read(AvbFooter.SIZE))
2235 # Existing footer found. Just truncate.
2236 original_image_size = footer.original_image_size
2237 image.truncate(footer.original_image_size)
2238 except (LookupError, struct.error):
2239 original_image_size = image.image_size
2240
2241 # If anything goes wrong from here-on, restore the image back to
2242 # its original size.
2243 try:
2244 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2245 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2246
2247 # If the image isn't sparse, its size might not be a multiple of
2248 # the block size. This will screw up padding later so just grow it.
2249 if image.image_size % image.block_size != 0:
2250 assert not image.is_sparse
2251 padding_needed = image.block_size - (image.image_size%image.block_size)
2252 image.truncate(image.image_size + padding_needed)
2253
2254 # The append_raw() method requires content with size being a
2255 # multiple of |block_size| so add padding as needed. Also record
2256 # where this is written to since we'll need to put that in the
2257 # footer.
2258 vbmeta_offset = image.image_size
2259 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2260 len(vbmeta_blob))
2261 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2262
2263 # Append vbmeta blob and footer
2264 image.append_raw(vbmeta_blob_with_padding)
2265 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2266
2267 # Now insert a DONT_CARE chunk with enough bytes such that the
2268 # final Footer block is at the end of partition_size..
2269 image.append_dont_care(partition_size - vbmeta_end_offset -
2270 1*image.block_size)
2271
2272 # Generate the Footer that tells where the VBMeta footer
2273 # is. Also put enough padding in the front of the footer since
2274 # we'll write out an entire block.
2275 footer = AvbFooter()
2276 footer.original_image_size = original_image_size
2277 footer.vbmeta_offset = vbmeta_offset
2278 footer.vbmeta_size = len(vbmeta_blob)
2279 footer_blob = footer.encode()
2280 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2281 footer_blob)
2282 image.append_raw(footer_blob_with_padding)
2283
2284 except:
2285 # Truncate back to original size, then re-raise
2286 image.truncate(original_image_size)
2287 raise
2288
David Zeuthena4fee8b2016-08-22 15:20:43 -04002289 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002290 hash_algorithm, salt, chain_partitions, algorithm_name,
2291 key_path,
2292 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002293 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002294 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002295 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002296 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002297 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002298 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002299
2300 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002301 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002302 partition_size: Size of partition.
2303 partition_name: Name of partition (without A/B suffix).
2304 hash_algorithm: Hash algorithm to use.
2305 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002306 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002307 algorithm_name: Name of algorithm to use.
2308 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002309 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002310 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002311 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002312 props: Properties to insert (List of strings of the form 'key:value').
2313 props_from_file: Properties to insert (List of strings 'key:<path>').
2314 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002315 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002316 dm-verity kernel cmdline from.
2317 include_descriptors_from_image: List of file objects for which
2318 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002319 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002320 release_string: None or avbtool release string.
2321 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002322 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2323 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002324
2325 Raises:
2326 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002327 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002328 image = ImageHandler(image_filename)
2329
2330 if partition_size % image.block_size != 0:
2331 raise AvbError('Partition size of {} is not a multiple of the image '
2332 'block size {}.'.format(partition_size,
2333 image.block_size))
2334
David Zeuthen21e95262016-07-27 17:58:40 -04002335 # If there's already a footer, truncate the image to its original
2336 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2337 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002338 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002339 try:
2340 footer = AvbFooter(image.read(AvbFooter.SIZE))
2341 # Existing footer found. Just truncate.
2342 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002343 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002344 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002345 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002346
2347 # If anything goes wrong from here-on, restore the image back to
2348 # its original size.
2349 try:
David Zeuthen09692692016-09-30 16:16:40 -04002350 # First, calculate the maximum image size such that an image
2351 # this size + metadata (footer + vbmeta struct) fits in
2352 # |partition_size|.
2353 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2354 max_image_size = partition_size - max_metadata_size
2355
2356 # If image size exceeds the maximum image size, fail.
2357 if image.image_size > max_image_size:
2358 raise AvbError('Image size of {} exceeds maximum image '
2359 'size of {} in order to fit in a partition '
2360 'size of {}.'.format(image.image_size, max_image_size,
2361 partition_size))
2362
David Zeuthen21e95262016-07-27 17:58:40 -04002363 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2364 if salt:
2365 salt = salt.decode('hex')
2366 else:
2367 if salt is None:
2368 # If salt is not explicitly specified, choose a hash
2369 # that's the same size as the hash size.
2370 hash_size = digest_size
2371 salt = open('/dev/urandom').read(hash_size)
2372 else:
2373 salt = ''
2374
2375 hasher = hashlib.new(name=hash_algorithm, string=salt)
2376 # TODO(zeuthen): might want to read this in chunks to avoid
2377 # memory pressure, then again, this is only supposed to be used
2378 # on kernel/initramfs partitions. Possible optimization.
2379 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002380 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002381 digest = hasher.digest()
2382
2383 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002384 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002385 h_desc.hash_algorithm = hash_algorithm
2386 h_desc.partition_name = partition_name
2387 h_desc.salt = salt
2388 h_desc.digest = digest
2389
2390 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002391 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002392 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002393 chain_partitions, rollback_index, flags, props, props_from_file,
2394 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002395 include_descriptors_from_image, signing_helper, release_string,
2396 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002397
David Zeuthena4fee8b2016-08-22 15:20:43 -04002398 # If the image isn't sparse, its size might not be a multiple of
2399 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002400 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002401 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002402 padding_needed = image.block_size - (image.image_size%image.block_size)
2403 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002404
David Zeuthena4fee8b2016-08-22 15:20:43 -04002405 # The append_raw() method requires content with size being a
2406 # multiple of |block_size| so add padding as needed. Also record
2407 # where this is written to since we'll need to put that in the
2408 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002409 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002410 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2411 len(vbmeta_blob))
2412 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002413
David Zeuthend247fcb2017-02-16 12:09:27 -05002414 # Write vbmeta blob, if requested.
2415 if output_vbmeta_image:
2416 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002417
David Zeuthend247fcb2017-02-16 12:09:27 -05002418 # Append vbmeta blob and footer, unless requested not to.
2419 if not do_not_append_vbmeta_image:
2420 image.append_raw(vbmeta_blob_with_padding)
2421 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2422
2423 # Now insert a DONT_CARE chunk with enough bytes such that the
2424 # final Footer block is at the end of partition_size..
2425 image.append_dont_care(partition_size - vbmeta_end_offset -
2426 1*image.block_size)
2427
2428 # Generate the Footer that tells where the VBMeta footer
2429 # is. Also put enough padding in the front of the footer since
2430 # we'll write out an entire block.
2431 footer = AvbFooter()
2432 footer.original_image_size = original_image_size
2433 footer.vbmeta_offset = vbmeta_offset
2434 footer.vbmeta_size = len(vbmeta_blob)
2435 footer_blob = footer.encode()
2436 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2437 footer_blob)
2438 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002439
David Zeuthen21e95262016-07-27 17:58:40 -04002440 except:
2441 # Truncate back to original size, then re-raise
2442 image.truncate(original_image_size)
2443 raise
2444
David Zeuthena4fee8b2016-08-22 15:20:43 -04002445 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002446 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002447 block_size, salt, chain_partitions, algorithm_name,
2448 key_path,
2449 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002450 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002451 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002452 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002453 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002454 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002455 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002456 """Implements the 'add_hashtree_footer' command.
2457
2458 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2459 more information about dm-verity and these hashes.
2460
2461 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002462 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002463 partition_size: Size of partition.
2464 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002465 generate_fec: If True, generate FEC codes.
2466 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002467 hash_algorithm: Hash algorithm to use.
2468 block_size: Block size to use.
2469 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002470 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002471 algorithm_name: Name of algorithm to use.
2472 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002473 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002474 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002475 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002476 props: Properties to insert (List of strings of the form 'key:value').
2477 props_from_file: Properties to insert (List of strings 'key:<path>').
2478 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002479 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002480 dm-verity kernel cmdline from.
2481 include_descriptors_from_image: List of file objects for which
2482 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002483 calc_max_image_size: Don't store the hashtree or footer - instead
2484 calculate the maximum image size leaving enough room for hashtree
2485 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002486 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002487 release_string: None or avbtool release string.
2488 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002489 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2490 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002491
2492 Raises:
2493 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002494 """
David Zeuthen09692692016-09-30 16:16:40 -04002495 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2496 digest_padding = round_to_pow2(digest_size) - digest_size
2497
2498 # First, calculate the maximum image size such that an image
2499 # this size + the hashtree + metadata (footer + vbmeta struct)
2500 # fits in |partition_size|. We use very conservative figures for
2501 # metadata.
2502 (_, max_tree_size) = calc_hash_level_offsets(
2503 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002504 max_fec_size = 0
2505 if generate_fec:
2506 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2507 max_metadata_size = (max_fec_size + max_tree_size +
2508 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002509 self.MAX_FOOTER_SIZE)
2510 max_image_size = partition_size - max_metadata_size
2511
2512 # If we're asked to only calculate the maximum image size, we're done.
2513 if calc_max_image_size:
2514 print '{}'.format(max_image_size)
2515 return
2516
David Zeuthena4fee8b2016-08-22 15:20:43 -04002517 image = ImageHandler(image_filename)
2518
2519 if partition_size % image.block_size != 0:
2520 raise AvbError('Partition size of {} is not a multiple of the image '
2521 'block size {}.'.format(partition_size,
2522 image.block_size))
2523
David Zeuthen21e95262016-07-27 17:58:40 -04002524 # If there's already a footer, truncate the image to its original
2525 # size. This way 'avbtool add_hashtree_footer' is idempotent
2526 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002527 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002528 try:
2529 footer = AvbFooter(image.read(AvbFooter.SIZE))
2530 # Existing footer found. Just truncate.
2531 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002532 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002533 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002534 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002535
2536 # If anything goes wrong from here-on, restore the image back to
2537 # its original size.
2538 try:
2539 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002540 rounded_image_size = round_to_multiple(image.image_size, block_size)
2541 if rounded_image_size > image.image_size:
2542 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002543
David Zeuthen09692692016-09-30 16:16:40 -04002544 # If image size exceeds the maximum image size, fail.
2545 if image.image_size > max_image_size:
2546 raise AvbError('Image size of {} exceeds maximum image '
2547 'size of {} in order to fit in a partition '
2548 'size of {}.'.format(image.image_size, max_image_size,
2549 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002550
2551 if salt:
2552 salt = salt.decode('hex')
2553 else:
2554 if salt is None:
2555 # If salt is not explicitly specified, choose a hash
2556 # that's the same size as the hash size.
2557 hash_size = digest_size
2558 salt = open('/dev/urandom').read(hash_size)
2559 else:
2560 salt = ''
2561
David Zeuthena4fee8b2016-08-22 15:20:43 -04002562 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002563 # offsets in advance.
2564 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002565 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002566
David Zeuthena4fee8b2016-08-22 15:20:43 -04002567 # If the image isn't sparse, its size might not be a multiple of
2568 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002569 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002570 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002571 padding_needed = image.block_size - (image.image_size%image.block_size)
2572 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002573
David Zeuthena4fee8b2016-08-22 15:20:43 -04002574 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002575 tree_offset = image.image_size
2576 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002577 block_size,
2578 hash_algorithm, salt,
2579 digest_padding,
2580 hash_level_offsets,
2581 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002582
2583 # Generate HashtreeDescriptor with details about the tree we
2584 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002585 ht_desc = AvbHashtreeDescriptor()
2586 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002587 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002588 ht_desc.tree_offset = tree_offset
2589 ht_desc.tree_size = tree_size
2590 ht_desc.data_block_size = block_size
2591 ht_desc.hash_block_size = block_size
2592 ht_desc.hash_algorithm = hash_algorithm
2593 ht_desc.partition_name = partition_name
2594 ht_desc.salt = salt
2595 ht_desc.root_digest = root_digest
2596
David Zeuthen09692692016-09-30 16:16:40 -04002597 # Write the hash tree
2598 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2599 len(hash_tree))
2600 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2601 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002602 len_hashtree_and_fec = len(hash_tree_with_padding)
2603
2604 # Generate FEC codes, if requested.
2605 if generate_fec:
2606 fec_data = generate_fec_data(image_filename, fec_num_roots)
2607 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2608 len(fec_data))
2609 fec_data_with_padding = fec_data + '\0'*padding_needed
2610 fec_offset = image.image_size
2611 image.append_raw(fec_data_with_padding)
2612 len_hashtree_and_fec += len(fec_data_with_padding)
2613 # Update the hashtree descriptor.
2614 ht_desc.fec_num_roots = fec_num_roots
2615 ht_desc.fec_offset = fec_offset
2616 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002617
David Zeuthena4fee8b2016-08-22 15:20:43 -04002618 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002619 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002620 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002621 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002622 chain_partitions, rollback_index, flags, props, props_from_file,
2623 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002624 include_descriptors_from_image, signing_helper, release_string,
2625 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002626 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2627 len(vbmeta_blob))
2628 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002629
David Zeuthend247fcb2017-02-16 12:09:27 -05002630 # Write vbmeta blob, if requested.
2631 if output_vbmeta_image:
2632 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002633
David Zeuthend247fcb2017-02-16 12:09:27 -05002634 # Append vbmeta blob and footer, unless requested not to.
2635 if not do_not_append_vbmeta_image:
2636 image.append_raw(vbmeta_blob_with_padding)
2637
2638 # Now insert a DONT_CARE chunk with enough bytes such that the
2639 # final Footer block is at the end of partition_size..
2640 image.append_dont_care(partition_size - image.image_size -
2641 1*image.block_size)
2642
2643 # Generate the Footer that tells where the VBMeta footer
2644 # is. Also put enough padding in the front of the footer since
2645 # we'll write out an entire block.
2646 footer = AvbFooter()
2647 footer.original_image_size = original_image_size
2648 footer.vbmeta_offset = vbmeta_offset
2649 footer.vbmeta_size = len(vbmeta_blob)
2650 footer_blob = footer.encode()
2651 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2652 footer_blob)
2653 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002654
David Zeuthen21e95262016-07-27 17:58:40 -04002655 except:
David Zeuthen09692692016-09-30 16:16:40 -04002656 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002657 image.truncate(original_image_size)
2658 raise
2659
David Zeuthenc68f0822017-03-31 17:22:35 -04002660 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002661 subject_key_version, subject,
2662 is_intermediate_authority, signing_helper):
2663 """Implements the 'make_atx_certificate' command.
2664
2665 Android Things certificates are required for Android Things public key
2666 metadata. They chain the vbmeta signing key for a particular product back to
2667 a fused, permanent root key. These certificates are fixed-length and fixed-
2668 format with the explicit goal of not parsing ASN.1 in bootloader code.
2669
2670 Arguments:
2671 output: Certificate will be written to this file on success.
2672 authority_key_path: A PEM file path with the authority private key.
2673 If None, then a certificate will be created without a
2674 signature. The signature can be created out-of-band
2675 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04002676 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08002677 subject_key_version: A 64-bit version value. If this is None, the number
2678 of seconds since the epoch is used.
2679 subject: A subject identifier. For Product Signing Key certificates this
2680 should be the same Product ID found in the permanent attributes.
2681 is_intermediate_authority: True if the certificate is for an intermediate
2682 authority.
2683 signing_helper: Program which signs a hash and returns the signature.
2684 """
2685 signed_data = bytearray()
2686 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002687 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002688 hasher = hashlib.sha256()
2689 hasher.update(subject)
2690 signed_data.extend(hasher.digest())
2691 usage = 'com.google.android.things.vboot'
2692 if is_intermediate_authority:
2693 usage += '.ca'
2694 hasher = hashlib.sha256()
2695 hasher.update(usage)
2696 signed_data.extend(hasher.digest())
2697 if not subject_key_version:
2698 subject_key_version = int(time.time())
2699 signed_data.extend(struct.pack('<Q', subject_key_version))
2700 signature = bytearray()
2701 if authority_key_path:
2702 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002703 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002704 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002705 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002706 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002707 hasher.update(signed_data)
2708 padding_and_hash.extend(hasher.digest())
2709 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002710 alg.signature_num_bytes, authority_key_path,
2711 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002712 output.write(signed_data)
2713 output.write(signature)
2714
David Zeuthenc68f0822017-03-31 17:22:35 -04002715 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002716 product_id):
2717 """Implements the 'make_atx_permanent_attributes' command.
2718
2719 Android Things permanent attributes are designed to be permanent for a
2720 particular product and a hash of these attributes should be fused into
2721 hardware to enforce this.
2722
2723 Arguments:
2724 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04002725 root_authority_key_path: Path to a PEM or DER public key for
2726 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08002727 product_id: A 16-byte Product ID.
2728
2729 Raises:
2730 AvbError: If an argument is incorrect.
2731 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002732 EXPECTED_PRODUCT_ID_SIZE = 16
2733 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002734 raise AvbError('Invalid Product ID length.')
2735 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002736 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002737 output.write(product_id)
2738
2739 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002740 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002741 """Implements the 'make_atx_metadata' command.
2742
2743 Android Things metadata are included in vbmeta images to facilitate
2744 verification. The output of this command can be used as the
2745 public_key_metadata argument to other commands.
2746
2747 Arguments:
2748 output: Metadata will be written to this file on success.
2749 intermediate_key_certificate: A certificate file as output by
2750 make_atx_certificate with
2751 is_intermediate_authority set to true.
2752 product_key_certificate: A certificate file as output by
2753 make_atx_certificate with
2754 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002755
2756 Raises:
2757 AvbError: If an argument is incorrect.
2758 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002759 EXPECTED_CERTIFICATE_SIZE = 1620
2760 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002761 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002762 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002763 raise AvbError('Invalid product key certificate length.')
2764 output.write(struct.pack('<I', 1)) # Format Version
2765 output.write(intermediate_key_certificate)
2766 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002767
David Zeuthen21e95262016-07-27 17:58:40 -04002768
2769def calc_hash_level_offsets(image_size, block_size, digest_size):
2770 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2771
2772 Arguments:
2773 image_size: The size of the image to calculate a Merkle-tree for.
2774 block_size: The block size, e.g. 4096.
2775 digest_size: The size of each hash, e.g. 32 for SHA-256.
2776
2777 Returns:
2778 A tuple where the first argument is an array of offsets and the
2779 second is size of the tree, in bytes.
2780 """
2781 level_offsets = []
2782 level_sizes = []
2783 tree_size = 0
2784
2785 num_levels = 0
2786 size = image_size
2787 while size > block_size:
2788 num_blocks = (size + block_size - 1) / block_size
2789 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2790
2791 level_sizes.append(level_size)
2792 tree_size += level_size
2793 num_levels += 1
2794
2795 size = level_size
2796
2797 for n in range(0, num_levels):
2798 offset = 0
2799 for m in range(n + 1, num_levels):
2800 offset += level_sizes[m]
2801 level_offsets.append(offset)
2802
David Zeuthena4fee8b2016-08-22 15:20:43 -04002803 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002804
2805
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002806# See system/extras/libfec/include/fec/io.h for these definitions.
2807FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2808FEC_MAGIC = 0xfecfecfe
2809
2810
2811def calc_fec_data_size(image_size, num_roots):
2812 """Calculates how much space FEC data will take.
2813
2814 Args:
2815 image_size: The size of the image.
2816 num_roots: Number of roots.
2817
2818 Returns:
2819 The number of bytes needed for FEC for an image of the given size
2820 and with the requested number of FEC roots.
2821
2822 Raises:
2823 ValueError: If output from the 'fec' tool is invalid.
2824
2825 """
2826 p = subprocess.Popen(
2827 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2828 stdout=subprocess.PIPE,
2829 stderr=subprocess.PIPE)
2830 (pout, perr) = p.communicate()
2831 retcode = p.wait()
2832 if retcode != 0:
2833 raise ValueError('Error invoking fec: {}'.format(perr))
2834 return int(pout)
2835
2836
2837def generate_fec_data(image_filename, num_roots):
2838 """Generate FEC codes for an image.
2839
2840 Args:
2841 image_filename: The filename of the image.
2842 num_roots: Number of roots.
2843
2844 Returns:
2845 The FEC data blob.
2846
2847 Raises:
2848 ValueError: If output from the 'fec' tool is invalid.
2849 """
2850 fec_tmpfile = tempfile.NamedTemporaryFile()
2851 subprocess.check_call(
2852 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2853 fec_tmpfile.name],
2854 stderr=open(os.devnull))
2855 fec_data = fec_tmpfile.read()
2856 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2857 footer_data = fec_data[-footer_size:]
2858 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2859 footer_data)
2860 if magic != FEC_MAGIC:
2861 raise ValueError('Unexpected magic in FEC footer')
2862 return fec_data[0:fec_size]
2863
2864
David Zeuthen21e95262016-07-27 17:58:40 -04002865def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002866 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002867 """Generates a Merkle-tree for a file.
2868
2869 Args:
2870 image: The image, as a file.
2871 image_size: The size of the image.
2872 block_size: The block size, e.g. 4096.
2873 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2874 salt: The salt to use.
2875 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002876 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002877 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002878
2879 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002880 A tuple where the first element is the top-level hash and the
2881 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002882 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002883 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002884 hash_src_offset = 0
2885 hash_src_size = image_size
2886 level_num = 0
2887 while hash_src_size > block_size:
2888 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002889 remaining = hash_src_size
2890 while remaining > 0:
2891 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002892 # Only read from the file for the first level - for subsequent
2893 # levels, access the array we're building.
2894 if level_num == 0:
2895 image.seek(hash_src_offset + hash_src_size - remaining)
2896 data = image.read(min(remaining, block_size))
2897 else:
2898 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2899 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002900 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002901
2902 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002903 if len(data) < block_size:
2904 hasher.update('\0' * (block_size - len(data)))
2905 level_output += hasher.digest()
2906 if digest_padding > 0:
2907 level_output += '\0' * digest_padding
2908
2909 padding_needed = (round_to_multiple(
2910 len(level_output), block_size) - len(level_output))
2911 level_output += '\0' * padding_needed
2912
David Zeuthena4fee8b2016-08-22 15:20:43 -04002913 # Copy level-output into resulting tree.
2914 offset = hash_level_offsets[level_num]
2915 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002916
David Zeuthena4fee8b2016-08-22 15:20:43 -04002917 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002918 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002919 level_num += 1
2920
2921 hasher = hashlib.new(name=hash_alg_name, string=salt)
2922 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002923 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002924
2925
2926class AvbTool(object):
2927 """Object for avbtool command-line tool."""
2928
2929 def __init__(self):
2930 """Initializer method."""
2931 self.avb = Avb()
2932
2933 def _add_common_args(self, sub_parser):
2934 """Adds arguments used by several sub-commands.
2935
2936 Arguments:
2937 sub_parser: The parser to add arguments to.
2938 """
2939 sub_parser.add_argument('--algorithm',
2940 help='Algorithm to use (default: NONE)',
2941 metavar='ALGORITHM',
2942 default='NONE')
2943 sub_parser.add_argument('--key',
2944 help='Path to RSA private key file',
2945 metavar='KEY',
2946 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002947 sub_parser.add_argument('--signing_helper',
2948 help='Path to helper used for signing',
2949 metavar='APP',
2950 default=None,
2951 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002952 sub_parser.add_argument('--public_key_metadata',
2953 help='Path to public key metadata file',
2954 metavar='KEY_METADATA',
2955 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002956 sub_parser.add_argument('--rollback_index',
2957 help='Rollback Index',
2958 type=parse_number,
2959 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05002960 # This is used internally for unit tests. Do not include in --help output.
2961 sub_parser.add_argument('--internal_release_string',
2962 help=argparse.SUPPRESS)
2963 sub_parser.add_argument('--append_to_release_string',
2964 help='Text to append to release string',
2965 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04002966 sub_parser.add_argument('--prop',
2967 help='Add property',
2968 metavar='KEY:VALUE',
2969 action='append')
2970 sub_parser.add_argument('--prop_from_file',
2971 help='Add property from file',
2972 metavar='KEY:PATH',
2973 action='append')
2974 sub_parser.add_argument('--kernel_cmdline',
2975 help='Add kernel cmdline',
2976 metavar='CMDLINE',
2977 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002978 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2979 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2980 # at some future point.
2981 sub_parser.add_argument('--setup_rootfs_from_kernel',
2982 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002983 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002984 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002985 type=argparse.FileType('rb'))
2986 sub_parser.add_argument('--include_descriptors_from_image',
2987 help='Include descriptors from image',
2988 metavar='IMAGE',
2989 action='append',
2990 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05002991 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
2992 sub_parser.add_argument('--chain_partition',
2993 help='Allow signed integrity-data for partition',
2994 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2995 action='append')
2996 sub_parser.add_argument('--flags',
2997 help='VBMeta flags',
2998 type=parse_number,
2999 default=0)
3000 sub_parser.add_argument('--set_hashtree_disabled_flag',
3001 help='Set the HASHTREE_DISABLED flag',
3002 action='store_true')
3003
3004 def _fixup_common_args(self, args):
3005 """Common fixups needed by subcommands.
3006
3007 Arguments:
3008 args: Arguments to modify.
3009
3010 Returns:
3011 The modified arguments.
3012 """
3013 if args.set_hashtree_disabled_flag:
3014 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3015 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003016
3017 def run(self, argv):
3018 """Command-line processor.
3019
3020 Arguments:
3021 argv: Pass sys.argv from main.
3022 """
3023 parser = argparse.ArgumentParser()
3024 subparsers = parser.add_subparsers(title='subcommands')
3025
3026 sub_parser = subparsers.add_parser('version',
3027 help='Prints version of avbtool.')
3028 sub_parser.set_defaults(func=self.version)
3029
3030 sub_parser = subparsers.add_parser('extract_public_key',
3031 help='Extract public key.')
3032 sub_parser.add_argument('--key',
3033 help='Path to RSA private key file',
3034 required=True)
3035 sub_parser.add_argument('--output',
3036 help='Output file name',
3037 type=argparse.FileType('wb'),
3038 required=True)
3039 sub_parser.set_defaults(func=self.extract_public_key)
3040
3041 sub_parser = subparsers.add_parser('make_vbmeta_image',
3042 help='Makes a vbmeta image.')
3043 sub_parser.add_argument('--output',
3044 help='Output file name',
3045 type=argparse.FileType('wb'),
3046 required=True)
3047 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003048 sub_parser.set_defaults(func=self.make_vbmeta_image)
3049
3050 sub_parser = subparsers.add_parser('add_hash_footer',
3051 help='Add hashes and footer to image.')
3052 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003053 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003054 type=argparse.FileType('rab+'))
3055 sub_parser.add_argument('--partition_size',
3056 help='Partition size',
3057 type=parse_number,
3058 required=True)
3059 sub_parser.add_argument('--partition_name',
3060 help='Partition name',
3061 required=True)
3062 sub_parser.add_argument('--hash_algorithm',
3063 help='Hash algorithm to use (default: sha256)',
3064 default='sha256')
3065 sub_parser.add_argument('--salt',
3066 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003067 sub_parser.add_argument('--output_vbmeta_image',
3068 help='Also write vbmeta struct to file',
3069 type=argparse.FileType('wb'))
3070 sub_parser.add_argument('--do_not_append_vbmeta_image',
3071 help=('Do not append vbmeta struct or footer '
3072 'to the image'),
3073 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003074 self._add_common_args(sub_parser)
3075 sub_parser.set_defaults(func=self.add_hash_footer)
3076
David Zeuthenb1b994d2017-03-06 18:01:31 -05003077 sub_parser = subparsers.add_parser('append_vbmeta_image',
3078 help='Append vbmeta image to image.')
3079 sub_parser.add_argument('--image',
3080 help='Image to append vbmeta blob to',
3081 type=argparse.FileType('rab+'))
3082 sub_parser.add_argument('--partition_size',
3083 help='Partition size',
3084 type=parse_number,
3085 required=True)
3086 sub_parser.add_argument('--vbmeta_image',
3087 help='Image with vbmeta blob to append',
3088 type=argparse.FileType('rb'))
3089 sub_parser.set_defaults(func=self.append_vbmeta_image)
3090
David Zeuthen21e95262016-07-27 17:58:40 -04003091 sub_parser = subparsers.add_parser('add_hashtree_footer',
3092 help='Add hashtree and footer to image.')
3093 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003094 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003095 type=argparse.FileType('rab+'))
3096 sub_parser.add_argument('--partition_size',
3097 help='Partition size',
3098 type=parse_number,
3099 required=True)
3100 sub_parser.add_argument('--partition_name',
3101 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003102 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003103 sub_parser.add_argument('--hash_algorithm',
3104 help='Hash algorithm to use (default: sha1)',
3105 default='sha1')
3106 sub_parser.add_argument('--salt',
3107 help='Salt in hex (default: /dev/urandom)')
3108 sub_parser.add_argument('--block_size',
3109 help='Block size (default: 4096)',
3110 type=parse_number,
3111 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003112 sub_parser.add_argument('--generate_fec',
3113 help='Add forward-error-correction codes',
3114 action='store_true')
3115 sub_parser.add_argument('--fec_num_roots',
3116 help='Number of roots for FEC (default: 2)',
3117 type=parse_number,
3118 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003119 sub_parser.add_argument('--calc_max_image_size',
3120 help=('Don\'t store the hashtree or footer - '
3121 'instead calculate the maximum image size '
3122 'leaving enough room for hashtree '
3123 'and metadata with the given partition '
3124 'size.'),
3125 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003126 sub_parser.add_argument('--output_vbmeta_image',
3127 help='Also write vbmeta struct to file',
3128 type=argparse.FileType('wb'))
3129 sub_parser.add_argument('--do_not_append_vbmeta_image',
3130 help=('Do not append vbmeta struct or footer '
3131 'to the image'),
3132 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003133 self._add_common_args(sub_parser)
3134 sub_parser.set_defaults(func=self.add_hashtree_footer)
3135
3136 sub_parser = subparsers.add_parser('erase_footer',
3137 help='Erase footer from an image.')
3138 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003139 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003140 type=argparse.FileType('rwb+'),
3141 required=True)
3142 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003143 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003144 action='store_true')
3145 sub_parser.set_defaults(func=self.erase_footer)
3146
3147 sub_parser = subparsers.add_parser(
3148 'info_image',
3149 help='Show information about vbmeta or footer.')
3150 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003151 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003152 type=argparse.FileType('rb'),
3153 required=True)
3154 sub_parser.add_argument('--output',
3155 help='Write info to file',
3156 type=argparse.FileType('wt'),
3157 default=sys.stdout)
3158 sub_parser.set_defaults(func=self.info_image)
3159
David Zeuthen8b6973b2016-09-20 12:39:49 -04003160 sub_parser = subparsers.add_parser('set_ab_metadata',
3161 help='Set A/B metadata.')
3162 sub_parser.add_argument('--misc_image',
3163 help=('The misc image to modify. If the image does '
3164 'not exist, it will be created.'),
3165 type=argparse.FileType('r+b'),
3166 required=True)
3167 sub_parser.add_argument('--slot_data',
3168 help=('Slot data of the form "priority", '
3169 '"tries_remaining", "sucessful_boot" for '
3170 'slot A followed by the same for slot B, '
3171 'separated by colons. The default value '
3172 'is 15:7:0:14:7:0.'),
3173 default='15:7:0:14:7:0')
3174 sub_parser.set_defaults(func=self.set_ab_metadata)
3175
Darren Krahn147b08d2016-12-20 16:38:29 -08003176 sub_parser = subparsers.add_parser(
3177 'make_atx_certificate',
3178 help='Create an Android Things eXtension (ATX) certificate.')
3179 sub_parser.add_argument('--output',
3180 help='Write certificate to file',
3181 type=argparse.FileType('wb'),
3182 default=sys.stdout)
3183 sub_parser.add_argument('--subject',
3184 help=('Path to subject file'),
3185 type=argparse.FileType('rb'),
3186 required=True)
3187 sub_parser.add_argument('--subject_key',
3188 help=('Path to subject RSA public key file'),
3189 type=argparse.FileType('rb'),
3190 required=True)
3191 sub_parser.add_argument('--subject_key_version',
3192 help=('Version of the subject key'),
3193 type=parse_number,
3194 required=False)
3195 sub_parser.add_argument('--subject_is_intermediate_authority',
3196 help=('Generate an intermediate authority '
3197 'certificate'),
3198 action='store_true')
3199 sub_parser.add_argument('--authority_key',
3200 help='Path to authority RSA private key file',
3201 required=False)
3202 sub_parser.add_argument('--signing_helper',
3203 help='Path to helper used for signing',
3204 metavar='APP',
3205 default=None,
3206 required=False)
3207 sub_parser.set_defaults(func=self.make_atx_certificate)
3208
3209 sub_parser = subparsers.add_parser(
3210 'make_atx_permanent_attributes',
3211 help='Create Android Things eXtension (ATX) permanent attributes.')
3212 sub_parser.add_argument('--output',
3213 help='Write attributes to file',
3214 type=argparse.FileType('wb'),
3215 default=sys.stdout)
3216 sub_parser.add_argument('--root_authority_key',
3217 help='Path to authority RSA public key file',
3218 type=argparse.FileType('rb'),
3219 required=True)
3220 sub_parser.add_argument('--product_id',
3221 help=('Path to Product ID file'),
3222 type=argparse.FileType('rb'),
3223 required=True)
3224 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3225
3226 sub_parser = subparsers.add_parser(
3227 'make_atx_metadata',
3228 help='Create Android Things eXtension (ATX) metadata.')
3229 sub_parser.add_argument('--output',
3230 help='Write metadata to file',
3231 type=argparse.FileType('wb'),
3232 default=sys.stdout)
3233 sub_parser.add_argument('--intermediate_key_certificate',
3234 help='Path to intermediate key certificate file',
3235 type=argparse.FileType('rb'),
3236 required=True)
3237 sub_parser.add_argument('--product_key_certificate',
3238 help='Path to product key certificate file',
3239 type=argparse.FileType('rb'),
3240 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003241 sub_parser.set_defaults(func=self.make_atx_metadata)
3242
David Zeuthen21e95262016-07-27 17:58:40 -04003243 args = parser.parse_args(argv[1:])
3244 try:
3245 args.func(args)
3246 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003247 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003248 sys.exit(1)
3249
3250 def version(self, _):
3251 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003252 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003253
3254 def extract_public_key(self, args):
3255 """Implements the 'extract_public_key' sub-command."""
3256 self.avb.extract_public_key(args.key, args.output)
3257
3258 def make_vbmeta_image(self, args):
3259 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003260 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003261 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003262 args.algorithm, args.key,
3263 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003264 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003265 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003266 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003267 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003268 args.signing_helper,
3269 args.internal_release_string,
3270 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003271
David Zeuthenb1b994d2017-03-06 18:01:31 -05003272 def append_vbmeta_image(self, args):
3273 """Implements the 'append_vbmeta_image' sub-command."""
3274 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3275 args.partition_size)
3276
David Zeuthen21e95262016-07-27 17:58:40 -04003277 def add_hash_footer(self, args):
3278 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003279 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003280 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003281 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003282 args.salt, args.chain_partition, args.algorithm,
3283 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003284 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003285 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003286 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003287 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003288 args.include_descriptors_from_image,
3289 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003290 args.internal_release_string,
3291 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003292 args.output_vbmeta_image,
3293 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003294
3295 def add_hashtree_footer(self, args):
3296 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003297 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003298 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3299 args.partition_size,
3300 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003301 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003302 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003303 args.salt, args.chain_partition, args.algorithm,
3304 args.key, args.public_key_metadata,
3305 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003306 args.prop_from_file,
3307 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003308 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003309 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003310 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003311 args.internal_release_string,
3312 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003313 args.output_vbmeta_image,
3314 args.do_not_append_vbmeta_image)
3315
David Zeuthen21e95262016-07-27 17:58:40 -04003316 def erase_footer(self, args):
3317 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003318 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003319
David Zeuthen8b6973b2016-09-20 12:39:49 -04003320 def set_ab_metadata(self, args):
3321 """Implements the 'set_ab_metadata' sub-command."""
3322 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3323
David Zeuthen21e95262016-07-27 17:58:40 -04003324 def info_image(self, args):
3325 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003326 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003327
Darren Krahn147b08d2016-12-20 16:38:29 -08003328 def make_atx_certificate(self, args):
3329 """Implements the 'make_atx_certificate' sub-command."""
3330 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003331 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003332 args.subject_key_version,
3333 args.subject.read(),
3334 args.subject_is_intermediate_authority,
3335 args.signing_helper)
3336
3337 def make_atx_permanent_attributes(self, args):
3338 """Implements the 'make_atx_permanent_attributes' sub-command."""
3339 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003340 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003341 args.product_id.read())
3342
3343 def make_atx_metadata(self, args):
3344 """Implements the 'make_atx_metadata' sub-command."""
3345 self.avb.make_atx_metadata(args.output,
3346 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003347 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003348
David Zeuthen21e95262016-07-27 17:58:40 -04003349
3350if __name__ == '__main__':
3351 tool = AvbTool()
3352 tool.run(sys.argv)