blob: 4b1ed45a8f713b566f8420b0e3b7e76f5b104c32 [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
31import os
32import struct
33import subprocess
34import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040035import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080036import time
David Zeuthen21e95262016-07-27 17:58:40 -040037
38import Crypto.PublicKey.RSA
39
40# Keep in sync with avb_vbmeta_header.h.
41AVB_VERSION_MAJOR = 1
42AVB_VERSION_MINOR = 0
David Zeuthen58305522017-01-11 17:42:47 -050043AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040044
45class AvbError(Exception):
46 """Application-specific errors.
47
48 These errors represent issues for which a stack-trace should not be
49 presented.
50
51 Attributes:
52 message: Error message.
53 """
54
55 def __init__(self, message):
56 Exception.__init__(self, message)
57
58
59class Algorithm(object):
60 """Contains details about an algorithm.
61
62 See the avb_vbmeta_header.h file for more details about
63 algorithms.
64
65 The constant |ALGORITHMS| is a dictionary from human-readable
66 names (e.g 'SHA256_RSA2048') to instances of this class.
67
68 Attributes:
69 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
70 hash_num_bytes: Number of bytes used to store the hash.
71 signature_num_bytes: Number of bytes used to store the signature.
72 public_key_num_bytes: Number of bytes used to store the public key.
73 padding: Padding used for signature, if any.
74 """
75
76 def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
77 public_key_num_bytes, padding):
78 self.algorithm_type = algorithm_type
79 self.hash_num_bytes = hash_num_bytes
80 self.signature_num_bytes = signature_num_bytes
81 self.public_key_num_bytes = public_key_num_bytes
82 self.padding = padding
83
84# This must be kept in sync with the avb_crypto.h file.
85#
86# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
87# obtained from section 5.2.2 of RFC 4880.
88ALGORITHMS = {
89 'NONE': Algorithm(
90 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
91 hash_num_bytes=0,
92 signature_num_bytes=0,
93 public_key_num_bytes=0,
94 padding=[]),
95 'SHA256_RSA2048': Algorithm(
96 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
97 hash_num_bytes=32,
98 signature_num_bytes=256,
99 public_key_num_bytes=8 + 2*2048/8,
100 padding=[
101 # PKCS1-v1_5 padding
102 0x00, 0x01] + [0xff]*202 + [0x00] + [
103 # ASN.1 header
104 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
105 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
106 0x00, 0x04, 0x20,
107 ]),
108 'SHA256_RSA4096': Algorithm(
109 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
110 hash_num_bytes=32,
111 signature_num_bytes=512,
112 public_key_num_bytes=8 + 2*4096/8,
113 padding=[
114 # PKCS1-v1_5 padding
115 0x00, 0x01] + [0xff]*458 + [0x00] + [
116 # ASN.1 header
117 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
118 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
119 0x00, 0x04, 0x20,
120 ]),
121 'SHA256_RSA8192': Algorithm(
122 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
123 hash_num_bytes=32,
124 signature_num_bytes=1024,
125 public_key_num_bytes=8 + 2*8192/8,
126 padding=[
127 # PKCS1-v1_5 padding
128 0x00, 0x01] + [0xff]*970 + [0x00] + [
129 # ASN.1 header
130 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
131 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
132 0x00, 0x04, 0x20,
133 ]),
134 'SHA512_RSA2048': Algorithm(
135 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
136 hash_num_bytes=64,
137 signature_num_bytes=256,
138 public_key_num_bytes=8 + 2*2048/8,
139 padding=[
140 # PKCS1-v1_5 padding
141 0x00, 0x01] + [0xff]*170 + [0x00] + [
142 # ASN.1 header
143 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
144 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
145 0x00, 0x04, 0x40
146 ]),
147 'SHA512_RSA4096': Algorithm(
148 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
149 hash_num_bytes=64,
150 signature_num_bytes=512,
151 public_key_num_bytes=8 + 2*4096/8,
152 padding=[
153 # PKCS1-v1_5 padding
154 0x00, 0x01] + [0xff]*426 + [0x00] + [
155 # ASN.1 header
156 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
157 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
158 0x00, 0x04, 0x40
159 ]),
160 'SHA512_RSA8192': Algorithm(
161 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
162 hash_num_bytes=64,
163 signature_num_bytes=1024,
164 public_key_num_bytes=8 + 2*8192/8,
165 padding=[
166 # PKCS1-v1_5 padding
167 0x00, 0x01] + [0xff]*938 + [0x00] + [
168 # ASN.1 header
169 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
170 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
171 0x00, 0x04, 0x40
172 ]),
173}
174
175
176def round_to_multiple(number, size):
177 """Rounds a number up to nearest multiple of another number.
178
179 Args:
180 number: The number to round up.
181 size: The multiple to round up to.
182
183 Returns:
184 If |number| is a multiple of |size|, returns |number|, otherwise
185 returns |number| + |size|.
186 """
187 remainder = number % size
188 if remainder == 0:
189 return number
190 return number + size - remainder
191
192
193def round_to_pow2(number):
194 """Rounds a number up to the next power of 2.
195
196 Args:
197 number: The number to round up.
198
199 Returns:
200 If |number| is already a power of 2 then |number| is
201 returned. Otherwise the smallest power of 2 greater than |number|
202 is returned.
203 """
204 return 2**((number - 1).bit_length())
205
206
207def write_long(output, num_bits, value):
208 """Writes a long to an output stream using a given amount of bits.
209
210 This number is written big-endian, e.g. with the most significant
211 bit first.
212
213 Arguments:
214 output: The object to write the output to.
215 num_bits: The number of bits to write, e.g. 2048.
216 value: The value to write.
217 """
218 for bit_pos in range(num_bits, 0, -8):
219 octet = (value >> (bit_pos - 8)) & 0xff
220 output.write(struct.pack('!B', octet))
221
222
223def encode_long(num_bits, value):
224 """Encodes a long to a bytearray() using a given amount of bits.
225
226 This number is written big-endian, e.g. with the most significant
227 bit first.
228
229 Arguments:
230 num_bits: The number of bits to write, e.g. 2048.
231 value: The value to write.
232
233 Returns:
234 A bytearray() with the encoded long.
235 """
236 ret = bytearray()
237 for bit_pos in range(num_bits, 0, -8):
238 octet = (value >> (bit_pos - 8)) & 0xff
239 ret.extend(struct.pack('!B', octet))
240 return ret
241
242
243def egcd(a, b):
244 """Calculate greatest common divisor of two numbers.
245
246 This implementation uses a recursive version of the extended
247 Euclidian algorithm.
248
249 Arguments:
250 a: First number.
251 b: Second number.
252
253 Returns:
254 A tuple (gcd, x, y) that where |gcd| is the greatest common
255 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
256 """
257 if a == 0:
258 return (b, 0, 1)
259 else:
260 g, y, x = egcd(b % a, a)
261 return (g, x - (b // a) * y, y)
262
263
264def modinv(a, m):
265 """Calculate modular multiplicative inverse of |a| modulo |m|.
266
267 This calculates the number |x| such that |a| * |x| == 1 (modulo
268 |m|). This number only exists if |a| and |m| are co-prime - |None|
269 is returned if this isn't true.
270
271 Arguments:
272 a: The number to calculate a modular inverse of.
273 m: The modulo to use.
274
275 Returns:
276 The modular multiplicative inverse of |a| and |m| or |None| if
277 these numbers are not co-prime.
278 """
279 gcd, x, _ = egcd(a, m)
280 if gcd != 1:
281 return None # modular inverse does not exist
282 else:
283 return x % m
284
285
286def parse_number(string):
287 """Parse a string as a number.
288
289 This is just a short-hand for int(string, 0) suitable for use in the
290 |type| parameter of |ArgumentParser|'s add_argument() function. An
291 improvement to just using type=int is that this function supports
292 numbers in other bases, e.g. "0x1234".
293
294 Arguments:
295 string: The string to parse.
296
297 Returns:
298 The parsed integer.
299
300 Raises:
301 ValueError: If the number could not be parsed.
302 """
303 return int(string, 0)
304
305
306def write_rsa_key(output, key):
307 """Writes a public RSA key in |AvbRSAPublicKeyHeader| format.
308
309 This writes the |AvbRSAPublicKeyHeader| as well as the two large
310 numbers (|key_num_bits| bits long) following it.
311
312 Arguments:
313 output: The object to write the output to.
314 key: A Crypto.PublicKey.RSA object.
315 """
316 # key.e is exponent
317 # key.n is modulus
318 key_num_bits = key.size() + 1
319 # Calculate n0inv = -1/n[0] (mod 2^32)
320 b = 2L**32
321 n0inv = b - modinv(key.n, b)
322 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
323 r = 2L**key.n.bit_length()
324 rrmodn = r * r % key.n
325 output.write(struct.pack('!II', key_num_bits, n0inv))
326 write_long(output, key_num_bits, key.n)
327 write_long(output, key_num_bits, rrmodn)
328
329
330def encode_rsa_key(key):
331 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
332
333 This creates a |AvbRSAPublicKeyHeader| as well as the two large
334 numbers (|key_num_bits| bits long) following it.
335
336 Arguments:
337 key: A Crypto.PublicKey.RSA object.
338
339 Returns:
340 A bytearray() with the |AvbRSAPublicKeyHeader|.
341 """
342 ret = bytearray()
343 # key.e is exponent
344 # key.n is modulus
345 key_num_bits = key.size() + 1
346 # Calculate n0inv = -1/n[0] (mod 2^32)
347 b = 2L**32
348 n0inv = b - modinv(key.n, b)
349 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
350 r = 2L**key.n.bit_length()
351 rrmodn = r * r % key.n
352 ret.extend(struct.pack('!II', key_num_bits, n0inv))
353 ret.extend(encode_long(key_num_bits, key.n))
354 ret.extend(encode_long(key_num_bits, rrmodn))
355 return ret
356
357
358def lookup_algorithm_by_type(alg_type):
359 """Looks up algorithm by type.
360
361 Arguments:
362 alg_type: The integer representing the type.
363
364 Returns:
365 A tuple with the algorithm name and an |Algorithm| instance.
366
367 Raises:
368 Exception: If the algorithm cannot be found
369 """
370 for alg_name in ALGORITHMS:
371 alg_data = ALGORITHMS[alg_name]
372 if alg_data.algorithm_type == alg_type:
373 return (alg_name, alg_data)
374 raise AvbError('Unknown algorithm type {}'.format(alg_type))
375
376
Darren Krahn147b08d2016-12-20 16:38:29 -0800377def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
378 """Computes a raw RSA signature using |signing_helper| or openssl.
379
380 Arguments:
381 signing_helper: Program which signs a hash and returns the signature.
382 algorithm_name: The algorithm name as per the ALGORITHMS dict.
383 key_path: Path to the private key file. Must be PEM format.
384 raw_data_to_sign: Data to sign (bytearray or str expected).
385
386 Returns:
387 A bytearray containing the signature.
388
389 Raises:
390 Exception: If an error occurs.
391 """
392 p = None
393 if signing_helper is not None:
394 p = subprocess.Popen(
395 [signing_helper, algorithm_name, key_path],
396 stdin=subprocess.PIPE,
397 stdout=subprocess.PIPE,
398 stderr=subprocess.PIPE)
399 else:
400 p = subprocess.Popen(
401 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
402 stdin=subprocess.PIPE,
403 stdout=subprocess.PIPE,
404 stderr=subprocess.PIPE)
405 (pout, perr) = p.communicate(str(raw_data_to_sign))
406 retcode = p.wait()
407 if retcode != 0:
408 raise AvbError('Error signing: {}'.format(perr))
409 return bytearray(pout)
410
411
David Zeuthena4fee8b2016-08-22 15:20:43 -0400412class ImageChunk(object):
413 """Data structure used for representing chunks in Android sparse files.
414
415 Attributes:
416 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
417 chunk_offset: Offset in the sparse file where this chunk begins.
418 output_offset: Offset in de-sparsified file where output begins.
419 output_size: Number of bytes in output.
420 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
421 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
422 """
423
424 FORMAT = '<2H2I'
425 TYPE_RAW = 0xcac1
426 TYPE_FILL = 0xcac2
427 TYPE_DONT_CARE = 0xcac3
428 TYPE_CRC32 = 0xcac4
429
430 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
431 input_offset, fill_data):
432 """Initializes an ImageChunk object.
433
434 Arguments:
435 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
436 chunk_offset: Offset in the sparse file where this chunk begins.
437 output_offset: Offset in de-sparsified file.
438 output_size: Number of bytes in output.
439 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
440 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
441
442 Raises:
443 ValueError: If data is not well-formed.
444 """
445 self.chunk_type = chunk_type
446 self.chunk_offset = chunk_offset
447 self.output_offset = output_offset
448 self.output_size = output_size
449 self.input_offset = input_offset
450 self.fill_data = fill_data
451 # Check invariants.
452 if self.chunk_type == self.TYPE_RAW:
453 if self.fill_data is not None:
454 raise ValueError('RAW chunk cannot have fill_data set.')
455 if not self.input_offset:
456 raise ValueError('RAW chunk must have input_offset set.')
457 elif self.chunk_type == self.TYPE_FILL:
458 if self.fill_data is None:
459 raise ValueError('FILL chunk must have fill_data set.')
460 if self.input_offset:
461 raise ValueError('FILL chunk cannot have input_offset set.')
462 elif self.chunk_type == self.TYPE_DONT_CARE:
463 if self.fill_data is not None:
464 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
465 if self.input_offset:
466 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
467 else:
468 raise ValueError('Invalid chunk type')
469
470
471class ImageHandler(object):
472 """Abstraction for image I/O with support for Android sparse images.
473
474 This class provides an interface for working with image files that
475 may be using the Android Sparse Image format. When an instance is
476 constructed, we test whether it's an Android sparse file. If so,
477 operations will be on the sparse file by interpreting the sparse
478 format, otherwise they will be directly on the file. Either way the
479 operations do the same.
480
481 For reading, this interface mimics a file object - it has seek(),
482 tell(), and read() methods. For writing, only truncation
483 (truncate()) and appending is supported (append_raw() and
484 append_dont_care()). Additionally, data can only be written in units
485 of the block size.
486
487 Attributes:
488 is_sparse: Whether the file being operated on is sparse.
489 block_size: The block size, typically 4096.
490 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400491 """
492 # See system/core/libsparse/sparse_format.h for details.
493 MAGIC = 0xed26ff3a
494 HEADER_FORMAT = '<I4H4I'
495
496 # These are formats and offset of just the |total_chunks| and
497 # |total_blocks| fields.
498 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
499 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
500
501 def __init__(self, image_filename):
502 """Initializes an image handler.
503
504 Arguments:
505 image_filename: The name of the file to operate on.
506
507 Raises:
508 ValueError: If data in the file is invalid.
509 """
510 self._image_filename = image_filename
511 self._read_header()
512
513 def _read_header(self):
514 """Initializes internal data structures used for reading file.
515
516 This may be called multiple times and is typically called after
517 modifying the file (e.g. appending, truncation).
518
519 Raises:
520 ValueError: If data in the file is invalid.
521 """
522 self.is_sparse = False
523 self.block_size = 4096
524 self._file_pos = 0
525 self._image = open(self._image_filename, 'r+b')
526 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400527 self.image_size = self._image.tell()
528
529 self._image.seek(0, os.SEEK_SET)
530 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
531 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
532 block_size, self._num_total_blocks, self._num_total_chunks,
533 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
534 if magic != self.MAGIC:
535 # Not a sparse image, our job here is done.
536 return
537 if not (major_version == 1 and minor_version == 0):
538 raise ValueError('Encountered sparse image format version {}.{} but '
539 'only 1.0 is supported'.format(major_version,
540 minor_version))
541 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
542 raise ValueError('Unexpected file_hdr_sz value {}.'.
543 format(file_hdr_sz))
544 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
545 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
546 format(chunk_hdr_sz))
547
548 self.block_size = block_size
549
550 # Build an list of chunks by parsing the file.
551 self._chunks = []
552
553 # Find the smallest offset where only "Don't care" chunks
554 # follow. This will be the size of the content in the sparse
555 # image.
556 offset = 0
557 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400558 for _ in xrange(1, self._num_total_chunks + 1):
559 chunk_offset = self._image.tell()
560
561 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
562 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
563 header_bin)
564 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
565
David Zeuthena4fee8b2016-08-22 15:20:43 -0400566 if chunk_type == ImageChunk.TYPE_RAW:
567 if data_sz != (chunk_sz * self.block_size):
568 raise ValueError('Raw chunk input size ({}) does not match output '
569 'size ({})'.
570 format(data_sz, chunk_sz*self.block_size))
571 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
572 chunk_offset,
573 output_offset,
574 chunk_sz*self.block_size,
575 self._image.tell(),
576 None))
577 self._image.read(data_sz)
578
579 elif chunk_type == ImageChunk.TYPE_FILL:
580 if data_sz != 4:
581 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
582 'has {}'.format(data_sz))
583 fill_data = self._image.read(4)
584 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
585 chunk_offset,
586 output_offset,
587 chunk_sz*self.block_size,
588 None,
589 fill_data))
590 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
591 if data_sz != 0:
592 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
593 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400594 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
595 chunk_offset,
596 output_offset,
597 chunk_sz*self.block_size,
598 None,
599 None))
600 elif chunk_type == ImageChunk.TYPE_CRC32:
601 if data_sz != 4:
602 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
603 'this has {}'.format(data_sz))
604 self._image.read(4)
605 else:
606 raise ValueError('Unknown chunk type {}'.format(chunk_type))
607
608 offset += chunk_sz
609 output_offset += chunk_sz*self.block_size
610
611 # Record where sparse data end.
612 self._sparse_end = self._image.tell()
613
614 # Now that we've traversed all chunks, sanity check.
615 if self._num_total_blocks != offset:
616 raise ValueError('The header said we should have {} output blocks, '
617 'but we saw {}'.format(self._num_total_blocks, offset))
618 junk_len = len(self._image.read())
619 if junk_len > 0:
620 raise ValueError('There were {} bytes of extra data at the end of the '
621 'file.'.format(junk_len))
622
David Zeuthen09692692016-09-30 16:16:40 -0400623 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400624 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400625
626 # This is used when bisecting in read() to find the initial slice.
627 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
628
629 self.is_sparse = True
630
631 def _update_chunks_and_blocks(self):
632 """Helper function to update the image header.
633
634 The the |total_chunks| and |total_blocks| fields in the header
635 will be set to value of the |_num_total_blocks| and
636 |_num_total_chunks| attributes.
637
638 """
639 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
640 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
641 self._num_total_blocks,
642 self._num_total_chunks))
643
644 def append_dont_care(self, num_bytes):
645 """Appends a DONT_CARE chunk to the sparse file.
646
647 The given number of bytes must be a multiple of the block size.
648
649 Arguments:
650 num_bytes: Size in number of bytes of the DONT_CARE chunk.
651 """
652 assert num_bytes % self.block_size == 0
653
654 if not self.is_sparse:
655 self._image.seek(0, os.SEEK_END)
656 # This is more efficient that writing NUL bytes since it'll add
657 # a hole on file systems that support sparse files (native
658 # sparse, not Android sparse).
659 self._image.truncate(self._image.tell() + num_bytes)
660 self._read_header()
661 return
662
663 self._num_total_chunks += 1
664 self._num_total_blocks += num_bytes / self.block_size
665 self._update_chunks_and_blocks()
666
667 self._image.seek(self._sparse_end, os.SEEK_SET)
668 self._image.write(struct.pack(ImageChunk.FORMAT,
669 ImageChunk.TYPE_DONT_CARE,
670 0, # Reserved
671 num_bytes / self.block_size,
672 struct.calcsize(ImageChunk.FORMAT)))
673 self._read_header()
674
675 def append_raw(self, data):
676 """Appends a RAW chunk to the sparse file.
677
678 The length of the given data must be a multiple of the block size.
679
680 Arguments:
681 data: Data to append.
682 """
683 assert len(data) % self.block_size == 0
684
685 if not self.is_sparse:
686 self._image.seek(0, os.SEEK_END)
687 self._image.write(data)
688 self._read_header()
689 return
690
691 self._num_total_chunks += 1
692 self._num_total_blocks += len(data) / self.block_size
693 self._update_chunks_and_blocks()
694
695 self._image.seek(self._sparse_end, os.SEEK_SET)
696 self._image.write(struct.pack(ImageChunk.FORMAT,
697 ImageChunk.TYPE_RAW,
698 0, # Reserved
699 len(data) / self.block_size,
700 len(data) +
701 struct.calcsize(ImageChunk.FORMAT)))
702 self._image.write(data)
703 self._read_header()
704
705 def append_fill(self, fill_data, size):
706 """Appends a fill chunk to the sparse file.
707
708 The total length of the fill data must be a multiple of the block size.
709
710 Arguments:
711 fill_data: Fill data to append - must be four bytes.
712 size: Number of chunk - must be a multiple of four and the block size.
713 """
714 assert len(fill_data) == 4
715 assert size % 4 == 0
716 assert size % self.block_size == 0
717
718 if not self.is_sparse:
719 self._image.seek(0, os.SEEK_END)
720 self._image.write(fill_data * (size/4))
721 self._read_header()
722 return
723
724 self._num_total_chunks += 1
725 self._num_total_blocks += size / self.block_size
726 self._update_chunks_and_blocks()
727
728 self._image.seek(self._sparse_end, os.SEEK_SET)
729 self._image.write(struct.pack(ImageChunk.FORMAT,
730 ImageChunk.TYPE_FILL,
731 0, # Reserved
732 size / self.block_size,
733 4 + struct.calcsize(ImageChunk.FORMAT)))
734 self._image.write(fill_data)
735 self._read_header()
736
737 def seek(self, offset):
738 """Sets the cursor position for reading from unsparsified file.
739
740 Arguments:
741 offset: Offset to seek to from the beginning of the file.
742 """
743 self._file_pos = offset
744
745 def read(self, size):
746 """Reads data from the unsparsified file.
747
748 This method may return fewer than |size| bytes of data if the end
749 of the file was encountered.
750
751 The file cursor for reading is advanced by the number of bytes
752 read.
753
754 Arguments:
755 size: Number of bytes to read.
756
757 Returns:
758 The data.
759
760 """
761 if not self.is_sparse:
762 self._image.seek(self._file_pos)
763 data = self._image.read(size)
764 self._file_pos += len(data)
765 return data
766
767 # Iterate over all chunks.
768 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
769 self._file_pos) - 1
770 data = bytearray()
771 to_go = size
772 while to_go > 0:
773 chunk = self._chunks[chunk_idx]
774 chunk_pos_offset = self._file_pos - chunk.output_offset
775 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
776
777 if chunk.chunk_type == ImageChunk.TYPE_RAW:
778 self._image.seek(chunk.input_offset + chunk_pos_offset)
779 data.extend(self._image.read(chunk_pos_to_go))
780 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
781 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
782 offset_mod = chunk_pos_offset % len(chunk.fill_data)
783 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
784 else:
785 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
786 data.extend('\0' * chunk_pos_to_go)
787
788 to_go -= chunk_pos_to_go
789 self._file_pos += chunk_pos_to_go
790 chunk_idx += 1
791 # Generate partial read in case of EOF.
792 if chunk_idx >= len(self._chunks):
793 break
794
795 return data
796
797 def tell(self):
798 """Returns the file cursor position for reading from unsparsified file.
799
800 Returns:
801 The file cursor position for reading.
802 """
803 return self._file_pos
804
805 def truncate(self, size):
806 """Truncates the unsparsified file.
807
808 Arguments:
809 size: Desired size of unsparsified file.
810
811 Raises:
812 ValueError: If desired size isn't a multiple of the block size.
813 """
814 if not self.is_sparse:
815 self._image.truncate(size)
816 self._read_header()
817 return
818
819 if size % self.block_size != 0:
820 raise ValueError('Cannot truncate to a size which is not a multiple '
821 'of the block size')
822
823 if size == self.image_size:
824 # Trivial where there's nothing to do.
825 return
826 elif size < self.image_size:
827 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
828 chunk = self._chunks[chunk_idx]
829 if chunk.output_offset != size:
830 # Truncation in the middle of a trunk - need to keep the chunk
831 # and modify it.
832 chunk_idx_for_update = chunk_idx + 1
833 num_to_keep = size - chunk.output_offset
834 assert num_to_keep % self.block_size == 0
835 if chunk.chunk_type == ImageChunk.TYPE_RAW:
836 truncate_at = (chunk.chunk_offset +
837 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
838 data_sz = num_to_keep
839 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
840 truncate_at = (chunk.chunk_offset +
841 struct.calcsize(ImageChunk.FORMAT) + 4)
842 data_sz = 4
843 else:
844 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
845 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
846 data_sz = 0
847 chunk_sz = num_to_keep/self.block_size
848 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
849 self._image.seek(chunk.chunk_offset)
850 self._image.write(struct.pack(ImageChunk.FORMAT,
851 chunk.chunk_type,
852 0, # Reserved
853 chunk_sz,
854 total_sz))
855 chunk.output_size = num_to_keep
856 else:
857 # Truncation at trunk boundary.
858 truncate_at = chunk.chunk_offset
859 chunk_idx_for_update = chunk_idx
860
861 self._num_total_chunks = chunk_idx_for_update
862 self._num_total_blocks = 0
863 for i in range(0, chunk_idx_for_update):
864 self._num_total_blocks += self._chunks[i].output_size / self.block_size
865 self._update_chunks_and_blocks()
866 self._image.truncate(truncate_at)
867
868 # We've modified the file so re-read all data.
869 self._read_header()
870 else:
871 # Truncating to grow - just add a DONT_CARE section.
872 self.append_dont_care(size - self.image_size)
873
874
David Zeuthen21e95262016-07-27 17:58:40 -0400875class AvbDescriptor(object):
876 """Class for AVB descriptor.
877
878 See the |AvbDescriptor| C struct for more information.
879
880 Attributes:
881 tag: The tag identifying what kind of descriptor this is.
882 data: The data in the descriptor.
883 """
884
885 SIZE = 16
886 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
887
888 def __init__(self, data):
889 """Initializes a new property descriptor.
890
891 Arguments:
892 data: If not None, must be a bytearray().
893
894 Raises:
895 LookupError: If the given descriptor is malformed.
896 """
897 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
898
899 if data:
900 (self.tag, num_bytes_following) = (
901 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
902 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
903 else:
904 self.tag = None
905 self.data = None
906
907 def print_desc(self, o):
908 """Print the descriptor.
909
910 Arguments:
911 o: The object to write the output to.
912 """
913 o.write(' Unknown descriptor:\n')
914 o.write(' Tag: {}\n'.format(self.tag))
915 if len(self.data) < 256:
916 o.write(' Data: {} ({} bytes)\n'.format(
917 repr(str(self.data)), len(self.data)))
918 else:
919 o.write(' Data: {} bytes\n'.format(len(self.data)))
920
921 def encode(self):
922 """Serializes the descriptor.
923
924 Returns:
925 A bytearray() with the descriptor data.
926 """
927 num_bytes_following = len(self.data)
928 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
929 padding_size = nbf_with_padding - num_bytes_following
930 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
931 padding = struct.pack(str(padding_size) + 'x')
932 ret = desc + self.data + padding
933 return bytearray(ret)
934
935
936class AvbPropertyDescriptor(AvbDescriptor):
937 """A class for property descriptors.
938
939 See the |AvbPropertyDescriptor| C struct for more information.
940
941 Attributes:
942 key: The key.
943 value: The key.
944 """
945
946 TAG = 0
947 SIZE = 32
948 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
949 'Q' # key size (bytes)
950 'Q') # value size (bytes)
951
952 def __init__(self, data=None):
953 """Initializes a new property descriptor.
954
955 Arguments:
956 data: If not None, must be a bytearray of size |SIZE|.
957
958 Raises:
959 LookupError: If the given descriptor is malformed.
960 """
961 AvbDescriptor.__init__(self, None)
962 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
963
964 if data:
965 (tag, num_bytes_following, key_size,
966 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
967 expected_size = round_to_multiple(
968 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
969 if tag != self.TAG or num_bytes_following != expected_size:
970 raise LookupError('Given data does not look like a property '
971 'descriptor.')
972 self.key = data[self.SIZE:(self.SIZE + key_size)]
973 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
974 value_size)]
975 else:
976 self.key = ''
977 self.value = ''
978
979 def print_desc(self, o):
980 """Print the descriptor.
981
982 Arguments:
983 o: The object to write the output to.
984 """
985 if len(self.value) < 256:
986 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
987 else:
988 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
989
990 def encode(self):
991 """Serializes the descriptor.
992
993 Returns:
994 A bytearray() with the descriptor data.
995 """
996 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
997 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
998 padding_size = nbf_with_padding - num_bytes_following
999 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1000 len(self.key), len(self.value))
1001 padding = struct.pack(str(padding_size) + 'x')
1002 ret = desc + self.key + '\0' + self.value + '\0' + padding
1003 return bytearray(ret)
1004
1005
1006class AvbHashtreeDescriptor(AvbDescriptor):
1007 """A class for hashtree descriptors.
1008
1009 See the |AvbHashtreeDescriptor| C struct for more information.
1010
1011 Attributes:
1012 dm_verity_version: dm-verity version used.
1013 image_size: Size of the image, after rounding up to |block_size|.
1014 tree_offset: Offset of the hash tree in the file.
1015 tree_size: Size of the tree.
1016 data_block_size: Data block size
1017 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001018 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1019 fec_offset: Offset of FEC data (0 if FEC is not used).
1020 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001021 hash_algorithm: Hash algorithm used.
1022 partition_name: Partition name.
1023 salt: Salt used.
1024 root_digest: Root digest.
1025 """
1026
1027 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001028 RESERVED = 64
1029 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001030 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1031 'L' # dm-verity version used
1032 'Q' # image size (bytes)
1033 'Q' # tree offset (bytes)
1034 'Q' # tree size (bytes)
1035 'L' # data block size (bytes)
1036 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001037 'L' # FEC number of roots
1038 'Q' # FEC offset (bytes)
1039 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001040 '32s' # hash algorithm used
1041 'L' # partition name (bytes)
1042 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001043 'L' + # root digest length (bytes)
1044 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001045
1046 def __init__(self, data=None):
1047 """Initializes a new hashtree descriptor.
1048
1049 Arguments:
1050 data: If not None, must be a bytearray of size |SIZE|.
1051
1052 Raises:
1053 LookupError: If the given descriptor is malformed.
1054 """
1055 AvbDescriptor.__init__(self, None)
1056 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1057
1058 if data:
1059 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1060 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001061 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1062 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001063 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1064 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001065 expected_size = round_to_multiple(
1066 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1067 if tag != self.TAG or num_bytes_following != expected_size:
1068 raise LookupError('Given data does not look like a hashtree '
1069 'descriptor.')
1070 # Nuke NUL-bytes at the end.
1071 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1072 o = 0
1073 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1074 partition_name_len)])
1075 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1076 self.partition_name.decode('utf-8')
1077 o += partition_name_len
1078 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1079 o += salt_len
1080 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1081 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1082 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1083
1084 else:
1085 self.dm_verity_version = 0
1086 self.image_size = 0
1087 self.tree_offset = 0
1088 self.tree_size = 0
1089 self.data_block_size = 0
1090 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001091 self.fec_num_roots = 0
1092 self.fec_offset = 0
1093 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001094 self.hash_algorithm = ''
1095 self.partition_name = ''
1096 self.salt = bytearray()
1097 self.root_digest = bytearray()
1098
1099 def print_desc(self, o):
1100 """Print the descriptor.
1101
1102 Arguments:
1103 o: The object to write the output to.
1104 """
1105 o.write(' Hashtree descriptor:\n')
1106 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1107 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1108 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1109 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1110 o.write(' Data Block Size: {} bytes\n'.format(
1111 self.data_block_size))
1112 o.write(' Hash Block Size: {} bytes\n'.format(
1113 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001114 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1115 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1116 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001117 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1118 o.write(' Partition Name: {}\n'.format(self.partition_name))
1119 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1120 'hex')))
1121 o.write(' Root Digest: {}\n'.format(str(
1122 self.root_digest).encode('hex')))
1123
1124 def encode(self):
1125 """Serializes the descriptor.
1126
1127 Returns:
1128 A bytearray() with the descriptor data.
1129 """
1130 encoded_name = self.partition_name.encode('utf-8')
1131 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1132 len(self.root_digest) - 16)
1133 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1134 padding_size = nbf_with_padding - num_bytes_following
1135 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1136 self.dm_verity_version, self.image_size,
1137 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001138 self.hash_block_size, self.fec_num_roots,
1139 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001140 len(encoded_name), len(self.salt), len(self.root_digest),
1141 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001142 padding = struct.pack(str(padding_size) + 'x')
1143 ret = desc + encoded_name + self.salt + self.root_digest + padding
1144 return bytearray(ret)
1145
1146
1147class AvbHashDescriptor(AvbDescriptor):
1148 """A class for hash descriptors.
1149
1150 See the |AvbHashDescriptor| C struct for more information.
1151
1152 Attributes:
1153 image_size: Image size, in bytes.
1154 hash_algorithm: Hash algorithm used.
1155 partition_name: Partition name.
1156 salt: Salt used.
1157 digest: The hash value of salt and data combined.
1158 """
1159
1160 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001161 RESERVED = 64
1162 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001163 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1164 'Q' # image size (bytes)
1165 '32s' # hash algorithm used
1166 'L' # partition name (bytes)
1167 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001168 'L' + # digest length (bytes)
1169 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001170
1171 def __init__(self, data=None):
1172 """Initializes a new hash descriptor.
1173
1174 Arguments:
1175 data: If not None, must be a bytearray of size |SIZE|.
1176
1177 Raises:
1178 LookupError: If the given descriptor is malformed.
1179 """
1180 AvbDescriptor.__init__(self, None)
1181 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1182
1183 if data:
1184 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1185 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001186 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001187 expected_size = round_to_multiple(
1188 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1189 if tag != self.TAG or num_bytes_following != expected_size:
1190 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1191 # Nuke NUL-bytes at the end.
1192 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1193 o = 0
1194 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1195 partition_name_len)])
1196 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1197 self.partition_name.decode('utf-8')
1198 o += partition_name_len
1199 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1200 o += salt_len
1201 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1202 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1203 raise LookupError('digest_len doesn\'t match hash algorithm')
1204
1205 else:
1206 self.image_size = 0
1207 self.hash_algorithm = ''
1208 self.partition_name = ''
1209 self.salt = bytearray()
1210 self.digest = bytearray()
1211
1212 def print_desc(self, o):
1213 """Print the descriptor.
1214
1215 Arguments:
1216 o: The object to write the output to.
1217 """
1218 o.write(' Hash descriptor:\n')
1219 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1220 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1221 o.write(' Partition Name: {}\n'.format(self.partition_name))
1222 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1223 'hex')))
1224 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1225 'hex')))
1226
1227 def encode(self):
1228 """Serializes the descriptor.
1229
1230 Returns:
1231 A bytearray() with the descriptor data.
1232 """
1233 encoded_name = self.partition_name.encode('utf-8')
1234 num_bytes_following = (
1235 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1236 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1237 padding_size = nbf_with_padding - num_bytes_following
1238 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1239 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001240 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001241 padding = struct.pack(str(padding_size) + 'x')
1242 ret = desc + encoded_name + self.salt + self.digest + padding
1243 return bytearray(ret)
1244
1245
1246class AvbKernelCmdlineDescriptor(AvbDescriptor):
1247 """A class for kernel command-line descriptors.
1248
1249 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1250
1251 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001252 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001253 kernel_cmdline: The kernel command-line.
1254 """
1255
1256 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001257 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001258 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001259 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001260 'L') # cmdline length (bytes)
1261
David Zeuthenfd41eb92016-11-17 12:24:47 -05001262 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1263 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1264
David Zeuthen21e95262016-07-27 17:58:40 -04001265 def __init__(self, data=None):
1266 """Initializes a new kernel cmdline descriptor.
1267
1268 Arguments:
1269 data: If not None, must be a bytearray of size |SIZE|.
1270
1271 Raises:
1272 LookupError: If the given descriptor is malformed.
1273 """
1274 AvbDescriptor.__init__(self, None)
1275 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1276
1277 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001278 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001279 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1280 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1281 8)
1282 if tag != self.TAG or num_bytes_following != expected_size:
1283 raise LookupError('Given data does not look like a kernel cmdline '
1284 'descriptor.')
1285 # Nuke NUL-bytes at the end.
1286 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1287 kernel_cmdline_length)])
1288 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1289 self.kernel_cmdline.decode('utf-8')
1290 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001291 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001292 self.kernel_cmdline = ''
1293
1294 def print_desc(self, o):
1295 """Print the descriptor.
1296
1297 Arguments:
1298 o: The object to write the output to.
1299 """
1300 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001301 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001302 o.write(' Kernel Cmdline: {}\n'.format(repr(
1303 self.kernel_cmdline)))
1304
1305 def encode(self):
1306 """Serializes the descriptor.
1307
1308 Returns:
1309 A bytearray() with the descriptor data.
1310 """
1311 encoded_str = self.kernel_cmdline.encode('utf-8')
1312 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1313 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1314 padding_size = nbf_with_padding - num_bytes_following
1315 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001316 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001317 padding = struct.pack(str(padding_size) + 'x')
1318 ret = desc + encoded_str + padding
1319 return bytearray(ret)
1320
1321
1322class AvbChainPartitionDescriptor(AvbDescriptor):
1323 """A class for chained partition descriptors.
1324
1325 See the |AvbChainPartitionDescriptor| C struct for more information.
1326
1327 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001328 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001329 partition_name: Partition name.
1330 public_key: Bytes for the public key.
1331 """
1332
1333 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001334 RESERVED = 64
1335 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001336 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001337 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001338 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001339 'L' + # public_key_size (bytes)
1340 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001341
1342 def __init__(self, data=None):
1343 """Initializes a new chain partition descriptor.
1344
1345 Arguments:
1346 data: If not None, must be a bytearray of size |SIZE|.
1347
1348 Raises:
1349 LookupError: If the given descriptor is malformed.
1350 """
1351 AvbDescriptor.__init__(self, None)
1352 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1353
1354 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001355 (tag, num_bytes_following, self.rollback_index_location,
1356 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001357 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001358 expected_size = round_to_multiple(
1359 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1360 if tag != self.TAG or num_bytes_following != expected_size:
1361 raise LookupError('Given data does not look like a chain partition '
1362 'descriptor.')
1363 o = 0
1364 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1365 partition_name_len)])
1366 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1367 self.partition_name.decode('utf-8')
1368 o += partition_name_len
1369 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1370
1371 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001372 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001373 self.partition_name = ''
1374 self.public_key = bytearray()
1375
1376 def print_desc(self, o):
1377 """Print the descriptor.
1378
1379 Arguments:
1380 o: The object to write the output to.
1381 """
1382 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001383 o.write(' Partition Name: {}\n'.format(self.partition_name))
1384 o.write(' Rollback Index Location: {}\n'.format(
1385 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001386 # Just show the SHA1 of the key, for size reasons.
1387 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001388 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001389
1390 def encode(self):
1391 """Serializes the descriptor.
1392
1393 Returns:
1394 A bytearray() with the descriptor data.
1395 """
1396 encoded_name = self.partition_name.encode('utf-8')
1397 num_bytes_following = (
1398 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1399 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1400 padding_size = nbf_with_padding - num_bytes_following
1401 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001402 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001403 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001404 padding = struct.pack(str(padding_size) + 'x')
1405 ret = desc + encoded_name + self.public_key + padding
1406 return bytearray(ret)
1407
1408
1409DESCRIPTOR_CLASSES = [
1410 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1411 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1412]
1413
1414
1415def parse_descriptors(data):
1416 """Parses a blob of data into descriptors.
1417
1418 Arguments:
1419 data: A bytearray() with encoded descriptors.
1420
1421 Returns:
1422 A list of instances of objects derived from AvbDescriptor. For
1423 unknown descriptors, the class AvbDescriptor is used.
1424 """
1425 o = 0
1426 ret = []
1427 while o < len(data):
1428 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1429 if tag < len(DESCRIPTOR_CLASSES):
1430 c = DESCRIPTOR_CLASSES[tag]
1431 else:
1432 c = AvbDescriptor
1433 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1434 o += 16 + nb_following
1435 return ret
1436
1437
1438class AvbFooter(object):
1439 """A class for parsing and writing footers.
1440
1441 Footers are stored at the end of partitions and point to where the
1442 AvbVBMeta blob is located. They also contain the original size of
1443 the image before AVB information was added.
1444
1445 Attributes:
1446 magic: Magic for identifying the footer, see |MAGIC|.
1447 version_major: The major version of avbtool that wrote the footer.
1448 version_minor: The minor version of avbtool that wrote the footer.
1449 original_image_size: Original image size.
1450 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1451 vbmeta_size: Size of the AvbVBMeta blob.
1452 """
1453
1454 MAGIC = 'AVBf'
1455 SIZE = 64
1456 RESERVED = 28
1457 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1458 'Q' # Original image size.
1459 'Q' # Offset of VBMeta blob.
1460 'Q' + # Size of VBMeta blob.
1461 str(RESERVED) + 'x') # padding for reserved bytes
1462
1463 def __init__(self, data=None):
1464 """Initializes a new footer object.
1465
1466 Arguments:
1467 data: If not None, must be a bytearray of size 4096.
1468
1469 Raises:
1470 LookupError: If the given footer is malformed.
1471 struct.error: If the given data has no footer.
1472 """
1473 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1474
1475 if data:
1476 (self.magic, self.version_major, self.version_minor,
1477 self.original_image_size, self.vbmeta_offset,
1478 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1479 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001480 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001481 else:
1482 self.magic = self.MAGIC
1483 self.version_major = AVB_VERSION_MAJOR
1484 self.version_minor = AVB_VERSION_MINOR
1485 self.original_image_size = 0
1486 self.vbmeta_offset = 0
1487 self.vbmeta_size = 0
1488
David Zeuthena4fee8b2016-08-22 15:20:43 -04001489 def encode(self):
1490 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001491
David Zeuthena4fee8b2016-08-22 15:20:43 -04001492 Returns:
1493 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001494 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001495 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1496 self.version_minor, self.original_image_size,
1497 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001498
1499
1500class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001501 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001502
1503 Attributes:
1504 The attributes correspond to the |AvbVBMetaHeader| struct
1505 defined in avb_vbmeta_header.h.
1506 """
1507
1508 SIZE = 256
1509
1510 # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001511 RESERVED = 132
David Zeuthen21e95262016-07-27 17:58:40 -04001512
1513 # Keep in sync with |AvbVBMetaImageHeader|.
1514 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1515 '2Q' # 2 x block size
1516 'L' # algorithm type
1517 '2Q' # offset, size (hash)
1518 '2Q' # offset, size (signature)
1519 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001520 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001521 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001522 'Q' # rollback_index
1523 'L' + # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001524 str(RESERVED) + 'x') # padding for reserved bytes
1525
1526 def __init__(self, data=None):
1527 """Initializes a new header object.
1528
1529 Arguments:
1530 data: If not None, must be a bytearray of size 8192.
1531
1532 Raises:
1533 Exception: If the given data is malformed.
1534 """
1535 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1536
1537 if data:
1538 (self.magic, self.header_version_major, self.header_version_minor,
1539 self.authentication_data_block_size, self.auxiliary_data_block_size,
1540 self.algorithm_type, self.hash_offset, self.hash_size,
1541 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001542 self.public_key_size, self.public_key_metadata_offset,
1543 self.public_key_metadata_size, self.descriptors_offset,
1544 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001545 self.rollback_index,
1546 self.flags) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001547 # Nuke NUL-bytes at the end of the string.
1548 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001549 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001550 else:
1551 self.magic = 'AVB0'
1552 self.header_version_major = AVB_VERSION_MAJOR
1553 self.header_version_minor = AVB_VERSION_MINOR
1554 self.authentication_data_block_size = 0
1555 self.auxiliary_data_block_size = 0
1556 self.algorithm_type = 0
1557 self.hash_offset = 0
1558 self.hash_size = 0
1559 self.signature_offset = 0
1560 self.signature_size = 0
1561 self.public_key_offset = 0
1562 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001563 self.public_key_metadata_offset = 0
1564 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001565 self.descriptors_offset = 0
1566 self.descriptors_size = 0
1567 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001568 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001569
1570 def save(self, output):
1571 """Serializes the header (256 bytes) to disk.
1572
1573 Arguments:
1574 output: The object to write the output to.
1575 """
1576 output.write(struct.pack(
1577 self.FORMAT_STRING, self.magic, self.header_version_major,
1578 self.header_version_minor, self.authentication_data_block_size,
1579 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1580 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001581 self.public_key_offset, self.public_key_size,
1582 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001583 self.descriptors_offset, self.descriptors_size, self.rollback_index,
1584 self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001585
1586 def encode(self):
1587 """Serializes the header (256) to a bytearray().
1588
1589 Returns:
1590 A bytearray() with the encoded header.
1591 """
1592 return struct.pack(self.FORMAT_STRING, self.magic,
1593 self.header_version_major, self.header_version_minor,
1594 self.authentication_data_block_size,
1595 self.auxiliary_data_block_size, self.algorithm_type,
1596 self.hash_offset, self.hash_size, self.signature_offset,
1597 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001598 self.public_key_size, self.public_key_metadata_offset,
1599 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001600 self.descriptors_size, self.rollback_index, self.flags)
David Zeuthen21e95262016-07-27 17:58:40 -04001601
1602
1603class Avb(object):
1604 """Business logic for avbtool command-line tool."""
1605
David Zeuthen8b6973b2016-09-20 12:39:49 -04001606 # Keep in sync with avb_ab_flow.h.
1607 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1608 AB_MAGIC = '\0AB0'
1609 AB_MAJOR_VERSION = 1
1610 AB_MINOR_VERSION = 0
1611 AB_MISC_METADATA_OFFSET = 2048
1612
David Zeuthen09692692016-09-30 16:16:40 -04001613 # Constants for maximum metadata size. These are used to give
1614 # meaningful errors if the value passed in via --partition_size is
1615 # too small and when --calc_max_image_size is used. We use
1616 # conservative figures.
1617 MAX_VBMETA_SIZE = 64 * 1024
1618 MAX_FOOTER_SIZE = 4096
1619
David Zeuthena4fee8b2016-08-22 15:20:43 -04001620 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001621 """Implements the 'erase_footer' command.
1622
1623 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001624 image_filename: File to erase a footer from.
David Zeuthen21e95262016-07-27 17:58:40 -04001625 keep_hashtree: If True, keep the hashtree around.
1626
1627 Raises:
1628 AvbError: If there's no footer in the image.
1629 """
1630
David Zeuthena4fee8b2016-08-22 15:20:43 -04001631 image = ImageHandler(image_filename)
1632
David Zeuthen21e95262016-07-27 17:58:40 -04001633 (footer, _, descriptors, _) = self._parse_image(image)
1634
1635 if not footer:
1636 raise AvbError('Given image does not have a footer.')
1637
1638 new_image_size = None
1639 if not keep_hashtree:
1640 new_image_size = footer.original_image_size
1641 else:
1642 # If requested to keep the hashtree, search for a hashtree
1643 # descriptor to figure out the location and size of the hashtree.
1644 for desc in descriptors:
1645 if isinstance(desc, AvbHashtreeDescriptor):
1646 # The hashtree is always just following the main data so the
1647 # new size is easily derived.
1648 new_image_size = desc.tree_offset + desc.tree_size
1649 break
1650 if not new_image_size:
1651 raise AvbError('Requested to keep hashtree but no hashtree '
1652 'descriptor was found.')
1653
1654 # And cut...
1655 image.truncate(new_image_size)
1656
David Zeuthen8b6973b2016-09-20 12:39:49 -04001657 def set_ab_metadata(self, misc_image, slot_data):
1658 """Implements the 'set_ab_metadata' command.
1659
1660 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1661 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1662
1663 Arguments:
1664 misc_image: The misc image to write to.
1665 slot_data: Slot data as a string
1666
1667 Raises:
1668 AvbError: If slot data is malformed.
1669 """
1670 tokens = slot_data.split(':')
1671 if len(tokens) != 6:
1672 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1673 a_priority = int(tokens[0])
1674 a_tries_remaining = int(tokens[1])
1675 a_success = True if int(tokens[2]) != 0 else False
1676 b_priority = int(tokens[3])
1677 b_tries_remaining = int(tokens[4])
1678 b_success = True if int(tokens[5]) != 0 else False
1679
1680 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1681 self.AB_MAGIC,
1682 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1683 a_priority, a_tries_remaining, a_success,
1684 b_priority, b_tries_remaining, b_success)
1685 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1686 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1687 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1688 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1689 misc_image.write(ab_data)
1690
David Zeuthena4fee8b2016-08-22 15:20:43 -04001691 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001692 """Implements the 'info_image' command.
1693
1694 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001695 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001696 output: Output file to write human-readable information to (file object).
1697 """
1698
David Zeuthena4fee8b2016-08-22 15:20:43 -04001699 image = ImageHandler(image_filename)
1700
David Zeuthen21e95262016-07-27 17:58:40 -04001701 o = output
1702
1703 (footer, header, descriptors, image_size) = self._parse_image(image)
1704
1705 if footer:
1706 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1707 footer.version_minor))
1708 o.write('Image size: {} bytes\n'.format(image_size))
1709 o.write('Original image size: {} bytes\n'.format(
1710 footer.original_image_size))
1711 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1712 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1713 o.write('--\n')
1714
1715 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1716
David Zeuthena4fee8b2016-08-22 15:20:43 -04001717 o.write('VBMeta image version: {}.{}{}\n'.format(
1718 header.header_version_major, header.header_version_minor,
1719 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001720 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1721 o.write('Authentication Block: {} bytes\n'.format(
1722 header.authentication_data_block_size))
1723 o.write('Auxiliary Block: {} bytes\n'.format(
1724 header.auxiliary_data_block_size))
1725 o.write('Algorithm: {}\n'.format(alg_name))
1726 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001727 o.write('Flags: {}\n'.format(header.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001728
1729 # Print descriptors.
1730 num_printed = 0
1731 o.write('Descriptors:\n')
1732 for desc in descriptors:
1733 desc.print_desc(o)
1734 num_printed += 1
1735 if num_printed == 0:
1736 o.write(' (none)\n')
1737
1738 def _parse_image(self, image):
1739 """Gets information about an image.
1740
1741 The image can either be a vbmeta or an image with a footer.
1742
1743 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001744 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001745
1746 Returns:
1747 A tuple where the first argument is a AvbFooter (None if there
1748 is no footer on the image), the second argument is a
1749 AvbVBMetaHeader, the third argument is a list of
1750 AvbDescriptor-derived instances, and the fourth argument is the
1751 size of |image|.
1752 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001753 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001754 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001755 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001756 try:
1757 footer = AvbFooter(image.read(AvbFooter.SIZE))
1758 except (LookupError, struct.error):
1759 # Nope, just seek back to the start.
1760 image.seek(0)
1761
1762 vbmeta_offset = 0
1763 if footer:
1764 vbmeta_offset = footer.vbmeta_offset
1765
1766 image.seek(vbmeta_offset)
1767 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1768
1769 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1770 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1771 desc_start_offset = aux_block_offset + h.descriptors_offset
1772 image.seek(desc_start_offset)
1773 descriptors = parse_descriptors(image.read(h.descriptors_size))
1774
David Zeuthen09692692016-09-30 16:16:40 -04001775 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001776
David Zeuthenfd41eb92016-11-17 12:24:47 -05001777 def _get_cmdline_descriptors_for_dm_verity(self, image):
1778 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001779
1780 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001781 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001782
1783 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001784 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
1785 instructions. There is one for when hashtree is not disabled and one for
1786 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04001787
1788 Raises:
1789 AvbError: If |image| doesn't have a hashtree descriptor.
1790
1791 """
1792
1793 (_, _, descriptors, _) = self._parse_image(image)
1794
1795 ht = None
1796 for desc in descriptors:
1797 if isinstance(desc, AvbHashtreeDescriptor):
1798 ht = desc
1799 break
1800
1801 if not ht:
1802 raise AvbError('No hashtree descriptor in given image')
1803
1804 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001805 c += '0' # start
1806 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
1807 c += ' verity {}'.format(ht.dm_verity_version) # type and version
1808 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
1809 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
1810 c += ' {}'.format(ht.data_block_size) # data_block
1811 c += ' {}'.format(ht.hash_block_size) # hash_block
1812 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
1813 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
1814 c += ' {}'.format(ht.hash_algorithm) # hash_alg
1815 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
1816 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
1817 if ht.fec_num_roots > 0:
1818 c += ' 9' # number of optional args
1819 c += ' ignore_zero_blocks'
1820 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1821 c += ' fec_roots {}'.format(ht.fec_num_roots)
1822 # Note that fec_blocks is the size that FEC covers, *not* the
1823 # size of the FEC data. Since we use FEC for everything up until
1824 # the FEC data, it's the same as the offset.
1825 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
1826 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
1827 else:
1828 c += ' 1' # number of optional args
1829 c += ' ignore_zero_blocks'
David Zeuthenfd41eb92016-11-17 12:24:47 -05001830 c += '" root=0xfd00'
David Zeuthen21e95262016-07-27 17:58:40 -04001831
David Zeuthenfd41eb92016-11-17 12:24:47 -05001832 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001833 desc = AvbKernelCmdlineDescriptor()
1834 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05001835 desc.flags = (
1836 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
1837
1838 # The descriptor for when hashtree verification is disabled is a lot
1839 # simpler - we just set the root to the partition.
1840 desc_no_ht = AvbKernelCmdlineDescriptor()
1841 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
1842 desc_no_ht.flags = (
1843 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
1844
1845 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04001846
1847 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05001848 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001849 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001850 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001851 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001852 """Implements the 'make_vbmeta_image' command.
1853
1854 Arguments:
1855 output: File to write the image to.
1856 chain_partitions: List of partitions to chain.
1857 algorithm_name: Name of algorithm to use.
1858 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05001859 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001860 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001861 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001862 props: Properties to insert (list of strings of the form 'key:value').
1863 props_from_file: Properties to insert (list of strings 'key:<path>').
1864 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001865 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04001866 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001867 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001868
1869 Raises:
1870 AvbError: If a chained partition is malformed.
1871 """
1872
1873 descriptors = []
1874
1875 # Insert chained partition descriptors.
1876 if chain_partitions:
1877 for cp in chain_partitions:
1878 cp_tokens = cp.split(':')
1879 if len(cp_tokens) != 3:
1880 raise AvbError('Malformed chained partition "{}".'.format(cp))
1881 desc = AvbChainPartitionDescriptor()
1882 desc.partition_name = cp_tokens[0]
David Zeuthen40ee1da2016-11-23 15:14:49 -05001883 desc.rollback_index_location = int(cp_tokens[1])
1884 if desc.rollback_index_location < 1:
1885 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthen21e95262016-07-27 17:58:40 -04001886 file_path = cp_tokens[2]
1887 desc.public_key = open(file_path, 'rb').read()
1888 descriptors.append(desc)
1889
1890 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05001891 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001892 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001893 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001894 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04001895
1896 # Write entire vbmeta blob (header, authentication, auxiliary).
1897 output.seek(0)
1898 output.write(vbmeta_blob)
1899
David Zeuthen18666ab2016-11-15 11:18:05 -05001900 def _generate_vbmeta_blob(self, algorithm_name, key_path,
1901 public_key_metadata_path, descriptors,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001902 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04001903 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001904 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001905 include_descriptors_from_image, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04001906 """Generates a VBMeta blob.
1907
1908 This blob contains the header (struct AvbVBMetaHeader), the
1909 authentication data block (which contains the hash and signature
1910 for the header and auxiliary block), and the auxiliary block
1911 (which contains descriptors, the public key used, and other data).
1912
1913 The |key| parameter can |None| only if the |algorithm_name| is
1914 'NONE'.
1915
1916 Arguments:
1917 algorithm_name: The algorithm name as per the ALGORITHMS dict.
1918 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05001919 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04001920 descriptors: A list of descriptors to insert or None.
1921 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05001922 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04001923 props: Properties to insert (List of strings of the form 'key:value').
1924 props_from_file: Properties to insert (List of strings 'key:<path>').
1925 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001926 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04001927 dm-verity kernel cmdline from.
1928 include_descriptors_from_image: List of file objects for which
1929 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08001930 signing_helper: Program which signs a hash and return signature.
David Zeuthen21e95262016-07-27 17:58:40 -04001931
1932 Returns:
1933 A bytearray() with the VBMeta blob.
1934
1935 Raises:
1936 Exception: If the |algorithm_name| is not found, if no key has
1937 been given and the given algorithm requires one, or the key is
1938 of the wrong size.
1939
1940 """
1941 try:
1942 alg = ALGORITHMS[algorithm_name]
1943 except KeyError:
1944 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
1945
1946 # Descriptors.
1947 encoded_descriptors = bytearray()
1948 if descriptors:
1949 for desc in descriptors:
1950 encoded_descriptors.extend(desc.encode())
1951
1952 # Add properties.
1953 if props:
1954 for prop in props:
1955 idx = prop.find(':')
1956 if idx == -1:
1957 raise AvbError('Malformed property "{}".'.format(prop))
1958 desc = AvbPropertyDescriptor()
1959 desc.key = prop[0:idx]
1960 desc.value = prop[(idx + 1):]
1961 encoded_descriptors.extend(desc.encode())
1962 if props_from_file:
1963 for prop in props_from_file:
1964 idx = prop.find(':')
1965 if idx == -1:
1966 raise AvbError('Malformed property "{}".'.format(prop))
1967 desc = AvbPropertyDescriptor()
1968 desc.key = prop[0:idx]
1969 desc.value = prop[(idx + 1):]
1970 file_path = prop[(idx + 1):]
1971 desc.value = open(file_path, 'rb').read()
1972 encoded_descriptors.extend(desc.encode())
1973
1974 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001975 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001976 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05001977 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001978 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
1979 encoded_descriptors.extend(cmdline_desc[0].encode())
1980 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04001981
1982 # Add kernel command-lines.
1983 if kernel_cmdlines:
1984 for i in kernel_cmdlines:
1985 desc = AvbKernelCmdlineDescriptor()
1986 desc.kernel_cmdline = i
1987 encoded_descriptors.extend(desc.encode())
1988
1989 # Add descriptors from other images.
1990 if include_descriptors_from_image:
1991 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001992 image_handler = ImageHandler(image.name)
1993 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04001994 for desc in image_descriptors:
1995 encoded_descriptors.extend(desc.encode())
1996
David Zeuthen18666ab2016-11-15 11:18:05 -05001997 # Load public key metadata blob, if requested.
1998 pkmd_blob = []
1999 if public_key_metadata_path:
2000 with open(public_key_metadata_path) as f:
2001 pkmd_blob = f.read()
2002
David Zeuthen21e95262016-07-27 17:58:40 -04002003 key = None
2004 encoded_key = bytearray()
2005 if alg.public_key_num_bytes > 0:
2006 if not key_path:
2007 raise AvbError('Key is required for algorithm {}'.format(
2008 algorithm_name))
2009 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2010 encoded_key = encode_rsa_key(key)
2011 if len(encoded_key) != alg.public_key_num_bytes:
2012 raise AvbError('Key is wrong size for algorithm {}'.format(
2013 algorithm_name))
2014
2015 h = AvbVBMetaHeader()
2016
David Zeuthen18666ab2016-11-15 11:18:05 -05002017 # For the Auxiliary data block, descriptors are stored at offset 0,
2018 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002019 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002020 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002021 h.descriptors_offset = 0
2022 h.descriptors_size = len(encoded_descriptors)
2023 h.public_key_offset = h.descriptors_size
2024 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002025 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2026 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002027
2028 # For the Authentication data block, the hash is first and then
2029 # the signature.
2030 h.authentication_data_block_size = round_to_multiple(
2031 alg.hash_num_bytes + alg.public_key_num_bytes, 64)
2032 h.algorithm_type = alg.algorithm_type
2033 h.hash_offset = 0
2034 h.hash_size = alg.hash_num_bytes
2035 # Signature offset and size - it's stored right after the hash
2036 # (in Authentication data block).
2037 h.signature_offset = alg.hash_num_bytes
2038 h.signature_size = alg.signature_num_bytes
2039
2040 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002041 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002042
2043 # Generate Header data block.
2044 header_data_blob = h.encode()
2045
2046 # Generate Auxiliary data block.
2047 aux_data_blob = bytearray()
2048 aux_data_blob.extend(encoded_descriptors)
2049 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002050 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002051 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2052 aux_data_blob.extend('\0' * padding_bytes)
2053
2054 # Calculate the hash.
2055 binary_hash = bytearray()
2056 binary_signature = bytearray()
2057 if algorithm_name != 'NONE':
2058 if algorithm_name[0:6] == 'SHA256':
2059 ha = hashlib.sha256()
2060 elif algorithm_name[0:6] == 'SHA512':
2061 ha = hashlib.sha512()
2062 else:
2063 raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
2064 ha.update(header_data_blob)
2065 ha.update(aux_data_blob)
2066 binary_hash.extend(ha.digest())
2067
2068 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002069 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Darren Krahn147b08d2016-12-20 16:38:29 -08002070 binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
2071 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002072
2073 # Generate Authentication data block.
2074 auth_data_blob = bytearray()
2075 auth_data_blob.extend(binary_hash)
2076 auth_data_blob.extend(binary_signature)
2077 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2078 auth_data_blob.extend('\0' * padding_bytes)
2079
2080 return header_data_blob + auth_data_blob + aux_data_blob
2081
2082 def extract_public_key(self, key_path, output):
2083 """Implements the 'extract_public_key' command.
2084
2085 Arguments:
2086 key_path: The path to a RSA private key file.
2087 output: The file to write to.
2088 """
2089 key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
2090 write_rsa_key(output, key)
2091
David Zeuthena4fee8b2016-08-22 15:20:43 -04002092 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthen21e95262016-07-27 17:58:40 -04002093 hash_algorithm, salt, algorithm_name, key_path,
David Zeuthen18666ab2016-11-15 11:18:05 -05002094 public_key_metadata_path, rollback_index, props,
2095 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002096 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002097 include_descriptors_from_image, signing_helper):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002098 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002099
2100 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002101 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002102 partition_size: Size of partition.
2103 partition_name: Name of partition (without A/B suffix).
2104 hash_algorithm: Hash algorithm to use.
2105 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2106 algorithm_name: Name of algorithm to use.
2107 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002108 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002109 rollback_index: Rollback index.
2110 props: Properties to insert (List of strings of the form 'key:value').
2111 props_from_file: Properties to insert (List of strings 'key:<path>').
2112 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002113 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002114 dm-verity kernel cmdline from.
2115 include_descriptors_from_image: List of file objects for which
2116 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002117 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002118
2119 Raises:
2120 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002121 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002122 image = ImageHandler(image_filename)
2123
2124 if partition_size % image.block_size != 0:
2125 raise AvbError('Partition size of {} is not a multiple of the image '
2126 'block size {}.'.format(partition_size,
2127 image.block_size))
2128
David Zeuthen21e95262016-07-27 17:58:40 -04002129 # If there's already a footer, truncate the image to its original
2130 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2131 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002132 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002133 try:
2134 footer = AvbFooter(image.read(AvbFooter.SIZE))
2135 # Existing footer found. Just truncate.
2136 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002137 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002138 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002139 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002140
2141 # If anything goes wrong from here-on, restore the image back to
2142 # its original size.
2143 try:
David Zeuthen09692692016-09-30 16:16:40 -04002144 # First, calculate the maximum image size such that an image
2145 # this size + metadata (footer + vbmeta struct) fits in
2146 # |partition_size|.
2147 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2148 max_image_size = partition_size - max_metadata_size
2149
2150 # If image size exceeds the maximum image size, fail.
2151 if image.image_size > max_image_size:
2152 raise AvbError('Image size of {} exceeds maximum image '
2153 'size of {} in order to fit in a partition '
2154 'size of {}.'.format(image.image_size, max_image_size,
2155 partition_size))
2156
David Zeuthen21e95262016-07-27 17:58:40 -04002157 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2158 if salt:
2159 salt = salt.decode('hex')
2160 else:
2161 if salt is None:
2162 # If salt is not explicitly specified, choose a hash
2163 # that's the same size as the hash size.
2164 hash_size = digest_size
2165 salt = open('/dev/urandom').read(hash_size)
2166 else:
2167 salt = ''
2168
2169 hasher = hashlib.new(name=hash_algorithm, string=salt)
2170 # TODO(zeuthen): might want to read this in chunks to avoid
2171 # memory pressure, then again, this is only supposed to be used
2172 # on kernel/initramfs partitions. Possible optimization.
2173 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002174 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002175 digest = hasher.digest()
2176
2177 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002178 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002179 h_desc.hash_algorithm = hash_algorithm
2180 h_desc.partition_name = partition_name
2181 h_desc.salt = salt
2182 h_desc.digest = digest
2183
David Zeuthenfd41eb92016-11-17 12:24:47 -05002184 # Flags are only allowed on top-level vbmeta struct.
2185 flags = 0
2186
David Zeuthen21e95262016-07-27 17:58:40 -04002187 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002188 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002189 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002190 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002191 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002192 include_descriptors_from_image, signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04002193
David Zeuthena4fee8b2016-08-22 15:20:43 -04002194 # If the image isn't sparse, its size might not be a multiple of
2195 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002196 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002197 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002198 padding_needed = image.block_size - (image.image_size%image.block_size)
2199 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002200
David Zeuthena4fee8b2016-08-22 15:20:43 -04002201 # The append_raw() method requires content with size being a
2202 # multiple of |block_size| so add padding as needed. Also record
2203 # where this is written to since we'll need to put that in the
2204 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002205 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002206 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2207 len(vbmeta_blob))
2208 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2209 image.append_raw(vbmeta_blob_with_padding)
2210 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2211
2212 # Now insert a DONT_CARE chunk with enough bytes such that the
2213 # final Footer block is at the end of partition_size..
2214 image.append_dont_care(partition_size - vbmeta_end_offset -
2215 1*image.block_size)
2216
2217 # Generate the Footer that tells where the VBMeta footer
2218 # is. Also put enough padding in the front of the footer since
2219 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002220 footer = AvbFooter()
2221 footer.original_image_size = original_image_size
2222 footer.vbmeta_offset = vbmeta_offset
2223 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002224 footer_blob = footer.encode()
2225 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2226 footer_blob)
2227 image.append_raw(footer_blob_with_padding)
2228
David Zeuthen21e95262016-07-27 17:58:40 -04002229 except:
2230 # Truncate back to original size, then re-raise
2231 image.truncate(original_image_size)
2232 raise
2233
David Zeuthena4fee8b2016-08-22 15:20:43 -04002234 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002235 generate_fec, fec_num_roots, hash_algorithm,
2236 block_size, salt, algorithm_name, key_path,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002237 public_key_metadata_path, rollback_index,
2238 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002239 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002240 include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002241 calc_max_image_size, signing_helper):
David Zeuthen21e95262016-07-27 17:58:40 -04002242 """Implements the 'add_hashtree_footer' command.
2243
2244 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2245 more information about dm-verity and these hashes.
2246
2247 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002248 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002249 partition_size: Size of partition.
2250 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002251 generate_fec: If True, generate FEC codes.
2252 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002253 hash_algorithm: Hash algorithm to use.
2254 block_size: Block size to use.
2255 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
2256 algorithm_name: Name of algorithm to use.
2257 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002258 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002259 rollback_index: Rollback index.
2260 props: Properties to insert (List of strings of the form 'key:value').
2261 props_from_file: Properties to insert (List of strings 'key:<path>').
2262 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002263 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002264 dm-verity kernel cmdline from.
2265 include_descriptors_from_image: List of file objects for which
2266 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002267 calc_max_image_size: Don't store the hashtree or footer - instead
2268 calculate the maximum image size leaving enough room for hashtree
2269 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002270 signing_helper: Program which signs a hash and return signature.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002271
2272 Raises:
2273 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002274 """
David Zeuthen09692692016-09-30 16:16:40 -04002275 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2276 digest_padding = round_to_pow2(digest_size) - digest_size
2277
2278 # First, calculate the maximum image size such that an image
2279 # this size + the hashtree + metadata (footer + vbmeta struct)
2280 # fits in |partition_size|. We use very conservative figures for
2281 # metadata.
2282 (_, max_tree_size) = calc_hash_level_offsets(
2283 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002284 max_fec_size = 0
2285 if generate_fec:
2286 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2287 max_metadata_size = (max_fec_size + max_tree_size +
2288 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002289 self.MAX_FOOTER_SIZE)
2290 max_image_size = partition_size - max_metadata_size
2291
2292 # If we're asked to only calculate the maximum image size, we're done.
2293 if calc_max_image_size:
2294 print '{}'.format(max_image_size)
2295 return
2296
David Zeuthena4fee8b2016-08-22 15:20:43 -04002297 image = ImageHandler(image_filename)
2298
2299 if partition_size % image.block_size != 0:
2300 raise AvbError('Partition size of {} is not a multiple of the image '
2301 'block size {}.'.format(partition_size,
2302 image.block_size))
2303
David Zeuthen21e95262016-07-27 17:58:40 -04002304 # If there's already a footer, truncate the image to its original
2305 # size. This way 'avbtool add_hashtree_footer' is idempotent
2306 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002307 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002308 try:
2309 footer = AvbFooter(image.read(AvbFooter.SIZE))
2310 # Existing footer found. Just truncate.
2311 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002312 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002313 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002314 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002315
2316 # If anything goes wrong from here-on, restore the image back to
2317 # its original size.
2318 try:
2319 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002320 rounded_image_size = round_to_multiple(image.image_size, block_size)
2321 if rounded_image_size > image.image_size:
2322 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002323
David Zeuthen09692692016-09-30 16:16:40 -04002324 # If image size exceeds the maximum image size, fail.
2325 if image.image_size > max_image_size:
2326 raise AvbError('Image size of {} exceeds maximum image '
2327 'size of {} in order to fit in a partition '
2328 'size of {}.'.format(image.image_size, max_image_size,
2329 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002330
2331 if salt:
2332 salt = salt.decode('hex')
2333 else:
2334 if salt is None:
2335 # If salt is not explicitly specified, choose a hash
2336 # that's the same size as the hash size.
2337 hash_size = digest_size
2338 salt = open('/dev/urandom').read(hash_size)
2339 else:
2340 salt = ''
2341
David Zeuthena4fee8b2016-08-22 15:20:43 -04002342 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002343 # offsets in advance.
2344 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002345 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002346
David Zeuthena4fee8b2016-08-22 15:20:43 -04002347 # If the image isn't sparse, its size might not be a multiple of
2348 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002349 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002350 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002351 padding_needed = image.block_size - (image.image_size%image.block_size)
2352 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002353
David Zeuthena4fee8b2016-08-22 15:20:43 -04002354 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002355 tree_offset = image.image_size
2356 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002357 block_size,
2358 hash_algorithm, salt,
2359 digest_padding,
2360 hash_level_offsets,
2361 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002362
2363 # Generate HashtreeDescriptor with details about the tree we
2364 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002365 ht_desc = AvbHashtreeDescriptor()
2366 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002367 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002368 ht_desc.tree_offset = tree_offset
2369 ht_desc.tree_size = tree_size
2370 ht_desc.data_block_size = block_size
2371 ht_desc.hash_block_size = block_size
2372 ht_desc.hash_algorithm = hash_algorithm
2373 ht_desc.partition_name = partition_name
2374 ht_desc.salt = salt
2375 ht_desc.root_digest = root_digest
2376
David Zeuthen09692692016-09-30 16:16:40 -04002377 # Write the hash tree
2378 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2379 len(hash_tree))
2380 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2381 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002382 len_hashtree_and_fec = len(hash_tree_with_padding)
2383
2384 # Generate FEC codes, if requested.
2385 if generate_fec:
2386 fec_data = generate_fec_data(image_filename, fec_num_roots)
2387 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2388 len(fec_data))
2389 fec_data_with_padding = fec_data + '\0'*padding_needed
2390 fec_offset = image.image_size
2391 image.append_raw(fec_data_with_padding)
2392 len_hashtree_and_fec += len(fec_data_with_padding)
2393 # Update the hashtree descriptor.
2394 ht_desc.fec_num_roots = fec_num_roots
2395 ht_desc.fec_offset = fec_offset
2396 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002397
David Zeuthenfd41eb92016-11-17 12:24:47 -05002398 # Flags are only allowed on top-level vbmeta struct.
2399 flags = 0
2400
David Zeuthena4fee8b2016-08-22 15:20:43 -04002401 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002402 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002403 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002404 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthenfd41eb92016-11-17 12:24:47 -05002405 rollback_index, flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002406 setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002407 include_descriptors_from_image, signing_helper)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002408 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2409 len(vbmeta_blob))
2410 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2411 image.append_raw(vbmeta_blob_with_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002412
David Zeuthena4fee8b2016-08-22 15:20:43 -04002413 # Now insert a DONT_CARE chunk with enough bytes such that the
2414 # final Footer block is at the end of partition_size..
David Zeuthen09692692016-09-30 16:16:40 -04002415 image.append_dont_care(partition_size - image.image_size -
David Zeuthena4fee8b2016-08-22 15:20:43 -04002416 1*image.block_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002417
David Zeuthena4fee8b2016-08-22 15:20:43 -04002418 # Generate the Footer that tells where the VBMeta footer
2419 # is. Also put enough padding in the front of the footer since
2420 # we'll write out an entire block.
David Zeuthen21e95262016-07-27 17:58:40 -04002421 footer = AvbFooter()
2422 footer.original_image_size = original_image_size
2423 footer.vbmeta_offset = vbmeta_offset
2424 footer.vbmeta_size = len(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002425 footer_blob = footer.encode()
2426 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2427 footer_blob)
2428 image.append_raw(footer_blob_with_padding)
2429
David Zeuthen21e95262016-07-27 17:58:40 -04002430 except:
David Zeuthen09692692016-09-30 16:16:40 -04002431 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002432 image.truncate(original_image_size)
2433 raise
2434
Darren Krahn147b08d2016-12-20 16:38:29 -08002435 def make_atx_certificate(self, output, authority_key_path, subject_key,
2436 subject_key_version, subject,
2437 is_intermediate_authority, signing_helper):
2438 """Implements the 'make_atx_certificate' command.
2439
2440 Android Things certificates are required for Android Things public key
2441 metadata. They chain the vbmeta signing key for a particular product back to
2442 a fused, permanent root key. These certificates are fixed-length and fixed-
2443 format with the explicit goal of not parsing ASN.1 in bootloader code.
2444
2445 Arguments:
2446 output: Certificate will be written to this file on success.
2447 authority_key_path: A PEM file path with the authority private key.
2448 If None, then a certificate will be created without a
2449 signature. The signature can be created out-of-band
2450 and appended.
2451 subject_key: A PEM or DER subject public key.
2452 subject_key_version: A 64-bit version value. If this is None, the number
2453 of seconds since the epoch is used.
2454 subject: A subject identifier. For Product Signing Key certificates this
2455 should be the same Product ID found in the permanent attributes.
2456 is_intermediate_authority: True if the certificate is for an intermediate
2457 authority.
2458 signing_helper: Program which signs a hash and returns the signature.
2459 """
2460 signed_data = bytearray()
2461 signed_data.extend(struct.pack('<I', 1)) # Format Version
2462 signed_data.extend(
2463 encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
2464 hasher = hashlib.sha256()
2465 hasher.update(subject)
2466 signed_data.extend(hasher.digest())
2467 usage = 'com.google.android.things.vboot'
2468 if is_intermediate_authority:
2469 usage += '.ca'
2470 hasher = hashlib.sha256()
2471 hasher.update(usage)
2472 signed_data.extend(hasher.digest())
2473 if not subject_key_version:
2474 subject_key_version = int(time.time())
2475 signed_data.extend(struct.pack('<Q', subject_key_version))
2476 signature = bytearray()
2477 if authority_key_path:
2478 padding_and_hash = bytearray()
2479 algorithm_name = None
2480 hasher = None
2481 if is_intermediate_authority:
2482 hasher = hashlib.sha512()
2483 algorithm_name = 'SHA512_RSA4096'
2484 else:
2485 hasher = hashlib.sha256()
2486 algorithm_name = 'SHA256_RSA2048'
2487 padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
2488 hasher.update(signed_data)
2489 padding_and_hash.extend(hasher.digest())
2490 signature.extend(raw_sign(signing_helper, algorithm_name,
2491 authority_key_path, padding_and_hash))
2492 output.write(signed_data)
2493 output.write(signature)
2494
2495 def make_atx_permanent_attributes(self, output, root_authority_key,
2496 product_id):
2497 """Implements the 'make_atx_permanent_attributes' command.
2498
2499 Android Things permanent attributes are designed to be permanent for a
2500 particular product and a hash of these attributes should be fused into
2501 hardware to enforce this.
2502
2503 Arguments:
2504 output: Attributes will be written to this file on success.
2505 root_authority_key: A PEM or DER public key for the root authority.
2506 product_id: A 16-byte Product ID.
2507
2508 Raises:
2509 AvbError: If an argument is incorrect.
2510 """
2511 if len(product_id) != 16:
2512 raise AvbError('Invalid Product ID length.')
2513 output.write(struct.pack('<I', 1)) # Format Version
2514 write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
2515 output.write(product_id)
2516
2517 def make_atx_metadata(self, output, intermediate_key_certificate,
2518 product_key_certificate, google_key_version):
2519 """Implements the 'make_atx_metadata' command.
2520
2521 Android Things metadata are included in vbmeta images to facilitate
2522 verification. The output of this command can be used as the
2523 public_key_metadata argument to other commands.
2524
2525 Arguments:
2526 output: Metadata will be written to this file on success.
2527 intermediate_key_certificate: A certificate file as output by
2528 make_atx_certificate with
2529 is_intermediate_authority set to true.
2530 product_key_certificate: A certificate file as output by
2531 make_atx_certificate with
2532 is_intermediate_authority set to false.
2533 google_key_version: The version of the Google Signing Key used in the
2534 associated vbmeta image.
2535
2536 Raises:
2537 AvbError: If an argument is incorrect.
2538 """
2539 if len(intermediate_key_certificate) != 1108:
2540 raise AvbError('Invalid intermediate key certificate length.')
2541 if len(product_key_certificate) != 852:
2542 raise AvbError('Invalid product key certificate length.')
2543 output.write(struct.pack('<I', 1)) # Format Version
2544 output.write(intermediate_key_certificate)
2545 output.write(product_key_certificate)
2546 output.write(struct.pack('<Q', google_key_version))
2547
David Zeuthen21e95262016-07-27 17:58:40 -04002548
2549def calc_hash_level_offsets(image_size, block_size, digest_size):
2550 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2551
2552 Arguments:
2553 image_size: The size of the image to calculate a Merkle-tree for.
2554 block_size: The block size, e.g. 4096.
2555 digest_size: The size of each hash, e.g. 32 for SHA-256.
2556
2557 Returns:
2558 A tuple where the first argument is an array of offsets and the
2559 second is size of the tree, in bytes.
2560 """
2561 level_offsets = []
2562 level_sizes = []
2563 tree_size = 0
2564
2565 num_levels = 0
2566 size = image_size
2567 while size > block_size:
2568 num_blocks = (size + block_size - 1) / block_size
2569 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2570
2571 level_sizes.append(level_size)
2572 tree_size += level_size
2573 num_levels += 1
2574
2575 size = level_size
2576
2577 for n in range(0, num_levels):
2578 offset = 0
2579 for m in range(n + 1, num_levels):
2580 offset += level_sizes[m]
2581 level_offsets.append(offset)
2582
David Zeuthena4fee8b2016-08-22 15:20:43 -04002583 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002584
2585
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002586# See system/extras/libfec/include/fec/io.h for these definitions.
2587FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2588FEC_MAGIC = 0xfecfecfe
2589
2590
2591def calc_fec_data_size(image_size, num_roots):
2592 """Calculates how much space FEC data will take.
2593
2594 Args:
2595 image_size: The size of the image.
2596 num_roots: Number of roots.
2597
2598 Returns:
2599 The number of bytes needed for FEC for an image of the given size
2600 and with the requested number of FEC roots.
2601
2602 Raises:
2603 ValueError: If output from the 'fec' tool is invalid.
2604
2605 """
2606 p = subprocess.Popen(
2607 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2608 stdout=subprocess.PIPE,
2609 stderr=subprocess.PIPE)
2610 (pout, perr) = p.communicate()
2611 retcode = p.wait()
2612 if retcode != 0:
2613 raise ValueError('Error invoking fec: {}'.format(perr))
2614 return int(pout)
2615
2616
2617def generate_fec_data(image_filename, num_roots):
2618 """Generate FEC codes for an image.
2619
2620 Args:
2621 image_filename: The filename of the image.
2622 num_roots: Number of roots.
2623
2624 Returns:
2625 The FEC data blob.
2626
2627 Raises:
2628 ValueError: If output from the 'fec' tool is invalid.
2629 """
2630 fec_tmpfile = tempfile.NamedTemporaryFile()
2631 subprocess.check_call(
2632 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2633 fec_tmpfile.name],
2634 stderr=open(os.devnull))
2635 fec_data = fec_tmpfile.read()
2636 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2637 footer_data = fec_data[-footer_size:]
2638 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2639 footer_data)
2640 if magic != FEC_MAGIC:
2641 raise ValueError('Unexpected magic in FEC footer')
2642 return fec_data[0:fec_size]
2643
2644
David Zeuthen21e95262016-07-27 17:58:40 -04002645def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002646 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002647 """Generates a Merkle-tree for a file.
2648
2649 Args:
2650 image: The image, as a file.
2651 image_size: The size of the image.
2652 block_size: The block size, e.g. 4096.
2653 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2654 salt: The salt to use.
2655 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002656 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002657 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002658
2659 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002660 A tuple where the first element is the top-level hash and the
2661 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002662 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002663 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002664 hash_src_offset = 0
2665 hash_src_size = image_size
2666 level_num = 0
2667 while hash_src_size > block_size:
2668 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04002669 remaining = hash_src_size
2670 while remaining > 0:
2671 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002672 # Only read from the file for the first level - for subsequent
2673 # levels, access the array we're building.
2674 if level_num == 0:
2675 image.seek(hash_src_offset + hash_src_size - remaining)
2676 data = image.read(min(remaining, block_size))
2677 else:
2678 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
2679 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04002680 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002681
2682 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04002683 if len(data) < block_size:
2684 hasher.update('\0' * (block_size - len(data)))
2685 level_output += hasher.digest()
2686 if digest_padding > 0:
2687 level_output += '\0' * digest_padding
2688
2689 padding_needed = (round_to_multiple(
2690 len(level_output), block_size) - len(level_output))
2691 level_output += '\0' * padding_needed
2692
David Zeuthena4fee8b2016-08-22 15:20:43 -04002693 # Copy level-output into resulting tree.
2694 offset = hash_level_offsets[level_num]
2695 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04002696
David Zeuthena4fee8b2016-08-22 15:20:43 -04002697 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04002698 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04002699 level_num += 1
2700
2701 hasher = hashlib.new(name=hash_alg_name, string=salt)
2702 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002703 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04002704
2705
2706class AvbTool(object):
2707 """Object for avbtool command-line tool."""
2708
2709 def __init__(self):
2710 """Initializer method."""
2711 self.avb = Avb()
2712
2713 def _add_common_args(self, sub_parser):
2714 """Adds arguments used by several sub-commands.
2715
2716 Arguments:
2717 sub_parser: The parser to add arguments to.
2718 """
2719 sub_parser.add_argument('--algorithm',
2720 help='Algorithm to use (default: NONE)',
2721 metavar='ALGORITHM',
2722 default='NONE')
2723 sub_parser.add_argument('--key',
2724 help='Path to RSA private key file',
2725 metavar='KEY',
2726 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002727 sub_parser.add_argument('--signing_helper',
2728 help='Path to helper used for signing',
2729 metavar='APP',
2730 default=None,
2731 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05002732 sub_parser.add_argument('--public_key_metadata',
2733 help='Path to public key metadata file',
2734 metavar='KEY_METADATA',
2735 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04002736 sub_parser.add_argument('--rollback_index',
2737 help='Rollback Index',
2738 type=parse_number,
2739 default=0)
2740 sub_parser.add_argument('--prop',
2741 help='Add property',
2742 metavar='KEY:VALUE',
2743 action='append')
2744 sub_parser.add_argument('--prop_from_file',
2745 help='Add property from file',
2746 metavar='KEY:PATH',
2747 action='append')
2748 sub_parser.add_argument('--kernel_cmdline',
2749 help='Add kernel cmdline',
2750 metavar='CMDLINE',
2751 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002752 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
2753 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
2754 # at some future point.
2755 sub_parser.add_argument('--setup_rootfs_from_kernel',
2756 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04002757 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002758 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04002759 type=argparse.FileType('rb'))
2760 sub_parser.add_argument('--include_descriptors_from_image',
2761 help='Include descriptors from image',
2762 metavar='IMAGE',
2763 action='append',
2764 type=argparse.FileType('rb'))
2765
2766 def run(self, argv):
2767 """Command-line processor.
2768
2769 Arguments:
2770 argv: Pass sys.argv from main.
2771 """
2772 parser = argparse.ArgumentParser()
2773 subparsers = parser.add_subparsers(title='subcommands')
2774
2775 sub_parser = subparsers.add_parser('version',
2776 help='Prints version of avbtool.')
2777 sub_parser.set_defaults(func=self.version)
2778
2779 sub_parser = subparsers.add_parser('extract_public_key',
2780 help='Extract public key.')
2781 sub_parser.add_argument('--key',
2782 help='Path to RSA private key file',
2783 required=True)
2784 sub_parser.add_argument('--output',
2785 help='Output file name',
2786 type=argparse.FileType('wb'),
2787 required=True)
2788 sub_parser.set_defaults(func=self.extract_public_key)
2789
2790 sub_parser = subparsers.add_parser('make_vbmeta_image',
2791 help='Makes a vbmeta image.')
2792 sub_parser.add_argument('--output',
2793 help='Output file name',
2794 type=argparse.FileType('wb'),
2795 required=True)
2796 self._add_common_args(sub_parser)
2797 sub_parser.add_argument('--chain_partition',
2798 help='Allow signed integrity-data for partition',
2799 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
2800 action='append')
David Zeuthenfd41eb92016-11-17 12:24:47 -05002801 sub_parser.add_argument('--flags',
2802 help='VBMeta flags',
2803 type=parse_number,
2804 default=0)
David Zeuthen58305522017-01-11 17:42:47 -05002805 sub_parser.add_argument('--set_hashtree_disabled_flag',
2806 help='Set the HASHTREE_DISABLED flag',
2807 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002808 sub_parser.set_defaults(func=self.make_vbmeta_image)
2809
2810 sub_parser = subparsers.add_parser('add_hash_footer',
2811 help='Add hashes and footer to image.')
2812 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002813 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04002814 type=argparse.FileType('rab+'))
2815 sub_parser.add_argument('--partition_size',
2816 help='Partition size',
2817 type=parse_number,
2818 required=True)
2819 sub_parser.add_argument('--partition_name',
2820 help='Partition name',
2821 required=True)
2822 sub_parser.add_argument('--hash_algorithm',
2823 help='Hash algorithm to use (default: sha256)',
2824 default='sha256')
2825 sub_parser.add_argument('--salt',
2826 help='Salt in hex (default: /dev/urandom)')
2827 self._add_common_args(sub_parser)
2828 sub_parser.set_defaults(func=self.add_hash_footer)
2829
2830 sub_parser = subparsers.add_parser('add_hashtree_footer',
2831 help='Add hashtree and footer to image.')
2832 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002833 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04002834 type=argparse.FileType('rab+'))
2835 sub_parser.add_argument('--partition_size',
2836 help='Partition size',
2837 type=parse_number,
2838 required=True)
2839 sub_parser.add_argument('--partition_name',
2840 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04002841 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04002842 sub_parser.add_argument('--hash_algorithm',
2843 help='Hash algorithm to use (default: sha1)',
2844 default='sha1')
2845 sub_parser.add_argument('--salt',
2846 help='Salt in hex (default: /dev/urandom)')
2847 sub_parser.add_argument('--block_size',
2848 help='Block size (default: 4096)',
2849 type=parse_number,
2850 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002851 sub_parser.add_argument('--generate_fec',
2852 help='Add forward-error-correction codes',
2853 action='store_true')
2854 sub_parser.add_argument('--fec_num_roots',
2855 help='Number of roots for FEC (default: 2)',
2856 type=parse_number,
2857 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04002858 sub_parser.add_argument('--calc_max_image_size',
2859 help=('Don\'t store the hashtree or footer - '
2860 'instead calculate the maximum image size '
2861 'leaving enough room for hashtree '
2862 'and metadata with the given partition '
2863 'size.'),
2864 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04002865 self._add_common_args(sub_parser)
2866 sub_parser.set_defaults(func=self.add_hashtree_footer)
2867
2868 sub_parser = subparsers.add_parser('erase_footer',
2869 help='Erase footer from an image.')
2870 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002871 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04002872 type=argparse.FileType('rwb+'),
2873 required=True)
2874 sub_parser.add_argument('--keep_hashtree',
2875 help='Keep the hashtree in the image',
2876 action='store_true')
2877 sub_parser.set_defaults(func=self.erase_footer)
2878
2879 sub_parser = subparsers.add_parser(
2880 'info_image',
2881 help='Show information about vbmeta or footer.')
2882 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04002883 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04002884 type=argparse.FileType('rb'),
2885 required=True)
2886 sub_parser.add_argument('--output',
2887 help='Write info to file',
2888 type=argparse.FileType('wt'),
2889 default=sys.stdout)
2890 sub_parser.set_defaults(func=self.info_image)
2891
David Zeuthen8b6973b2016-09-20 12:39:49 -04002892 sub_parser = subparsers.add_parser('set_ab_metadata',
2893 help='Set A/B metadata.')
2894 sub_parser.add_argument('--misc_image',
2895 help=('The misc image to modify. If the image does '
2896 'not exist, it will be created.'),
2897 type=argparse.FileType('r+b'),
2898 required=True)
2899 sub_parser.add_argument('--slot_data',
2900 help=('Slot data of the form "priority", '
2901 '"tries_remaining", "sucessful_boot" for '
2902 'slot A followed by the same for slot B, '
2903 'separated by colons. The default value '
2904 'is 15:7:0:14:7:0.'),
2905 default='15:7:0:14:7:0')
2906 sub_parser.set_defaults(func=self.set_ab_metadata)
2907
Darren Krahn147b08d2016-12-20 16:38:29 -08002908 sub_parser = subparsers.add_parser(
2909 'make_atx_certificate',
2910 help='Create an Android Things eXtension (ATX) certificate.')
2911 sub_parser.add_argument('--output',
2912 help='Write certificate to file',
2913 type=argparse.FileType('wb'),
2914 default=sys.stdout)
2915 sub_parser.add_argument('--subject',
2916 help=('Path to subject file'),
2917 type=argparse.FileType('rb'),
2918 required=True)
2919 sub_parser.add_argument('--subject_key',
2920 help=('Path to subject RSA public key file'),
2921 type=argparse.FileType('rb'),
2922 required=True)
2923 sub_parser.add_argument('--subject_key_version',
2924 help=('Version of the subject key'),
2925 type=parse_number,
2926 required=False)
2927 sub_parser.add_argument('--subject_is_intermediate_authority',
2928 help=('Generate an intermediate authority '
2929 'certificate'),
2930 action='store_true')
2931 sub_parser.add_argument('--authority_key',
2932 help='Path to authority RSA private key file',
2933 required=False)
2934 sub_parser.add_argument('--signing_helper',
2935 help='Path to helper used for signing',
2936 metavar='APP',
2937 default=None,
2938 required=False)
2939 sub_parser.set_defaults(func=self.make_atx_certificate)
2940
2941 sub_parser = subparsers.add_parser(
2942 'make_atx_permanent_attributes',
2943 help='Create Android Things eXtension (ATX) permanent attributes.')
2944 sub_parser.add_argument('--output',
2945 help='Write attributes to file',
2946 type=argparse.FileType('wb'),
2947 default=sys.stdout)
2948 sub_parser.add_argument('--root_authority_key',
2949 help='Path to authority RSA public key file',
2950 type=argparse.FileType('rb'),
2951 required=True)
2952 sub_parser.add_argument('--product_id',
2953 help=('Path to Product ID file'),
2954 type=argparse.FileType('rb'),
2955 required=True)
2956 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
2957
2958 sub_parser = subparsers.add_parser(
2959 'make_atx_metadata',
2960 help='Create Android Things eXtension (ATX) metadata.')
2961 sub_parser.add_argument('--output',
2962 help='Write metadata to file',
2963 type=argparse.FileType('wb'),
2964 default=sys.stdout)
2965 sub_parser.add_argument('--intermediate_key_certificate',
2966 help='Path to intermediate key certificate file',
2967 type=argparse.FileType('rb'),
2968 required=True)
2969 sub_parser.add_argument('--product_key_certificate',
2970 help='Path to product key certificate file',
2971 type=argparse.FileType('rb'),
2972 required=True)
2973 sub_parser.add_argument('--google_key_version',
2974 help=('Version of the Google signing key'),
2975 type=parse_number,
2976 default=0)
2977 sub_parser.set_defaults(func=self.make_atx_metadata)
2978
David Zeuthen21e95262016-07-27 17:58:40 -04002979 args = parser.parse_args(argv[1:])
2980 try:
2981 args.func(args)
2982 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002983 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04002984 sys.exit(1)
2985
2986 def version(self, _):
2987 """Implements the 'version' sub-command."""
2988 print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
2989
2990 def extract_public_key(self, args):
2991 """Implements the 'extract_public_key' sub-command."""
2992 self.avb.extract_public_key(args.key, args.output)
2993
2994 def make_vbmeta_image(self, args):
2995 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthen58305522017-01-11 17:42:47 -05002996 if args.set_hashtree_disabled_flag:
2997 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
David Zeuthen21e95262016-07-27 17:58:40 -04002998 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05002999 args.algorithm, args.key,
3000 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003001 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003002 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003003 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003004 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003005
3006 def add_hash_footer(self, args):
3007 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003008 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003009 args.partition_name, args.hash_algorithm,
3010 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003011 args.public_key_metadata, args.rollback_index,
3012 args.prop, args.prop_from_file,
3013 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003014 args.setup_rootfs_from_kernel,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003015 args.include_descriptors_from_image, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003016
3017 def add_hashtree_footer(self, args):
3018 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthen09692692016-09-30 16:16:40 -04003019 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3020 args.partition_size,
3021 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003022 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003023 args.hash_algorithm, args.block_size,
3024 args.salt, args.algorithm, args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003025 args.public_key_metadata,
David Zeuthen09692692016-09-30 16:16:40 -04003026 args.rollback_index, args.prop,
3027 args.prop_from_file,
3028 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003029 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003030 args.include_descriptors_from_image,
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003031 args.calc_max_image_size, args.signing_helper)
David Zeuthen21e95262016-07-27 17:58:40 -04003032
3033 def erase_footer(self, args):
3034 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003035 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003036
David Zeuthen8b6973b2016-09-20 12:39:49 -04003037 def set_ab_metadata(self, args):
3038 """Implements the 'set_ab_metadata' sub-command."""
3039 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3040
David Zeuthen21e95262016-07-27 17:58:40 -04003041 def info_image(self, args):
3042 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003043 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003044
Darren Krahn147b08d2016-12-20 16:38:29 -08003045 def make_atx_certificate(self, args):
3046 """Implements the 'make_atx_certificate' sub-command."""
3047 self.avb.make_atx_certificate(args.output, args.authority_key,
3048 args.subject_key.read(),
3049 args.subject_key_version,
3050 args.subject.read(),
3051 args.subject_is_intermediate_authority,
3052 args.signing_helper)
3053
3054 def make_atx_permanent_attributes(self, args):
3055 """Implements the 'make_atx_permanent_attributes' sub-command."""
3056 self.avb.make_atx_permanent_attributes(args.output,
3057 args.root_authority_key.read(),
3058 args.product_id.read())
3059
3060 def make_atx_metadata(self, args):
3061 """Implements the 'make_atx_metadata' sub-command."""
3062 self.avb.make_atx_metadata(args.output,
3063 args.intermediate_key_certificate.read(),
3064 args.product_key_certificate.read(),
3065 args.google_key_version)
3066
David Zeuthen21e95262016-07-27 17:58:40 -04003067
3068if __name__ == '__main__':
3069 tool = AvbTool()
3070 tool.run(sys.argv)