blob: 1b459f7dfa724283d5252cca4ab4a7b707d0cd44 [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
David Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
David Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040031import math
David Zeuthen21e95262016-07-27 17:58:40 -040032import os
33import struct
34import subprocess
35import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040036import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080037import time
David Zeuthen21e95262016-07-27 17:58:40 -040038
David Zeuthene3cadca2017-02-22 21:25:46 -050039# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040040AVB_VERSION_MAJOR = 1
41AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
David Zeuthen58305522017-01-11 17:42:47 -050044AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040045
David Zeuthene3cadca2017-02-22 21:25:46 -050046
David Zeuthen21e95262016-07-27 17:58:40 -040047class AvbError(Exception):
48 """Application-specific errors.
49
50 These errors represent issues for which a stack-trace should not be
51 presented.
52
53 Attributes:
54 message: Error message.
55 """
56
57 def __init__(self, message):
58 Exception.__init__(self, message)
59
60
61class Algorithm(object):
62 """Contains details about an algorithm.
63
64 See the avb_vbmeta_header.h file for more details about
65 algorithms.
66
67 The constant |ALGORITHMS| is a dictionary from human-readable
68 names (e.g 'SHA256_RSA2048') to instances of this class.
69
70 Attributes:
71 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040072 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040073 hash_num_bytes: Number of bytes used to store the hash.
74 signature_num_bytes: Number of bytes used to store the signature.
75 public_key_num_bytes: Number of bytes used to store the public key.
76 padding: Padding used for signature, if any.
77 """
78
David Zeuthenb623d8b2017-04-04 16:05:53 -040079 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
80 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040081 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040083 self.hash_num_bytes = hash_num_bytes
84 self.signature_num_bytes = signature_num_bytes
85 self.public_key_num_bytes = public_key_num_bytes
86 self.padding = padding
87
David Zeuthenb623d8b2017-04-04 16:05:53 -040088
David Zeuthen21e95262016-07-27 17:58:40 -040089# This must be kept in sync with the avb_crypto.h file.
90#
91# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
92# obtained from section 5.2.2 of RFC 4880.
93ALGORITHMS = {
94 'NONE': Algorithm(
95 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040096 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -040097 hash_num_bytes=0,
98 signature_num_bytes=0,
99 public_key_num_bytes=0,
100 padding=[]),
101 'SHA256_RSA2048': Algorithm(
102 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400103 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400104 hash_num_bytes=32,
105 signature_num_bytes=256,
106 public_key_num_bytes=8 + 2*2048/8,
107 padding=[
108 # PKCS1-v1_5 padding
109 0x00, 0x01] + [0xff]*202 + [0x00] + [
110 # ASN.1 header
111 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
112 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
113 0x00, 0x04, 0x20,
114 ]),
115 'SHA256_RSA4096': Algorithm(
116 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400117 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400118 hash_num_bytes=32,
119 signature_num_bytes=512,
120 public_key_num_bytes=8 + 2*4096/8,
121 padding=[
122 # PKCS1-v1_5 padding
123 0x00, 0x01] + [0xff]*458 + [0x00] + [
124 # ASN.1 header
125 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
126 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
127 0x00, 0x04, 0x20,
128 ]),
129 'SHA256_RSA8192': Algorithm(
130 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400131 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400132 hash_num_bytes=32,
133 signature_num_bytes=1024,
134 public_key_num_bytes=8 + 2*8192/8,
135 padding=[
136 # PKCS1-v1_5 padding
137 0x00, 0x01] + [0xff]*970 + [0x00] + [
138 # ASN.1 header
139 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
140 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
141 0x00, 0x04, 0x20,
142 ]),
143 'SHA512_RSA2048': Algorithm(
144 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400145 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400146 hash_num_bytes=64,
147 signature_num_bytes=256,
148 public_key_num_bytes=8 + 2*2048/8,
149 padding=[
150 # PKCS1-v1_5 padding
151 0x00, 0x01] + [0xff]*170 + [0x00] + [
152 # ASN.1 header
153 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
154 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
155 0x00, 0x04, 0x40
156 ]),
157 'SHA512_RSA4096': Algorithm(
158 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400159 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400160 hash_num_bytes=64,
161 signature_num_bytes=512,
162 public_key_num_bytes=8 + 2*4096/8,
163 padding=[
164 # PKCS1-v1_5 padding
165 0x00, 0x01] + [0xff]*426 + [0x00] + [
166 # ASN.1 header
167 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
168 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
169 0x00, 0x04, 0x40
170 ]),
171 'SHA512_RSA8192': Algorithm(
172 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400173 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400174 hash_num_bytes=64,
175 signature_num_bytes=1024,
176 public_key_num_bytes=8 + 2*8192/8,
177 padding=[
178 # PKCS1-v1_5 padding
179 0x00, 0x01] + [0xff]*938 + [0x00] + [
180 # ASN.1 header
181 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
182 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
183 0x00, 0x04, 0x40
184 ]),
185}
186
187
David Zeuthene3cadca2017-02-22 21:25:46 -0500188def get_release_string():
189 """Calculates the release string to use in the VBMeta struct."""
190 # Keep in sync with libavb/avb_version.c:avb_version_string().
191 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
192 AVB_VERSION_MINOR,
193 AVB_VERSION_SUB)
194
195
David Zeuthen21e95262016-07-27 17:58:40 -0400196def round_to_multiple(number, size):
197 """Rounds a number up to nearest multiple of another number.
198
199 Args:
200 number: The number to round up.
201 size: The multiple to round up to.
202
203 Returns:
204 If |number| is a multiple of |size|, returns |number|, otherwise
205 returns |number| + |size|.
206 """
207 remainder = number % size
208 if remainder == 0:
209 return number
210 return number + size - remainder
211
212
213def round_to_pow2(number):
214 """Rounds a number up to the next power of 2.
215
216 Args:
217 number: The number to round up.
218
219 Returns:
220 If |number| is already a power of 2 then |number| is
221 returned. Otherwise the smallest power of 2 greater than |number|
222 is returned.
223 """
224 return 2**((number - 1).bit_length())
225
226
David Zeuthen21e95262016-07-27 17:58:40 -0400227def encode_long(num_bits, value):
228 """Encodes a long to a bytearray() using a given amount of bits.
229
230 This number is written big-endian, e.g. with the most significant
231 bit first.
232
David Zeuthenb623d8b2017-04-04 16:05:53 -0400233 This is the reverse of decode_long().
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235 Arguments:
236 num_bits: The number of bits to write, e.g. 2048.
237 value: The value to write.
238
239 Returns:
240 A bytearray() with the encoded long.
241 """
242 ret = bytearray()
243 for bit_pos in range(num_bits, 0, -8):
244 octet = (value >> (bit_pos - 8)) & 0xff
245 ret.extend(struct.pack('!B', octet))
246 return ret
247
248
David Zeuthenb623d8b2017-04-04 16:05:53 -0400249def decode_long(blob):
250 """Decodes a long from a bytearray() using a given amount of bits.
251
252 This number is expected to be in big-endian, e.g. with the most
253 significant bit first.
254
255 This is the reverse of encode_long().
256
257 Arguments:
258 value: A bytearray() with the encoded long.
259
260 Returns:
261 The decoded value.
262 """
263 ret = 0
264 for b in bytearray(blob):
265 ret *= 256
266 ret += b
267 return ret
268
269
David Zeuthen21e95262016-07-27 17:58:40 -0400270def egcd(a, b):
271 """Calculate greatest common divisor of two numbers.
272
273 This implementation uses a recursive version of the extended
274 Euclidian algorithm.
275
276 Arguments:
277 a: First number.
278 b: Second number.
279
280 Returns:
281 A tuple (gcd, x, y) that where |gcd| is the greatest common
282 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
283 """
284 if a == 0:
285 return (b, 0, 1)
286 else:
287 g, y, x = egcd(b % a, a)
288 return (g, x - (b // a) * y, y)
289
290
291def modinv(a, m):
292 """Calculate modular multiplicative inverse of |a| modulo |m|.
293
294 This calculates the number |x| such that |a| * |x| == 1 (modulo
295 |m|). This number only exists if |a| and |m| are co-prime - |None|
296 is returned if this isn't true.
297
298 Arguments:
299 a: The number to calculate a modular inverse of.
300 m: The modulo to use.
301
302 Returns:
303 The modular multiplicative inverse of |a| and |m| or |None| if
304 these numbers are not co-prime.
305 """
306 gcd, x, _ = egcd(a, m)
307 if gcd != 1:
308 return None # modular inverse does not exist
309 else:
310 return x % m
311
312
313def parse_number(string):
314 """Parse a string as a number.
315
316 This is just a short-hand for int(string, 0) suitable for use in the
317 |type| parameter of |ArgumentParser|'s add_argument() function. An
318 improvement to just using type=int is that this function supports
319 numbers in other bases, e.g. "0x1234".
320
321 Arguments:
322 string: The string to parse.
323
324 Returns:
325 The parsed integer.
326
327 Raises:
328 ValueError: If the number could not be parsed.
329 """
330 return int(string, 0)
331
332
David Zeuthenc68f0822017-03-31 17:22:35 -0400333class RSAPublicKey(object):
334 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336 Attributes:
337 exponent: The key exponent.
338 modulus: The key modulus.
339 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400340 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400341
342 MODULUS_PREFIX = 'modulus='
343
344 def __init__(self, key_path):
345 """Loads and parses an RSA key from either a private or public key file.
346
347 Arguments:
348 key_path: The path to a key file.
349 """
350 # We used to have something as simple as this:
351 #
352 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
353 # self.exponent = key.e
354 # self.modulus = key.n
355 # self.num_bits = key.size() + 1
356 #
357 # but unfortunately PyCrypto is not available in the builder. So
358 # instead just parse openssl(1) output to get this
359 # information. It's ugly but...
360 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
361 p = subprocess.Popen(args,
362 stdin=subprocess.PIPE,
363 stdout=subprocess.PIPE,
364 stderr=subprocess.PIPE)
365 (pout, perr) = p.communicate()
366 if p.wait() != 0:
367 # Could be just a public key is passed, try that.
368 args.append('-pubin')
369 p = subprocess.Popen(args,
370 stdin=subprocess.PIPE,
371 stdout=subprocess.PIPE,
372 stderr=subprocess.PIPE)
373 (pout, perr) = p.communicate()
374 if p.wait() != 0:
375 raise AvbError('Error getting public key: {}'.format(perr))
376
377 if not pout.lower().startswith(self.MODULUS_PREFIX):
378 raise AvbError('Unexpected modulus output')
379
380 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
381
382 # The exponent is assumed to always be 65537 and the number of
383 # bits can be derived from the modulus by rounding up to the
384 # nearest power of 2.
385 self.modulus = int(modulus_hexstr, 16)
386 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
387 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400388
389
David Zeuthenc68f0822017-03-31 17:22:35 -0400390def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400391 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
392
393 This creates a |AvbRSAPublicKeyHeader| as well as the two large
394 numbers (|key_num_bits| bits long) following it.
395
396 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400397 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400398
399 Returns:
400 A bytearray() with the |AvbRSAPublicKeyHeader|.
401 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400402 key = RSAPublicKey(key_path)
403 if key.exponent != 65537:
404 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400405 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400406 # Calculate n0inv = -1/n[0] (mod 2^32)
407 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400408 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400409 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400410 r = 2L**key.modulus.bit_length()
411 rrmodn = r * r % key.modulus
412 ret.extend(struct.pack('!II', key.num_bits, n0inv))
413 ret.extend(encode_long(key.num_bits, key.modulus))
414 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400415 return ret
416
417
418def lookup_algorithm_by_type(alg_type):
419 """Looks up algorithm by type.
420
421 Arguments:
422 alg_type: The integer representing the type.
423
424 Returns:
425 A tuple with the algorithm name and an |Algorithm| instance.
426
427 Raises:
428 Exception: If the algorithm cannot be found
429 """
430 for alg_name in ALGORITHMS:
431 alg_data = ALGORITHMS[alg_name]
432 if alg_data.algorithm_type == alg_type:
433 return (alg_name, alg_data)
434 raise AvbError('Unknown algorithm type {}'.format(alg_type))
435
436
Esun Kimff44f232017-03-30 10:34:54 +0900437def raw_sign(signing_helper, algorithm_name, signature_num_bytes, key_path,
438 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800439 """Computes a raw RSA signature using |signing_helper| or openssl.
440
441 Arguments:
442 signing_helper: Program which signs a hash and returns the signature.
443 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900444 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800445 key_path: Path to the private key file. Must be PEM format.
446 raw_data_to_sign: Data to sign (bytearray or str expected).
447
448 Returns:
449 A bytearray containing the signature.
450
451 Raises:
452 Exception: If an error occurs.
453 """
454 p = None
455 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500456 p = subprocess.Popen(
457 [signing_helper, algorithm_name, key_path],
458 stdin=subprocess.PIPE,
459 stdout=subprocess.PIPE,
460 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800461 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500462 p = subprocess.Popen(
463 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
464 stdin=subprocess.PIPE,
465 stdout=subprocess.PIPE,
466 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800467 (pout, perr) = p.communicate(str(raw_data_to_sign))
468 retcode = p.wait()
469 if retcode != 0:
470 raise AvbError('Error signing: {}'.format(perr))
Esun Kimff44f232017-03-30 10:34:54 +0900471 signature = bytearray(pout)
472 if len(signature) != signature_num_bytes:
473 raise AvbError('Error signing: Invalid length of signature')
474 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800475
476
David Zeuthenb623d8b2017-04-04 16:05:53 -0400477def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
478 """Checks that the signature in a vbmeta blob was made by
479 the embedded public key.
480
481 Arguments:
482 vbmeta_header: A AvbVBMetaHeader.
483 vbmeta_blob: The whole vbmeta blob, including the header.
484
485 Returns:
486 True if the signature is valid and corresponds to the embedded
487 public key. Also returns True if the vbmeta blob is not signed.
488 """
489 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
490 if alg.hash_name == '':
491 return True
492 header_blob = vbmeta_blob[0:256]
493 auth_offset = 256
494 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
495 aux_size = vbmeta_header.auxiliary_data_block_size
496 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
497 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
498 pubkey_size = vbmeta_header.public_key_size
499 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
500
501 digest_offset = auth_offset + vbmeta_header.hash_offset
502 digest_size = vbmeta_header.hash_size
503 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
504
505 sig_offset = auth_offset + vbmeta_header.signature_offset
506 sig_size = vbmeta_header.signature_size
507 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
508
509 # Now that we've got the stored digest, public key, and signature
510 # all we need to do is to verify. This is the exactly the same
511 # steps as performed in the avb_vbmeta_image_verify() function in
512 # libavb/avb_vbmeta_image.c.
513
514 ha = hashlib.new(alg.hash_name)
515 ha.update(header_blob)
516 ha.update(aux_blob)
517 computed_digest = ha.digest()
518
519 if computed_digest != digest_blob:
520 return False
521
522 padding_and_digest = bytearray(alg.padding)
523 padding_and_digest.extend(computed_digest)
524
525 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
526 modulus_blob = pubkey_blob[8:8 + num_bits/8]
527 modulus = decode_long(modulus_blob)
528 exponent = 65537
529
530 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
531 # is OK since 'avbtool verify_image' is not expected to run on the
532 # Android builders (see bug #36809096).
533 import Crypto.PublicKey.RSA
534 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
535 if not key.verify(decode_long(padding_and_digest),
536 (decode_long(sig_blob), None)):
537 return False
538 return True
539
540
David Zeuthena4fee8b2016-08-22 15:20:43 -0400541class ImageChunk(object):
542 """Data structure used for representing chunks in Android sparse files.
543
544 Attributes:
545 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
546 chunk_offset: Offset in the sparse file where this chunk begins.
547 output_offset: Offset in de-sparsified file where output begins.
548 output_size: Number of bytes in output.
549 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
550 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
551 """
552
553 FORMAT = '<2H2I'
554 TYPE_RAW = 0xcac1
555 TYPE_FILL = 0xcac2
556 TYPE_DONT_CARE = 0xcac3
557 TYPE_CRC32 = 0xcac4
558
559 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
560 input_offset, fill_data):
561 """Initializes an ImageChunk object.
562
563 Arguments:
564 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
565 chunk_offset: Offset in the sparse file where this chunk begins.
566 output_offset: Offset in de-sparsified file.
567 output_size: Number of bytes in output.
568 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
569 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
570
571 Raises:
572 ValueError: If data is not well-formed.
573 """
574 self.chunk_type = chunk_type
575 self.chunk_offset = chunk_offset
576 self.output_offset = output_offset
577 self.output_size = output_size
578 self.input_offset = input_offset
579 self.fill_data = fill_data
580 # Check invariants.
581 if self.chunk_type == self.TYPE_RAW:
582 if self.fill_data is not None:
583 raise ValueError('RAW chunk cannot have fill_data set.')
584 if not self.input_offset:
585 raise ValueError('RAW chunk must have input_offset set.')
586 elif self.chunk_type == self.TYPE_FILL:
587 if self.fill_data is None:
588 raise ValueError('FILL chunk must have fill_data set.')
589 if self.input_offset:
590 raise ValueError('FILL chunk cannot have input_offset set.')
591 elif self.chunk_type == self.TYPE_DONT_CARE:
592 if self.fill_data is not None:
593 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
594 if self.input_offset:
595 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
596 else:
597 raise ValueError('Invalid chunk type')
598
599
600class ImageHandler(object):
601 """Abstraction for image I/O with support for Android sparse images.
602
603 This class provides an interface for working with image files that
604 may be using the Android Sparse Image format. When an instance is
605 constructed, we test whether it's an Android sparse file. If so,
606 operations will be on the sparse file by interpreting the sparse
607 format, otherwise they will be directly on the file. Either way the
608 operations do the same.
609
610 For reading, this interface mimics a file object - it has seek(),
611 tell(), and read() methods. For writing, only truncation
612 (truncate()) and appending is supported (append_raw() and
613 append_dont_care()). Additionally, data can only be written in units
614 of the block size.
615
616 Attributes:
617 is_sparse: Whether the file being operated on is sparse.
618 block_size: The block size, typically 4096.
619 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400620 """
621 # See system/core/libsparse/sparse_format.h for details.
622 MAGIC = 0xed26ff3a
623 HEADER_FORMAT = '<I4H4I'
624
625 # These are formats and offset of just the |total_chunks| and
626 # |total_blocks| fields.
627 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
628 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
629
630 def __init__(self, image_filename):
631 """Initializes an image handler.
632
633 Arguments:
634 image_filename: The name of the file to operate on.
635
636 Raises:
637 ValueError: If data in the file is invalid.
638 """
639 self._image_filename = image_filename
640 self._read_header()
641
642 def _read_header(self):
643 """Initializes internal data structures used for reading file.
644
645 This may be called multiple times and is typically called after
646 modifying the file (e.g. appending, truncation).
647
648 Raises:
649 ValueError: If data in the file is invalid.
650 """
651 self.is_sparse = False
652 self.block_size = 4096
653 self._file_pos = 0
654 self._image = open(self._image_filename, 'r+b')
655 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400656 self.image_size = self._image.tell()
657
658 self._image.seek(0, os.SEEK_SET)
659 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
660 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
661 block_size, self._num_total_blocks, self._num_total_chunks,
662 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
663 if magic != self.MAGIC:
664 # Not a sparse image, our job here is done.
665 return
666 if not (major_version == 1 and minor_version == 0):
667 raise ValueError('Encountered sparse image format version {}.{} but '
668 'only 1.0 is supported'.format(major_version,
669 minor_version))
670 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
671 raise ValueError('Unexpected file_hdr_sz value {}.'.
672 format(file_hdr_sz))
673 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
674 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
675 format(chunk_hdr_sz))
676
677 self.block_size = block_size
678
679 # Build an list of chunks by parsing the file.
680 self._chunks = []
681
682 # Find the smallest offset where only "Don't care" chunks
683 # follow. This will be the size of the content in the sparse
684 # image.
685 offset = 0
686 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400687 for _ in xrange(1, self._num_total_chunks + 1):
688 chunk_offset = self._image.tell()
689
690 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
691 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
692 header_bin)
693 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
694
David Zeuthena4fee8b2016-08-22 15:20:43 -0400695 if chunk_type == ImageChunk.TYPE_RAW:
696 if data_sz != (chunk_sz * self.block_size):
697 raise ValueError('Raw chunk input size ({}) does not match output '
698 'size ({})'.
699 format(data_sz, chunk_sz*self.block_size))
700 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
701 chunk_offset,
702 output_offset,
703 chunk_sz*self.block_size,
704 self._image.tell(),
705 None))
706 self._image.read(data_sz)
707
708 elif chunk_type == ImageChunk.TYPE_FILL:
709 if data_sz != 4:
710 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
711 'has {}'.format(data_sz))
712 fill_data = self._image.read(4)
713 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
714 chunk_offset,
715 output_offset,
716 chunk_sz*self.block_size,
717 None,
718 fill_data))
719 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
720 if data_sz != 0:
721 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
722 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400723 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
724 chunk_offset,
725 output_offset,
726 chunk_sz*self.block_size,
727 None,
728 None))
729 elif chunk_type == ImageChunk.TYPE_CRC32:
730 if data_sz != 4:
731 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
732 'this has {}'.format(data_sz))
733 self._image.read(4)
734 else:
735 raise ValueError('Unknown chunk type {}'.format(chunk_type))
736
737 offset += chunk_sz
738 output_offset += chunk_sz*self.block_size
739
740 # Record where sparse data end.
741 self._sparse_end = self._image.tell()
742
743 # Now that we've traversed all chunks, sanity check.
744 if self._num_total_blocks != offset:
745 raise ValueError('The header said we should have {} output blocks, '
746 'but we saw {}'.format(self._num_total_blocks, offset))
747 junk_len = len(self._image.read())
748 if junk_len > 0:
749 raise ValueError('There were {} bytes of extra data at the end of the '
750 'file.'.format(junk_len))
751
David Zeuthen09692692016-09-30 16:16:40 -0400752 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400753 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400754
755 # This is used when bisecting in read() to find the initial slice.
756 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
757
758 self.is_sparse = True
759
760 def _update_chunks_and_blocks(self):
761 """Helper function to update the image header.
762
763 The the |total_chunks| and |total_blocks| fields in the header
764 will be set to value of the |_num_total_blocks| and
765 |_num_total_chunks| attributes.
766
767 """
768 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
769 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
770 self._num_total_blocks,
771 self._num_total_chunks))
772
773 def append_dont_care(self, num_bytes):
774 """Appends a DONT_CARE chunk to the sparse file.
775
776 The given number of bytes must be a multiple of the block size.
777
778 Arguments:
779 num_bytes: Size in number of bytes of the DONT_CARE chunk.
780 """
781 assert num_bytes % self.block_size == 0
782
783 if not self.is_sparse:
784 self._image.seek(0, os.SEEK_END)
785 # This is more efficient that writing NUL bytes since it'll add
786 # a hole on file systems that support sparse files (native
787 # sparse, not Android sparse).
788 self._image.truncate(self._image.tell() + num_bytes)
789 self._read_header()
790 return
791
792 self._num_total_chunks += 1
793 self._num_total_blocks += num_bytes / self.block_size
794 self._update_chunks_and_blocks()
795
796 self._image.seek(self._sparse_end, os.SEEK_SET)
797 self._image.write(struct.pack(ImageChunk.FORMAT,
798 ImageChunk.TYPE_DONT_CARE,
799 0, # Reserved
800 num_bytes / self.block_size,
801 struct.calcsize(ImageChunk.FORMAT)))
802 self._read_header()
803
804 def append_raw(self, data):
805 """Appends a RAW chunk to the sparse file.
806
807 The length of the given data must be a multiple of the block size.
808
809 Arguments:
810 data: Data to append.
811 """
812 assert len(data) % self.block_size == 0
813
814 if not self.is_sparse:
815 self._image.seek(0, os.SEEK_END)
816 self._image.write(data)
817 self._read_header()
818 return
819
820 self._num_total_chunks += 1
821 self._num_total_blocks += len(data) / self.block_size
822 self._update_chunks_and_blocks()
823
824 self._image.seek(self._sparse_end, os.SEEK_SET)
825 self._image.write(struct.pack(ImageChunk.FORMAT,
826 ImageChunk.TYPE_RAW,
827 0, # Reserved
828 len(data) / self.block_size,
829 len(data) +
830 struct.calcsize(ImageChunk.FORMAT)))
831 self._image.write(data)
832 self._read_header()
833
834 def append_fill(self, fill_data, size):
835 """Appends a fill chunk to the sparse file.
836
837 The total length of the fill data must be a multiple of the block size.
838
839 Arguments:
840 fill_data: Fill data to append - must be four bytes.
841 size: Number of chunk - must be a multiple of four and the block size.
842 """
843 assert len(fill_data) == 4
844 assert size % 4 == 0
845 assert size % self.block_size == 0
846
847 if not self.is_sparse:
848 self._image.seek(0, os.SEEK_END)
849 self._image.write(fill_data * (size/4))
850 self._read_header()
851 return
852
853 self._num_total_chunks += 1
854 self._num_total_blocks += size / self.block_size
855 self._update_chunks_and_blocks()
856
857 self._image.seek(self._sparse_end, os.SEEK_SET)
858 self._image.write(struct.pack(ImageChunk.FORMAT,
859 ImageChunk.TYPE_FILL,
860 0, # Reserved
861 size / self.block_size,
862 4 + struct.calcsize(ImageChunk.FORMAT)))
863 self._image.write(fill_data)
864 self._read_header()
865
866 def seek(self, offset):
867 """Sets the cursor position for reading from unsparsified file.
868
869 Arguments:
870 offset: Offset to seek to from the beginning of the file.
871 """
872 self._file_pos = offset
873
874 def read(self, size):
875 """Reads data from the unsparsified file.
876
877 This method may return fewer than |size| bytes of data if the end
878 of the file was encountered.
879
880 The file cursor for reading is advanced by the number of bytes
881 read.
882
883 Arguments:
884 size: Number of bytes to read.
885
886 Returns:
887 The data.
888
889 """
890 if not self.is_sparse:
891 self._image.seek(self._file_pos)
892 data = self._image.read(size)
893 self._file_pos += len(data)
894 return data
895
896 # Iterate over all chunks.
897 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
898 self._file_pos) - 1
899 data = bytearray()
900 to_go = size
901 while to_go > 0:
902 chunk = self._chunks[chunk_idx]
903 chunk_pos_offset = self._file_pos - chunk.output_offset
904 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
905
906 if chunk.chunk_type == ImageChunk.TYPE_RAW:
907 self._image.seek(chunk.input_offset + chunk_pos_offset)
908 data.extend(self._image.read(chunk_pos_to_go))
909 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
910 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
911 offset_mod = chunk_pos_offset % len(chunk.fill_data)
912 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
913 else:
914 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
915 data.extend('\0' * chunk_pos_to_go)
916
917 to_go -= chunk_pos_to_go
918 self._file_pos += chunk_pos_to_go
919 chunk_idx += 1
920 # Generate partial read in case of EOF.
921 if chunk_idx >= len(self._chunks):
922 break
923
924 return data
925
926 def tell(self):
927 """Returns the file cursor position for reading from unsparsified file.
928
929 Returns:
930 The file cursor position for reading.
931 """
932 return self._file_pos
933
934 def truncate(self, size):
935 """Truncates the unsparsified file.
936
937 Arguments:
938 size: Desired size of unsparsified file.
939
940 Raises:
941 ValueError: If desired size isn't a multiple of the block size.
942 """
943 if not self.is_sparse:
944 self._image.truncate(size)
945 self._read_header()
946 return
947
948 if size % self.block_size != 0:
949 raise ValueError('Cannot truncate to a size which is not a multiple '
950 'of the block size')
951
952 if size == self.image_size:
953 # Trivial where there's nothing to do.
954 return
955 elif size < self.image_size:
956 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
957 chunk = self._chunks[chunk_idx]
958 if chunk.output_offset != size:
959 # Truncation in the middle of a trunk - need to keep the chunk
960 # and modify it.
961 chunk_idx_for_update = chunk_idx + 1
962 num_to_keep = size - chunk.output_offset
963 assert num_to_keep % self.block_size == 0
964 if chunk.chunk_type == ImageChunk.TYPE_RAW:
965 truncate_at = (chunk.chunk_offset +
966 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
967 data_sz = num_to_keep
968 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
969 truncate_at = (chunk.chunk_offset +
970 struct.calcsize(ImageChunk.FORMAT) + 4)
971 data_sz = 4
972 else:
973 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
974 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
975 data_sz = 0
976 chunk_sz = num_to_keep/self.block_size
977 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
978 self._image.seek(chunk.chunk_offset)
979 self._image.write(struct.pack(ImageChunk.FORMAT,
980 chunk.chunk_type,
981 0, # Reserved
982 chunk_sz,
983 total_sz))
984 chunk.output_size = num_to_keep
985 else:
986 # Truncation at trunk boundary.
987 truncate_at = chunk.chunk_offset
988 chunk_idx_for_update = chunk_idx
989
990 self._num_total_chunks = chunk_idx_for_update
991 self._num_total_blocks = 0
992 for i in range(0, chunk_idx_for_update):
993 self._num_total_blocks += self._chunks[i].output_size / self.block_size
994 self._update_chunks_and_blocks()
995 self._image.truncate(truncate_at)
996
997 # We've modified the file so re-read all data.
998 self._read_header()
999 else:
1000 # Truncating to grow - just add a DONT_CARE section.
1001 self.append_dont_care(size - self.image_size)
1002
1003
David Zeuthen21e95262016-07-27 17:58:40 -04001004class AvbDescriptor(object):
1005 """Class for AVB descriptor.
1006
1007 See the |AvbDescriptor| C struct for more information.
1008
1009 Attributes:
1010 tag: The tag identifying what kind of descriptor this is.
1011 data: The data in the descriptor.
1012 """
1013
1014 SIZE = 16
1015 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1016
1017 def __init__(self, data):
1018 """Initializes a new property descriptor.
1019
1020 Arguments:
1021 data: If not None, must be a bytearray().
1022
1023 Raises:
1024 LookupError: If the given descriptor is malformed.
1025 """
1026 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1027
1028 if data:
1029 (self.tag, num_bytes_following) = (
1030 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1031 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1032 else:
1033 self.tag = None
1034 self.data = None
1035
1036 def print_desc(self, o):
1037 """Print the descriptor.
1038
1039 Arguments:
1040 o: The object to write the output to.
1041 """
1042 o.write(' Unknown descriptor:\n')
1043 o.write(' Tag: {}\n'.format(self.tag))
1044 if len(self.data) < 256:
1045 o.write(' Data: {} ({} bytes)\n'.format(
1046 repr(str(self.data)), len(self.data)))
1047 else:
1048 o.write(' Data: {} bytes\n'.format(len(self.data)))
1049
1050 def encode(self):
1051 """Serializes the descriptor.
1052
1053 Returns:
1054 A bytearray() with the descriptor data.
1055 """
1056 num_bytes_following = len(self.data)
1057 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1058 padding_size = nbf_with_padding - num_bytes_following
1059 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1060 padding = struct.pack(str(padding_size) + 'x')
1061 ret = desc + self.data + padding
1062 return bytearray(ret)
1063
1064
1065class AvbPropertyDescriptor(AvbDescriptor):
1066 """A class for property descriptors.
1067
1068 See the |AvbPropertyDescriptor| C struct for more information.
1069
1070 Attributes:
1071 key: The key.
1072 value: The key.
1073 """
1074
1075 TAG = 0
1076 SIZE = 32
1077 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1078 'Q' # key size (bytes)
1079 'Q') # value size (bytes)
1080
1081 def __init__(self, data=None):
1082 """Initializes a new property descriptor.
1083
1084 Arguments:
1085 data: If not None, must be a bytearray of size |SIZE|.
1086
1087 Raises:
1088 LookupError: If the given descriptor is malformed.
1089 """
1090 AvbDescriptor.__init__(self, None)
1091 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1092
1093 if data:
1094 (tag, num_bytes_following, key_size,
1095 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1096 expected_size = round_to_multiple(
1097 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1098 if tag != self.TAG or num_bytes_following != expected_size:
1099 raise LookupError('Given data does not look like a property '
1100 'descriptor.')
1101 self.key = data[self.SIZE:(self.SIZE + key_size)]
1102 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1103 value_size)]
1104 else:
1105 self.key = ''
1106 self.value = ''
1107
1108 def print_desc(self, o):
1109 """Print the descriptor.
1110
1111 Arguments:
1112 o: The object to write the output to.
1113 """
1114 if len(self.value) < 256:
1115 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1116 else:
1117 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1118
1119 def encode(self):
1120 """Serializes the descriptor.
1121
1122 Returns:
1123 A bytearray() with the descriptor data.
1124 """
1125 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1126 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1127 padding_size = nbf_with_padding - num_bytes_following
1128 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1129 len(self.key), len(self.value))
1130 padding = struct.pack(str(padding_size) + 'x')
1131 ret = desc + self.key + '\0' + self.value + '\0' + padding
1132 return bytearray(ret)
1133
1134
1135class AvbHashtreeDescriptor(AvbDescriptor):
1136 """A class for hashtree descriptors.
1137
1138 See the |AvbHashtreeDescriptor| C struct for more information.
1139
1140 Attributes:
1141 dm_verity_version: dm-verity version used.
1142 image_size: Size of the image, after rounding up to |block_size|.
1143 tree_offset: Offset of the hash tree in the file.
1144 tree_size: Size of the tree.
1145 data_block_size: Data block size
1146 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001147 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1148 fec_offset: Offset of FEC data (0 if FEC is not used).
1149 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001150 hash_algorithm: Hash algorithm used.
1151 partition_name: Partition name.
1152 salt: Salt used.
1153 root_digest: Root digest.
1154 """
1155
1156 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001157 RESERVED = 64
1158 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001159 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1160 'L' # dm-verity version used
1161 'Q' # image size (bytes)
1162 'Q' # tree offset (bytes)
1163 'Q' # tree size (bytes)
1164 'L' # data block size (bytes)
1165 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001166 'L' # FEC number of roots
1167 'Q' # FEC offset (bytes)
1168 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001169 '32s' # hash algorithm used
1170 'L' # partition name (bytes)
1171 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 'L' + # root digest length (bytes)
1173 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001174
1175 def __init__(self, data=None):
1176 """Initializes a new hashtree descriptor.
1177
1178 Arguments:
1179 data: If not None, must be a bytearray of size |SIZE|.
1180
1181 Raises:
1182 LookupError: If the given descriptor is malformed.
1183 """
1184 AvbDescriptor.__init__(self, None)
1185 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1186
1187 if data:
1188 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1189 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001190 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1191 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001192 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1193 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001194 expected_size = round_to_multiple(
1195 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1196 if tag != self.TAG or num_bytes_following != expected_size:
1197 raise LookupError('Given data does not look like a hashtree '
1198 'descriptor.')
1199 # Nuke NUL-bytes at the end.
1200 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1201 o = 0
1202 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1203 partition_name_len)])
1204 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1205 self.partition_name.decode('utf-8')
1206 o += partition_name_len
1207 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1208 o += salt_len
1209 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1210 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1211 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1212
1213 else:
1214 self.dm_verity_version = 0
1215 self.image_size = 0
1216 self.tree_offset = 0
1217 self.tree_size = 0
1218 self.data_block_size = 0
1219 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001220 self.fec_num_roots = 0
1221 self.fec_offset = 0
1222 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001223 self.hash_algorithm = ''
1224 self.partition_name = ''
1225 self.salt = bytearray()
1226 self.root_digest = bytearray()
1227
1228 def print_desc(self, o):
1229 """Print the descriptor.
1230
1231 Arguments:
1232 o: The object to write the output to.
1233 """
1234 o.write(' Hashtree descriptor:\n')
1235 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1236 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1237 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1238 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1239 o.write(' Data Block Size: {} bytes\n'.format(
1240 self.data_block_size))
1241 o.write(' Hash Block Size: {} bytes\n'.format(
1242 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001243 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1244 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1245 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001246 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1247 o.write(' Partition Name: {}\n'.format(self.partition_name))
1248 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1249 'hex')))
1250 o.write(' Root Digest: {}\n'.format(str(
1251 self.root_digest).encode('hex')))
1252
1253 def encode(self):
1254 """Serializes the descriptor.
1255
1256 Returns:
1257 A bytearray() with the descriptor data.
1258 """
1259 encoded_name = self.partition_name.encode('utf-8')
1260 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1261 len(self.root_digest) - 16)
1262 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1263 padding_size = nbf_with_padding - num_bytes_following
1264 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1265 self.dm_verity_version, self.image_size,
1266 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001267 self.hash_block_size, self.fec_num_roots,
1268 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001269 len(encoded_name), len(self.salt), len(self.root_digest),
1270 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001271 padding = struct.pack(str(padding_size) + 'x')
1272 ret = desc + encoded_name + self.salt + self.root_digest + padding
1273 return bytearray(ret)
1274
1275
1276class AvbHashDescriptor(AvbDescriptor):
1277 """A class for hash descriptors.
1278
1279 See the |AvbHashDescriptor| C struct for more information.
1280
1281 Attributes:
1282 image_size: Image size, in bytes.
1283 hash_algorithm: Hash algorithm used.
1284 partition_name: Partition name.
1285 salt: Salt used.
1286 digest: The hash value of salt and data combined.
1287 """
1288
1289 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001290 RESERVED = 64
1291 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001292 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1293 'Q' # image size (bytes)
1294 '32s' # hash algorithm used
1295 'L' # partition name (bytes)
1296 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001297 'L' + # digest length (bytes)
1298 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001299
1300 def __init__(self, data=None):
1301 """Initializes a new hash descriptor.
1302
1303 Arguments:
1304 data: If not None, must be a bytearray of size |SIZE|.
1305
1306 Raises:
1307 LookupError: If the given descriptor is malformed.
1308 """
1309 AvbDescriptor.__init__(self, None)
1310 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1311
1312 if data:
1313 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1314 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001315 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001316 expected_size = round_to_multiple(
1317 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1318 if tag != self.TAG or num_bytes_following != expected_size:
1319 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1320 # Nuke NUL-bytes at the end.
1321 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1322 o = 0
1323 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1324 partition_name_len)])
1325 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1326 self.partition_name.decode('utf-8')
1327 o += partition_name_len
1328 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1329 o += salt_len
1330 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1331 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1332 raise LookupError('digest_len doesn\'t match hash algorithm')
1333
1334 else:
1335 self.image_size = 0
1336 self.hash_algorithm = ''
1337 self.partition_name = ''
1338 self.salt = bytearray()
1339 self.digest = bytearray()
1340
1341 def print_desc(self, o):
1342 """Print the descriptor.
1343
1344 Arguments:
1345 o: The object to write the output to.
1346 """
1347 o.write(' Hash descriptor:\n')
1348 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1349 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1350 o.write(' Partition Name: {}\n'.format(self.partition_name))
1351 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1352 'hex')))
1353 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1354 'hex')))
1355
1356 def encode(self):
1357 """Serializes the descriptor.
1358
1359 Returns:
1360 A bytearray() with the descriptor data.
1361 """
1362 encoded_name = self.partition_name.encode('utf-8')
1363 num_bytes_following = (
1364 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1365 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1366 padding_size = nbf_with_padding - num_bytes_following
1367 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1368 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001369 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001370 padding = struct.pack(str(padding_size) + 'x')
1371 ret = desc + encoded_name + self.salt + self.digest + padding
1372 return bytearray(ret)
1373
1374
1375class AvbKernelCmdlineDescriptor(AvbDescriptor):
1376 """A class for kernel command-line descriptors.
1377
1378 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1379
1380 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001381 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001382 kernel_cmdline: The kernel command-line.
1383 """
1384
1385 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001386 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001387 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001388 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001389 'L') # cmdline length (bytes)
1390
David Zeuthenfd41eb92016-11-17 12:24:47 -05001391 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1392 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1393
David Zeuthen21e95262016-07-27 17:58:40 -04001394 def __init__(self, data=None):
1395 """Initializes a new kernel cmdline descriptor.
1396
1397 Arguments:
1398 data: If not None, must be a bytearray of size |SIZE|.
1399
1400 Raises:
1401 LookupError: If the given descriptor is malformed.
1402 """
1403 AvbDescriptor.__init__(self, None)
1404 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1405
1406 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001407 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001408 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1409 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1410 8)
1411 if tag != self.TAG or num_bytes_following != expected_size:
1412 raise LookupError('Given data does not look like a kernel cmdline '
1413 'descriptor.')
1414 # Nuke NUL-bytes at the end.
1415 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1416 kernel_cmdline_length)])
1417 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1418 self.kernel_cmdline.decode('utf-8')
1419 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001420 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001421 self.kernel_cmdline = ''
1422
1423 def print_desc(self, o):
1424 """Print the descriptor.
1425
1426 Arguments:
1427 o: The object to write the output to.
1428 """
1429 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001430 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001431 o.write(' Kernel Cmdline: {}\n'.format(repr(
1432 self.kernel_cmdline)))
1433
1434 def encode(self):
1435 """Serializes the descriptor.
1436
1437 Returns:
1438 A bytearray() with the descriptor data.
1439 """
1440 encoded_str = self.kernel_cmdline.encode('utf-8')
1441 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1442 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1443 padding_size = nbf_with_padding - num_bytes_following
1444 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001445 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001446 padding = struct.pack(str(padding_size) + 'x')
1447 ret = desc + encoded_str + padding
1448 return bytearray(ret)
1449
1450
1451class AvbChainPartitionDescriptor(AvbDescriptor):
1452 """A class for chained partition descriptors.
1453
1454 See the |AvbChainPartitionDescriptor| C struct for more information.
1455
1456 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001457 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001458 partition_name: Partition name.
1459 public_key: Bytes for the public key.
1460 """
1461
1462 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001463 RESERVED = 64
1464 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001465 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001466 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001467 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001468 'L' + # public_key_size (bytes)
1469 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001470
1471 def __init__(self, data=None):
1472 """Initializes a new chain partition descriptor.
1473
1474 Arguments:
1475 data: If not None, must be a bytearray of size |SIZE|.
1476
1477 Raises:
1478 LookupError: If the given descriptor is malformed.
1479 """
1480 AvbDescriptor.__init__(self, None)
1481 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1482
1483 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001484 (tag, num_bytes_following, self.rollback_index_location,
1485 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001486 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001487 expected_size = round_to_multiple(
1488 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1489 if tag != self.TAG or num_bytes_following != expected_size:
1490 raise LookupError('Given data does not look like a chain partition '
1491 'descriptor.')
1492 o = 0
1493 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1494 partition_name_len)])
1495 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1496 self.partition_name.decode('utf-8')
1497 o += partition_name_len
1498 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1499
1500 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001501 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001502 self.partition_name = ''
1503 self.public_key = bytearray()
1504
1505 def print_desc(self, o):
1506 """Print the descriptor.
1507
1508 Arguments:
1509 o: The object to write the output to.
1510 """
1511 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001512 o.write(' Partition Name: {}\n'.format(self.partition_name))
1513 o.write(' Rollback Index Location: {}\n'.format(
1514 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001515 # Just show the SHA1 of the key, for size reasons.
1516 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001517 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001518
1519 def encode(self):
1520 """Serializes the descriptor.
1521
1522 Returns:
1523 A bytearray() with the descriptor data.
1524 """
1525 encoded_name = self.partition_name.encode('utf-8')
1526 num_bytes_following = (
1527 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1528 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1529 padding_size = nbf_with_padding - num_bytes_following
1530 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001531 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001532 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001533 padding = struct.pack(str(padding_size) + 'x')
1534 ret = desc + encoded_name + self.public_key + padding
1535 return bytearray(ret)
1536
1537
1538DESCRIPTOR_CLASSES = [
1539 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1540 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1541]
1542
1543
1544def parse_descriptors(data):
1545 """Parses a blob of data into descriptors.
1546
1547 Arguments:
1548 data: A bytearray() with encoded descriptors.
1549
1550 Returns:
1551 A list of instances of objects derived from AvbDescriptor. For
1552 unknown descriptors, the class AvbDescriptor is used.
1553 """
1554 o = 0
1555 ret = []
1556 while o < len(data):
1557 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1558 if tag < len(DESCRIPTOR_CLASSES):
1559 c = DESCRIPTOR_CLASSES[tag]
1560 else:
1561 c = AvbDescriptor
1562 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1563 o += 16 + nb_following
1564 return ret
1565
1566
1567class AvbFooter(object):
1568 """A class for parsing and writing footers.
1569
1570 Footers are stored at the end of partitions and point to where the
1571 AvbVBMeta blob is located. They also contain the original size of
1572 the image before AVB information was added.
1573
1574 Attributes:
1575 magic: Magic for identifying the footer, see |MAGIC|.
1576 version_major: The major version of avbtool that wrote the footer.
1577 version_minor: The minor version of avbtool that wrote the footer.
1578 original_image_size: Original image size.
1579 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1580 vbmeta_size: Size of the AvbVBMeta blob.
1581 """
1582
1583 MAGIC = 'AVBf'
1584 SIZE = 64
1585 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001586 FOOTER_VERSION_MAJOR = 1
1587 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001588 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1589 'Q' # Original image size.
1590 'Q' # Offset of VBMeta blob.
1591 'Q' + # Size of VBMeta blob.
1592 str(RESERVED) + 'x') # padding for reserved bytes
1593
1594 def __init__(self, data=None):
1595 """Initializes a new footer object.
1596
1597 Arguments:
1598 data: If not None, must be a bytearray of size 4096.
1599
1600 Raises:
1601 LookupError: If the given footer is malformed.
1602 struct.error: If the given data has no footer.
1603 """
1604 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1605
1606 if data:
1607 (self.magic, self.version_major, self.version_minor,
1608 self.original_image_size, self.vbmeta_offset,
1609 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1610 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001611 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001612 else:
1613 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001614 self.version_major = self.FOOTER_VERSION_MAJOR
1615 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001616 self.original_image_size = 0
1617 self.vbmeta_offset = 0
1618 self.vbmeta_size = 0
1619
David Zeuthena4fee8b2016-08-22 15:20:43 -04001620 def encode(self):
1621 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001622
David Zeuthena4fee8b2016-08-22 15:20:43 -04001623 Returns:
1624 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001625 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001626 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1627 self.version_minor, self.original_image_size,
1628 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001629
1630
1631class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001632 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001633
1634 Attributes:
1635 The attributes correspond to the |AvbVBMetaHeader| struct
1636 defined in avb_vbmeta_header.h.
1637 """
1638
1639 SIZE = 256
1640
David Zeuthene3cadca2017-02-22 21:25:46 -05001641 # Keep in sync with |reserved0| and |reserved| field of
1642 # |AvbVBMetaImageHeader|.
1643 RESERVED0 = 4
1644 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646 # Keep in sync with |AvbVBMetaImageHeader|.
1647 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1648 '2Q' # 2 x block size
1649 'L' # algorithm type
1650 '2Q' # offset, size (hash)
1651 '2Q' # offset, size (signature)
1652 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001653 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001654 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001655 'Q' # rollback_index
1656 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001657 str(RESERVED0) + 'x' + # padding for reserved bytes
1658 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001659 str(RESERVED) + 'x') # padding for reserved bytes
1660
1661 def __init__(self, data=None):
1662 """Initializes a new header object.
1663
1664 Arguments:
1665 data: If not None, must be a bytearray of size 8192.
1666
1667 Raises:
1668 Exception: If the given data is malformed.
1669 """
1670 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1671
1672 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001673 (self.magic, self.required_libavb_version_major,
1674 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001675 self.authentication_data_block_size, self.auxiliary_data_block_size,
1676 self.algorithm_type, self.hash_offset, self.hash_size,
1677 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001678 self.public_key_size, self.public_key_metadata_offset,
1679 self.public_key_metadata_size, self.descriptors_offset,
1680 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001681 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001682 self.flags,
1683 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001684 # Nuke NUL-bytes at the end of the string.
1685 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001686 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001687 else:
1688 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001689 # Start by just requiring version 1.0. Code that adds features
1690 # in a future version can use bump_required_libavb_version_minor() to
1691 # bump the minor.
1692 self.required_libavb_version_major = AVB_VERSION_MAJOR
1693 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.authentication_data_block_size = 0
1695 self.auxiliary_data_block_size = 0
1696 self.algorithm_type = 0
1697 self.hash_offset = 0
1698 self.hash_size = 0
1699 self.signature_offset = 0
1700 self.signature_size = 0
1701 self.public_key_offset = 0
1702 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001703 self.public_key_metadata_offset = 0
1704 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001705 self.descriptors_offset = 0
1706 self.descriptors_size = 0
1707 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001708 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001709 self.release_string = get_release_string()
1710
1711 def bump_required_libavb_version_minor(self, minor):
1712 """Function to bump required_libavb_version_minor.
1713
1714 Call this when writing data that requires a specific libavb
1715 version to parse it.
1716
1717 Arguments:
1718 minor: The minor version of libavb that has support for the feature.
1719 """
1720 self.required_libavb_version_minor = (
1721 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001722
1723 def save(self, output):
1724 """Serializes the header (256 bytes) to disk.
1725
1726 Arguments:
1727 output: The object to write the output to.
1728 """
1729 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001730 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1731 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001732 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1733 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001734 self.public_key_offset, self.public_key_size,
1735 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001736 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001737 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001738
1739 def encode(self):
1740 """Serializes the header (256) to a bytearray().
1741
1742 Returns:
1743 A bytearray() with the encoded header.
1744 """
1745 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001746 self.required_libavb_version_major,
1747 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001748 self.authentication_data_block_size,
1749 self.auxiliary_data_block_size, self.algorithm_type,
1750 self.hash_offset, self.hash_size, self.signature_offset,
1751 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001752 self.public_key_size, self.public_key_metadata_offset,
1753 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001754 self.descriptors_size, self.rollback_index, self.flags,
1755 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001756
1757
1758class Avb(object):
1759 """Business logic for avbtool command-line tool."""
1760
David Zeuthen8b6973b2016-09-20 12:39:49 -04001761 # Keep in sync with avb_ab_flow.h.
1762 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1763 AB_MAGIC = '\0AB0'
1764 AB_MAJOR_VERSION = 1
1765 AB_MINOR_VERSION = 0
1766 AB_MISC_METADATA_OFFSET = 2048
1767
David Zeuthen09692692016-09-30 16:16:40 -04001768 # Constants for maximum metadata size. These are used to give
1769 # meaningful errors if the value passed in via --partition_size is
1770 # too small and when --calc_max_image_size is used. We use
1771 # conservative figures.
1772 MAX_VBMETA_SIZE = 64 * 1024
1773 MAX_FOOTER_SIZE = 4096
1774
David Zeuthena4fee8b2016-08-22 15:20:43 -04001775 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001776 """Implements the 'erase_footer' command.
1777
1778 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001779 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001780 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001781
1782 Raises:
1783 AvbError: If there's no footer in the image.
1784 """
1785
David Zeuthena4fee8b2016-08-22 15:20:43 -04001786 image = ImageHandler(image_filename)
1787
David Zeuthen21e95262016-07-27 17:58:40 -04001788 (footer, _, descriptors, _) = self._parse_image(image)
1789
1790 if not footer:
1791 raise AvbError('Given image does not have a footer.')
1792
1793 new_image_size = None
1794 if not keep_hashtree:
1795 new_image_size = footer.original_image_size
1796 else:
1797 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001798 # descriptor to figure out the location and size of the hashtree
1799 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001800 for desc in descriptors:
1801 if isinstance(desc, AvbHashtreeDescriptor):
1802 # The hashtree is always just following the main data so the
1803 # new size is easily derived.
1804 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001805 # If the image has FEC codes, also keep those.
1806 if desc.fec_offset > 0:
1807 fec_end = desc.fec_offset + desc.fec_size
1808 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001809 break
1810 if not new_image_size:
1811 raise AvbError('Requested to keep hashtree but no hashtree '
1812 'descriptor was found.')
1813
1814 # And cut...
1815 image.truncate(new_image_size)
1816
David Zeuthen2bc232b2017-04-19 14:25:19 -04001817 def resize_image(self, image_filename, partition_size):
1818 """Implements the 'resize_image' command.
1819
1820 Arguments:
1821 image_filename: File with footer to resize.
1822 partition_size: The new size of the image.
1823
1824 Raises:
1825 AvbError: If there's no footer in the image.
1826 """
1827
1828 image = ImageHandler(image_filename)
1829
1830 if partition_size % image.block_size != 0:
1831 raise AvbError('Partition size of {} is not a multiple of the image '
1832 'block size {}.'.format(partition_size,
1833 image.block_size))
1834
1835 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
1836
1837 if not footer:
1838 raise AvbError('Given image does not have a footer.')
1839
1840 # The vbmeta blob is always at the end of the data so resizing an
1841 # image amounts to just moving the footer around.
1842
1843 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
1844 if vbmeta_end_offset % image.block_size != 0:
1845 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
1846
1847 if partition_size < vbmeta_end_offset + 1*image.block_size:
1848 raise AvbError('Requested size of {} is too small for an image '
1849 'of size {}.'
1850 .format(partition_size,
1851 vbmeta_end_offset + 1*image.block_size))
1852
1853 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
1854 # with enough bytes such that the final Footer block is at the end
1855 # of partition_size.
1856 image.truncate(vbmeta_end_offset)
1857 image.append_dont_care(partition_size - vbmeta_end_offset -
1858 1*image.block_size)
1859
1860 # Just reuse the same footer - only difference is that we're
1861 # writing it in a different place.
1862 footer_blob = footer.encode()
1863 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
1864 footer_blob)
1865 image.append_raw(footer_blob_with_padding)
1866
David Zeuthen8b6973b2016-09-20 12:39:49 -04001867 def set_ab_metadata(self, misc_image, slot_data):
1868 """Implements the 'set_ab_metadata' command.
1869
1870 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1871 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1872
1873 Arguments:
1874 misc_image: The misc image to write to.
1875 slot_data: Slot data as a string
1876
1877 Raises:
1878 AvbError: If slot data is malformed.
1879 """
1880 tokens = slot_data.split(':')
1881 if len(tokens) != 6:
1882 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1883 a_priority = int(tokens[0])
1884 a_tries_remaining = int(tokens[1])
1885 a_success = True if int(tokens[2]) != 0 else False
1886 b_priority = int(tokens[3])
1887 b_tries_remaining = int(tokens[4])
1888 b_success = True if int(tokens[5]) != 0 else False
1889
1890 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1891 self.AB_MAGIC,
1892 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1893 a_priority, a_tries_remaining, a_success,
1894 b_priority, b_tries_remaining, b_success)
1895 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1896 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1897 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1898 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1899 misc_image.write(ab_data)
1900
David Zeuthena4fee8b2016-08-22 15:20:43 -04001901 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001902 """Implements the 'info_image' command.
1903
1904 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001905 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001906 output: Output file to write human-readable information to (file object).
1907 """
1908
David Zeuthena4fee8b2016-08-22 15:20:43 -04001909 image = ImageHandler(image_filename)
1910
David Zeuthen21e95262016-07-27 17:58:40 -04001911 o = output
1912
1913 (footer, header, descriptors, image_size) = self._parse_image(image)
1914
1915 if footer:
1916 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1917 footer.version_minor))
1918 o.write('Image size: {} bytes\n'.format(image_size))
1919 o.write('Original image size: {} bytes\n'.format(
1920 footer.original_image_size))
1921 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1922 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1923 o.write('--\n')
1924
1925 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1926
David Zeuthene3cadca2017-02-22 21:25:46 -05001927 o.write('Minimum libavb version: {}.{}{}\n'.format(
1928 header.required_libavb_version_major,
1929 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001930 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001931 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1932 o.write('Authentication Block: {} bytes\n'.format(
1933 header.authentication_data_block_size))
1934 o.write('Auxiliary Block: {} bytes\n'.format(
1935 header.auxiliary_data_block_size))
1936 o.write('Algorithm: {}\n'.format(alg_name))
1937 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001938 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001939 o.write('Release String: \'{}\'\n'.format(
1940 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001941
1942 # Print descriptors.
1943 num_printed = 0
1944 o.write('Descriptors:\n')
1945 for desc in descriptors:
1946 desc.print_desc(o)
1947 num_printed += 1
1948 if num_printed == 0:
1949 o.write(' (none)\n')
1950
David Zeuthenb623d8b2017-04-04 16:05:53 -04001951 def verify_image(self, image_filename):
1952 """Implements the 'verify_image' command.
1953
1954 Arguments:
1955 image_filename: Image file to get information from (file object).
1956 """
1957
1958 image = ImageHandler(image_filename)
1959 (footer, header, descriptors, image_size) = self._parse_image(image)
1960 offset = 0
1961 if footer:
1962 offset = footer.vbmeta_offset
1963 size = (header.SIZE + header.authentication_data_block_size +
1964 header.auxiliary_data_block_size)
1965 image.seek(offset)
1966 vbmeta_blob = image.read(size)
1967 if not verify_vbmeta_signature(header, vbmeta_blob):
1968 raise AvbError('Signature check failed.')
1969
David Zeuthen21e95262016-07-27 17:58:40 -04001970 def _parse_image(self, image):
1971 """Gets information about an image.
1972
1973 The image can either be a vbmeta or an image with a footer.
1974
1975 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001976 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001977
1978 Returns:
1979 A tuple where the first argument is a AvbFooter (None if there
1980 is no footer on the image), the second argument is a
1981 AvbVBMetaHeader, the third argument is a list of
1982 AvbDescriptor-derived instances, and the fourth argument is the
1983 size of |image|.
1984 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001985 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001986 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001987 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001988 try:
1989 footer = AvbFooter(image.read(AvbFooter.SIZE))
1990 except (LookupError, struct.error):
1991 # Nope, just seek back to the start.
1992 image.seek(0)
1993
1994 vbmeta_offset = 0
1995 if footer:
1996 vbmeta_offset = footer.vbmeta_offset
1997
1998 image.seek(vbmeta_offset)
1999 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2000
2001 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2002 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2003 desc_start_offset = aux_block_offset + h.descriptors_offset
2004 image.seek(desc_start_offset)
2005 descriptors = parse_descriptors(image.read(h.descriptors_size))
2006
David Zeuthen09692692016-09-30 16:16:40 -04002007 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002008
David Zeuthenb1b994d2017-03-06 18:01:31 -05002009 def _load_vbmeta_blob(self, image):
2010 """Gets the vbmeta struct and associated sections.
2011
2012 The image can either be a vbmeta.img or an image with a footer.
2013
2014 Arguments:
2015 image: An ImageHandler (vbmeta or footer).
2016
2017 Returns:
2018 A blob with the vbmeta struct and other sections.
2019 """
2020 assert isinstance(image, ImageHandler)
2021 footer = None
2022 image.seek(image.image_size - AvbFooter.SIZE)
2023 try:
2024 footer = AvbFooter(image.read(AvbFooter.SIZE))
2025 except (LookupError, struct.error):
2026 # Nope, just seek back to the start.
2027 image.seek(0)
2028
2029 vbmeta_offset = 0
2030 if footer:
2031 vbmeta_offset = footer.vbmeta_offset
2032
2033 image.seek(vbmeta_offset)
2034 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2035
2036 image.seek(vbmeta_offset)
2037 data_size = AvbVBMetaHeader.SIZE
2038 data_size += h.authentication_data_block_size
2039 data_size += h.auxiliary_data_block_size
2040 return image.read(data_size)
2041
David Zeuthen73f2afa2017-05-17 16:54:11 -04002042 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002043 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002044
2045 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002046 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002047
2048 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002049 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2050 instructions. There is one for when hashtree is not disabled and one for
2051 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002052
2053 Raises:
2054 AvbError: If |image| doesn't have a hashtree descriptor.
2055
2056 """
2057
David Zeuthen21e95262016-07-27 17:58:40 -04002058 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002059 c += '0' # start
2060 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2061 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2062 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2063 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2064 c += ' {}'.format(ht.data_block_size) # data_block
2065 c += ' {}'.format(ht.hash_block_size) # hash_block
2066 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2067 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2068 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2069 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2070 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2071 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002072 c += ' 10' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002073 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002074 c += ' ignore_zero_blocks'
2075 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2076 c += ' fec_roots {}'.format(ht.fec_num_roots)
2077 # Note that fec_blocks is the size that FEC covers, *not* the
2078 # size of the FEC data. Since we use FEC for everything up until
2079 # the FEC data, it's the same as the offset.
2080 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2081 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2082 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002083 c += ' 2' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002084 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002085 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002086 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002087
David Zeuthenfd41eb92016-11-17 12:24:47 -05002088 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002089 desc = AvbKernelCmdlineDescriptor()
2090 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002091 desc.flags = (
2092 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2093
2094 # The descriptor for when hashtree verification is disabled is a lot
2095 # simpler - we just set the root to the partition.
2096 desc_no_ht = AvbKernelCmdlineDescriptor()
2097 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2098 desc_no_ht.flags = (
2099 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2100
2101 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002102
David Zeuthen73f2afa2017-05-17 16:54:11 -04002103 def _get_cmdline_descriptors_for_dm_verity(self, image):
2104 """Generate kernel cmdline descriptors for dm-verity.
2105
2106 Arguments:
2107 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2108
2109 Returns:
2110 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2111 instructions. There is one for when hashtree is not disabled and one for
2112 when it is.
2113
2114 Raises:
2115 AvbError: If |image| doesn't have a hashtree descriptor.
2116
2117 """
2118
2119 (_, _, descriptors, _) = self._parse_image(image)
2120
2121 ht = None
2122 for desc in descriptors:
2123 if isinstance(desc, AvbHashtreeDescriptor):
2124 ht = desc
2125 break
2126
2127 if not ht:
2128 raise AvbError('No hashtree descriptor in given image')
2129
2130 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2131
David Zeuthen21e95262016-07-27 17:58:40 -04002132 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002133 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002134 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002135 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002136 include_descriptors_from_image, signing_helper,
2137 release_string,
2138 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002139 """Implements the 'make_vbmeta_image' command.
2140
2141 Arguments:
2142 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002143 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002144 algorithm_name: Name of algorithm to use.
2145 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002146 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002147 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002148 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002149 props: Properties to insert (list of strings of the form 'key:value').
2150 props_from_file: Properties to insert (list of strings 'key:<path>').
2151 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002152 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002153 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002154 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002155 release_string: None or avbtool release string to use instead of default.
2156 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002157
2158 Raises:
2159 AvbError: If a chained partition is malformed.
2160 """
2161
2162 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002163 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002164 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002165 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002166 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002167 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002168 include_descriptors_from_image, signing_helper, release_string,
2169 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002170
2171 # Write entire vbmeta blob (header, authentication, auxiliary).
2172 output.seek(0)
2173 output.write(vbmeta_blob)
2174
David Zeuthen18666ab2016-11-15 11:18:05 -05002175 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2176 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002177 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002178 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002179 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002180 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002181 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002182 include_descriptors_from_image, signing_helper,
2183 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002184 """Generates a VBMeta blob.
2185
2186 This blob contains the header (struct AvbVBMetaHeader), the
2187 authentication data block (which contains the hash and signature
2188 for the header and auxiliary block), and the auxiliary block
2189 (which contains descriptors, the public key used, and other data).
2190
2191 The |key| parameter can |None| only if the |algorithm_name| is
2192 'NONE'.
2193
2194 Arguments:
2195 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2196 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002197 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002198 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002199 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002200 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002201 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002202 props: Properties to insert (List of strings of the form 'key:value').
2203 props_from_file: Properties to insert (List of strings 'key:<path>').
2204 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002205 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002206 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002207 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2208 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002209 include_descriptors_from_image: List of file objects for which
2210 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002211 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002212 release_string: None or avbtool release string.
2213 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002214
2215 Returns:
2216 A bytearray() with the VBMeta blob.
2217
2218 Raises:
2219 Exception: If the |algorithm_name| is not found, if no key has
2220 been given and the given algorithm requires one, or the key is
2221 of the wrong size.
2222
2223 """
2224 try:
2225 alg = ALGORITHMS[algorithm_name]
2226 except KeyError:
2227 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2228
David Zeuthena5fd3a42017-02-27 16:38:54 -05002229 if not descriptors:
2230 descriptors = []
2231
2232 # Insert chained partition descriptors, if any
2233 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002234 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002235 for cp in chain_partitions:
2236 cp_tokens = cp.split(':')
2237 if len(cp_tokens) != 3:
2238 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002239 partition_name = cp_tokens[0]
2240 rollback_index_location = int(cp_tokens[1])
2241 file_path = cp_tokens[2]
2242 # Check that the same rollback location isn't being used by
2243 # multiple chained partitions.
2244 if used_locations.get(rollback_index_location):
2245 raise AvbError('Rollback Index Location {} is already in use.'.format(
2246 rollback_index_location))
2247 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002248 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002249 desc.partition_name = partition_name
2250 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002251 if desc.rollback_index_location < 1:
2252 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002253 desc.public_key = open(file_path, 'rb').read()
2254 descriptors.append(desc)
2255
David Zeuthen21e95262016-07-27 17:58:40 -04002256 # Descriptors.
2257 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002258 for desc in descriptors:
2259 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002260
2261 # Add properties.
2262 if props:
2263 for prop in props:
2264 idx = prop.find(':')
2265 if idx == -1:
2266 raise AvbError('Malformed property "{}".'.format(prop))
2267 desc = AvbPropertyDescriptor()
2268 desc.key = prop[0:idx]
2269 desc.value = prop[(idx + 1):]
2270 encoded_descriptors.extend(desc.encode())
2271 if props_from_file:
2272 for prop in props_from_file:
2273 idx = prop.find(':')
2274 if idx == -1:
2275 raise AvbError('Malformed property "{}".'.format(prop))
2276 desc = AvbPropertyDescriptor()
2277 desc.key = prop[0:idx]
2278 desc.value = prop[(idx + 1):]
2279 file_path = prop[(idx + 1):]
2280 desc.value = open(file_path, 'rb').read()
2281 encoded_descriptors.extend(desc.encode())
2282
David Zeuthen73f2afa2017-05-17 16:54:11 -04002283 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002284 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002285 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002286 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002287 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2288 encoded_descriptors.extend(cmdline_desc[0].encode())
2289 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002290
David Zeuthen73f2afa2017-05-17 16:54:11 -04002291 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2292 if ht_desc_to_setup:
2293 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2294 ht_desc_to_setup)
2295 encoded_descriptors.extend(cmdline_desc[0].encode())
2296 encoded_descriptors.extend(cmdline_desc[1].encode())
2297
David Zeuthen21e95262016-07-27 17:58:40 -04002298 # Add kernel command-lines.
2299 if kernel_cmdlines:
2300 for i in kernel_cmdlines:
2301 desc = AvbKernelCmdlineDescriptor()
2302 desc.kernel_cmdline = i
2303 encoded_descriptors.extend(desc.encode())
2304
2305 # Add descriptors from other images.
2306 if include_descriptors_from_image:
2307 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002308 image_handler = ImageHandler(image.name)
2309 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002310 for desc in image_descriptors:
2311 encoded_descriptors.extend(desc.encode())
2312
David Zeuthen18666ab2016-11-15 11:18:05 -05002313 # Load public key metadata blob, if requested.
2314 pkmd_blob = []
2315 if public_key_metadata_path:
2316 with open(public_key_metadata_path) as f:
2317 pkmd_blob = f.read()
2318
David Zeuthen21e95262016-07-27 17:58:40 -04002319 key = None
2320 encoded_key = bytearray()
2321 if alg.public_key_num_bytes > 0:
2322 if not key_path:
2323 raise AvbError('Key is required for algorithm {}'.format(
2324 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002325 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002326 if len(encoded_key) != alg.public_key_num_bytes:
2327 raise AvbError('Key is wrong size for algorithm {}'.format(
2328 algorithm_name))
2329
2330 h = AvbVBMetaHeader()
2331
David Zeuthene3cadca2017-02-22 21:25:46 -05002332 # Override release string, if requested.
2333 if isinstance(release_string, (str, unicode)):
2334 h.release_string = release_string
2335
2336 # Append to release string, if requested. Also insert a space before.
2337 if isinstance(append_to_release_string, (str, unicode)):
2338 h.release_string += ' ' + append_to_release_string
2339
David Zeuthen18666ab2016-11-15 11:18:05 -05002340 # For the Auxiliary data block, descriptors are stored at offset 0,
2341 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002342 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002343 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002344 h.descriptors_offset = 0
2345 h.descriptors_size = len(encoded_descriptors)
2346 h.public_key_offset = h.descriptors_size
2347 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002348 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2349 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002350
2351 # For the Authentication data block, the hash is first and then
2352 # the signature.
2353 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002354 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002355 h.algorithm_type = alg.algorithm_type
2356 h.hash_offset = 0
2357 h.hash_size = alg.hash_num_bytes
2358 # Signature offset and size - it's stored right after the hash
2359 # (in Authentication data block).
2360 h.signature_offset = alg.hash_num_bytes
2361 h.signature_size = alg.signature_num_bytes
2362
2363 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002364 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002365
2366 # Generate Header data block.
2367 header_data_blob = h.encode()
2368
2369 # Generate Auxiliary data block.
2370 aux_data_blob = bytearray()
2371 aux_data_blob.extend(encoded_descriptors)
2372 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002373 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002374 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2375 aux_data_blob.extend('\0' * padding_bytes)
2376
2377 # Calculate the hash.
2378 binary_hash = bytearray()
2379 binary_signature = bytearray()
2380 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002381 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002382 ha.update(header_data_blob)
2383 ha.update(aux_data_blob)
2384 binary_hash.extend(ha.digest())
2385
2386 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002387 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002388 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2389 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002390 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002391
2392 # Generate Authentication data block.
2393 auth_data_blob = bytearray()
2394 auth_data_blob.extend(binary_hash)
2395 auth_data_blob.extend(binary_signature)
2396 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2397 auth_data_blob.extend('\0' * padding_bytes)
2398
2399 return header_data_blob + auth_data_blob + aux_data_blob
2400
2401 def extract_public_key(self, key_path, output):
2402 """Implements the 'extract_public_key' command.
2403
2404 Arguments:
2405 key_path: The path to a RSA private key file.
2406 output: The file to write to.
2407 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002408 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002409
David Zeuthenb1b994d2017-03-06 18:01:31 -05002410 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2411 partition_size):
2412 """Implementation of the append_vbmeta_image command.
2413
2414 Arguments:
2415 image_filename: File to add the footer to.
2416 vbmeta_image_filename: File to get vbmeta struct from.
2417 partition_size: Size of partition.
2418
2419 Raises:
2420 AvbError: If an argument is incorrect.
2421 """
2422 image = ImageHandler(image_filename)
2423
2424 if partition_size % image.block_size != 0:
2425 raise AvbError('Partition size of {} is not a multiple of the image '
2426 'block size {}.'.format(partition_size,
2427 image.block_size))
2428
2429 # If there's already a footer, truncate the image to its original
2430 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2431 image.seek(image.image_size - AvbFooter.SIZE)
2432 try:
2433 footer = AvbFooter(image.read(AvbFooter.SIZE))
2434 # Existing footer found. Just truncate.
2435 original_image_size = footer.original_image_size
2436 image.truncate(footer.original_image_size)
2437 except (LookupError, struct.error):
2438 original_image_size = image.image_size
2439
2440 # If anything goes wrong from here-on, restore the image back to
2441 # its original size.
2442 try:
2443 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2444 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2445
2446 # If the image isn't sparse, its size might not be a multiple of
2447 # the block size. This will screw up padding later so just grow it.
2448 if image.image_size % image.block_size != 0:
2449 assert not image.is_sparse
2450 padding_needed = image.block_size - (image.image_size%image.block_size)
2451 image.truncate(image.image_size + padding_needed)
2452
2453 # The append_raw() method requires content with size being a
2454 # multiple of |block_size| so add padding as needed. Also record
2455 # where this is written to since we'll need to put that in the
2456 # footer.
2457 vbmeta_offset = image.image_size
2458 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2459 len(vbmeta_blob))
2460 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2461
2462 # Append vbmeta blob and footer
2463 image.append_raw(vbmeta_blob_with_padding)
2464 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2465
2466 # Now insert a DONT_CARE chunk with enough bytes such that the
2467 # final Footer block is at the end of partition_size..
2468 image.append_dont_care(partition_size - vbmeta_end_offset -
2469 1*image.block_size)
2470
2471 # Generate the Footer that tells where the VBMeta footer
2472 # is. Also put enough padding in the front of the footer since
2473 # we'll write out an entire block.
2474 footer = AvbFooter()
2475 footer.original_image_size = original_image_size
2476 footer.vbmeta_offset = vbmeta_offset
2477 footer.vbmeta_size = len(vbmeta_blob)
2478 footer_blob = footer.encode()
2479 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2480 footer_blob)
2481 image.append_raw(footer_blob_with_padding)
2482
2483 except:
2484 # Truncate back to original size, then re-raise
2485 image.truncate(original_image_size)
2486 raise
2487
David Zeuthena4fee8b2016-08-22 15:20:43 -04002488 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002489 hash_algorithm, salt, chain_partitions, algorithm_name,
2490 key_path,
2491 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002492 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002493 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002494 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002495 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002496 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002497 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002498
2499 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002500 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002501 partition_size: Size of partition.
2502 partition_name: Name of partition (without A/B suffix).
2503 hash_algorithm: Hash algorithm to use.
2504 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002505 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002506 algorithm_name: Name of algorithm to use.
2507 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002508 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002509 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002510 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002511 props: Properties to insert (List of strings of the form 'key:value').
2512 props_from_file: Properties to insert (List of strings 'key:<path>').
2513 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002514 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002515 dm-verity kernel cmdline from.
2516 include_descriptors_from_image: List of file objects for which
2517 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002518 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002519 release_string: None or avbtool release string.
2520 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002521 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2522 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002523
2524 Raises:
2525 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002526 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002527 image = ImageHandler(image_filename)
2528
2529 if partition_size % image.block_size != 0:
2530 raise AvbError('Partition size of {} is not a multiple of the image '
2531 'block size {}.'.format(partition_size,
2532 image.block_size))
2533
David Zeuthen21e95262016-07-27 17:58:40 -04002534 # If there's already a footer, truncate the image to its original
2535 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2536 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002537 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002538 try:
2539 footer = AvbFooter(image.read(AvbFooter.SIZE))
2540 # Existing footer found. Just truncate.
2541 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002542 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002543 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002544 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002545
2546 # If anything goes wrong from here-on, restore the image back to
2547 # its original size.
2548 try:
David Zeuthen09692692016-09-30 16:16:40 -04002549 # First, calculate the maximum image size such that an image
2550 # this size + metadata (footer + vbmeta struct) fits in
2551 # |partition_size|.
2552 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2553 max_image_size = partition_size - max_metadata_size
2554
2555 # If image size exceeds the maximum image size, fail.
2556 if image.image_size > max_image_size:
2557 raise AvbError('Image size of {} exceeds maximum image '
2558 'size of {} in order to fit in a partition '
2559 'size of {}.'.format(image.image_size, max_image_size,
2560 partition_size))
2561
David Zeuthen21e95262016-07-27 17:58:40 -04002562 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2563 if salt:
2564 salt = salt.decode('hex')
2565 else:
2566 if salt is None:
2567 # If salt is not explicitly specified, choose a hash
2568 # that's the same size as the hash size.
2569 hash_size = digest_size
2570 salt = open('/dev/urandom').read(hash_size)
2571 else:
2572 salt = ''
2573
2574 hasher = hashlib.new(name=hash_algorithm, string=salt)
2575 # TODO(zeuthen): might want to read this in chunks to avoid
2576 # memory pressure, then again, this is only supposed to be used
2577 # on kernel/initramfs partitions. Possible optimization.
2578 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002579 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002580 digest = hasher.digest()
2581
2582 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002583 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002584 h_desc.hash_algorithm = hash_algorithm
2585 h_desc.partition_name = partition_name
2586 h_desc.salt = salt
2587 h_desc.digest = digest
2588
2589 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002590 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002591 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002592 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002593 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002594 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002595 include_descriptors_from_image, signing_helper, release_string,
2596 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002597
David Zeuthena4fee8b2016-08-22 15:20:43 -04002598 # If the image isn't sparse, its size might not be a multiple of
2599 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002600 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002601 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002602 padding_needed = image.block_size - (image.image_size%image.block_size)
2603 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002604
David Zeuthena4fee8b2016-08-22 15:20:43 -04002605 # The append_raw() method requires content with size being a
2606 # multiple of |block_size| so add padding as needed. Also record
2607 # where this is written to since we'll need to put that in the
2608 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002609 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002610 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2611 len(vbmeta_blob))
2612 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002613
David Zeuthend247fcb2017-02-16 12:09:27 -05002614 # Write vbmeta blob, if requested.
2615 if output_vbmeta_image:
2616 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002617
David Zeuthend247fcb2017-02-16 12:09:27 -05002618 # Append vbmeta blob and footer, unless requested not to.
2619 if not do_not_append_vbmeta_image:
2620 image.append_raw(vbmeta_blob_with_padding)
2621 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2622
2623 # Now insert a DONT_CARE chunk with enough bytes such that the
2624 # final Footer block is at the end of partition_size..
2625 image.append_dont_care(partition_size - vbmeta_end_offset -
2626 1*image.block_size)
2627
2628 # Generate the Footer that tells where the VBMeta footer
2629 # is. Also put enough padding in the front of the footer since
2630 # we'll write out an entire block.
2631 footer = AvbFooter()
2632 footer.original_image_size = original_image_size
2633 footer.vbmeta_offset = vbmeta_offset
2634 footer.vbmeta_size = len(vbmeta_blob)
2635 footer_blob = footer.encode()
2636 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2637 footer_blob)
2638 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002639
David Zeuthen21e95262016-07-27 17:58:40 -04002640 except:
2641 # Truncate back to original size, then re-raise
2642 image.truncate(original_image_size)
2643 raise
2644
David Zeuthena4fee8b2016-08-22 15:20:43 -04002645 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002646 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002647 block_size, salt, chain_partitions, algorithm_name,
2648 key_path,
2649 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002650 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002651 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002652 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002653 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002654 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002655 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002656 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002657 """Implements the 'add_hashtree_footer' command.
2658
2659 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2660 more information about dm-verity and these hashes.
2661
2662 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002663 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002664 partition_size: Size of partition.
2665 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002666 generate_fec: If True, generate FEC codes.
2667 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002668 hash_algorithm: Hash algorithm to use.
2669 block_size: Block size to use.
2670 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002671 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002672 algorithm_name: Name of algorithm to use.
2673 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002674 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002675 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002676 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002677 props: Properties to insert (List of strings of the form 'key:value').
2678 props_from_file: Properties to insert (List of strings 'key:<path>').
2679 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002680 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002681 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002682 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
2683 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04002684 include_descriptors_from_image: List of file objects for which
2685 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002686 calc_max_image_size: Don't store the hashtree or footer - instead
2687 calculate the maximum image size leaving enough room for hashtree
2688 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002689 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002690 release_string: None or avbtool release string.
2691 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002692 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2693 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002694
2695 Raises:
2696 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002697 """
David Zeuthen09692692016-09-30 16:16:40 -04002698 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2699 digest_padding = round_to_pow2(digest_size) - digest_size
2700
2701 # First, calculate the maximum image size such that an image
2702 # this size + the hashtree + metadata (footer + vbmeta struct)
2703 # fits in |partition_size|. We use very conservative figures for
2704 # metadata.
2705 (_, max_tree_size) = calc_hash_level_offsets(
2706 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002707 max_fec_size = 0
2708 if generate_fec:
2709 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2710 max_metadata_size = (max_fec_size + max_tree_size +
2711 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002712 self.MAX_FOOTER_SIZE)
2713 max_image_size = partition_size - max_metadata_size
2714
2715 # If we're asked to only calculate the maximum image size, we're done.
2716 if calc_max_image_size:
2717 print '{}'.format(max_image_size)
2718 return
2719
David Zeuthena4fee8b2016-08-22 15:20:43 -04002720 image = ImageHandler(image_filename)
2721
2722 if partition_size % image.block_size != 0:
2723 raise AvbError('Partition size of {} is not a multiple of the image '
2724 'block size {}.'.format(partition_size,
2725 image.block_size))
2726
David Zeuthen21e95262016-07-27 17:58:40 -04002727 # If there's already a footer, truncate the image to its original
2728 # size. This way 'avbtool add_hashtree_footer' is idempotent
2729 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002730 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002731 try:
2732 footer = AvbFooter(image.read(AvbFooter.SIZE))
2733 # Existing footer found. Just truncate.
2734 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002735 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002736 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002737 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002738
2739 # If anything goes wrong from here-on, restore the image back to
2740 # its original size.
2741 try:
2742 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002743 rounded_image_size = round_to_multiple(image.image_size, block_size)
2744 if rounded_image_size > image.image_size:
2745 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002746
David Zeuthen09692692016-09-30 16:16:40 -04002747 # If image size exceeds the maximum image size, fail.
2748 if image.image_size > max_image_size:
2749 raise AvbError('Image size of {} exceeds maximum image '
2750 'size of {} in order to fit in a partition '
2751 'size of {}.'.format(image.image_size, max_image_size,
2752 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002753
2754 if salt:
2755 salt = salt.decode('hex')
2756 else:
2757 if salt is None:
2758 # If salt is not explicitly specified, choose a hash
2759 # that's the same size as the hash size.
2760 hash_size = digest_size
2761 salt = open('/dev/urandom').read(hash_size)
2762 else:
2763 salt = ''
2764
David Zeuthena4fee8b2016-08-22 15:20:43 -04002765 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002766 # offsets in advance.
2767 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002768 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002769
David Zeuthena4fee8b2016-08-22 15:20:43 -04002770 # If the image isn't sparse, its size might not be a multiple of
2771 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002772 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002773 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002774 padding_needed = image.block_size - (image.image_size%image.block_size)
2775 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002776
David Zeuthena4fee8b2016-08-22 15:20:43 -04002777 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002778 tree_offset = image.image_size
2779 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002780 block_size,
2781 hash_algorithm, salt,
2782 digest_padding,
2783 hash_level_offsets,
2784 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002785
2786 # Generate HashtreeDescriptor with details about the tree we
2787 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002788 ht_desc = AvbHashtreeDescriptor()
2789 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002790 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002791 ht_desc.tree_offset = tree_offset
2792 ht_desc.tree_size = tree_size
2793 ht_desc.data_block_size = block_size
2794 ht_desc.hash_block_size = block_size
2795 ht_desc.hash_algorithm = hash_algorithm
2796 ht_desc.partition_name = partition_name
2797 ht_desc.salt = salt
2798 ht_desc.root_digest = root_digest
2799
David Zeuthen09692692016-09-30 16:16:40 -04002800 # Write the hash tree
2801 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2802 len(hash_tree))
2803 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2804 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002805 len_hashtree_and_fec = len(hash_tree_with_padding)
2806
2807 # Generate FEC codes, if requested.
2808 if generate_fec:
2809 fec_data = generate_fec_data(image_filename, fec_num_roots)
2810 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2811 len(fec_data))
2812 fec_data_with_padding = fec_data + '\0'*padding_needed
2813 fec_offset = image.image_size
2814 image.append_raw(fec_data_with_padding)
2815 len_hashtree_and_fec += len(fec_data_with_padding)
2816 # Update the hashtree descriptor.
2817 ht_desc.fec_num_roots = fec_num_roots
2818 ht_desc.fec_offset = fec_offset
2819 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002820
David Zeuthen73f2afa2017-05-17 16:54:11 -04002821 ht_desc_to_setup = None
2822 if setup_as_rootfs_from_kernel:
2823 ht_desc_to_setup = ht_desc
2824
David Zeuthena4fee8b2016-08-22 15:20:43 -04002825 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002826 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002827 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002828 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002829 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002830 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002831 include_descriptors_from_image, signing_helper, release_string,
2832 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002833 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2834 len(vbmeta_blob))
2835 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002836
David Zeuthend247fcb2017-02-16 12:09:27 -05002837 # Write vbmeta blob, if requested.
2838 if output_vbmeta_image:
2839 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002840
David Zeuthend247fcb2017-02-16 12:09:27 -05002841 # Append vbmeta blob and footer, unless requested not to.
2842 if not do_not_append_vbmeta_image:
2843 image.append_raw(vbmeta_blob_with_padding)
2844
2845 # Now insert a DONT_CARE chunk with enough bytes such that the
2846 # final Footer block is at the end of partition_size..
2847 image.append_dont_care(partition_size - image.image_size -
2848 1*image.block_size)
2849
2850 # Generate the Footer that tells where the VBMeta footer
2851 # is. Also put enough padding in the front of the footer since
2852 # we'll write out an entire block.
2853 footer = AvbFooter()
2854 footer.original_image_size = original_image_size
2855 footer.vbmeta_offset = vbmeta_offset
2856 footer.vbmeta_size = len(vbmeta_blob)
2857 footer_blob = footer.encode()
2858 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2859 footer_blob)
2860 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002861
David Zeuthen21e95262016-07-27 17:58:40 -04002862 except:
David Zeuthen09692692016-09-30 16:16:40 -04002863 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002864 image.truncate(original_image_size)
2865 raise
2866
David Zeuthenc68f0822017-03-31 17:22:35 -04002867 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002868 subject_key_version, subject,
2869 is_intermediate_authority, signing_helper):
2870 """Implements the 'make_atx_certificate' command.
2871
2872 Android Things certificates are required for Android Things public key
2873 metadata. They chain the vbmeta signing key for a particular product back to
2874 a fused, permanent root key. These certificates are fixed-length and fixed-
2875 format with the explicit goal of not parsing ASN.1 in bootloader code.
2876
2877 Arguments:
2878 output: Certificate will be written to this file on success.
2879 authority_key_path: A PEM file path with the authority private key.
2880 If None, then a certificate will be created without a
2881 signature. The signature can be created out-of-band
2882 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04002883 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08002884 subject_key_version: A 64-bit version value. If this is None, the number
2885 of seconds since the epoch is used.
2886 subject: A subject identifier. For Product Signing Key certificates this
2887 should be the same Product ID found in the permanent attributes.
2888 is_intermediate_authority: True if the certificate is for an intermediate
2889 authority.
2890 signing_helper: Program which signs a hash and returns the signature.
2891 """
2892 signed_data = bytearray()
2893 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002894 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002895 hasher = hashlib.sha256()
2896 hasher.update(subject)
2897 signed_data.extend(hasher.digest())
2898 usage = 'com.google.android.things.vboot'
2899 if is_intermediate_authority:
2900 usage += '.ca'
2901 hasher = hashlib.sha256()
2902 hasher.update(usage)
2903 signed_data.extend(hasher.digest())
2904 if not subject_key_version:
2905 subject_key_version = int(time.time())
2906 signed_data.extend(struct.pack('<Q', subject_key_version))
2907 signature = bytearray()
2908 if authority_key_path:
2909 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002910 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002911 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002912 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002913 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002914 hasher.update(signed_data)
2915 padding_and_hash.extend(hasher.digest())
2916 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002917 alg.signature_num_bytes, authority_key_path,
2918 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002919 output.write(signed_data)
2920 output.write(signature)
2921
David Zeuthenc68f0822017-03-31 17:22:35 -04002922 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002923 product_id):
2924 """Implements the 'make_atx_permanent_attributes' command.
2925
2926 Android Things permanent attributes are designed to be permanent for a
2927 particular product and a hash of these attributes should be fused into
2928 hardware to enforce this.
2929
2930 Arguments:
2931 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04002932 root_authority_key_path: Path to a PEM or DER public key for
2933 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08002934 product_id: A 16-byte Product ID.
2935
2936 Raises:
2937 AvbError: If an argument is incorrect.
2938 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002939 EXPECTED_PRODUCT_ID_SIZE = 16
2940 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002941 raise AvbError('Invalid Product ID length.')
2942 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002943 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002944 output.write(product_id)
2945
2946 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002947 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002948 """Implements the 'make_atx_metadata' command.
2949
2950 Android Things metadata are included in vbmeta images to facilitate
2951 verification. The output of this command can be used as the
2952 public_key_metadata argument to other commands.
2953
2954 Arguments:
2955 output: Metadata will be written to this file on success.
2956 intermediate_key_certificate: A certificate file as output by
2957 make_atx_certificate with
2958 is_intermediate_authority set to true.
2959 product_key_certificate: A certificate file as output by
2960 make_atx_certificate with
2961 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002962
2963 Raises:
2964 AvbError: If an argument is incorrect.
2965 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002966 EXPECTED_CERTIFICATE_SIZE = 1620
2967 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002968 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002969 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002970 raise AvbError('Invalid product key certificate length.')
2971 output.write(struct.pack('<I', 1)) # Format Version
2972 output.write(intermediate_key_certificate)
2973 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002974
David Zeuthen21e95262016-07-27 17:58:40 -04002975
2976def calc_hash_level_offsets(image_size, block_size, digest_size):
2977 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2978
2979 Arguments:
2980 image_size: The size of the image to calculate a Merkle-tree for.
2981 block_size: The block size, e.g. 4096.
2982 digest_size: The size of each hash, e.g. 32 for SHA-256.
2983
2984 Returns:
2985 A tuple where the first argument is an array of offsets and the
2986 second is size of the tree, in bytes.
2987 """
2988 level_offsets = []
2989 level_sizes = []
2990 tree_size = 0
2991
2992 num_levels = 0
2993 size = image_size
2994 while size > block_size:
2995 num_blocks = (size + block_size - 1) / block_size
2996 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2997
2998 level_sizes.append(level_size)
2999 tree_size += level_size
3000 num_levels += 1
3001
3002 size = level_size
3003
3004 for n in range(0, num_levels):
3005 offset = 0
3006 for m in range(n + 1, num_levels):
3007 offset += level_sizes[m]
3008 level_offsets.append(offset)
3009
David Zeuthena4fee8b2016-08-22 15:20:43 -04003010 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003011
3012
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003013# See system/extras/libfec/include/fec/io.h for these definitions.
3014FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3015FEC_MAGIC = 0xfecfecfe
3016
3017
3018def calc_fec_data_size(image_size, num_roots):
3019 """Calculates how much space FEC data will take.
3020
3021 Args:
3022 image_size: The size of the image.
3023 num_roots: Number of roots.
3024
3025 Returns:
3026 The number of bytes needed for FEC for an image of the given size
3027 and with the requested number of FEC roots.
3028
3029 Raises:
3030 ValueError: If output from the 'fec' tool is invalid.
3031
3032 """
3033 p = subprocess.Popen(
3034 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3035 stdout=subprocess.PIPE,
3036 stderr=subprocess.PIPE)
3037 (pout, perr) = p.communicate()
3038 retcode = p.wait()
3039 if retcode != 0:
3040 raise ValueError('Error invoking fec: {}'.format(perr))
3041 return int(pout)
3042
3043
3044def generate_fec_data(image_filename, num_roots):
3045 """Generate FEC codes for an image.
3046
3047 Args:
3048 image_filename: The filename of the image.
3049 num_roots: Number of roots.
3050
3051 Returns:
3052 The FEC data blob.
3053
3054 Raises:
3055 ValueError: If output from the 'fec' tool is invalid.
3056 """
3057 fec_tmpfile = tempfile.NamedTemporaryFile()
3058 subprocess.check_call(
3059 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3060 fec_tmpfile.name],
3061 stderr=open(os.devnull))
3062 fec_data = fec_tmpfile.read()
3063 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3064 footer_data = fec_data[-footer_size:]
3065 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3066 footer_data)
3067 if magic != FEC_MAGIC:
3068 raise ValueError('Unexpected magic in FEC footer')
3069 return fec_data[0:fec_size]
3070
3071
David Zeuthen21e95262016-07-27 17:58:40 -04003072def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003073 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003074 """Generates a Merkle-tree for a file.
3075
3076 Args:
3077 image: The image, as a file.
3078 image_size: The size of the image.
3079 block_size: The block size, e.g. 4096.
3080 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3081 salt: The salt to use.
3082 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003083 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003084 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003085
3086 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003087 A tuple where the first element is the top-level hash and the
3088 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003089 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003090 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003091 hash_src_offset = 0
3092 hash_src_size = image_size
3093 level_num = 0
3094 while hash_src_size > block_size:
3095 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003096 remaining = hash_src_size
3097 while remaining > 0:
3098 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003099 # Only read from the file for the first level - for subsequent
3100 # levels, access the array we're building.
3101 if level_num == 0:
3102 image.seek(hash_src_offset + hash_src_size - remaining)
3103 data = image.read(min(remaining, block_size))
3104 else:
3105 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3106 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003107 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003108
3109 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003110 if len(data) < block_size:
3111 hasher.update('\0' * (block_size - len(data)))
3112 level_output += hasher.digest()
3113 if digest_padding > 0:
3114 level_output += '\0' * digest_padding
3115
3116 padding_needed = (round_to_multiple(
3117 len(level_output), block_size) - len(level_output))
3118 level_output += '\0' * padding_needed
3119
David Zeuthena4fee8b2016-08-22 15:20:43 -04003120 # Copy level-output into resulting tree.
3121 offset = hash_level_offsets[level_num]
3122 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003123
David Zeuthena4fee8b2016-08-22 15:20:43 -04003124 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003125 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003126 level_num += 1
3127
3128 hasher = hashlib.new(name=hash_alg_name, string=salt)
3129 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003130 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003131
3132
3133class AvbTool(object):
3134 """Object for avbtool command-line tool."""
3135
3136 def __init__(self):
3137 """Initializer method."""
3138 self.avb = Avb()
3139
3140 def _add_common_args(self, sub_parser):
3141 """Adds arguments used by several sub-commands.
3142
3143 Arguments:
3144 sub_parser: The parser to add arguments to.
3145 """
3146 sub_parser.add_argument('--algorithm',
3147 help='Algorithm to use (default: NONE)',
3148 metavar='ALGORITHM',
3149 default='NONE')
3150 sub_parser.add_argument('--key',
3151 help='Path to RSA private key file',
3152 metavar='KEY',
3153 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003154 sub_parser.add_argument('--signing_helper',
3155 help='Path to helper used for signing',
3156 metavar='APP',
3157 default=None,
3158 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003159 sub_parser.add_argument('--public_key_metadata',
3160 help='Path to public key metadata file',
3161 metavar='KEY_METADATA',
3162 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003163 sub_parser.add_argument('--rollback_index',
3164 help='Rollback Index',
3165 type=parse_number,
3166 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003167 # This is used internally for unit tests. Do not include in --help output.
3168 sub_parser.add_argument('--internal_release_string',
3169 help=argparse.SUPPRESS)
3170 sub_parser.add_argument('--append_to_release_string',
3171 help='Text to append to release string',
3172 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003173 sub_parser.add_argument('--prop',
3174 help='Add property',
3175 metavar='KEY:VALUE',
3176 action='append')
3177 sub_parser.add_argument('--prop_from_file',
3178 help='Add property from file',
3179 metavar='KEY:PATH',
3180 action='append')
3181 sub_parser.add_argument('--kernel_cmdline',
3182 help='Add kernel cmdline',
3183 metavar='CMDLINE',
3184 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003185 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3186 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3187 # at some future point.
3188 sub_parser.add_argument('--setup_rootfs_from_kernel',
3189 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003190 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003191 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003192 type=argparse.FileType('rb'))
3193 sub_parser.add_argument('--include_descriptors_from_image',
3194 help='Include descriptors from image',
3195 metavar='IMAGE',
3196 action='append',
3197 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05003198 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3199 sub_parser.add_argument('--chain_partition',
3200 help='Allow signed integrity-data for partition',
3201 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3202 action='append')
3203 sub_parser.add_argument('--flags',
3204 help='VBMeta flags',
3205 type=parse_number,
3206 default=0)
3207 sub_parser.add_argument('--set_hashtree_disabled_flag',
3208 help='Set the HASHTREE_DISABLED flag',
3209 action='store_true')
3210
3211 def _fixup_common_args(self, args):
3212 """Common fixups needed by subcommands.
3213
3214 Arguments:
3215 args: Arguments to modify.
3216
3217 Returns:
3218 The modified arguments.
3219 """
3220 if args.set_hashtree_disabled_flag:
3221 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3222 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003223
3224 def run(self, argv):
3225 """Command-line processor.
3226
3227 Arguments:
3228 argv: Pass sys.argv from main.
3229 """
3230 parser = argparse.ArgumentParser()
3231 subparsers = parser.add_subparsers(title='subcommands')
3232
3233 sub_parser = subparsers.add_parser('version',
3234 help='Prints version of avbtool.')
3235 sub_parser.set_defaults(func=self.version)
3236
3237 sub_parser = subparsers.add_parser('extract_public_key',
3238 help='Extract public key.')
3239 sub_parser.add_argument('--key',
3240 help='Path to RSA private key file',
3241 required=True)
3242 sub_parser.add_argument('--output',
3243 help='Output file name',
3244 type=argparse.FileType('wb'),
3245 required=True)
3246 sub_parser.set_defaults(func=self.extract_public_key)
3247
3248 sub_parser = subparsers.add_parser('make_vbmeta_image',
3249 help='Makes a vbmeta image.')
3250 sub_parser.add_argument('--output',
3251 help='Output file name',
3252 type=argparse.FileType('wb'),
3253 required=True)
3254 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003255 sub_parser.set_defaults(func=self.make_vbmeta_image)
3256
3257 sub_parser = subparsers.add_parser('add_hash_footer',
3258 help='Add hashes and footer to image.')
3259 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003260 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003261 type=argparse.FileType('rab+'))
3262 sub_parser.add_argument('--partition_size',
3263 help='Partition size',
3264 type=parse_number,
3265 required=True)
3266 sub_parser.add_argument('--partition_name',
3267 help='Partition name',
3268 required=True)
3269 sub_parser.add_argument('--hash_algorithm',
3270 help='Hash algorithm to use (default: sha256)',
3271 default='sha256')
3272 sub_parser.add_argument('--salt',
3273 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003274 sub_parser.add_argument('--output_vbmeta_image',
3275 help='Also write vbmeta struct to file',
3276 type=argparse.FileType('wb'))
3277 sub_parser.add_argument('--do_not_append_vbmeta_image',
3278 help=('Do not append vbmeta struct or footer '
3279 'to the image'),
3280 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003281 self._add_common_args(sub_parser)
3282 sub_parser.set_defaults(func=self.add_hash_footer)
3283
David Zeuthenb1b994d2017-03-06 18:01:31 -05003284 sub_parser = subparsers.add_parser('append_vbmeta_image',
3285 help='Append vbmeta image to image.')
3286 sub_parser.add_argument('--image',
3287 help='Image to append vbmeta blob to',
3288 type=argparse.FileType('rab+'))
3289 sub_parser.add_argument('--partition_size',
3290 help='Partition size',
3291 type=parse_number,
3292 required=True)
3293 sub_parser.add_argument('--vbmeta_image',
3294 help='Image with vbmeta blob to append',
3295 type=argparse.FileType('rb'))
3296 sub_parser.set_defaults(func=self.append_vbmeta_image)
3297
David Zeuthen21e95262016-07-27 17:58:40 -04003298 sub_parser = subparsers.add_parser('add_hashtree_footer',
3299 help='Add hashtree and footer to image.')
3300 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003301 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003302 type=argparse.FileType('rab+'))
3303 sub_parser.add_argument('--partition_size',
3304 help='Partition size',
3305 type=parse_number,
3306 required=True)
3307 sub_parser.add_argument('--partition_name',
3308 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003309 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003310 sub_parser.add_argument('--hash_algorithm',
3311 help='Hash algorithm to use (default: sha1)',
3312 default='sha1')
3313 sub_parser.add_argument('--salt',
3314 help='Salt in hex (default: /dev/urandom)')
3315 sub_parser.add_argument('--block_size',
3316 help='Block size (default: 4096)',
3317 type=parse_number,
3318 default=4096)
David Zeuthen02c550f2017-05-10 17:18:04 -04003319 # TODO(zeuthen): The --generate_fec option was removed when we
3320 # moved to generating FEC by default. To avoid breaking existing
3321 # users needing to transition we simply just print a warning below
3322 # in add_hashtree_footer(). Remove this option and the warning at
3323 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003324 sub_parser.add_argument('--generate_fec',
David Zeuthen02c550f2017-05-10 17:18:04 -04003325 help=argparse.SUPPRESS,
3326 action='store_true')
3327 sub_parser.add_argument('--do_not_generate_fec',
3328 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003329 action='store_true')
3330 sub_parser.add_argument('--fec_num_roots',
3331 help='Number of roots for FEC (default: 2)',
3332 type=parse_number,
3333 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003334 sub_parser.add_argument('--calc_max_image_size',
3335 help=('Don\'t store the hashtree or footer - '
3336 'instead calculate the maximum image size '
3337 'leaving enough room for hashtree '
3338 'and metadata with the given partition '
3339 'size.'),
3340 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003341 sub_parser.add_argument('--output_vbmeta_image',
3342 help='Also write vbmeta struct to file',
3343 type=argparse.FileType('wb'))
3344 sub_parser.add_argument('--do_not_append_vbmeta_image',
3345 help=('Do not append vbmeta struct or footer '
3346 'to the image'),
3347 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003348 # This is different from --setup_rootfs_from_kernel insofar that
3349 # it doesn't take an IMAGE, the generated cmdline will be for the
3350 # hashtree we're adding.
3351 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3352 action='store_true',
3353 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003354 self._add_common_args(sub_parser)
3355 sub_parser.set_defaults(func=self.add_hashtree_footer)
3356
3357 sub_parser = subparsers.add_parser('erase_footer',
3358 help='Erase footer from an image.')
3359 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003360 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003361 type=argparse.FileType('rwb+'),
3362 required=True)
3363 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003364 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003365 action='store_true')
3366 sub_parser.set_defaults(func=self.erase_footer)
3367
David Zeuthen2bc232b2017-04-19 14:25:19 -04003368 sub_parser = subparsers.add_parser('resize_image',
3369 help='Resize image with a footer.')
3370 sub_parser.add_argument('--image',
3371 help='Image with a footer',
3372 type=argparse.FileType('rwb+'),
3373 required=True)
3374 sub_parser.add_argument('--partition_size',
3375 help='New partition size',
3376 type=parse_number)
3377 sub_parser.set_defaults(func=self.resize_image)
3378
David Zeuthen21e95262016-07-27 17:58:40 -04003379 sub_parser = subparsers.add_parser(
3380 'info_image',
3381 help='Show information about vbmeta or footer.')
3382 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003383 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003384 type=argparse.FileType('rb'),
3385 required=True)
3386 sub_parser.add_argument('--output',
3387 help='Write info to file',
3388 type=argparse.FileType('wt'),
3389 default=sys.stdout)
3390 sub_parser.set_defaults(func=self.info_image)
3391
David Zeuthenb623d8b2017-04-04 16:05:53 -04003392 sub_parser = subparsers.add_parser(
3393 'verify_image',
3394 help='Verify an image.')
3395 sub_parser.add_argument('--image',
3396 help='Image to verify',
3397 type=argparse.FileType('rb'),
3398 required=True)
3399 sub_parser.set_defaults(func=self.verify_image)
3400
David Zeuthen8b6973b2016-09-20 12:39:49 -04003401 sub_parser = subparsers.add_parser('set_ab_metadata',
3402 help='Set A/B metadata.')
3403 sub_parser.add_argument('--misc_image',
3404 help=('The misc image to modify. If the image does '
3405 'not exist, it will be created.'),
3406 type=argparse.FileType('r+b'),
3407 required=True)
3408 sub_parser.add_argument('--slot_data',
3409 help=('Slot data of the form "priority", '
3410 '"tries_remaining", "sucessful_boot" for '
3411 'slot A followed by the same for slot B, '
3412 'separated by colons. The default value '
3413 'is 15:7:0:14:7:0.'),
3414 default='15:7:0:14:7:0')
3415 sub_parser.set_defaults(func=self.set_ab_metadata)
3416
Darren Krahn147b08d2016-12-20 16:38:29 -08003417 sub_parser = subparsers.add_parser(
3418 'make_atx_certificate',
3419 help='Create an Android Things eXtension (ATX) certificate.')
3420 sub_parser.add_argument('--output',
3421 help='Write certificate to file',
3422 type=argparse.FileType('wb'),
3423 default=sys.stdout)
3424 sub_parser.add_argument('--subject',
3425 help=('Path to subject file'),
3426 type=argparse.FileType('rb'),
3427 required=True)
3428 sub_parser.add_argument('--subject_key',
3429 help=('Path to subject RSA public key file'),
3430 type=argparse.FileType('rb'),
3431 required=True)
3432 sub_parser.add_argument('--subject_key_version',
3433 help=('Version of the subject key'),
3434 type=parse_number,
3435 required=False)
3436 sub_parser.add_argument('--subject_is_intermediate_authority',
3437 help=('Generate an intermediate authority '
3438 'certificate'),
3439 action='store_true')
3440 sub_parser.add_argument('--authority_key',
3441 help='Path to authority RSA private key file',
3442 required=False)
3443 sub_parser.add_argument('--signing_helper',
3444 help='Path to helper used for signing',
3445 metavar='APP',
3446 default=None,
3447 required=False)
3448 sub_parser.set_defaults(func=self.make_atx_certificate)
3449
3450 sub_parser = subparsers.add_parser(
3451 'make_atx_permanent_attributes',
3452 help='Create Android Things eXtension (ATX) permanent attributes.')
3453 sub_parser.add_argument('--output',
3454 help='Write attributes to file',
3455 type=argparse.FileType('wb'),
3456 default=sys.stdout)
3457 sub_parser.add_argument('--root_authority_key',
3458 help='Path to authority RSA public key file',
3459 type=argparse.FileType('rb'),
3460 required=True)
3461 sub_parser.add_argument('--product_id',
3462 help=('Path to Product ID file'),
3463 type=argparse.FileType('rb'),
3464 required=True)
3465 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3466
3467 sub_parser = subparsers.add_parser(
3468 'make_atx_metadata',
3469 help='Create Android Things eXtension (ATX) metadata.')
3470 sub_parser.add_argument('--output',
3471 help='Write metadata to file',
3472 type=argparse.FileType('wb'),
3473 default=sys.stdout)
3474 sub_parser.add_argument('--intermediate_key_certificate',
3475 help='Path to intermediate key certificate file',
3476 type=argparse.FileType('rb'),
3477 required=True)
3478 sub_parser.add_argument('--product_key_certificate',
3479 help='Path to product key certificate file',
3480 type=argparse.FileType('rb'),
3481 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003482 sub_parser.set_defaults(func=self.make_atx_metadata)
3483
David Zeuthen21e95262016-07-27 17:58:40 -04003484 args = parser.parse_args(argv[1:])
3485 try:
3486 args.func(args)
3487 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003488 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003489 sys.exit(1)
3490
3491 def version(self, _):
3492 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003493 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003494
3495 def extract_public_key(self, args):
3496 """Implements the 'extract_public_key' sub-command."""
3497 self.avb.extract_public_key(args.key, args.output)
3498
3499 def make_vbmeta_image(self, args):
3500 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003501 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003502 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003503 args.algorithm, args.key,
3504 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003505 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003506 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003507 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003508 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003509 args.signing_helper,
3510 args.internal_release_string,
3511 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003512
David Zeuthenb1b994d2017-03-06 18:01:31 -05003513 def append_vbmeta_image(self, args):
3514 """Implements the 'append_vbmeta_image' sub-command."""
3515 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3516 args.partition_size)
3517
David Zeuthen21e95262016-07-27 17:58:40 -04003518 def add_hash_footer(self, args):
3519 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003520 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003521 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003522 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003523 args.salt, args.chain_partition, args.algorithm,
3524 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003525 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003526 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003527 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003528 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003529 args.include_descriptors_from_image,
3530 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003531 args.internal_release_string,
3532 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003533 args.output_vbmeta_image,
3534 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003535
3536 def add_hashtree_footer(self, args):
3537 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003538 args = self._fixup_common_args(args)
David Zeuthen02c550f2017-05-10 17:18:04 -04003539 # TODO(zeuthen): Remove when removing support for the
3540 # '--generate_fec' option above.
3541 if args.generate_fec:
3542 sys.stderr.write('The --generate_fec option is deprecated since FEC '
3543 'is now generated by default. Use the option '
3544 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04003545 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3546 args.partition_size,
3547 args.partition_name,
David Zeuthen02c550f2017-05-10 17:18:04 -04003548 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003549 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003550 args.salt, args.chain_partition, args.algorithm,
3551 args.key, args.public_key_metadata,
3552 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003553 args.prop_from_file,
3554 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003555 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003556 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003557 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003558 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003559 args.internal_release_string,
3560 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003561 args.output_vbmeta_image,
3562 args.do_not_append_vbmeta_image)
3563
David Zeuthen21e95262016-07-27 17:58:40 -04003564 def erase_footer(self, args):
3565 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003566 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003567
David Zeuthen2bc232b2017-04-19 14:25:19 -04003568 def resize_image(self, args):
3569 """Implements the 'resize_image' sub-command."""
3570 self.avb.resize_image(args.image.name, args.partition_size)
3571
David Zeuthen8b6973b2016-09-20 12:39:49 -04003572 def set_ab_metadata(self, args):
3573 """Implements the 'set_ab_metadata' sub-command."""
3574 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3575
David Zeuthen21e95262016-07-27 17:58:40 -04003576 def info_image(self, args):
3577 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003578 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003579
David Zeuthenb623d8b2017-04-04 16:05:53 -04003580 def verify_image(self, args):
3581 """Implements the 'verify_image' sub-command."""
3582 self.avb.verify_image(args.image.name)
3583
Darren Krahn147b08d2016-12-20 16:38:29 -08003584 def make_atx_certificate(self, args):
3585 """Implements the 'make_atx_certificate' sub-command."""
3586 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003587 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003588 args.subject_key_version,
3589 args.subject.read(),
3590 args.subject_is_intermediate_authority,
3591 args.signing_helper)
3592
3593 def make_atx_permanent_attributes(self, args):
3594 """Implements the 'make_atx_permanent_attributes' sub-command."""
3595 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003596 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003597 args.product_id.read())
3598
3599 def make_atx_metadata(self, args):
3600 """Implements the 'make_atx_metadata' sub-command."""
3601 self.avb.make_atx_metadata(args.output,
3602 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003603 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003604
David Zeuthen21e95262016-07-27 17:58:40 -04003605
3606if __name__ == '__main__':
3607 tool = AvbTool()
3608 tool.run(sys.argv)