blob: 5df31b4008716bb39200f2429cbe95e144d7ae8d [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
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001064 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1065 """Verifies contents of the descriptor - used in verify_image sub-command.
1066
1067 Arguments:
1068 image_dir: The directory of the file being verified.
1069 image_ext: The extension of the file being verified (e.g. '.img').
1070 expected_chain_partitions_map: A map from partition name to the
1071 tuple (rollback_index_location, key_blob).
1072
1073 Returns:
1074 True if the descriptor verifies, False otherwise.
1075 """
1076 # Nothing to do.
1077 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001078
1079class AvbPropertyDescriptor(AvbDescriptor):
1080 """A class for property descriptors.
1081
1082 See the |AvbPropertyDescriptor| C struct for more information.
1083
1084 Attributes:
1085 key: The key.
1086 value: The key.
1087 """
1088
1089 TAG = 0
1090 SIZE = 32
1091 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1092 'Q' # key size (bytes)
1093 'Q') # value size (bytes)
1094
1095 def __init__(self, data=None):
1096 """Initializes a new property descriptor.
1097
1098 Arguments:
1099 data: If not None, must be a bytearray of size |SIZE|.
1100
1101 Raises:
1102 LookupError: If the given descriptor is malformed.
1103 """
1104 AvbDescriptor.__init__(self, None)
1105 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1106
1107 if data:
1108 (tag, num_bytes_following, key_size,
1109 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1110 expected_size = round_to_multiple(
1111 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1112 if tag != self.TAG or num_bytes_following != expected_size:
1113 raise LookupError('Given data does not look like a property '
1114 'descriptor.')
1115 self.key = data[self.SIZE:(self.SIZE + key_size)]
1116 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1117 value_size)]
1118 else:
1119 self.key = ''
1120 self.value = ''
1121
1122 def print_desc(self, o):
1123 """Print the descriptor.
1124
1125 Arguments:
1126 o: The object to write the output to.
1127 """
1128 if len(self.value) < 256:
1129 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1130 else:
1131 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1132
1133 def encode(self):
1134 """Serializes the descriptor.
1135
1136 Returns:
1137 A bytearray() with the descriptor data.
1138 """
1139 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1140 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1141 padding_size = nbf_with_padding - num_bytes_following
1142 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1143 len(self.key), len(self.value))
1144 padding = struct.pack(str(padding_size) + 'x')
1145 ret = desc + self.key + '\0' + self.value + '\0' + padding
1146 return bytearray(ret)
1147
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001148 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1149 """Verifies contents of the descriptor - used in verify_image sub-command.
1150
1151 Arguments:
1152 image_dir: The directory of the file being verified.
1153 image_ext: The extension of the file being verified (e.g. '.img').
1154 expected_chain_partitions_map: A map from partition name to the
1155 tuple (rollback_index_location, key_blob).
1156
1157 Returns:
1158 True if the descriptor verifies, False otherwise.
1159 """
1160 # Nothing to do.
1161 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001162
1163class AvbHashtreeDescriptor(AvbDescriptor):
1164 """A class for hashtree descriptors.
1165
1166 See the |AvbHashtreeDescriptor| C struct for more information.
1167
1168 Attributes:
1169 dm_verity_version: dm-verity version used.
1170 image_size: Size of the image, after rounding up to |block_size|.
1171 tree_offset: Offset of the hash tree in the file.
1172 tree_size: Size of the tree.
1173 data_block_size: Data block size
1174 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001175 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1176 fec_offset: Offset of FEC data (0 if FEC is not used).
1177 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001178 hash_algorithm: Hash algorithm used.
1179 partition_name: Partition name.
1180 salt: Salt used.
1181 root_digest: Root digest.
1182 """
1183
1184 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001185 RESERVED = 64
1186 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001187 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1188 'L' # dm-verity version used
1189 'Q' # image size (bytes)
1190 'Q' # tree offset (bytes)
1191 'Q' # tree size (bytes)
1192 'L' # data block size (bytes)
1193 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001194 'L' # FEC number of roots
1195 'Q' # FEC offset (bytes)
1196 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001197 '32s' # hash algorithm used
1198 'L' # partition name (bytes)
1199 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001200 'L' + # root digest length (bytes)
1201 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001202
1203 def __init__(self, data=None):
1204 """Initializes a new hashtree descriptor.
1205
1206 Arguments:
1207 data: If not None, must be a bytearray of size |SIZE|.
1208
1209 Raises:
1210 LookupError: If the given descriptor is malformed.
1211 """
1212 AvbDescriptor.__init__(self, None)
1213 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1214
1215 if data:
1216 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1217 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001218 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1219 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001220 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1221 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001222 expected_size = round_to_multiple(
1223 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1224 if tag != self.TAG or num_bytes_following != expected_size:
1225 raise LookupError('Given data does not look like a hashtree '
1226 'descriptor.')
1227 # Nuke NUL-bytes at the end.
1228 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1229 o = 0
1230 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1231 partition_name_len)])
1232 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1233 self.partition_name.decode('utf-8')
1234 o += partition_name_len
1235 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1236 o += salt_len
1237 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1238 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1239 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1240
1241 else:
1242 self.dm_verity_version = 0
1243 self.image_size = 0
1244 self.tree_offset = 0
1245 self.tree_size = 0
1246 self.data_block_size = 0
1247 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001248 self.fec_num_roots = 0
1249 self.fec_offset = 0
1250 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001251 self.hash_algorithm = ''
1252 self.partition_name = ''
1253 self.salt = bytearray()
1254 self.root_digest = bytearray()
1255
1256 def print_desc(self, o):
1257 """Print the descriptor.
1258
1259 Arguments:
1260 o: The object to write the output to.
1261 """
1262 o.write(' Hashtree descriptor:\n')
1263 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1264 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1265 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1266 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1267 o.write(' Data Block Size: {} bytes\n'.format(
1268 self.data_block_size))
1269 o.write(' Hash Block Size: {} bytes\n'.format(
1270 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001271 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1272 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1273 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001274 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1275 o.write(' Partition Name: {}\n'.format(self.partition_name))
1276 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1277 'hex')))
1278 o.write(' Root Digest: {}\n'.format(str(
1279 self.root_digest).encode('hex')))
1280
1281 def encode(self):
1282 """Serializes the descriptor.
1283
1284 Returns:
1285 A bytearray() with the descriptor data.
1286 """
1287 encoded_name = self.partition_name.encode('utf-8')
1288 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1289 len(self.root_digest) - 16)
1290 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1291 padding_size = nbf_with_padding - num_bytes_following
1292 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1293 self.dm_verity_version, self.image_size,
1294 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001295 self.hash_block_size, self.fec_num_roots,
1296 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001297 len(encoded_name), len(self.salt), len(self.root_digest),
1298 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001299 padding = struct.pack(str(padding_size) + 'x')
1300 ret = desc + encoded_name + self.salt + self.root_digest + padding
1301 return bytearray(ret)
1302
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001303 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1304 """Verifies contents of the descriptor - used in verify_image sub-command.
1305
1306 Arguments:
1307 image_dir: The directory of the file being verified.
1308 image_ext: The extension of the file being verified (e.g. '.img').
1309 expected_chain_partitions_map: A map from partition name to the
1310 tuple (rollback_index_location, key_blob).
1311
1312 Returns:
1313 True if the descriptor verifies, False otherwise.
1314 """
1315 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1316 image = ImageHandler(image_filename)
1317 # Generate the hashtree and checks that it matches what's in the file.
1318 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1319 digest_padding = round_to_pow2(digest_size) - digest_size
1320 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1321 self.image_size, self.data_block_size, digest_size + digest_padding)
1322 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1323 self.data_block_size,
1324 self.hash_algorithm, self.salt,
1325 digest_padding,
1326 hash_level_offsets,
1327 tree_size)
1328 # The root digest must match...
1329 if root_digest != self.root_digest:
1330 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1331 format(image_filename))
1332 return False
1333 # ... also check that the on-disk hashtree matches
1334 image.seek(self.tree_offset)
1335 hash_tree_ondisk = image.read(self.tree_size)
1336 if hash_tree != hash_tree_ondisk:
1337 sys.stderr.write('hashtree of {} contains invalid data\n'.
1338 format(image_filename))
1339 return False
1340 # TODO: we could also verify that the FEC stored in the image is
1341 # correct but this a) currently requires the 'fec' binary; and b)
1342 # takes a long time; and c) is not strictly needed for
1343 # verification purposes as we've already verified the root hash.
1344 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1345 .format(self.partition_name, self.hash_algorithm, image_filename,
1346 self.image_size))
1347 return True
1348
David Zeuthen21e95262016-07-27 17:58:40 -04001349
1350class AvbHashDescriptor(AvbDescriptor):
1351 """A class for hash descriptors.
1352
1353 See the |AvbHashDescriptor| C struct for more information.
1354
1355 Attributes:
1356 image_size: Image size, in bytes.
1357 hash_algorithm: Hash algorithm used.
1358 partition_name: Partition name.
1359 salt: Salt used.
1360 digest: The hash value of salt and data combined.
1361 """
1362
1363 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001364 RESERVED = 64
1365 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001366 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1367 'Q' # image size (bytes)
1368 '32s' # hash algorithm used
1369 'L' # partition name (bytes)
1370 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001371 'L' + # digest length (bytes)
1372 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001373
1374 def __init__(self, data=None):
1375 """Initializes a new hash descriptor.
1376
1377 Arguments:
1378 data: If not None, must be a bytearray of size |SIZE|.
1379
1380 Raises:
1381 LookupError: If the given descriptor is malformed.
1382 """
1383 AvbDescriptor.__init__(self, None)
1384 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1385
1386 if data:
1387 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1388 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001389 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001390 expected_size = round_to_multiple(
1391 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1392 if tag != self.TAG or num_bytes_following != expected_size:
1393 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1394 # Nuke NUL-bytes at the end.
1395 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1396 o = 0
1397 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1398 partition_name_len)])
1399 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1400 self.partition_name.decode('utf-8')
1401 o += partition_name_len
1402 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1403 o += salt_len
1404 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1405 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1406 raise LookupError('digest_len doesn\'t match hash algorithm')
1407
1408 else:
1409 self.image_size = 0
1410 self.hash_algorithm = ''
1411 self.partition_name = ''
1412 self.salt = bytearray()
1413 self.digest = bytearray()
1414
1415 def print_desc(self, o):
1416 """Print the descriptor.
1417
1418 Arguments:
1419 o: The object to write the output to.
1420 """
1421 o.write(' Hash descriptor:\n')
1422 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1423 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1424 o.write(' Partition Name: {}\n'.format(self.partition_name))
1425 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1426 'hex')))
1427 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1428 'hex')))
1429
1430 def encode(self):
1431 """Serializes the descriptor.
1432
1433 Returns:
1434 A bytearray() with the descriptor data.
1435 """
1436 encoded_name = self.partition_name.encode('utf-8')
1437 num_bytes_following = (
1438 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1439 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1440 padding_size = nbf_with_padding - num_bytes_following
1441 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1442 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001443 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001444 padding = struct.pack(str(padding_size) + 'x')
1445 ret = desc + encoded_name + self.salt + self.digest + padding
1446 return bytearray(ret)
1447
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001448 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1449 """Verifies contents of the descriptor - used in verify_image sub-command.
1450
1451 Arguments:
1452 image_dir: The directory of the file being verified.
1453 image_ext: The extension of the file being verified (e.g. '.img').
1454 expected_chain_partitions_map: A map from partition name to the
1455 tuple (rollback_index_location, key_blob).
1456
1457 Returns:
1458 True if the descriptor verifies, False otherwise.
1459 """
1460 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1461 image = ImageHandler(image_filename)
1462 data = image.read(self.image_size)
1463 ha = hashlib.new(self.hash_algorithm)
1464 ha.update(self.salt)
1465 ha.update(data)
1466 digest = ha.digest()
1467 if digest != self.digest:
1468 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1469 format(self.hash_algorithm, image_filename))
1470 return False
1471 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
1472 .format(self.partition_name, self.hash_algorithm, image_filename,
1473 self.image_size))
1474 return True
1475
David Zeuthen21e95262016-07-27 17:58:40 -04001476
1477class AvbKernelCmdlineDescriptor(AvbDescriptor):
1478 """A class for kernel command-line descriptors.
1479
1480 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1481
1482 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001483 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001484 kernel_cmdline: The kernel command-line.
1485 """
1486
1487 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001488 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001489 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001490 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001491 'L') # cmdline length (bytes)
1492
David Zeuthenfd41eb92016-11-17 12:24:47 -05001493 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1494 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1495
David Zeuthen21e95262016-07-27 17:58:40 -04001496 def __init__(self, data=None):
1497 """Initializes a new kernel cmdline descriptor.
1498
1499 Arguments:
1500 data: If not None, must be a bytearray of size |SIZE|.
1501
1502 Raises:
1503 LookupError: If the given descriptor is malformed.
1504 """
1505 AvbDescriptor.__init__(self, None)
1506 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1507
1508 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001509 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001510 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1511 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1512 8)
1513 if tag != self.TAG or num_bytes_following != expected_size:
1514 raise LookupError('Given data does not look like a kernel cmdline '
1515 'descriptor.')
1516 # Nuke NUL-bytes at the end.
1517 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1518 kernel_cmdline_length)])
1519 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1520 self.kernel_cmdline.decode('utf-8')
1521 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001522 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001523 self.kernel_cmdline = ''
1524
1525 def print_desc(self, o):
1526 """Print the descriptor.
1527
1528 Arguments:
1529 o: The object to write the output to.
1530 """
1531 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001532 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001533 o.write(' Kernel Cmdline: {}\n'.format(repr(
1534 self.kernel_cmdline)))
1535
1536 def encode(self):
1537 """Serializes the descriptor.
1538
1539 Returns:
1540 A bytearray() with the descriptor data.
1541 """
1542 encoded_str = self.kernel_cmdline.encode('utf-8')
1543 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1544 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1545 padding_size = nbf_with_padding - num_bytes_following
1546 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001547 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001548 padding = struct.pack(str(padding_size) + 'x')
1549 ret = desc + encoded_str + padding
1550 return bytearray(ret)
1551
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001552 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1553 """Verifies contents of the descriptor - used in verify_image sub-command.
1554
1555 Arguments:
1556 image_dir: The directory of the file being verified.
1557 image_ext: The extension of the file being verified (e.g. '.img').
1558 expected_chain_partitions_map: A map from partition name to the
1559 tuple (rollback_index_location, key_blob).
1560
1561 Returns:
1562 True if the descriptor verifies, False otherwise.
1563 """
1564 # Nothing to verify.
1565 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001566
1567class AvbChainPartitionDescriptor(AvbDescriptor):
1568 """A class for chained partition descriptors.
1569
1570 See the |AvbChainPartitionDescriptor| C struct for more information.
1571
1572 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001573 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001574 partition_name: Partition name.
1575 public_key: Bytes for the public key.
1576 """
1577
1578 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001579 RESERVED = 64
1580 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001581 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001582 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001583 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001584 'L' + # public_key_size (bytes)
1585 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001586
1587 def __init__(self, data=None):
1588 """Initializes a new chain partition descriptor.
1589
1590 Arguments:
1591 data: If not None, must be a bytearray of size |SIZE|.
1592
1593 Raises:
1594 LookupError: If the given descriptor is malformed.
1595 """
1596 AvbDescriptor.__init__(self, None)
1597 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1598
1599 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001600 (tag, num_bytes_following, self.rollback_index_location,
1601 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001602 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001603 expected_size = round_to_multiple(
1604 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1605 if tag != self.TAG or num_bytes_following != expected_size:
1606 raise LookupError('Given data does not look like a chain partition '
1607 'descriptor.')
1608 o = 0
1609 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1610 partition_name_len)])
1611 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1612 self.partition_name.decode('utf-8')
1613 o += partition_name_len
1614 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1615
1616 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001617 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001618 self.partition_name = ''
1619 self.public_key = bytearray()
1620
1621 def print_desc(self, o):
1622 """Print the descriptor.
1623
1624 Arguments:
1625 o: The object to write the output to.
1626 """
1627 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001628 o.write(' Partition Name: {}\n'.format(self.partition_name))
1629 o.write(' Rollback Index Location: {}\n'.format(
1630 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001631 # Just show the SHA1 of the key, for size reasons.
1632 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001633 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001634
1635 def encode(self):
1636 """Serializes the descriptor.
1637
1638 Returns:
1639 A bytearray() with the descriptor data.
1640 """
1641 encoded_name = self.partition_name.encode('utf-8')
1642 num_bytes_following = (
1643 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1644 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1645 padding_size = nbf_with_padding - num_bytes_following
1646 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001647 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001648 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001649 padding = struct.pack(str(padding_size) + 'x')
1650 ret = desc + encoded_name + self.public_key + padding
1651 return bytearray(ret)
1652
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001653 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1654 """Verifies contents of the descriptor - used in verify_image sub-command.
1655
1656 Arguments:
1657 image_dir: The directory of the file being verified.
1658 image_ext: The extension of the file being verified (e.g. '.img').
1659 expected_chain_partitions_map: A map from partition name to the
1660 tuple (rollback_index_location, key_blob).
1661
1662 Returns:
1663 True if the descriptor verifies, False otherwise.
1664 """
1665 value = expected_chain_partitions_map.get(self.partition_name)
1666 if not value:
1667 sys.stderr.write('No expected chain partition for partition {}. Use '
1668 '--expected_chain_partition to specify expected '
1669 'contents.\n'.
1670 format(self.partition_name))
1671 return False
1672 rollback_index_location, pk_blob = value
1673
1674 if self.rollback_index_location != rollback_index_location:
1675 sys.stderr.write('Expected rollback_index_location {} does not '
1676 'match {} in descriptor for partition {}\n'.
1677 format(rollback_index_location,
1678 self.rollback_index_location,
1679 self.partition_name))
1680 return False
1681
1682 if self.public_key != pk_blob:
1683 sys.stderr.write('Expected public key blob does not match public '
1684 'key blob in descriptor for partition {}\n'.
1685 format(self.partition_name))
1686 return False
1687
1688 print ('{}: Successfully verified chain partition descriptor matches '
1689 'expected data'.format(self.partition_name))
1690
1691 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001692
1693DESCRIPTOR_CLASSES = [
1694 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1695 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1696]
1697
1698
1699def parse_descriptors(data):
1700 """Parses a blob of data into descriptors.
1701
1702 Arguments:
1703 data: A bytearray() with encoded descriptors.
1704
1705 Returns:
1706 A list of instances of objects derived from AvbDescriptor. For
1707 unknown descriptors, the class AvbDescriptor is used.
1708 """
1709 o = 0
1710 ret = []
1711 while o < len(data):
1712 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1713 if tag < len(DESCRIPTOR_CLASSES):
1714 c = DESCRIPTOR_CLASSES[tag]
1715 else:
1716 c = AvbDescriptor
1717 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1718 o += 16 + nb_following
1719 return ret
1720
1721
1722class AvbFooter(object):
1723 """A class for parsing and writing footers.
1724
1725 Footers are stored at the end of partitions and point to where the
1726 AvbVBMeta blob is located. They also contain the original size of
1727 the image before AVB information was added.
1728
1729 Attributes:
1730 magic: Magic for identifying the footer, see |MAGIC|.
1731 version_major: The major version of avbtool that wrote the footer.
1732 version_minor: The minor version of avbtool that wrote the footer.
1733 original_image_size: Original image size.
1734 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1735 vbmeta_size: Size of the AvbVBMeta blob.
1736 """
1737
1738 MAGIC = 'AVBf'
1739 SIZE = 64
1740 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001741 FOOTER_VERSION_MAJOR = 1
1742 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001743 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1744 'Q' # Original image size.
1745 'Q' # Offset of VBMeta blob.
1746 'Q' + # Size of VBMeta blob.
1747 str(RESERVED) + 'x') # padding for reserved bytes
1748
1749 def __init__(self, data=None):
1750 """Initializes a new footer object.
1751
1752 Arguments:
1753 data: If not None, must be a bytearray of size 4096.
1754
1755 Raises:
1756 LookupError: If the given footer is malformed.
1757 struct.error: If the given data has no footer.
1758 """
1759 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1760
1761 if data:
1762 (self.magic, self.version_major, self.version_minor,
1763 self.original_image_size, self.vbmeta_offset,
1764 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1765 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001766 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001767 else:
1768 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001769 self.version_major = self.FOOTER_VERSION_MAJOR
1770 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001771 self.original_image_size = 0
1772 self.vbmeta_offset = 0
1773 self.vbmeta_size = 0
1774
David Zeuthena4fee8b2016-08-22 15:20:43 -04001775 def encode(self):
1776 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001777
David Zeuthena4fee8b2016-08-22 15:20:43 -04001778 Returns:
1779 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001780 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001781 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1782 self.version_minor, self.original_image_size,
1783 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001784
1785
1786class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001787 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001788
1789 Attributes:
1790 The attributes correspond to the |AvbVBMetaHeader| struct
1791 defined in avb_vbmeta_header.h.
1792 """
1793
1794 SIZE = 256
1795
David Zeuthene3cadca2017-02-22 21:25:46 -05001796 # Keep in sync with |reserved0| and |reserved| field of
1797 # |AvbVBMetaImageHeader|.
1798 RESERVED0 = 4
1799 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001800
1801 # Keep in sync with |AvbVBMetaImageHeader|.
1802 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1803 '2Q' # 2 x block size
1804 'L' # algorithm type
1805 '2Q' # offset, size (hash)
1806 '2Q' # offset, size (signature)
1807 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001808 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001809 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001810 'Q' # rollback_index
1811 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001812 str(RESERVED0) + 'x' + # padding for reserved bytes
1813 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001814 str(RESERVED) + 'x') # padding for reserved bytes
1815
1816 def __init__(self, data=None):
1817 """Initializes a new header object.
1818
1819 Arguments:
1820 data: If not None, must be a bytearray of size 8192.
1821
1822 Raises:
1823 Exception: If the given data is malformed.
1824 """
1825 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1826
1827 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001828 (self.magic, self.required_libavb_version_major,
1829 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001830 self.authentication_data_block_size, self.auxiliary_data_block_size,
1831 self.algorithm_type, self.hash_offset, self.hash_size,
1832 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001833 self.public_key_size, self.public_key_metadata_offset,
1834 self.public_key_metadata_size, self.descriptors_offset,
1835 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001836 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001837 self.flags,
1838 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001839 # Nuke NUL-bytes at the end of the string.
1840 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001841 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001842 else:
1843 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001844 # Start by just requiring version 1.0. Code that adds features
1845 # in a future version can use bump_required_libavb_version_minor() to
1846 # bump the minor.
1847 self.required_libavb_version_major = AVB_VERSION_MAJOR
1848 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001849 self.authentication_data_block_size = 0
1850 self.auxiliary_data_block_size = 0
1851 self.algorithm_type = 0
1852 self.hash_offset = 0
1853 self.hash_size = 0
1854 self.signature_offset = 0
1855 self.signature_size = 0
1856 self.public_key_offset = 0
1857 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001858 self.public_key_metadata_offset = 0
1859 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001860 self.descriptors_offset = 0
1861 self.descriptors_size = 0
1862 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001863 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001864 self.release_string = get_release_string()
1865
1866 def bump_required_libavb_version_minor(self, minor):
1867 """Function to bump required_libavb_version_minor.
1868
1869 Call this when writing data that requires a specific libavb
1870 version to parse it.
1871
1872 Arguments:
1873 minor: The minor version of libavb that has support for the feature.
1874 """
1875 self.required_libavb_version_minor = (
1876 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001877
1878 def save(self, output):
1879 """Serializes the header (256 bytes) to disk.
1880
1881 Arguments:
1882 output: The object to write the output to.
1883 """
1884 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001885 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1886 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001887 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1888 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001889 self.public_key_offset, self.public_key_size,
1890 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001891 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001892 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001893
1894 def encode(self):
1895 """Serializes the header (256) to a bytearray().
1896
1897 Returns:
1898 A bytearray() with the encoded header.
1899 """
1900 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001901 self.required_libavb_version_major,
1902 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001903 self.authentication_data_block_size,
1904 self.auxiliary_data_block_size, self.algorithm_type,
1905 self.hash_offset, self.hash_size, self.signature_offset,
1906 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001907 self.public_key_size, self.public_key_metadata_offset,
1908 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001909 self.descriptors_size, self.rollback_index, self.flags,
1910 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001911
1912
1913class Avb(object):
1914 """Business logic for avbtool command-line tool."""
1915
David Zeuthen8b6973b2016-09-20 12:39:49 -04001916 # Keep in sync with avb_ab_flow.h.
1917 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1918 AB_MAGIC = '\0AB0'
1919 AB_MAJOR_VERSION = 1
1920 AB_MINOR_VERSION = 0
1921 AB_MISC_METADATA_OFFSET = 2048
1922
David Zeuthen09692692016-09-30 16:16:40 -04001923 # Constants for maximum metadata size. These are used to give
1924 # meaningful errors if the value passed in via --partition_size is
1925 # too small and when --calc_max_image_size is used. We use
1926 # conservative figures.
1927 MAX_VBMETA_SIZE = 64 * 1024
1928 MAX_FOOTER_SIZE = 4096
1929
David Zeuthena4fee8b2016-08-22 15:20:43 -04001930 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001931 """Implements the 'erase_footer' command.
1932
1933 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001934 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001935 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001936
1937 Raises:
1938 AvbError: If there's no footer in the image.
1939 """
1940
David Zeuthena4fee8b2016-08-22 15:20:43 -04001941 image = ImageHandler(image_filename)
1942
David Zeuthen21e95262016-07-27 17:58:40 -04001943 (footer, _, descriptors, _) = self._parse_image(image)
1944
1945 if not footer:
1946 raise AvbError('Given image does not have a footer.')
1947
1948 new_image_size = None
1949 if not keep_hashtree:
1950 new_image_size = footer.original_image_size
1951 else:
1952 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001953 # descriptor to figure out the location and size of the hashtree
1954 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001955 for desc in descriptors:
1956 if isinstance(desc, AvbHashtreeDescriptor):
1957 # The hashtree is always just following the main data so the
1958 # new size is easily derived.
1959 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001960 # If the image has FEC codes, also keep those.
1961 if desc.fec_offset > 0:
1962 fec_end = desc.fec_offset + desc.fec_size
1963 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001964 break
1965 if not new_image_size:
1966 raise AvbError('Requested to keep hashtree but no hashtree '
1967 'descriptor was found.')
1968
1969 # And cut...
1970 image.truncate(new_image_size)
1971
David Zeuthen2bc232b2017-04-19 14:25:19 -04001972 def resize_image(self, image_filename, partition_size):
1973 """Implements the 'resize_image' command.
1974
1975 Arguments:
1976 image_filename: File with footer to resize.
1977 partition_size: The new size of the image.
1978
1979 Raises:
1980 AvbError: If there's no footer in the image.
1981 """
1982
1983 image = ImageHandler(image_filename)
1984
1985 if partition_size % image.block_size != 0:
1986 raise AvbError('Partition size of {} is not a multiple of the image '
1987 'block size {}.'.format(partition_size,
1988 image.block_size))
1989
1990 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
1991
1992 if not footer:
1993 raise AvbError('Given image does not have a footer.')
1994
1995 # The vbmeta blob is always at the end of the data so resizing an
1996 # image amounts to just moving the footer around.
1997
1998 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
1999 if vbmeta_end_offset % image.block_size != 0:
2000 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2001
2002 if partition_size < vbmeta_end_offset + 1*image.block_size:
2003 raise AvbError('Requested size of {} is too small for an image '
2004 'of size {}.'
2005 .format(partition_size,
2006 vbmeta_end_offset + 1*image.block_size))
2007
2008 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2009 # with enough bytes such that the final Footer block is at the end
2010 # of partition_size.
2011 image.truncate(vbmeta_end_offset)
2012 image.append_dont_care(partition_size - vbmeta_end_offset -
2013 1*image.block_size)
2014
2015 # Just reuse the same footer - only difference is that we're
2016 # writing it in a different place.
2017 footer_blob = footer.encode()
2018 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2019 footer_blob)
2020 image.append_raw(footer_blob_with_padding)
2021
David Zeuthen8b6973b2016-09-20 12:39:49 -04002022 def set_ab_metadata(self, misc_image, slot_data):
2023 """Implements the 'set_ab_metadata' command.
2024
2025 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2026 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2027
2028 Arguments:
2029 misc_image: The misc image to write to.
2030 slot_data: Slot data as a string
2031
2032 Raises:
2033 AvbError: If slot data is malformed.
2034 """
2035 tokens = slot_data.split(':')
2036 if len(tokens) != 6:
2037 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2038 a_priority = int(tokens[0])
2039 a_tries_remaining = int(tokens[1])
2040 a_success = True if int(tokens[2]) != 0 else False
2041 b_priority = int(tokens[3])
2042 b_tries_remaining = int(tokens[4])
2043 b_success = True if int(tokens[5]) != 0 else False
2044
2045 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2046 self.AB_MAGIC,
2047 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2048 a_priority, a_tries_remaining, a_success,
2049 b_priority, b_tries_remaining, b_success)
2050 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2051 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2052 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2053 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2054 misc_image.write(ab_data)
2055
David Zeuthena4fee8b2016-08-22 15:20:43 -04002056 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002057 """Implements the 'info_image' command.
2058
2059 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002060 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002061 output: Output file to write human-readable information to (file object).
2062 """
2063
David Zeuthena4fee8b2016-08-22 15:20:43 -04002064 image = ImageHandler(image_filename)
2065
David Zeuthen21e95262016-07-27 17:58:40 -04002066 o = output
2067
2068 (footer, header, descriptors, image_size) = self._parse_image(image)
2069
2070 if footer:
2071 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2072 footer.version_minor))
2073 o.write('Image size: {} bytes\n'.format(image_size))
2074 o.write('Original image size: {} bytes\n'.format(
2075 footer.original_image_size))
2076 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2077 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2078 o.write('--\n')
2079
2080 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2081
David Zeuthene3cadca2017-02-22 21:25:46 -05002082 o.write('Minimum libavb version: {}.{}{}\n'.format(
2083 header.required_libavb_version_major,
2084 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002085 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002086 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2087 o.write('Authentication Block: {} bytes\n'.format(
2088 header.authentication_data_block_size))
2089 o.write('Auxiliary Block: {} bytes\n'.format(
2090 header.auxiliary_data_block_size))
2091 o.write('Algorithm: {}\n'.format(alg_name))
2092 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002093 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002094 o.write('Release String: \'{}\'\n'.format(
2095 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002096
2097 # Print descriptors.
2098 num_printed = 0
2099 o.write('Descriptors:\n')
2100 for desc in descriptors:
2101 desc.print_desc(o)
2102 num_printed += 1
2103 if num_printed == 0:
2104 o.write(' (none)\n')
2105
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002106 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002107 """Implements the 'verify_image' command.
2108
2109 Arguments:
2110 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002111 key_path: None or check that embedded public key matches key at given path.
2112 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002113 """
2114
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002115 expected_chain_partitions_map = {}
2116 if expected_chain_partitions:
2117 used_locations = {}
2118 for cp in expected_chain_partitions:
2119 cp_tokens = cp.split(':')
2120 if len(cp_tokens) != 3:
2121 raise AvbError('Malformed chained partition "{}".'.format(cp))
2122 partition_name = cp_tokens[0]
2123 rollback_index_location = int(cp_tokens[1])
2124 file_path = cp_tokens[2]
2125 pk_blob = open(file_path).read()
2126 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2127
2128 image_dir = os.path.dirname(image_filename)
2129 image_ext = os.path.splitext(image_filename)[1]
2130
2131 key_blob = None
2132 if key_path:
2133 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2134 key_blob = encode_rsa_key(key_path)
2135 else:
2136 print 'Verifying image {} using embedded public key'.format(image_filename)
2137
David Zeuthenb623d8b2017-04-04 16:05:53 -04002138 image = ImageHandler(image_filename)
2139 (footer, header, descriptors, image_size) = self._parse_image(image)
2140 offset = 0
2141 if footer:
2142 offset = footer.vbmeta_offset
2143 size = (header.SIZE + header.authentication_data_block_size +
2144 header.auxiliary_data_block_size)
2145 image.seek(offset)
2146 vbmeta_blob = image.read(size)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002147 h = AvbVBMetaHeader(vbmeta_blob[0:AvbVBMetaHeader.SIZE])
2148 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002149 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002150 raise AvbError('Signature check failed for {} vbmeta struct {}'
2151 .format(alg_name, image_filename))
2152
2153 if key_blob:
2154 # The embedded public key is in the auxiliary block at an offset.
2155 key_offset = AvbVBMetaHeader.SIZE
2156 key_offset += h.authentication_data_block_size
2157 key_offset += h.public_key_offset
2158 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + h.public_key_size]
2159 if key_blob != key_blob_in_vbmeta:
2160 raise AvbError('Embedded public key does not match given key.')
2161
2162 if footer:
2163 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2164 .format(alg_name, image_filename))
2165 else:
2166 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
2167 .format(alg_name, image_filename))
2168
2169 for desc in descriptors:
2170 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map):
2171 raise AvbError('Error verifying descriptor.')
2172
David Zeuthenb623d8b2017-04-04 16:05:53 -04002173
David Zeuthen21e95262016-07-27 17:58:40 -04002174 def _parse_image(self, image):
2175 """Gets information about an image.
2176
2177 The image can either be a vbmeta or an image with a footer.
2178
2179 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002180 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002181
2182 Returns:
2183 A tuple where the first argument is a AvbFooter (None if there
2184 is no footer on the image), the second argument is a
2185 AvbVBMetaHeader, the third argument is a list of
2186 AvbDescriptor-derived instances, and the fourth argument is the
2187 size of |image|.
2188 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002189 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002190 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002191 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002192 try:
2193 footer = AvbFooter(image.read(AvbFooter.SIZE))
2194 except (LookupError, struct.error):
2195 # Nope, just seek back to the start.
2196 image.seek(0)
2197
2198 vbmeta_offset = 0
2199 if footer:
2200 vbmeta_offset = footer.vbmeta_offset
2201
2202 image.seek(vbmeta_offset)
2203 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2204
2205 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2206 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2207 desc_start_offset = aux_block_offset + h.descriptors_offset
2208 image.seek(desc_start_offset)
2209 descriptors = parse_descriptors(image.read(h.descriptors_size))
2210
David Zeuthen09692692016-09-30 16:16:40 -04002211 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002212
David Zeuthenb1b994d2017-03-06 18:01:31 -05002213 def _load_vbmeta_blob(self, image):
2214 """Gets the vbmeta struct and associated sections.
2215
2216 The image can either be a vbmeta.img or an image with a footer.
2217
2218 Arguments:
2219 image: An ImageHandler (vbmeta or footer).
2220
2221 Returns:
2222 A blob with the vbmeta struct and other sections.
2223 """
2224 assert isinstance(image, ImageHandler)
2225 footer = None
2226 image.seek(image.image_size - AvbFooter.SIZE)
2227 try:
2228 footer = AvbFooter(image.read(AvbFooter.SIZE))
2229 except (LookupError, struct.error):
2230 # Nope, just seek back to the start.
2231 image.seek(0)
2232
2233 vbmeta_offset = 0
2234 if footer:
2235 vbmeta_offset = footer.vbmeta_offset
2236
2237 image.seek(vbmeta_offset)
2238 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2239
2240 image.seek(vbmeta_offset)
2241 data_size = AvbVBMetaHeader.SIZE
2242 data_size += h.authentication_data_block_size
2243 data_size += h.auxiliary_data_block_size
2244 return image.read(data_size)
2245
David Zeuthen73f2afa2017-05-17 16:54:11 -04002246 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002247 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002248
2249 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002250 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002251
2252 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002253 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2254 instructions. There is one for when hashtree is not disabled and one for
2255 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002256
David Zeuthen21e95262016-07-27 17:58:40 -04002257 """
2258
David Zeuthen21e95262016-07-27 17:58:40 -04002259 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002260 c += '0' # start
2261 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2262 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2263 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2264 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2265 c += ' {}'.format(ht.data_block_size) # data_block
2266 c += ' {}'.format(ht.hash_block_size) # hash_block
2267 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2268 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2269 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2270 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2271 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2272 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002273 c += ' 10' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002274 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002275 c += ' ignore_zero_blocks'
2276 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2277 c += ' fec_roots {}'.format(ht.fec_num_roots)
2278 # Note that fec_blocks is the size that FEC covers, *not* the
2279 # size of the FEC data. Since we use FEC for everything up until
2280 # the FEC data, it's the same as the offset.
2281 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2282 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2283 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002284 c += ' 2' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002285 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002286 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002287 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002288
David Zeuthenfd41eb92016-11-17 12:24:47 -05002289 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002290 desc = AvbKernelCmdlineDescriptor()
2291 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002292 desc.flags = (
2293 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2294
2295 # The descriptor for when hashtree verification is disabled is a lot
2296 # simpler - we just set the root to the partition.
2297 desc_no_ht = AvbKernelCmdlineDescriptor()
2298 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2299 desc_no_ht.flags = (
2300 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2301
2302 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002303
David Zeuthen73f2afa2017-05-17 16:54:11 -04002304 def _get_cmdline_descriptors_for_dm_verity(self, image):
2305 """Generate kernel cmdline descriptors for dm-verity.
2306
2307 Arguments:
2308 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2309
2310 Returns:
2311 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2312 instructions. There is one for when hashtree is not disabled and one for
2313 when it is.
2314
2315 Raises:
2316 AvbError: If |image| doesn't have a hashtree descriptor.
2317
2318 """
2319
2320 (_, _, descriptors, _) = self._parse_image(image)
2321
2322 ht = None
2323 for desc in descriptors:
2324 if isinstance(desc, AvbHashtreeDescriptor):
2325 ht = desc
2326 break
2327
2328 if not ht:
2329 raise AvbError('No hashtree descriptor in given image')
2330
2331 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2332
David Zeuthen21e95262016-07-27 17:58:40 -04002333 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002334 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002335 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002336 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002337 include_descriptors_from_image, signing_helper,
2338 release_string,
2339 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002340 """Implements the 'make_vbmeta_image' command.
2341
2342 Arguments:
2343 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002344 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002345 algorithm_name: Name of algorithm to use.
2346 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002347 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002348 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002349 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002350 props: Properties to insert (list of strings of the form 'key:value').
2351 props_from_file: Properties to insert (list of strings 'key:<path>').
2352 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002353 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002354 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002355 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002356 release_string: None or avbtool release string to use instead of default.
2357 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002358
2359 Raises:
2360 AvbError: If a chained partition is malformed.
2361 """
2362
2363 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002364 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002365 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002366 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002367 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002368 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002369 include_descriptors_from_image, signing_helper, release_string,
2370 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002371
2372 # Write entire vbmeta blob (header, authentication, auxiliary).
2373 output.seek(0)
2374 output.write(vbmeta_blob)
2375
David Zeuthen18666ab2016-11-15 11:18:05 -05002376 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2377 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002378 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002379 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002380 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002381 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002382 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002383 include_descriptors_from_image, signing_helper,
2384 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002385 """Generates a VBMeta blob.
2386
2387 This blob contains the header (struct AvbVBMetaHeader), the
2388 authentication data block (which contains the hash and signature
2389 for the header and auxiliary block), and the auxiliary block
2390 (which contains descriptors, the public key used, and other data).
2391
2392 The |key| parameter can |None| only if the |algorithm_name| is
2393 'NONE'.
2394
2395 Arguments:
2396 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2397 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002398 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002399 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002400 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002401 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002402 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002403 props: Properties to insert (List of strings of the form 'key:value').
2404 props_from_file: Properties to insert (List of strings 'key:<path>').
2405 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002406 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002407 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002408 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2409 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002410 include_descriptors_from_image: List of file objects for which
2411 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002412 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002413 release_string: None or avbtool release string.
2414 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002415
2416 Returns:
2417 A bytearray() with the VBMeta blob.
2418
2419 Raises:
2420 Exception: If the |algorithm_name| is not found, if no key has
2421 been given and the given algorithm requires one, or the key is
2422 of the wrong size.
2423
2424 """
2425 try:
2426 alg = ALGORITHMS[algorithm_name]
2427 except KeyError:
2428 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2429
David Zeuthena5fd3a42017-02-27 16:38:54 -05002430 if not descriptors:
2431 descriptors = []
2432
2433 # Insert chained partition descriptors, if any
2434 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002435 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002436 for cp in chain_partitions:
2437 cp_tokens = cp.split(':')
2438 if len(cp_tokens) != 3:
2439 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002440 partition_name = cp_tokens[0]
2441 rollback_index_location = int(cp_tokens[1])
2442 file_path = cp_tokens[2]
2443 # Check that the same rollback location isn't being used by
2444 # multiple chained partitions.
2445 if used_locations.get(rollback_index_location):
2446 raise AvbError('Rollback Index Location {} is already in use.'.format(
2447 rollback_index_location))
2448 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002449 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002450 desc.partition_name = partition_name
2451 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002452 if desc.rollback_index_location < 1:
2453 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002454 desc.public_key = open(file_path, 'rb').read()
2455 descriptors.append(desc)
2456
David Zeuthen21e95262016-07-27 17:58:40 -04002457 # Descriptors.
2458 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002459 for desc in descriptors:
2460 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002461
2462 # Add properties.
2463 if props:
2464 for prop in props:
2465 idx = prop.find(':')
2466 if idx == -1:
2467 raise AvbError('Malformed property "{}".'.format(prop))
2468 desc = AvbPropertyDescriptor()
2469 desc.key = prop[0:idx]
2470 desc.value = prop[(idx + 1):]
2471 encoded_descriptors.extend(desc.encode())
2472 if props_from_file:
2473 for prop in props_from_file:
2474 idx = prop.find(':')
2475 if idx == -1:
2476 raise AvbError('Malformed property "{}".'.format(prop))
2477 desc = AvbPropertyDescriptor()
2478 desc.key = prop[0:idx]
2479 desc.value = prop[(idx + 1):]
2480 file_path = prop[(idx + 1):]
2481 desc.value = open(file_path, 'rb').read()
2482 encoded_descriptors.extend(desc.encode())
2483
David Zeuthen73f2afa2017-05-17 16:54:11 -04002484 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002485 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002486 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002487 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002488 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2489 encoded_descriptors.extend(cmdline_desc[0].encode())
2490 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002491
David Zeuthen73f2afa2017-05-17 16:54:11 -04002492 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2493 if ht_desc_to_setup:
2494 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2495 ht_desc_to_setup)
2496 encoded_descriptors.extend(cmdline_desc[0].encode())
2497 encoded_descriptors.extend(cmdline_desc[1].encode())
2498
David Zeuthen21e95262016-07-27 17:58:40 -04002499 # Add kernel command-lines.
2500 if kernel_cmdlines:
2501 for i in kernel_cmdlines:
2502 desc = AvbKernelCmdlineDescriptor()
2503 desc.kernel_cmdline = i
2504 encoded_descriptors.extend(desc.encode())
2505
2506 # Add descriptors from other images.
2507 if include_descriptors_from_image:
2508 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002509 image_handler = ImageHandler(image.name)
2510 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002511 for desc in image_descriptors:
2512 encoded_descriptors.extend(desc.encode())
2513
David Zeuthen18666ab2016-11-15 11:18:05 -05002514 # Load public key metadata blob, if requested.
2515 pkmd_blob = []
2516 if public_key_metadata_path:
2517 with open(public_key_metadata_path) as f:
2518 pkmd_blob = f.read()
2519
David Zeuthen21e95262016-07-27 17:58:40 -04002520 key = None
2521 encoded_key = bytearray()
2522 if alg.public_key_num_bytes > 0:
2523 if not key_path:
2524 raise AvbError('Key is required for algorithm {}'.format(
2525 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002526 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002527 if len(encoded_key) != alg.public_key_num_bytes:
2528 raise AvbError('Key is wrong size for algorithm {}'.format(
2529 algorithm_name))
2530
2531 h = AvbVBMetaHeader()
2532
David Zeuthene3cadca2017-02-22 21:25:46 -05002533 # Override release string, if requested.
2534 if isinstance(release_string, (str, unicode)):
2535 h.release_string = release_string
2536
2537 # Append to release string, if requested. Also insert a space before.
2538 if isinstance(append_to_release_string, (str, unicode)):
2539 h.release_string += ' ' + append_to_release_string
2540
David Zeuthen18666ab2016-11-15 11:18:05 -05002541 # For the Auxiliary data block, descriptors are stored at offset 0,
2542 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002543 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002544 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002545 h.descriptors_offset = 0
2546 h.descriptors_size = len(encoded_descriptors)
2547 h.public_key_offset = h.descriptors_size
2548 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002549 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2550 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002551
2552 # For the Authentication data block, the hash is first and then
2553 # the signature.
2554 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002555 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002556 h.algorithm_type = alg.algorithm_type
2557 h.hash_offset = 0
2558 h.hash_size = alg.hash_num_bytes
2559 # Signature offset and size - it's stored right after the hash
2560 # (in Authentication data block).
2561 h.signature_offset = alg.hash_num_bytes
2562 h.signature_size = alg.signature_num_bytes
2563
2564 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002565 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002566
2567 # Generate Header data block.
2568 header_data_blob = h.encode()
2569
2570 # Generate Auxiliary data block.
2571 aux_data_blob = bytearray()
2572 aux_data_blob.extend(encoded_descriptors)
2573 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002574 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002575 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2576 aux_data_blob.extend('\0' * padding_bytes)
2577
2578 # Calculate the hash.
2579 binary_hash = bytearray()
2580 binary_signature = bytearray()
2581 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002582 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002583 ha.update(header_data_blob)
2584 ha.update(aux_data_blob)
2585 binary_hash.extend(ha.digest())
2586
2587 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002588 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002589 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2590 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002591 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002592
2593 # Generate Authentication data block.
2594 auth_data_blob = bytearray()
2595 auth_data_blob.extend(binary_hash)
2596 auth_data_blob.extend(binary_signature)
2597 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2598 auth_data_blob.extend('\0' * padding_bytes)
2599
2600 return header_data_blob + auth_data_blob + aux_data_blob
2601
2602 def extract_public_key(self, key_path, output):
2603 """Implements the 'extract_public_key' command.
2604
2605 Arguments:
2606 key_path: The path to a RSA private key file.
2607 output: The file to write to.
2608 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002609 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002610
David Zeuthenb1b994d2017-03-06 18:01:31 -05002611 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2612 partition_size):
2613 """Implementation of the append_vbmeta_image command.
2614
2615 Arguments:
2616 image_filename: File to add the footer to.
2617 vbmeta_image_filename: File to get vbmeta struct from.
2618 partition_size: Size of partition.
2619
2620 Raises:
2621 AvbError: If an argument is incorrect.
2622 """
2623 image = ImageHandler(image_filename)
2624
2625 if partition_size % image.block_size != 0:
2626 raise AvbError('Partition size of {} is not a multiple of the image '
2627 'block size {}.'.format(partition_size,
2628 image.block_size))
2629
2630 # If there's already a footer, truncate the image to its original
2631 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2632 image.seek(image.image_size - AvbFooter.SIZE)
2633 try:
2634 footer = AvbFooter(image.read(AvbFooter.SIZE))
2635 # Existing footer found. Just truncate.
2636 original_image_size = footer.original_image_size
2637 image.truncate(footer.original_image_size)
2638 except (LookupError, struct.error):
2639 original_image_size = image.image_size
2640
2641 # If anything goes wrong from here-on, restore the image back to
2642 # its original size.
2643 try:
2644 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2645 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2646
2647 # If the image isn't sparse, its size might not be a multiple of
2648 # the block size. This will screw up padding later so just grow it.
2649 if image.image_size % image.block_size != 0:
2650 assert not image.is_sparse
2651 padding_needed = image.block_size - (image.image_size%image.block_size)
2652 image.truncate(image.image_size + padding_needed)
2653
2654 # The append_raw() method requires content with size being a
2655 # multiple of |block_size| so add padding as needed. Also record
2656 # where this is written to since we'll need to put that in the
2657 # footer.
2658 vbmeta_offset = image.image_size
2659 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2660 len(vbmeta_blob))
2661 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2662
2663 # Append vbmeta blob and footer
2664 image.append_raw(vbmeta_blob_with_padding)
2665 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2666
2667 # Now insert a DONT_CARE chunk with enough bytes such that the
2668 # final Footer block is at the end of partition_size..
2669 image.append_dont_care(partition_size - vbmeta_end_offset -
2670 1*image.block_size)
2671
2672 # Generate the Footer that tells where the VBMeta footer
2673 # is. Also put enough padding in the front of the footer since
2674 # we'll write out an entire block.
2675 footer = AvbFooter()
2676 footer.original_image_size = original_image_size
2677 footer.vbmeta_offset = vbmeta_offset
2678 footer.vbmeta_size = len(vbmeta_blob)
2679 footer_blob = footer.encode()
2680 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2681 footer_blob)
2682 image.append_raw(footer_blob_with_padding)
2683
2684 except:
2685 # Truncate back to original size, then re-raise
2686 image.truncate(original_image_size)
2687 raise
2688
David Zeuthena4fee8b2016-08-22 15:20:43 -04002689 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002690 hash_algorithm, salt, chain_partitions, algorithm_name,
2691 key_path,
2692 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002693 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002694 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002695 include_descriptors_from_image, calc_max_image_size,
2696 signing_helper, release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002697 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002698 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002699
2700 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002701 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002702 partition_size: Size of partition.
2703 partition_name: Name of partition (without A/B suffix).
2704 hash_algorithm: Hash algorithm to use.
2705 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002706 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002707 algorithm_name: Name of algorithm to use.
2708 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002709 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002710 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002711 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002712 props: Properties to insert (List of strings of the form 'key:value').
2713 props_from_file: Properties to insert (List of strings 'key:<path>').
2714 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002715 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002716 dm-verity kernel cmdline from.
2717 include_descriptors_from_image: List of file objects for which
2718 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002719 calc_max_image_size: Don't store the footer - instead calculate the
2720 maximum image size leaving enough room for metadata with the
2721 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002722 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002723 release_string: None or avbtool release string.
2724 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002725 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2726 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002727
2728 Raises:
2729 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002730 """
David Zeuthenbf562452017-05-17 18:04:43 -04002731 # First, calculate the maximum image size such that an image
2732 # this size + metadata (footer + vbmeta struct) fits in
2733 # |partition_size|.
2734 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2735 max_image_size = partition_size - max_metadata_size
2736
2737 # If we're asked to only calculate the maximum image size, we're done.
2738 if calc_max_image_size:
2739 print '{}'.format(max_image_size)
2740 return
2741
David Zeuthena4fee8b2016-08-22 15:20:43 -04002742 image = ImageHandler(image_filename)
2743
2744 if partition_size % image.block_size != 0:
2745 raise AvbError('Partition size of {} is not a multiple of the image '
2746 'block size {}.'.format(partition_size,
2747 image.block_size))
2748
David Zeuthen21e95262016-07-27 17:58:40 -04002749 # If there's already a footer, truncate the image to its original
2750 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2751 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002752 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002753 try:
2754 footer = AvbFooter(image.read(AvbFooter.SIZE))
2755 # Existing footer found. Just truncate.
2756 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002757 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002758 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002759 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002760
2761 # If anything goes wrong from here-on, restore the image back to
2762 # its original size.
2763 try:
David Zeuthen09692692016-09-30 16:16:40 -04002764 # If image size exceeds the maximum image size, fail.
2765 if image.image_size > max_image_size:
2766 raise AvbError('Image size of {} exceeds maximum image '
2767 'size of {} in order to fit in a partition '
2768 'size of {}.'.format(image.image_size, max_image_size,
2769 partition_size))
2770
David Zeuthen21e95262016-07-27 17:58:40 -04002771 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2772 if salt:
2773 salt = salt.decode('hex')
2774 else:
2775 if salt is None:
2776 # If salt is not explicitly specified, choose a hash
2777 # that's the same size as the hash size.
2778 hash_size = digest_size
2779 salt = open('/dev/urandom').read(hash_size)
2780 else:
2781 salt = ''
2782
2783 hasher = hashlib.new(name=hash_algorithm, string=salt)
2784 # TODO(zeuthen): might want to read this in chunks to avoid
2785 # memory pressure, then again, this is only supposed to be used
2786 # on kernel/initramfs partitions. Possible optimization.
2787 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002788 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002789 digest = hasher.digest()
2790
2791 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002792 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002793 h_desc.hash_algorithm = hash_algorithm
2794 h_desc.partition_name = partition_name
2795 h_desc.salt = salt
2796 h_desc.digest = digest
2797
2798 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002799 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002800 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002801 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002802 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002803 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002804 include_descriptors_from_image, signing_helper, release_string,
2805 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002806
David Zeuthena4fee8b2016-08-22 15:20:43 -04002807 # If the image isn't sparse, its size might not be a multiple of
2808 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002809 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002810 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002811 padding_needed = image.block_size - (image.image_size%image.block_size)
2812 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002813
David Zeuthena4fee8b2016-08-22 15:20:43 -04002814 # The append_raw() method requires content with size being a
2815 # multiple of |block_size| so add padding as needed. Also record
2816 # where this is written to since we'll need to put that in the
2817 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002818 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002819 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2820 len(vbmeta_blob))
2821 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002822
David Zeuthend247fcb2017-02-16 12:09:27 -05002823 # Write vbmeta blob, if requested.
2824 if output_vbmeta_image:
2825 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002826
David Zeuthend247fcb2017-02-16 12:09:27 -05002827 # Append vbmeta blob and footer, unless requested not to.
2828 if not do_not_append_vbmeta_image:
2829 image.append_raw(vbmeta_blob_with_padding)
2830 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2831
2832 # Now insert a DONT_CARE chunk with enough bytes such that the
2833 # final Footer block is at the end of partition_size..
2834 image.append_dont_care(partition_size - vbmeta_end_offset -
2835 1*image.block_size)
2836
2837 # Generate the Footer that tells where the VBMeta footer
2838 # is. Also put enough padding in the front of the footer since
2839 # we'll write out an entire block.
2840 footer = AvbFooter()
2841 footer.original_image_size = original_image_size
2842 footer.vbmeta_offset = vbmeta_offset
2843 footer.vbmeta_size = len(vbmeta_blob)
2844 footer_blob = footer.encode()
2845 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2846 footer_blob)
2847 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002848
David Zeuthen21e95262016-07-27 17:58:40 -04002849 except:
2850 # Truncate back to original size, then re-raise
2851 image.truncate(original_image_size)
2852 raise
2853
David Zeuthena4fee8b2016-08-22 15:20:43 -04002854 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002855 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002856 block_size, salt, chain_partitions, algorithm_name,
2857 key_path,
2858 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002859 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002860 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002861 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002862 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002863 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002864 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002865 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002866 """Implements the 'add_hashtree_footer' command.
2867
2868 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2869 more information about dm-verity and these hashes.
2870
2871 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002872 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002873 partition_size: Size of partition.
2874 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002875 generate_fec: If True, generate FEC codes.
2876 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002877 hash_algorithm: Hash algorithm to use.
2878 block_size: Block size to use.
2879 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002880 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002881 algorithm_name: Name of algorithm to use.
2882 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002883 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002884 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002885 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002886 props: Properties to insert (List of strings of the form 'key:value').
2887 props_from_file: Properties to insert (List of strings 'key:<path>').
2888 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002889 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002890 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002891 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
2892 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04002893 include_descriptors_from_image: List of file objects for which
2894 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002895 calc_max_image_size: Don't store the hashtree or footer - instead
2896 calculate the maximum image size leaving enough room for hashtree
2897 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002898 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002899 release_string: None or avbtool release string.
2900 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002901 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2902 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002903
2904 Raises:
2905 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002906 """
David Zeuthen09692692016-09-30 16:16:40 -04002907 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2908 digest_padding = round_to_pow2(digest_size) - digest_size
2909
2910 # First, calculate the maximum image size such that an image
2911 # this size + the hashtree + metadata (footer + vbmeta struct)
2912 # fits in |partition_size|. We use very conservative figures for
2913 # metadata.
2914 (_, max_tree_size) = calc_hash_level_offsets(
2915 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002916 max_fec_size = 0
2917 if generate_fec:
2918 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2919 max_metadata_size = (max_fec_size + max_tree_size +
2920 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002921 self.MAX_FOOTER_SIZE)
2922 max_image_size = partition_size - max_metadata_size
2923
2924 # If we're asked to only calculate the maximum image size, we're done.
2925 if calc_max_image_size:
2926 print '{}'.format(max_image_size)
2927 return
2928
David Zeuthena4fee8b2016-08-22 15:20:43 -04002929 image = ImageHandler(image_filename)
2930
2931 if partition_size % image.block_size != 0:
2932 raise AvbError('Partition size of {} is not a multiple of the image '
2933 'block size {}.'.format(partition_size,
2934 image.block_size))
2935
David Zeuthen21e95262016-07-27 17:58:40 -04002936 # If there's already a footer, truncate the image to its original
2937 # size. This way 'avbtool add_hashtree_footer' is idempotent
2938 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002939 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002940 try:
2941 footer = AvbFooter(image.read(AvbFooter.SIZE))
2942 # Existing footer found. Just truncate.
2943 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002944 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002945 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002946 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002947
2948 # If anything goes wrong from here-on, restore the image back to
2949 # its original size.
2950 try:
2951 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002952 rounded_image_size = round_to_multiple(image.image_size, block_size)
2953 if rounded_image_size > image.image_size:
2954 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002955
David Zeuthen09692692016-09-30 16:16:40 -04002956 # If image size exceeds the maximum image size, fail.
2957 if image.image_size > max_image_size:
2958 raise AvbError('Image size of {} exceeds maximum image '
2959 'size of {} in order to fit in a partition '
2960 'size of {}.'.format(image.image_size, max_image_size,
2961 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002962
2963 if salt:
2964 salt = salt.decode('hex')
2965 else:
2966 if salt is None:
2967 # If salt is not explicitly specified, choose a hash
2968 # that's the same size as the hash size.
2969 hash_size = digest_size
2970 salt = open('/dev/urandom').read(hash_size)
2971 else:
2972 salt = ''
2973
David Zeuthena4fee8b2016-08-22 15:20:43 -04002974 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002975 # offsets in advance.
2976 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002977 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002978
David Zeuthena4fee8b2016-08-22 15:20:43 -04002979 # If the image isn't sparse, its size might not be a multiple of
2980 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002981 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002982 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002983 padding_needed = image.block_size - (image.image_size%image.block_size)
2984 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002985
David Zeuthena4fee8b2016-08-22 15:20:43 -04002986 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002987 tree_offset = image.image_size
2988 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002989 block_size,
2990 hash_algorithm, salt,
2991 digest_padding,
2992 hash_level_offsets,
2993 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002994
2995 # Generate HashtreeDescriptor with details about the tree we
2996 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002997 ht_desc = AvbHashtreeDescriptor()
2998 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002999 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003000 ht_desc.tree_offset = tree_offset
3001 ht_desc.tree_size = tree_size
3002 ht_desc.data_block_size = block_size
3003 ht_desc.hash_block_size = block_size
3004 ht_desc.hash_algorithm = hash_algorithm
3005 ht_desc.partition_name = partition_name
3006 ht_desc.salt = salt
3007 ht_desc.root_digest = root_digest
3008
David Zeuthen09692692016-09-30 16:16:40 -04003009 # Write the hash tree
3010 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3011 len(hash_tree))
3012 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3013 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003014 len_hashtree_and_fec = len(hash_tree_with_padding)
3015
3016 # Generate FEC codes, if requested.
3017 if generate_fec:
3018 fec_data = generate_fec_data(image_filename, fec_num_roots)
3019 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3020 len(fec_data))
3021 fec_data_with_padding = fec_data + '\0'*padding_needed
3022 fec_offset = image.image_size
3023 image.append_raw(fec_data_with_padding)
3024 len_hashtree_and_fec += len(fec_data_with_padding)
3025 # Update the hashtree descriptor.
3026 ht_desc.fec_num_roots = fec_num_roots
3027 ht_desc.fec_offset = fec_offset
3028 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003029
David Zeuthen73f2afa2017-05-17 16:54:11 -04003030 ht_desc_to_setup = None
3031 if setup_as_rootfs_from_kernel:
3032 ht_desc_to_setup = ht_desc
3033
David Zeuthena4fee8b2016-08-22 15:20:43 -04003034 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003035 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003036 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003037 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003038 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003039 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003040 include_descriptors_from_image, signing_helper, release_string,
3041 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003042 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3043 len(vbmeta_blob))
3044 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003045
David Zeuthend247fcb2017-02-16 12:09:27 -05003046 # Write vbmeta blob, if requested.
3047 if output_vbmeta_image:
3048 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003049
David Zeuthend247fcb2017-02-16 12:09:27 -05003050 # Append vbmeta blob and footer, unless requested not to.
3051 if not do_not_append_vbmeta_image:
3052 image.append_raw(vbmeta_blob_with_padding)
3053
3054 # Now insert a DONT_CARE chunk with enough bytes such that the
3055 # final Footer block is at the end of partition_size..
3056 image.append_dont_care(partition_size - image.image_size -
3057 1*image.block_size)
3058
3059 # Generate the Footer that tells where the VBMeta footer
3060 # is. Also put enough padding in the front of the footer since
3061 # we'll write out an entire block.
3062 footer = AvbFooter()
3063 footer.original_image_size = original_image_size
3064 footer.vbmeta_offset = vbmeta_offset
3065 footer.vbmeta_size = len(vbmeta_blob)
3066 footer_blob = footer.encode()
3067 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3068 footer_blob)
3069 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003070
David Zeuthen21e95262016-07-27 17:58:40 -04003071 except:
David Zeuthen09692692016-09-30 16:16:40 -04003072 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003073 image.truncate(original_image_size)
3074 raise
3075
David Zeuthenc68f0822017-03-31 17:22:35 -04003076 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003077 subject_key_version, subject,
3078 is_intermediate_authority, signing_helper):
3079 """Implements the 'make_atx_certificate' command.
3080
3081 Android Things certificates are required for Android Things public key
3082 metadata. They chain the vbmeta signing key for a particular product back to
3083 a fused, permanent root key. These certificates are fixed-length and fixed-
3084 format with the explicit goal of not parsing ASN.1 in bootloader code.
3085
3086 Arguments:
3087 output: Certificate will be written to this file on success.
3088 authority_key_path: A PEM file path with the authority private key.
3089 If None, then a certificate will be created without a
3090 signature. The signature can be created out-of-band
3091 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003092 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003093 subject_key_version: A 64-bit version value. If this is None, the number
3094 of seconds since the epoch is used.
3095 subject: A subject identifier. For Product Signing Key certificates this
3096 should be the same Product ID found in the permanent attributes.
3097 is_intermediate_authority: True if the certificate is for an intermediate
3098 authority.
3099 signing_helper: Program which signs a hash and returns the signature.
3100 """
3101 signed_data = bytearray()
3102 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003103 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003104 hasher = hashlib.sha256()
3105 hasher.update(subject)
3106 signed_data.extend(hasher.digest())
3107 usage = 'com.google.android.things.vboot'
3108 if is_intermediate_authority:
3109 usage += '.ca'
3110 hasher = hashlib.sha256()
3111 hasher.update(usage)
3112 signed_data.extend(hasher.digest())
3113 if not subject_key_version:
3114 subject_key_version = int(time.time())
3115 signed_data.extend(struct.pack('<Q', subject_key_version))
3116 signature = bytearray()
3117 if authority_key_path:
3118 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003119 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003120 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003121 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003122 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003123 hasher.update(signed_data)
3124 padding_and_hash.extend(hasher.digest())
3125 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003126 alg.signature_num_bytes, authority_key_path,
3127 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003128 output.write(signed_data)
3129 output.write(signature)
3130
David Zeuthenc68f0822017-03-31 17:22:35 -04003131 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003132 product_id):
3133 """Implements the 'make_atx_permanent_attributes' command.
3134
3135 Android Things permanent attributes are designed to be permanent for a
3136 particular product and a hash of these attributes should be fused into
3137 hardware to enforce this.
3138
3139 Arguments:
3140 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003141 root_authority_key_path: Path to a PEM or DER public key for
3142 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003143 product_id: A 16-byte Product ID.
3144
3145 Raises:
3146 AvbError: If an argument is incorrect.
3147 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003148 EXPECTED_PRODUCT_ID_SIZE = 16
3149 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003150 raise AvbError('Invalid Product ID length.')
3151 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003152 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003153 output.write(product_id)
3154
3155 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003156 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003157 """Implements the 'make_atx_metadata' command.
3158
3159 Android Things metadata are included in vbmeta images to facilitate
3160 verification. The output of this command can be used as the
3161 public_key_metadata argument to other commands.
3162
3163 Arguments:
3164 output: Metadata will be written to this file on success.
3165 intermediate_key_certificate: A certificate file as output by
3166 make_atx_certificate with
3167 is_intermediate_authority set to true.
3168 product_key_certificate: A certificate file as output by
3169 make_atx_certificate with
3170 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003171
3172 Raises:
3173 AvbError: If an argument is incorrect.
3174 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003175 EXPECTED_CERTIFICATE_SIZE = 1620
3176 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003177 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003178 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003179 raise AvbError('Invalid product key certificate length.')
3180 output.write(struct.pack('<I', 1)) # Format Version
3181 output.write(intermediate_key_certificate)
3182 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003183
David Zeuthen21e95262016-07-27 17:58:40 -04003184
3185def calc_hash_level_offsets(image_size, block_size, digest_size):
3186 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3187
3188 Arguments:
3189 image_size: The size of the image to calculate a Merkle-tree for.
3190 block_size: The block size, e.g. 4096.
3191 digest_size: The size of each hash, e.g. 32 for SHA-256.
3192
3193 Returns:
3194 A tuple where the first argument is an array of offsets and the
3195 second is size of the tree, in bytes.
3196 """
3197 level_offsets = []
3198 level_sizes = []
3199 tree_size = 0
3200
3201 num_levels = 0
3202 size = image_size
3203 while size > block_size:
3204 num_blocks = (size + block_size - 1) / block_size
3205 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3206
3207 level_sizes.append(level_size)
3208 tree_size += level_size
3209 num_levels += 1
3210
3211 size = level_size
3212
3213 for n in range(0, num_levels):
3214 offset = 0
3215 for m in range(n + 1, num_levels):
3216 offset += level_sizes[m]
3217 level_offsets.append(offset)
3218
David Zeuthena4fee8b2016-08-22 15:20:43 -04003219 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003220
3221
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003222# See system/extras/libfec/include/fec/io.h for these definitions.
3223FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3224FEC_MAGIC = 0xfecfecfe
3225
3226
3227def calc_fec_data_size(image_size, num_roots):
3228 """Calculates how much space FEC data will take.
3229
3230 Args:
3231 image_size: The size of the image.
3232 num_roots: Number of roots.
3233
3234 Returns:
3235 The number of bytes needed for FEC for an image of the given size
3236 and with the requested number of FEC roots.
3237
3238 Raises:
3239 ValueError: If output from the 'fec' tool is invalid.
3240
3241 """
3242 p = subprocess.Popen(
3243 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3244 stdout=subprocess.PIPE,
3245 stderr=subprocess.PIPE)
3246 (pout, perr) = p.communicate()
3247 retcode = p.wait()
3248 if retcode != 0:
3249 raise ValueError('Error invoking fec: {}'.format(perr))
3250 return int(pout)
3251
3252
3253def generate_fec_data(image_filename, num_roots):
3254 """Generate FEC codes for an image.
3255
3256 Args:
3257 image_filename: The filename of the image.
3258 num_roots: Number of roots.
3259
3260 Returns:
3261 The FEC data blob.
3262
3263 Raises:
3264 ValueError: If output from the 'fec' tool is invalid.
3265 """
3266 fec_tmpfile = tempfile.NamedTemporaryFile()
3267 subprocess.check_call(
3268 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3269 fec_tmpfile.name],
3270 stderr=open(os.devnull))
3271 fec_data = fec_tmpfile.read()
3272 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3273 footer_data = fec_data[-footer_size:]
3274 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3275 footer_data)
3276 if magic != FEC_MAGIC:
3277 raise ValueError('Unexpected magic in FEC footer')
3278 return fec_data[0:fec_size]
3279
3280
David Zeuthen21e95262016-07-27 17:58:40 -04003281def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003282 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003283 """Generates a Merkle-tree for a file.
3284
3285 Args:
3286 image: The image, as a file.
3287 image_size: The size of the image.
3288 block_size: The block size, e.g. 4096.
3289 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3290 salt: The salt to use.
3291 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003292 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003293 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003294
3295 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003296 A tuple where the first element is the top-level hash and the
3297 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003298 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003299 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003300 hash_src_offset = 0
3301 hash_src_size = image_size
3302 level_num = 0
3303 while hash_src_size > block_size:
3304 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003305 remaining = hash_src_size
3306 while remaining > 0:
3307 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003308 # Only read from the file for the first level - for subsequent
3309 # levels, access the array we're building.
3310 if level_num == 0:
3311 image.seek(hash_src_offset + hash_src_size - remaining)
3312 data = image.read(min(remaining, block_size))
3313 else:
3314 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3315 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003316 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003317
3318 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003319 if len(data) < block_size:
3320 hasher.update('\0' * (block_size - len(data)))
3321 level_output += hasher.digest()
3322 if digest_padding > 0:
3323 level_output += '\0' * digest_padding
3324
3325 padding_needed = (round_to_multiple(
3326 len(level_output), block_size) - len(level_output))
3327 level_output += '\0' * padding_needed
3328
David Zeuthena4fee8b2016-08-22 15:20:43 -04003329 # Copy level-output into resulting tree.
3330 offset = hash_level_offsets[level_num]
3331 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003332
David Zeuthena4fee8b2016-08-22 15:20:43 -04003333 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003334 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003335 level_num += 1
3336
3337 hasher = hashlib.new(name=hash_alg_name, string=salt)
3338 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003339 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003340
3341
3342class AvbTool(object):
3343 """Object for avbtool command-line tool."""
3344
3345 def __init__(self):
3346 """Initializer method."""
3347 self.avb = Avb()
3348
3349 def _add_common_args(self, sub_parser):
3350 """Adds arguments used by several sub-commands.
3351
3352 Arguments:
3353 sub_parser: The parser to add arguments to.
3354 """
3355 sub_parser.add_argument('--algorithm',
3356 help='Algorithm to use (default: NONE)',
3357 metavar='ALGORITHM',
3358 default='NONE')
3359 sub_parser.add_argument('--key',
3360 help='Path to RSA private key file',
3361 metavar='KEY',
3362 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003363 sub_parser.add_argument('--signing_helper',
3364 help='Path to helper used for signing',
3365 metavar='APP',
3366 default=None,
3367 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003368 sub_parser.add_argument('--public_key_metadata',
3369 help='Path to public key metadata file',
3370 metavar='KEY_METADATA',
3371 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003372 sub_parser.add_argument('--rollback_index',
3373 help='Rollback Index',
3374 type=parse_number,
3375 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003376 # This is used internally for unit tests. Do not include in --help output.
3377 sub_parser.add_argument('--internal_release_string',
3378 help=argparse.SUPPRESS)
3379 sub_parser.add_argument('--append_to_release_string',
3380 help='Text to append to release string',
3381 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003382 sub_parser.add_argument('--prop',
3383 help='Add property',
3384 metavar='KEY:VALUE',
3385 action='append')
3386 sub_parser.add_argument('--prop_from_file',
3387 help='Add property from file',
3388 metavar='KEY:PATH',
3389 action='append')
3390 sub_parser.add_argument('--kernel_cmdline',
3391 help='Add kernel cmdline',
3392 metavar='CMDLINE',
3393 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003394 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3395 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3396 # at some future point.
3397 sub_parser.add_argument('--setup_rootfs_from_kernel',
3398 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003399 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003400 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003401 type=argparse.FileType('rb'))
3402 sub_parser.add_argument('--include_descriptors_from_image',
3403 help='Include descriptors from image',
3404 metavar='IMAGE',
3405 action='append',
3406 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05003407 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3408 sub_parser.add_argument('--chain_partition',
3409 help='Allow signed integrity-data for partition',
3410 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3411 action='append')
3412 sub_parser.add_argument('--flags',
3413 help='VBMeta flags',
3414 type=parse_number,
3415 default=0)
3416 sub_parser.add_argument('--set_hashtree_disabled_flag',
3417 help='Set the HASHTREE_DISABLED flag',
3418 action='store_true')
3419
3420 def _fixup_common_args(self, args):
3421 """Common fixups needed by subcommands.
3422
3423 Arguments:
3424 args: Arguments to modify.
3425
3426 Returns:
3427 The modified arguments.
3428 """
3429 if args.set_hashtree_disabled_flag:
3430 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3431 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003432
3433 def run(self, argv):
3434 """Command-line processor.
3435
3436 Arguments:
3437 argv: Pass sys.argv from main.
3438 """
3439 parser = argparse.ArgumentParser()
3440 subparsers = parser.add_subparsers(title='subcommands')
3441
3442 sub_parser = subparsers.add_parser('version',
3443 help='Prints version of avbtool.')
3444 sub_parser.set_defaults(func=self.version)
3445
3446 sub_parser = subparsers.add_parser('extract_public_key',
3447 help='Extract public key.')
3448 sub_parser.add_argument('--key',
3449 help='Path to RSA private key file',
3450 required=True)
3451 sub_parser.add_argument('--output',
3452 help='Output file name',
3453 type=argparse.FileType('wb'),
3454 required=True)
3455 sub_parser.set_defaults(func=self.extract_public_key)
3456
3457 sub_parser = subparsers.add_parser('make_vbmeta_image',
3458 help='Makes a vbmeta image.')
3459 sub_parser.add_argument('--output',
3460 help='Output file name',
3461 type=argparse.FileType('wb'),
3462 required=True)
3463 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003464 sub_parser.set_defaults(func=self.make_vbmeta_image)
3465
3466 sub_parser = subparsers.add_parser('add_hash_footer',
3467 help='Add hashes and footer to image.')
3468 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003469 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003470 type=argparse.FileType('rab+'))
3471 sub_parser.add_argument('--partition_size',
3472 help='Partition size',
3473 type=parse_number,
3474 required=True)
3475 sub_parser.add_argument('--partition_name',
3476 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003477 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003478 sub_parser.add_argument('--hash_algorithm',
3479 help='Hash algorithm to use (default: sha256)',
3480 default='sha256')
3481 sub_parser.add_argument('--salt',
3482 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003483 sub_parser.add_argument('--calc_max_image_size',
3484 help=('Don\'t store the footer - '
3485 'instead calculate the maximum image size '
3486 'leaving enough room for metadata with '
3487 'the given partition size.'),
3488 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003489 sub_parser.add_argument('--output_vbmeta_image',
3490 help='Also write vbmeta struct to file',
3491 type=argparse.FileType('wb'))
3492 sub_parser.add_argument('--do_not_append_vbmeta_image',
3493 help=('Do not append vbmeta struct or footer '
3494 'to the image'),
3495 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003496 self._add_common_args(sub_parser)
3497 sub_parser.set_defaults(func=self.add_hash_footer)
3498
David Zeuthenb1b994d2017-03-06 18:01:31 -05003499 sub_parser = subparsers.add_parser('append_vbmeta_image',
3500 help='Append vbmeta image to image.')
3501 sub_parser.add_argument('--image',
3502 help='Image to append vbmeta blob to',
3503 type=argparse.FileType('rab+'))
3504 sub_parser.add_argument('--partition_size',
3505 help='Partition size',
3506 type=parse_number,
3507 required=True)
3508 sub_parser.add_argument('--vbmeta_image',
3509 help='Image with vbmeta blob to append',
3510 type=argparse.FileType('rb'))
3511 sub_parser.set_defaults(func=self.append_vbmeta_image)
3512
David Zeuthen21e95262016-07-27 17:58:40 -04003513 sub_parser = subparsers.add_parser('add_hashtree_footer',
3514 help='Add hashtree and footer to image.')
3515 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003516 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003517 type=argparse.FileType('rab+'))
3518 sub_parser.add_argument('--partition_size',
3519 help='Partition size',
3520 type=parse_number,
3521 required=True)
3522 sub_parser.add_argument('--partition_name',
3523 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003524 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003525 sub_parser.add_argument('--hash_algorithm',
3526 help='Hash algorithm to use (default: sha1)',
3527 default='sha1')
3528 sub_parser.add_argument('--salt',
3529 help='Salt in hex (default: /dev/urandom)')
3530 sub_parser.add_argument('--block_size',
3531 help='Block size (default: 4096)',
3532 type=parse_number,
3533 default=4096)
David Zeuthen02c550f2017-05-10 17:18:04 -04003534 # TODO(zeuthen): The --generate_fec option was removed when we
3535 # moved to generating FEC by default. To avoid breaking existing
3536 # users needing to transition we simply just print a warning below
3537 # in add_hashtree_footer(). Remove this option and the warning at
3538 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003539 sub_parser.add_argument('--generate_fec',
David Zeuthen02c550f2017-05-10 17:18:04 -04003540 help=argparse.SUPPRESS,
3541 action='store_true')
3542 sub_parser.add_argument('--do_not_generate_fec',
3543 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003544 action='store_true')
3545 sub_parser.add_argument('--fec_num_roots',
3546 help='Number of roots for FEC (default: 2)',
3547 type=parse_number,
3548 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003549 sub_parser.add_argument('--calc_max_image_size',
3550 help=('Don\'t store the hashtree or footer - '
3551 'instead calculate the maximum image size '
3552 'leaving enough room for hashtree '
3553 'and metadata with the given partition '
3554 'size.'),
3555 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003556 sub_parser.add_argument('--output_vbmeta_image',
3557 help='Also write vbmeta struct to file',
3558 type=argparse.FileType('wb'))
3559 sub_parser.add_argument('--do_not_append_vbmeta_image',
3560 help=('Do not append vbmeta struct or footer '
3561 'to the image'),
3562 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003563 # This is different from --setup_rootfs_from_kernel insofar that
3564 # it doesn't take an IMAGE, the generated cmdline will be for the
3565 # hashtree we're adding.
3566 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3567 action='store_true',
3568 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003569 self._add_common_args(sub_parser)
3570 sub_parser.set_defaults(func=self.add_hashtree_footer)
3571
3572 sub_parser = subparsers.add_parser('erase_footer',
3573 help='Erase footer from an image.')
3574 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003575 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003576 type=argparse.FileType('rwb+'),
3577 required=True)
3578 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003579 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003580 action='store_true')
3581 sub_parser.set_defaults(func=self.erase_footer)
3582
David Zeuthen2bc232b2017-04-19 14:25:19 -04003583 sub_parser = subparsers.add_parser('resize_image',
3584 help='Resize image with a footer.')
3585 sub_parser.add_argument('--image',
3586 help='Image with a footer',
3587 type=argparse.FileType('rwb+'),
3588 required=True)
3589 sub_parser.add_argument('--partition_size',
3590 help='New partition size',
3591 type=parse_number)
3592 sub_parser.set_defaults(func=self.resize_image)
3593
David Zeuthen21e95262016-07-27 17:58:40 -04003594 sub_parser = subparsers.add_parser(
3595 'info_image',
3596 help='Show information about vbmeta or footer.')
3597 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003598 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003599 type=argparse.FileType('rb'),
3600 required=True)
3601 sub_parser.add_argument('--output',
3602 help='Write info to file',
3603 type=argparse.FileType('wt'),
3604 default=sys.stdout)
3605 sub_parser.set_defaults(func=self.info_image)
3606
David Zeuthenb623d8b2017-04-04 16:05:53 -04003607 sub_parser = subparsers.add_parser(
3608 'verify_image',
3609 help='Verify an image.')
3610 sub_parser.add_argument('--image',
3611 help='Image to verify',
3612 type=argparse.FileType('rb'),
3613 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003614 sub_parser.add_argument('--key',
3615 help='Check embedded public key matches KEY',
3616 metavar='KEY',
3617 required=False)
3618 sub_parser.add_argument('--expected_chain_partition',
3619 help='Expected chain partition',
3620 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3621 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04003622 sub_parser.set_defaults(func=self.verify_image)
3623
David Zeuthen8b6973b2016-09-20 12:39:49 -04003624 sub_parser = subparsers.add_parser('set_ab_metadata',
3625 help='Set A/B metadata.')
3626 sub_parser.add_argument('--misc_image',
3627 help=('The misc image to modify. If the image does '
3628 'not exist, it will be created.'),
3629 type=argparse.FileType('r+b'),
3630 required=True)
3631 sub_parser.add_argument('--slot_data',
3632 help=('Slot data of the form "priority", '
3633 '"tries_remaining", "sucessful_boot" for '
3634 'slot A followed by the same for slot B, '
3635 'separated by colons. The default value '
3636 'is 15:7:0:14:7:0.'),
3637 default='15:7:0:14:7:0')
3638 sub_parser.set_defaults(func=self.set_ab_metadata)
3639
Darren Krahn147b08d2016-12-20 16:38:29 -08003640 sub_parser = subparsers.add_parser(
3641 'make_atx_certificate',
3642 help='Create an Android Things eXtension (ATX) certificate.')
3643 sub_parser.add_argument('--output',
3644 help='Write certificate to file',
3645 type=argparse.FileType('wb'),
3646 default=sys.stdout)
3647 sub_parser.add_argument('--subject',
3648 help=('Path to subject file'),
3649 type=argparse.FileType('rb'),
3650 required=True)
3651 sub_parser.add_argument('--subject_key',
3652 help=('Path to subject RSA public key file'),
3653 type=argparse.FileType('rb'),
3654 required=True)
3655 sub_parser.add_argument('--subject_key_version',
3656 help=('Version of the subject key'),
3657 type=parse_number,
3658 required=False)
3659 sub_parser.add_argument('--subject_is_intermediate_authority',
3660 help=('Generate an intermediate authority '
3661 'certificate'),
3662 action='store_true')
3663 sub_parser.add_argument('--authority_key',
3664 help='Path to authority RSA private key file',
3665 required=False)
3666 sub_parser.add_argument('--signing_helper',
3667 help='Path to helper used for signing',
3668 metavar='APP',
3669 default=None,
3670 required=False)
3671 sub_parser.set_defaults(func=self.make_atx_certificate)
3672
3673 sub_parser = subparsers.add_parser(
3674 'make_atx_permanent_attributes',
3675 help='Create Android Things eXtension (ATX) permanent attributes.')
3676 sub_parser.add_argument('--output',
3677 help='Write attributes to file',
3678 type=argparse.FileType('wb'),
3679 default=sys.stdout)
3680 sub_parser.add_argument('--root_authority_key',
3681 help='Path to authority RSA public key file',
3682 type=argparse.FileType('rb'),
3683 required=True)
3684 sub_parser.add_argument('--product_id',
3685 help=('Path to Product ID file'),
3686 type=argparse.FileType('rb'),
3687 required=True)
3688 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3689
3690 sub_parser = subparsers.add_parser(
3691 'make_atx_metadata',
3692 help='Create Android Things eXtension (ATX) metadata.')
3693 sub_parser.add_argument('--output',
3694 help='Write metadata to file',
3695 type=argparse.FileType('wb'),
3696 default=sys.stdout)
3697 sub_parser.add_argument('--intermediate_key_certificate',
3698 help='Path to intermediate key certificate file',
3699 type=argparse.FileType('rb'),
3700 required=True)
3701 sub_parser.add_argument('--product_key_certificate',
3702 help='Path to product key certificate file',
3703 type=argparse.FileType('rb'),
3704 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003705 sub_parser.set_defaults(func=self.make_atx_metadata)
3706
David Zeuthen21e95262016-07-27 17:58:40 -04003707 args = parser.parse_args(argv[1:])
3708 try:
3709 args.func(args)
3710 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003711 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003712 sys.exit(1)
3713
3714 def version(self, _):
3715 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003716 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003717
3718 def extract_public_key(self, args):
3719 """Implements the 'extract_public_key' sub-command."""
3720 self.avb.extract_public_key(args.key, args.output)
3721
3722 def make_vbmeta_image(self, args):
3723 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003724 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003725 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003726 args.algorithm, args.key,
3727 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003728 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003729 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003730 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003731 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003732 args.signing_helper,
3733 args.internal_release_string,
3734 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003735
David Zeuthenb1b994d2017-03-06 18:01:31 -05003736 def append_vbmeta_image(self, args):
3737 """Implements the 'append_vbmeta_image' sub-command."""
3738 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3739 args.partition_size)
3740
David Zeuthen21e95262016-07-27 17:58:40 -04003741 def add_hash_footer(self, args):
3742 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003743 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04003744 self.avb.add_hash_footer(args.image.name if args.image else None,
3745 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003746 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003747 args.salt, args.chain_partition, args.algorithm,
3748 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003749 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003750 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003751 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003752 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003753 args.include_descriptors_from_image,
David Zeuthenbf562452017-05-17 18:04:43 -04003754 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003755 args.internal_release_string,
3756 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003757 args.output_vbmeta_image,
3758 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003759
3760 def add_hashtree_footer(self, args):
3761 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003762 args = self._fixup_common_args(args)
David Zeuthen02c550f2017-05-10 17:18:04 -04003763 # TODO(zeuthen): Remove when removing support for the
3764 # '--generate_fec' option above.
3765 if args.generate_fec:
3766 sys.stderr.write('The --generate_fec option is deprecated since FEC '
3767 'is now generated by default. Use the option '
3768 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04003769 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3770 args.partition_size,
3771 args.partition_name,
David Zeuthen02c550f2017-05-10 17:18:04 -04003772 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003773 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003774 args.salt, args.chain_partition, args.algorithm,
3775 args.key, args.public_key_metadata,
3776 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003777 args.prop_from_file,
3778 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003779 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003780 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003781 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003782 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003783 args.internal_release_string,
3784 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003785 args.output_vbmeta_image,
3786 args.do_not_append_vbmeta_image)
3787
David Zeuthen21e95262016-07-27 17:58:40 -04003788 def erase_footer(self, args):
3789 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003790 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003791
David Zeuthen2bc232b2017-04-19 14:25:19 -04003792 def resize_image(self, args):
3793 """Implements the 'resize_image' sub-command."""
3794 self.avb.resize_image(args.image.name, args.partition_size)
3795
David Zeuthen8b6973b2016-09-20 12:39:49 -04003796 def set_ab_metadata(self, args):
3797 """Implements the 'set_ab_metadata' sub-command."""
3798 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3799
David Zeuthen21e95262016-07-27 17:58:40 -04003800 def info_image(self, args):
3801 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003802 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003803
David Zeuthenb623d8b2017-04-04 16:05:53 -04003804 def verify_image(self, args):
3805 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003806 self.avb.verify_image(args.image.name, args.key,
3807 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04003808
Darren Krahn147b08d2016-12-20 16:38:29 -08003809 def make_atx_certificate(self, args):
3810 """Implements the 'make_atx_certificate' sub-command."""
3811 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003812 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003813 args.subject_key_version,
3814 args.subject.read(),
3815 args.subject_is_intermediate_authority,
3816 args.signing_helper)
3817
3818 def make_atx_permanent_attributes(self, args):
3819 """Implements the 'make_atx_permanent_attributes' sub-command."""
3820 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003821 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003822 args.product_id.read())
3823
3824 def make_atx_metadata(self, args):
3825 """Implements the 'make_atx_metadata' sub-command."""
3826 self.avb.make_atx_metadata(args.output,
3827 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003828 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003829
David Zeuthen21e95262016-07-27 17:58:40 -04003830
3831if __name__ == '__main__':
3832 tool = AvbTool()
3833 tool.run(sys.argv)