blob: af5cccef92821868507647372b64f1d3775690f8 [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
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080041AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080044# Keep in sync with libavb/avb_footer.h.
45AVB_FOOTER_VERSION_MAJOR = 1
46AVB_FOOTER_VERSION_MINOR = 0
47
David Zeuthen58305522017-01-11 17:42:47 -050048AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040049
David Zeuthene3cadca2017-02-22 21:25:46 -050050
David Zeuthen21e95262016-07-27 17:58:40 -040051class AvbError(Exception):
52 """Application-specific errors.
53
54 These errors represent issues for which a stack-trace should not be
55 presented.
56
57 Attributes:
58 message: Error message.
59 """
60
61 def __init__(self, message):
62 Exception.__init__(self, message)
63
64
65class Algorithm(object):
66 """Contains details about an algorithm.
67
68 See the avb_vbmeta_header.h file for more details about
69 algorithms.
70
71 The constant |ALGORITHMS| is a dictionary from human-readable
72 names (e.g 'SHA256_RSA2048') to instances of this class.
73
74 Attributes:
75 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040076 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040077 hash_num_bytes: Number of bytes used to store the hash.
78 signature_num_bytes: Number of bytes used to store the signature.
79 public_key_num_bytes: Number of bytes used to store the public key.
80 padding: Padding used for signature, if any.
81 """
82
David Zeuthenb623d8b2017-04-04 16:05:53 -040083 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
84 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040085 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040086 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040087 self.hash_num_bytes = hash_num_bytes
88 self.signature_num_bytes = signature_num_bytes
89 self.public_key_num_bytes = public_key_num_bytes
90 self.padding = padding
91
David Zeuthenb623d8b2017-04-04 16:05:53 -040092
David Zeuthen21e95262016-07-27 17:58:40 -040093# This must be kept in sync with the avb_crypto.h file.
94#
95# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
96# obtained from section 5.2.2 of RFC 4880.
97ALGORITHMS = {
98 'NONE': Algorithm(
99 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400100 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400101 hash_num_bytes=0,
102 signature_num_bytes=0,
103 public_key_num_bytes=0,
104 padding=[]),
105 'SHA256_RSA2048': Algorithm(
106 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400107 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400108 hash_num_bytes=32,
109 signature_num_bytes=256,
110 public_key_num_bytes=8 + 2*2048/8,
111 padding=[
112 # PKCS1-v1_5 padding
113 0x00, 0x01] + [0xff]*202 + [0x00] + [
114 # ASN.1 header
115 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
116 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
117 0x00, 0x04, 0x20,
118 ]),
119 'SHA256_RSA4096': Algorithm(
120 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400121 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400122 hash_num_bytes=32,
123 signature_num_bytes=512,
124 public_key_num_bytes=8 + 2*4096/8,
125 padding=[
126 # PKCS1-v1_5 padding
127 0x00, 0x01] + [0xff]*458 + [0x00] + [
128 # ASN.1 header
129 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
130 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
131 0x00, 0x04, 0x20,
132 ]),
133 'SHA256_RSA8192': Algorithm(
134 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400135 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400136 hash_num_bytes=32,
137 signature_num_bytes=1024,
138 public_key_num_bytes=8 + 2*8192/8,
139 padding=[
140 # PKCS1-v1_5 padding
141 0x00, 0x01] + [0xff]*970 + [0x00] + [
142 # ASN.1 header
143 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
144 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
145 0x00, 0x04, 0x20,
146 ]),
147 'SHA512_RSA2048': Algorithm(
148 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400149 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400150 hash_num_bytes=64,
151 signature_num_bytes=256,
152 public_key_num_bytes=8 + 2*2048/8,
153 padding=[
154 # PKCS1-v1_5 padding
155 0x00, 0x01] + [0xff]*170 + [0x00] + [
156 # ASN.1 header
157 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
158 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
159 0x00, 0x04, 0x40
160 ]),
161 'SHA512_RSA4096': Algorithm(
162 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400163 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400164 hash_num_bytes=64,
165 signature_num_bytes=512,
166 public_key_num_bytes=8 + 2*4096/8,
167 padding=[
168 # PKCS1-v1_5 padding
169 0x00, 0x01] + [0xff]*426 + [0x00] + [
170 # ASN.1 header
171 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
172 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
173 0x00, 0x04, 0x40
174 ]),
175 'SHA512_RSA8192': Algorithm(
176 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400177 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400178 hash_num_bytes=64,
179 signature_num_bytes=1024,
180 public_key_num_bytes=8 + 2*8192/8,
181 padding=[
182 # PKCS1-v1_5 padding
183 0x00, 0x01] + [0xff]*938 + [0x00] + [
184 # ASN.1 header
185 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
186 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
187 0x00, 0x04, 0x40
188 ]),
189}
190
191
David Zeuthene3cadca2017-02-22 21:25:46 -0500192def get_release_string():
193 """Calculates the release string to use in the VBMeta struct."""
194 # Keep in sync with libavb/avb_version.c:avb_version_string().
195 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
196 AVB_VERSION_MINOR,
197 AVB_VERSION_SUB)
198
199
David Zeuthen21e95262016-07-27 17:58:40 -0400200def round_to_multiple(number, size):
201 """Rounds a number up to nearest multiple of another number.
202
203 Args:
204 number: The number to round up.
205 size: The multiple to round up to.
206
207 Returns:
208 If |number| is a multiple of |size|, returns |number|, otherwise
209 returns |number| + |size|.
210 """
211 remainder = number % size
212 if remainder == 0:
213 return number
214 return number + size - remainder
215
216
217def round_to_pow2(number):
218 """Rounds a number up to the next power of 2.
219
220 Args:
221 number: The number to round up.
222
223 Returns:
224 If |number| is already a power of 2 then |number| is
225 returned. Otherwise the smallest power of 2 greater than |number|
226 is returned.
227 """
228 return 2**((number - 1).bit_length())
229
230
David Zeuthen21e95262016-07-27 17:58:40 -0400231def encode_long(num_bits, value):
232 """Encodes a long to a bytearray() using a given amount of bits.
233
234 This number is written big-endian, e.g. with the most significant
235 bit first.
236
David Zeuthenb623d8b2017-04-04 16:05:53 -0400237 This is the reverse of decode_long().
238
David Zeuthen21e95262016-07-27 17:58:40 -0400239 Arguments:
240 num_bits: The number of bits to write, e.g. 2048.
241 value: The value to write.
242
243 Returns:
244 A bytearray() with the encoded long.
245 """
246 ret = bytearray()
247 for bit_pos in range(num_bits, 0, -8):
248 octet = (value >> (bit_pos - 8)) & 0xff
249 ret.extend(struct.pack('!B', octet))
250 return ret
251
252
David Zeuthenb623d8b2017-04-04 16:05:53 -0400253def decode_long(blob):
254 """Decodes a long from a bytearray() using a given amount of bits.
255
256 This number is expected to be in big-endian, e.g. with the most
257 significant bit first.
258
259 This is the reverse of encode_long().
260
261 Arguments:
262 value: A bytearray() with the encoded long.
263
264 Returns:
265 The decoded value.
266 """
267 ret = 0
268 for b in bytearray(blob):
269 ret *= 256
270 ret += b
271 return ret
272
273
David Zeuthen21e95262016-07-27 17:58:40 -0400274def egcd(a, b):
275 """Calculate greatest common divisor of two numbers.
276
277 This implementation uses a recursive version of the extended
278 Euclidian algorithm.
279
280 Arguments:
281 a: First number.
282 b: Second number.
283
284 Returns:
285 A tuple (gcd, x, y) that where |gcd| is the greatest common
286 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
287 """
288 if a == 0:
289 return (b, 0, 1)
290 else:
291 g, y, x = egcd(b % a, a)
292 return (g, x - (b // a) * y, y)
293
294
295def modinv(a, m):
296 """Calculate modular multiplicative inverse of |a| modulo |m|.
297
298 This calculates the number |x| such that |a| * |x| == 1 (modulo
299 |m|). This number only exists if |a| and |m| are co-prime - |None|
300 is returned if this isn't true.
301
302 Arguments:
303 a: The number to calculate a modular inverse of.
304 m: The modulo to use.
305
306 Returns:
307 The modular multiplicative inverse of |a| and |m| or |None| if
308 these numbers are not co-prime.
309 """
310 gcd, x, _ = egcd(a, m)
311 if gcd != 1:
312 return None # modular inverse does not exist
313 else:
314 return x % m
315
316
317def parse_number(string):
318 """Parse a string as a number.
319
320 This is just a short-hand for int(string, 0) suitable for use in the
321 |type| parameter of |ArgumentParser|'s add_argument() function. An
322 improvement to just using type=int is that this function supports
323 numbers in other bases, e.g. "0x1234".
324
325 Arguments:
326 string: The string to parse.
327
328 Returns:
329 The parsed integer.
330
331 Raises:
332 ValueError: If the number could not be parsed.
333 """
334 return int(string, 0)
335
336
David Zeuthenc68f0822017-03-31 17:22:35 -0400337class RSAPublicKey(object):
338 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400339
David Zeuthenc68f0822017-03-31 17:22:35 -0400340 Attributes:
341 exponent: The key exponent.
342 modulus: The key modulus.
343 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400344 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400345
346 MODULUS_PREFIX = 'modulus='
347
348 def __init__(self, key_path):
349 """Loads and parses an RSA key from either a private or public key file.
350
351 Arguments:
352 key_path: The path to a key file.
353 """
354 # We used to have something as simple as this:
355 #
356 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
357 # self.exponent = key.e
358 # self.modulus = key.n
359 # self.num_bits = key.size() + 1
360 #
361 # but unfortunately PyCrypto is not available in the builder. So
362 # instead just parse openssl(1) output to get this
363 # information. It's ugly but...
364 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
365 p = subprocess.Popen(args,
366 stdin=subprocess.PIPE,
367 stdout=subprocess.PIPE,
368 stderr=subprocess.PIPE)
369 (pout, perr) = p.communicate()
370 if p.wait() != 0:
371 # Could be just a public key is passed, try that.
372 args.append('-pubin')
373 p = subprocess.Popen(args,
374 stdin=subprocess.PIPE,
375 stdout=subprocess.PIPE,
376 stderr=subprocess.PIPE)
377 (pout, perr) = p.communicate()
378 if p.wait() != 0:
379 raise AvbError('Error getting public key: {}'.format(perr))
380
381 if not pout.lower().startswith(self.MODULUS_PREFIX):
382 raise AvbError('Unexpected modulus output')
383
384 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
385
386 # The exponent is assumed to always be 65537 and the number of
387 # bits can be derived from the modulus by rounding up to the
388 # nearest power of 2.
389 self.modulus = int(modulus_hexstr, 16)
390 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
391 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400392
393
David Zeuthenc68f0822017-03-31 17:22:35 -0400394def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400395 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
396
397 This creates a |AvbRSAPublicKeyHeader| as well as the two large
398 numbers (|key_num_bits| bits long) following it.
399
400 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400401 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400402
403 Returns:
404 A bytearray() with the |AvbRSAPublicKeyHeader|.
405 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key = RSAPublicKey(key_path)
407 if key.exponent != 65537:
408 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400409 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400410 # Calculate n0inv = -1/n[0] (mod 2^32)
411 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400412 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400413 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 r = 2L**key.modulus.bit_length()
415 rrmodn = r * r % key.modulus
416 ret.extend(struct.pack('!II', key.num_bits, n0inv))
417 ret.extend(encode_long(key.num_bits, key.modulus))
418 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400419 return ret
420
421
422def lookup_algorithm_by_type(alg_type):
423 """Looks up algorithm by type.
424
425 Arguments:
426 alg_type: The integer representing the type.
427
428 Returns:
429 A tuple with the algorithm name and an |Algorithm| instance.
430
431 Raises:
432 Exception: If the algorithm cannot be found
433 """
434 for alg_name in ALGORITHMS:
435 alg_data = ALGORITHMS[alg_name]
436 if alg_data.algorithm_type == alg_type:
437 return (alg_name, alg_data)
438 raise AvbError('Unknown algorithm type {}'.format(alg_type))
439
440
David Zeuthena156d3d2017-06-01 12:08:09 -0400441def raw_sign(signing_helper, signing_helper_with_files,
442 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900443 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800444 """Computes a raw RSA signature using |signing_helper| or openssl.
445
446 Arguments:
447 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400448 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800449 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900450 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800451 key_path: Path to the private key file. Must be PEM format.
452 raw_data_to_sign: Data to sign (bytearray or str expected).
453
454 Returns:
455 A bytearray containing the signature.
456
457 Raises:
458 Exception: If an error occurs.
459 """
460 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400461 if signing_helper_with_files is not None:
462 signing_file = tempfile.NamedTemporaryFile()
463 signing_file.write(str(raw_data_to_sign))
464 signing_file.flush()
David Zeuthene3cadca2017-02-22 21:25:46 -0500465 p = subprocess.Popen(
David Zeuthena156d3d2017-06-01 12:08:09 -0400466 [signing_helper_with_files, algorithm_name, key_path, signing_file.name])
467 retcode = p.wait()
468 if retcode != 0:
469 raise AvbError('Error signing')
470 signing_file.seek(0)
471 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800472 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400473 if signing_helper is not None:
474 p = subprocess.Popen(
475 [signing_helper, algorithm_name, key_path],
476 stdin=subprocess.PIPE,
477 stdout=subprocess.PIPE,
478 stderr=subprocess.PIPE)
479 else:
480 p = subprocess.Popen(
481 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
482 stdin=subprocess.PIPE,
483 stdout=subprocess.PIPE,
484 stderr=subprocess.PIPE)
485 (pout, perr) = p.communicate(str(raw_data_to_sign))
486 retcode = p.wait()
487 if retcode != 0:
488 raise AvbError('Error signing: {}'.format(perr))
489 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900490 if len(signature) != signature_num_bytes:
491 raise AvbError('Error signing: Invalid length of signature')
492 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800493
494
David Zeuthenb623d8b2017-04-04 16:05:53 -0400495def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
496 """Checks that the signature in a vbmeta blob was made by
497 the embedded public key.
498
499 Arguments:
500 vbmeta_header: A AvbVBMetaHeader.
501 vbmeta_blob: The whole vbmeta blob, including the header.
502
503 Returns:
504 True if the signature is valid and corresponds to the embedded
505 public key. Also returns True if the vbmeta blob is not signed.
506 """
507 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
508 if alg.hash_name == '':
509 return True
510 header_blob = vbmeta_blob[0:256]
511 auth_offset = 256
512 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
513 aux_size = vbmeta_header.auxiliary_data_block_size
514 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
515 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
516 pubkey_size = vbmeta_header.public_key_size
517 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
518
519 digest_offset = auth_offset + vbmeta_header.hash_offset
520 digest_size = vbmeta_header.hash_size
521 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
522
523 sig_offset = auth_offset + vbmeta_header.signature_offset
524 sig_size = vbmeta_header.signature_size
525 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
526
527 # Now that we've got the stored digest, public key, and signature
528 # all we need to do is to verify. This is the exactly the same
529 # steps as performed in the avb_vbmeta_image_verify() function in
530 # libavb/avb_vbmeta_image.c.
531
532 ha = hashlib.new(alg.hash_name)
533 ha.update(header_blob)
534 ha.update(aux_blob)
535 computed_digest = ha.digest()
536
537 if computed_digest != digest_blob:
538 return False
539
540 padding_and_digest = bytearray(alg.padding)
541 padding_and_digest.extend(computed_digest)
542
543 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
544 modulus_blob = pubkey_blob[8:8 + num_bits/8]
545 modulus = decode_long(modulus_blob)
546 exponent = 65537
547
548 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
549 # is OK since 'avbtool verify_image' is not expected to run on the
550 # Android builders (see bug #36809096).
551 import Crypto.PublicKey.RSA
552 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
553 if not key.verify(decode_long(padding_and_digest),
554 (decode_long(sig_blob), None)):
555 return False
556 return True
557
558
David Zeuthena4fee8b2016-08-22 15:20:43 -0400559class ImageChunk(object):
560 """Data structure used for representing chunks in Android sparse files.
561
562 Attributes:
563 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
564 chunk_offset: Offset in the sparse file where this chunk begins.
565 output_offset: Offset in de-sparsified file where output begins.
566 output_size: Number of bytes in output.
567 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
568 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
569 """
570
571 FORMAT = '<2H2I'
572 TYPE_RAW = 0xcac1
573 TYPE_FILL = 0xcac2
574 TYPE_DONT_CARE = 0xcac3
575 TYPE_CRC32 = 0xcac4
576
577 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
578 input_offset, fill_data):
579 """Initializes an ImageChunk object.
580
581 Arguments:
582 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
583 chunk_offset: Offset in the sparse file where this chunk begins.
584 output_offset: Offset in de-sparsified file.
585 output_size: Number of bytes in output.
586 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
587 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
588
589 Raises:
590 ValueError: If data is not well-formed.
591 """
592 self.chunk_type = chunk_type
593 self.chunk_offset = chunk_offset
594 self.output_offset = output_offset
595 self.output_size = output_size
596 self.input_offset = input_offset
597 self.fill_data = fill_data
598 # Check invariants.
599 if self.chunk_type == self.TYPE_RAW:
600 if self.fill_data is not None:
601 raise ValueError('RAW chunk cannot have fill_data set.')
602 if not self.input_offset:
603 raise ValueError('RAW chunk must have input_offset set.')
604 elif self.chunk_type == self.TYPE_FILL:
605 if self.fill_data is None:
606 raise ValueError('FILL chunk must have fill_data set.')
607 if self.input_offset:
608 raise ValueError('FILL chunk cannot have input_offset set.')
609 elif self.chunk_type == self.TYPE_DONT_CARE:
610 if self.fill_data is not None:
611 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
612 if self.input_offset:
613 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
614 else:
615 raise ValueError('Invalid chunk type')
616
617
618class ImageHandler(object):
619 """Abstraction for image I/O with support for Android sparse images.
620
621 This class provides an interface for working with image files that
622 may be using the Android Sparse Image format. When an instance is
623 constructed, we test whether it's an Android sparse file. If so,
624 operations will be on the sparse file by interpreting the sparse
625 format, otherwise they will be directly on the file. Either way the
626 operations do the same.
627
628 For reading, this interface mimics a file object - it has seek(),
629 tell(), and read() methods. For writing, only truncation
630 (truncate()) and appending is supported (append_raw() and
631 append_dont_care()). Additionally, data can only be written in units
632 of the block size.
633
634 Attributes:
635 is_sparse: Whether the file being operated on is sparse.
636 block_size: The block size, typically 4096.
637 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400638 """
639 # See system/core/libsparse/sparse_format.h for details.
640 MAGIC = 0xed26ff3a
641 HEADER_FORMAT = '<I4H4I'
642
643 # These are formats and offset of just the |total_chunks| and
644 # |total_blocks| fields.
645 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
646 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
647
648 def __init__(self, image_filename):
649 """Initializes an image handler.
650
651 Arguments:
652 image_filename: The name of the file to operate on.
653
654 Raises:
655 ValueError: If data in the file is invalid.
656 """
657 self._image_filename = image_filename
658 self._read_header()
659
660 def _read_header(self):
661 """Initializes internal data structures used for reading file.
662
663 This may be called multiple times and is typically called after
664 modifying the file (e.g. appending, truncation).
665
666 Raises:
667 ValueError: If data in the file is invalid.
668 """
669 self.is_sparse = False
670 self.block_size = 4096
671 self._file_pos = 0
672 self._image = open(self._image_filename, 'r+b')
673 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400674 self.image_size = self._image.tell()
675
676 self._image.seek(0, os.SEEK_SET)
677 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
678 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
679 block_size, self._num_total_blocks, self._num_total_chunks,
680 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
681 if magic != self.MAGIC:
682 # Not a sparse image, our job here is done.
683 return
684 if not (major_version == 1 and minor_version == 0):
685 raise ValueError('Encountered sparse image format version {}.{} but '
686 'only 1.0 is supported'.format(major_version,
687 minor_version))
688 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
689 raise ValueError('Unexpected file_hdr_sz value {}.'.
690 format(file_hdr_sz))
691 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
692 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
693 format(chunk_hdr_sz))
694
695 self.block_size = block_size
696
697 # Build an list of chunks by parsing the file.
698 self._chunks = []
699
700 # Find the smallest offset where only "Don't care" chunks
701 # follow. This will be the size of the content in the sparse
702 # image.
703 offset = 0
704 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 for _ in xrange(1, self._num_total_chunks + 1):
706 chunk_offset = self._image.tell()
707
708 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
709 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
710 header_bin)
711 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
712
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 if chunk_type == ImageChunk.TYPE_RAW:
714 if data_sz != (chunk_sz * self.block_size):
715 raise ValueError('Raw chunk input size ({}) does not match output '
716 'size ({})'.
717 format(data_sz, chunk_sz*self.block_size))
718 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
719 chunk_offset,
720 output_offset,
721 chunk_sz*self.block_size,
722 self._image.tell(),
723 None))
724 self._image.read(data_sz)
725
726 elif chunk_type == ImageChunk.TYPE_FILL:
727 if data_sz != 4:
728 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
729 'has {}'.format(data_sz))
730 fill_data = self._image.read(4)
731 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
732 chunk_offset,
733 output_offset,
734 chunk_sz*self.block_size,
735 None,
736 fill_data))
737 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
738 if data_sz != 0:
739 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
740 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400741 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
742 chunk_offset,
743 output_offset,
744 chunk_sz*self.block_size,
745 None,
746 None))
747 elif chunk_type == ImageChunk.TYPE_CRC32:
748 if data_sz != 4:
749 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
750 'this has {}'.format(data_sz))
751 self._image.read(4)
752 else:
753 raise ValueError('Unknown chunk type {}'.format(chunk_type))
754
755 offset += chunk_sz
756 output_offset += chunk_sz*self.block_size
757
758 # Record where sparse data end.
759 self._sparse_end = self._image.tell()
760
761 # Now that we've traversed all chunks, sanity check.
762 if self._num_total_blocks != offset:
763 raise ValueError('The header said we should have {} output blocks, '
764 'but we saw {}'.format(self._num_total_blocks, offset))
765 junk_len = len(self._image.read())
766 if junk_len > 0:
767 raise ValueError('There were {} bytes of extra data at the end of the '
768 'file.'.format(junk_len))
769
David Zeuthen09692692016-09-30 16:16:40 -0400770 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400771 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400772
773 # This is used when bisecting in read() to find the initial slice.
774 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
775
776 self.is_sparse = True
777
778 def _update_chunks_and_blocks(self):
779 """Helper function to update the image header.
780
781 The the |total_chunks| and |total_blocks| fields in the header
782 will be set to value of the |_num_total_blocks| and
783 |_num_total_chunks| attributes.
784
785 """
786 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
787 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
788 self._num_total_blocks,
789 self._num_total_chunks))
790
791 def append_dont_care(self, num_bytes):
792 """Appends a DONT_CARE chunk to the sparse file.
793
794 The given number of bytes must be a multiple of the block size.
795
796 Arguments:
797 num_bytes: Size in number of bytes of the DONT_CARE chunk.
798 """
799 assert num_bytes % self.block_size == 0
800
801 if not self.is_sparse:
802 self._image.seek(0, os.SEEK_END)
803 # This is more efficient that writing NUL bytes since it'll add
804 # a hole on file systems that support sparse files (native
805 # sparse, not Android sparse).
806 self._image.truncate(self._image.tell() + num_bytes)
807 self._read_header()
808 return
809
810 self._num_total_chunks += 1
811 self._num_total_blocks += num_bytes / self.block_size
812 self._update_chunks_and_blocks()
813
814 self._image.seek(self._sparse_end, os.SEEK_SET)
815 self._image.write(struct.pack(ImageChunk.FORMAT,
816 ImageChunk.TYPE_DONT_CARE,
817 0, # Reserved
818 num_bytes / self.block_size,
819 struct.calcsize(ImageChunk.FORMAT)))
820 self._read_header()
821
822 def append_raw(self, data):
823 """Appends a RAW chunk to the sparse file.
824
825 The length of the given data must be a multiple of the block size.
826
827 Arguments:
828 data: Data to append.
829 """
830 assert len(data) % self.block_size == 0
831
832 if not self.is_sparse:
833 self._image.seek(0, os.SEEK_END)
834 self._image.write(data)
835 self._read_header()
836 return
837
838 self._num_total_chunks += 1
839 self._num_total_blocks += len(data) / self.block_size
840 self._update_chunks_and_blocks()
841
842 self._image.seek(self._sparse_end, os.SEEK_SET)
843 self._image.write(struct.pack(ImageChunk.FORMAT,
844 ImageChunk.TYPE_RAW,
845 0, # Reserved
846 len(data) / self.block_size,
847 len(data) +
848 struct.calcsize(ImageChunk.FORMAT)))
849 self._image.write(data)
850 self._read_header()
851
852 def append_fill(self, fill_data, size):
853 """Appends a fill chunk to the sparse file.
854
855 The total length of the fill data must be a multiple of the block size.
856
857 Arguments:
858 fill_data: Fill data to append - must be four bytes.
859 size: Number of chunk - must be a multiple of four and the block size.
860 """
861 assert len(fill_data) == 4
862 assert size % 4 == 0
863 assert size % self.block_size == 0
864
865 if not self.is_sparse:
866 self._image.seek(0, os.SEEK_END)
867 self._image.write(fill_data * (size/4))
868 self._read_header()
869 return
870
871 self._num_total_chunks += 1
872 self._num_total_blocks += size / self.block_size
873 self._update_chunks_and_blocks()
874
875 self._image.seek(self._sparse_end, os.SEEK_SET)
876 self._image.write(struct.pack(ImageChunk.FORMAT,
877 ImageChunk.TYPE_FILL,
878 0, # Reserved
879 size / self.block_size,
880 4 + struct.calcsize(ImageChunk.FORMAT)))
881 self._image.write(fill_data)
882 self._read_header()
883
884 def seek(self, offset):
885 """Sets the cursor position for reading from unsparsified file.
886
887 Arguments:
888 offset: Offset to seek to from the beginning of the file.
889 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700890 if offset < 0:
891 raise RuntimeError("Seeking with negative offset: %d" % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400892 self._file_pos = offset
893
894 def read(self, size):
895 """Reads data from the unsparsified file.
896
897 This method may return fewer than |size| bytes of data if the end
898 of the file was encountered.
899
900 The file cursor for reading is advanced by the number of bytes
901 read.
902
903 Arguments:
904 size: Number of bytes to read.
905
906 Returns:
907 The data.
908
909 """
910 if not self.is_sparse:
911 self._image.seek(self._file_pos)
912 data = self._image.read(size)
913 self._file_pos += len(data)
914 return data
915
916 # Iterate over all chunks.
917 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
918 self._file_pos) - 1
919 data = bytearray()
920 to_go = size
921 while to_go > 0:
922 chunk = self._chunks[chunk_idx]
923 chunk_pos_offset = self._file_pos - chunk.output_offset
924 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
925
926 if chunk.chunk_type == ImageChunk.TYPE_RAW:
927 self._image.seek(chunk.input_offset + chunk_pos_offset)
928 data.extend(self._image.read(chunk_pos_to_go))
929 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
930 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
931 offset_mod = chunk_pos_offset % len(chunk.fill_data)
932 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
933 else:
934 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
935 data.extend('\0' * chunk_pos_to_go)
936
937 to_go -= chunk_pos_to_go
938 self._file_pos += chunk_pos_to_go
939 chunk_idx += 1
940 # Generate partial read in case of EOF.
941 if chunk_idx >= len(self._chunks):
942 break
943
944 return data
945
946 def tell(self):
947 """Returns the file cursor position for reading from unsparsified file.
948
949 Returns:
950 The file cursor position for reading.
951 """
952 return self._file_pos
953
954 def truncate(self, size):
955 """Truncates the unsparsified file.
956
957 Arguments:
958 size: Desired size of unsparsified file.
959
960 Raises:
961 ValueError: If desired size isn't a multiple of the block size.
962 """
963 if not self.is_sparse:
964 self._image.truncate(size)
965 self._read_header()
966 return
967
968 if size % self.block_size != 0:
969 raise ValueError('Cannot truncate to a size which is not a multiple '
970 'of the block size')
971
972 if size == self.image_size:
973 # Trivial where there's nothing to do.
974 return
975 elif size < self.image_size:
976 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
977 chunk = self._chunks[chunk_idx]
978 if chunk.output_offset != size:
979 # Truncation in the middle of a trunk - need to keep the chunk
980 # and modify it.
981 chunk_idx_for_update = chunk_idx + 1
982 num_to_keep = size - chunk.output_offset
983 assert num_to_keep % self.block_size == 0
984 if chunk.chunk_type == ImageChunk.TYPE_RAW:
985 truncate_at = (chunk.chunk_offset +
986 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
987 data_sz = num_to_keep
988 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
989 truncate_at = (chunk.chunk_offset +
990 struct.calcsize(ImageChunk.FORMAT) + 4)
991 data_sz = 4
992 else:
993 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
994 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
995 data_sz = 0
996 chunk_sz = num_to_keep/self.block_size
997 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
998 self._image.seek(chunk.chunk_offset)
999 self._image.write(struct.pack(ImageChunk.FORMAT,
1000 chunk.chunk_type,
1001 0, # Reserved
1002 chunk_sz,
1003 total_sz))
1004 chunk.output_size = num_to_keep
1005 else:
1006 # Truncation at trunk boundary.
1007 truncate_at = chunk.chunk_offset
1008 chunk_idx_for_update = chunk_idx
1009
1010 self._num_total_chunks = chunk_idx_for_update
1011 self._num_total_blocks = 0
1012 for i in range(0, chunk_idx_for_update):
1013 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1014 self._update_chunks_and_blocks()
1015 self._image.truncate(truncate_at)
1016
1017 # We've modified the file so re-read all data.
1018 self._read_header()
1019 else:
1020 # Truncating to grow - just add a DONT_CARE section.
1021 self.append_dont_care(size - self.image_size)
1022
1023
David Zeuthen21e95262016-07-27 17:58:40 -04001024class AvbDescriptor(object):
1025 """Class for AVB descriptor.
1026
1027 See the |AvbDescriptor| C struct for more information.
1028
1029 Attributes:
1030 tag: The tag identifying what kind of descriptor this is.
1031 data: The data in the descriptor.
1032 """
1033
1034 SIZE = 16
1035 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1036
1037 def __init__(self, data):
1038 """Initializes a new property descriptor.
1039
1040 Arguments:
1041 data: If not None, must be a bytearray().
1042
1043 Raises:
1044 LookupError: If the given descriptor is malformed.
1045 """
1046 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1047
1048 if data:
1049 (self.tag, num_bytes_following) = (
1050 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1051 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1052 else:
1053 self.tag = None
1054 self.data = None
1055
1056 def print_desc(self, o):
1057 """Print the descriptor.
1058
1059 Arguments:
1060 o: The object to write the output to.
1061 """
1062 o.write(' Unknown descriptor:\n')
1063 o.write(' Tag: {}\n'.format(self.tag))
1064 if len(self.data) < 256:
1065 o.write(' Data: {} ({} bytes)\n'.format(
1066 repr(str(self.data)), len(self.data)))
1067 else:
1068 o.write(' Data: {} bytes\n'.format(len(self.data)))
1069
1070 def encode(self):
1071 """Serializes the descriptor.
1072
1073 Returns:
1074 A bytearray() with the descriptor data.
1075 """
1076 num_bytes_following = len(self.data)
1077 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1078 padding_size = nbf_with_padding - num_bytes_following
1079 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1080 padding = struct.pack(str(padding_size) + 'x')
1081 ret = desc + self.data + padding
1082 return bytearray(ret)
1083
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001084 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1085 """Verifies contents of the descriptor - used in verify_image sub-command.
1086
1087 Arguments:
1088 image_dir: The directory of the file being verified.
1089 image_ext: The extension of the file being verified (e.g. '.img').
1090 expected_chain_partitions_map: A map from partition name to the
1091 tuple (rollback_index_location, key_blob).
1092
1093 Returns:
1094 True if the descriptor verifies, False otherwise.
1095 """
1096 # Nothing to do.
1097 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001098
1099class AvbPropertyDescriptor(AvbDescriptor):
1100 """A class for property descriptors.
1101
1102 See the |AvbPropertyDescriptor| C struct for more information.
1103
1104 Attributes:
1105 key: The key.
1106 value: The key.
1107 """
1108
1109 TAG = 0
1110 SIZE = 32
1111 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1112 'Q' # key size (bytes)
1113 'Q') # value size (bytes)
1114
1115 def __init__(self, data=None):
1116 """Initializes a new property descriptor.
1117
1118 Arguments:
1119 data: If not None, must be a bytearray of size |SIZE|.
1120
1121 Raises:
1122 LookupError: If the given descriptor is malformed.
1123 """
1124 AvbDescriptor.__init__(self, None)
1125 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1126
1127 if data:
1128 (tag, num_bytes_following, key_size,
1129 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1130 expected_size = round_to_multiple(
1131 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1132 if tag != self.TAG or num_bytes_following != expected_size:
1133 raise LookupError('Given data does not look like a property '
1134 'descriptor.')
1135 self.key = data[self.SIZE:(self.SIZE + key_size)]
1136 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1137 value_size)]
1138 else:
1139 self.key = ''
1140 self.value = ''
1141
1142 def print_desc(self, o):
1143 """Print the descriptor.
1144
1145 Arguments:
1146 o: The object to write the output to.
1147 """
1148 if len(self.value) < 256:
1149 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1150 else:
1151 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1152
1153 def encode(self):
1154 """Serializes the descriptor.
1155
1156 Returns:
1157 A bytearray() with the descriptor data.
1158 """
1159 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1160 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1161 padding_size = nbf_with_padding - num_bytes_following
1162 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1163 len(self.key), len(self.value))
1164 padding = struct.pack(str(padding_size) + 'x')
1165 ret = desc + self.key + '\0' + self.value + '\0' + padding
1166 return bytearray(ret)
1167
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001168 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1169 """Verifies contents of the descriptor - used in verify_image sub-command.
1170
1171 Arguments:
1172 image_dir: The directory of the file being verified.
1173 image_ext: The extension of the file being verified (e.g. '.img').
1174 expected_chain_partitions_map: A map from partition name to the
1175 tuple (rollback_index_location, key_blob).
1176
1177 Returns:
1178 True if the descriptor verifies, False otherwise.
1179 """
1180 # Nothing to do.
1181 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001182
1183class AvbHashtreeDescriptor(AvbDescriptor):
1184 """A class for hashtree descriptors.
1185
1186 See the |AvbHashtreeDescriptor| C struct for more information.
1187
1188 Attributes:
1189 dm_verity_version: dm-verity version used.
1190 image_size: Size of the image, after rounding up to |block_size|.
1191 tree_offset: Offset of the hash tree in the file.
1192 tree_size: Size of the tree.
1193 data_block_size: Data block size
1194 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001195 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1196 fec_offset: Offset of FEC data (0 if FEC is not used).
1197 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001198 hash_algorithm: Hash algorithm used.
1199 partition_name: Partition name.
1200 salt: Salt used.
1201 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001202 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001203 """
1204
1205 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001206 RESERVED = 60
1207 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001208 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1209 'L' # dm-verity version used
1210 'Q' # image size (bytes)
1211 'Q' # tree offset (bytes)
1212 'Q' # tree size (bytes)
1213 'L' # data block size (bytes)
1214 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001215 'L' # FEC number of roots
1216 'Q' # FEC offset (bytes)
1217 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001218 '32s' # hash algorithm used
1219 'L' # partition name (bytes)
1220 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001221 'L' # root digest length (bytes)
1222 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001223 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001224
1225 def __init__(self, data=None):
1226 """Initializes a new hashtree descriptor.
1227
1228 Arguments:
1229 data: If not None, must be a bytearray of size |SIZE|.
1230
1231 Raises:
1232 LookupError: If the given descriptor is malformed.
1233 """
1234 AvbDescriptor.__init__(self, None)
1235 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1236
1237 if data:
1238 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1239 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001240 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1241 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001242 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1243 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001244 expected_size = round_to_multiple(
1245 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1246 if tag != self.TAG or num_bytes_following != expected_size:
1247 raise LookupError('Given data does not look like a hashtree '
1248 'descriptor.')
1249 # Nuke NUL-bytes at the end.
1250 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1251 o = 0
1252 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1253 partition_name_len)])
1254 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1255 self.partition_name.decode('utf-8')
1256 o += partition_name_len
1257 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1258 o += salt_len
1259 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1260 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001261 if root_digest_len != 0:
1262 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001263
1264 else:
1265 self.dm_verity_version = 0
1266 self.image_size = 0
1267 self.tree_offset = 0
1268 self.tree_size = 0
1269 self.data_block_size = 0
1270 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001271 self.fec_num_roots = 0
1272 self.fec_offset = 0
1273 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001274 self.hash_algorithm = ''
1275 self.partition_name = ''
1276 self.salt = bytearray()
1277 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001278 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001279
1280 def print_desc(self, o):
1281 """Print the descriptor.
1282
1283 Arguments:
1284 o: The object to write the output to.
1285 """
1286 o.write(' Hashtree descriptor:\n')
1287 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1288 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1289 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1290 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1291 o.write(' Data Block Size: {} bytes\n'.format(
1292 self.data_block_size))
1293 o.write(' Hash Block Size: {} bytes\n'.format(
1294 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001295 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1296 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1297 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001298 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1299 o.write(' Partition Name: {}\n'.format(self.partition_name))
1300 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1301 'hex')))
1302 o.write(' Root Digest: {}\n'.format(str(
1303 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001304 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001305
1306 def encode(self):
1307 """Serializes the descriptor.
1308
1309 Returns:
1310 A bytearray() with the descriptor data.
1311 """
1312 encoded_name = self.partition_name.encode('utf-8')
1313 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1314 len(self.root_digest) - 16)
1315 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1316 padding_size = nbf_with_padding - num_bytes_following
1317 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1318 self.dm_verity_version, self.image_size,
1319 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001320 self.hash_block_size, self.fec_num_roots,
1321 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001322 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001323 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001324 padding = struct.pack(str(padding_size) + 'x')
1325 ret = desc + encoded_name + self.salt + self.root_digest + padding
1326 return bytearray(ret)
1327
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001328 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1329 """Verifies contents of the descriptor - used in verify_image sub-command.
1330
1331 Arguments:
1332 image_dir: The directory of the file being verified.
1333 image_ext: The extension of the file being verified (e.g. '.img').
1334 expected_chain_partitions_map: A map from partition name to the
1335 tuple (rollback_index_location, key_blob).
1336
1337 Returns:
1338 True if the descriptor verifies, False otherwise.
1339 """
1340 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1341 image = ImageHandler(image_filename)
1342 # Generate the hashtree and checks that it matches what's in the file.
1343 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1344 digest_padding = round_to_pow2(digest_size) - digest_size
1345 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1346 self.image_size, self.data_block_size, digest_size + digest_padding)
1347 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1348 self.data_block_size,
1349 self.hash_algorithm, self.salt,
1350 digest_padding,
1351 hash_level_offsets,
1352 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001353 # The root digest must match unless it is not embedded in the descriptor.
1354 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001355 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1356 format(image_filename))
1357 return False
1358 # ... also check that the on-disk hashtree matches
1359 image.seek(self.tree_offset)
1360 hash_tree_ondisk = image.read(self.tree_size)
1361 if hash_tree != hash_tree_ondisk:
1362 sys.stderr.write('hashtree of {} contains invalid data\n'.
1363 format(image_filename))
1364 return False
1365 # TODO: we could also verify that the FEC stored in the image is
1366 # correct but this a) currently requires the 'fec' binary; and b)
1367 # takes a long time; and c) is not strictly needed for
1368 # verification purposes as we've already verified the root hash.
1369 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1370 .format(self.partition_name, self.hash_algorithm, image_filename,
1371 self.image_size))
1372 return True
1373
David Zeuthen21e95262016-07-27 17:58:40 -04001374
1375class AvbHashDescriptor(AvbDescriptor):
1376 """A class for hash descriptors.
1377
1378 See the |AvbHashDescriptor| C struct for more information.
1379
1380 Attributes:
1381 image_size: Image size, in bytes.
1382 hash_algorithm: Hash algorithm used.
1383 partition_name: Partition name.
1384 salt: Salt used.
1385 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001386 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001387 """
1388
1389 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001390 RESERVED = 60
1391 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001392 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1393 'Q' # image size (bytes)
1394 '32s' # hash algorithm used
1395 'L' # partition name (bytes)
1396 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001397 'L' # digest length (bytes)
1398 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001399 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001400
1401 def __init__(self, data=None):
1402 """Initializes a new hash descriptor.
1403
1404 Arguments:
1405 data: If not None, must be a bytearray of size |SIZE|.
1406
1407 Raises:
1408 LookupError: If the given descriptor is malformed.
1409 """
1410 AvbDescriptor.__init__(self, None)
1411 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1412
1413 if data:
1414 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1415 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001416 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1417 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001418 expected_size = round_to_multiple(
1419 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1420 if tag != self.TAG or num_bytes_following != expected_size:
1421 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1422 # Nuke NUL-bytes at the end.
1423 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1424 o = 0
1425 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1426 partition_name_len)])
1427 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1428 self.partition_name.decode('utf-8')
1429 o += partition_name_len
1430 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1431 o += salt_len
1432 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1433 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001434 if digest_len != 0:
1435 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001436
1437 else:
1438 self.image_size = 0
1439 self.hash_algorithm = ''
1440 self.partition_name = ''
1441 self.salt = bytearray()
1442 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001443 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001444
1445 def print_desc(self, o):
1446 """Print the descriptor.
1447
1448 Arguments:
1449 o: The object to write the output to.
1450 """
1451 o.write(' Hash descriptor:\n')
1452 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1453 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1454 o.write(' Partition Name: {}\n'.format(self.partition_name))
1455 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1456 'hex')))
1457 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1458 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001459 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001460
1461 def encode(self):
1462 """Serializes the descriptor.
1463
1464 Returns:
1465 A bytearray() with the descriptor data.
1466 """
1467 encoded_name = self.partition_name.encode('utf-8')
1468 num_bytes_following = (
1469 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1470 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1471 padding_size = nbf_with_padding - num_bytes_following
1472 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1473 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001474 len(self.salt), len(self.digest), self.flags,
1475 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001476 padding = struct.pack(str(padding_size) + 'x')
1477 ret = desc + encoded_name + self.salt + self.digest + padding
1478 return bytearray(ret)
1479
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001480 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1481 """Verifies contents of the descriptor - used in verify_image sub-command.
1482
1483 Arguments:
1484 image_dir: The directory of the file being verified.
1485 image_ext: The extension of the file being verified (e.g. '.img').
1486 expected_chain_partitions_map: A map from partition name to the
1487 tuple (rollback_index_location, key_blob).
1488
1489 Returns:
1490 True if the descriptor verifies, False otherwise.
1491 """
1492 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1493 image = ImageHandler(image_filename)
1494 data = image.read(self.image_size)
1495 ha = hashlib.new(self.hash_algorithm)
1496 ha.update(self.salt)
1497 ha.update(data)
1498 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001499 # The digest must match unless there is no digest in the descriptor.
1500 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001501 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1502 format(self.hash_algorithm, image_filename))
1503 return False
1504 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
1505 .format(self.partition_name, self.hash_algorithm, image_filename,
1506 self.image_size))
1507 return True
1508
David Zeuthen21e95262016-07-27 17:58:40 -04001509
1510class AvbKernelCmdlineDescriptor(AvbDescriptor):
1511 """A class for kernel command-line descriptors.
1512
1513 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1514
1515 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001516 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001517 kernel_cmdline: The kernel command-line.
1518 """
1519
1520 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001521 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001522 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001523 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001524 'L') # cmdline length (bytes)
1525
David Zeuthenfd41eb92016-11-17 12:24:47 -05001526 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1527 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1528
David Zeuthen21e95262016-07-27 17:58:40 -04001529 def __init__(self, data=None):
1530 """Initializes a new kernel cmdline descriptor.
1531
1532 Arguments:
1533 data: If not None, must be a bytearray of size |SIZE|.
1534
1535 Raises:
1536 LookupError: If the given descriptor is malformed.
1537 """
1538 AvbDescriptor.__init__(self, None)
1539 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1540
1541 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001542 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001543 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1544 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1545 8)
1546 if tag != self.TAG or num_bytes_following != expected_size:
1547 raise LookupError('Given data does not look like a kernel cmdline '
1548 'descriptor.')
1549 # Nuke NUL-bytes at the end.
1550 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1551 kernel_cmdline_length)])
1552 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1553 self.kernel_cmdline.decode('utf-8')
1554 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001555 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001556 self.kernel_cmdline = ''
1557
1558 def print_desc(self, o):
1559 """Print the descriptor.
1560
1561 Arguments:
1562 o: The object to write the output to.
1563 """
1564 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001565 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001566 o.write(' Kernel Cmdline: {}\n'.format(repr(
1567 self.kernel_cmdline)))
1568
1569 def encode(self):
1570 """Serializes the descriptor.
1571
1572 Returns:
1573 A bytearray() with the descriptor data.
1574 """
1575 encoded_str = self.kernel_cmdline.encode('utf-8')
1576 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1577 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1578 padding_size = nbf_with_padding - num_bytes_following
1579 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001580 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001581 padding = struct.pack(str(padding_size) + 'x')
1582 ret = desc + encoded_str + padding
1583 return bytearray(ret)
1584
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001585 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1586 """Verifies contents of the descriptor - used in verify_image sub-command.
1587
1588 Arguments:
1589 image_dir: The directory of the file being verified.
1590 image_ext: The extension of the file being verified (e.g. '.img').
1591 expected_chain_partitions_map: A map from partition name to the
1592 tuple (rollback_index_location, key_blob).
1593
1594 Returns:
1595 True if the descriptor verifies, False otherwise.
1596 """
1597 # Nothing to verify.
1598 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001599
1600class AvbChainPartitionDescriptor(AvbDescriptor):
1601 """A class for chained partition descriptors.
1602
1603 See the |AvbChainPartitionDescriptor| C struct for more information.
1604
1605 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001606 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001607 partition_name: Partition name.
1608 public_key: Bytes for the public key.
1609 """
1610
1611 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001612 RESERVED = 64
1613 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001614 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001615 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001616 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001617 'L' + # public_key_size (bytes)
1618 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001619
1620 def __init__(self, data=None):
1621 """Initializes a new chain partition descriptor.
1622
1623 Arguments:
1624 data: If not None, must be a bytearray of size |SIZE|.
1625
1626 Raises:
1627 LookupError: If the given descriptor is malformed.
1628 """
1629 AvbDescriptor.__init__(self, None)
1630 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1631
1632 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001633 (tag, num_bytes_following, self.rollback_index_location,
1634 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001635 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001636 expected_size = round_to_multiple(
1637 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1638 if tag != self.TAG or num_bytes_following != expected_size:
1639 raise LookupError('Given data does not look like a chain partition '
1640 'descriptor.')
1641 o = 0
1642 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1643 partition_name_len)])
1644 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1645 self.partition_name.decode('utf-8')
1646 o += partition_name_len
1647 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1648
1649 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001650 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001651 self.partition_name = ''
1652 self.public_key = bytearray()
1653
1654 def print_desc(self, o):
1655 """Print the descriptor.
1656
1657 Arguments:
1658 o: The object to write the output to.
1659 """
1660 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001661 o.write(' Partition Name: {}\n'.format(self.partition_name))
1662 o.write(' Rollback Index Location: {}\n'.format(
1663 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001664 # Just show the SHA1 of the key, for size reasons.
1665 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001666 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001667
1668 def encode(self):
1669 """Serializes the descriptor.
1670
1671 Returns:
1672 A bytearray() with the descriptor data.
1673 """
1674 encoded_name = self.partition_name.encode('utf-8')
1675 num_bytes_following = (
1676 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1677 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1678 padding_size = nbf_with_padding - num_bytes_following
1679 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001680 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001681 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001682 padding = struct.pack(str(padding_size) + 'x')
1683 ret = desc + encoded_name + self.public_key + padding
1684 return bytearray(ret)
1685
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001686 def verify(self, image_dir, image_ext, expected_chain_partitions_map):
1687 """Verifies contents of the descriptor - used in verify_image sub-command.
1688
1689 Arguments:
1690 image_dir: The directory of the file being verified.
1691 image_ext: The extension of the file being verified (e.g. '.img').
1692 expected_chain_partitions_map: A map from partition name to the
1693 tuple (rollback_index_location, key_blob).
1694
1695 Returns:
1696 True if the descriptor verifies, False otherwise.
1697 """
1698 value = expected_chain_partitions_map.get(self.partition_name)
1699 if not value:
1700 sys.stderr.write('No expected chain partition for partition {}. Use '
1701 '--expected_chain_partition to specify expected '
1702 'contents.\n'.
1703 format(self.partition_name))
1704 return False
1705 rollback_index_location, pk_blob = value
1706
1707 if self.rollback_index_location != rollback_index_location:
1708 sys.stderr.write('Expected rollback_index_location {} does not '
1709 'match {} in descriptor for partition {}\n'.
1710 format(rollback_index_location,
1711 self.rollback_index_location,
1712 self.partition_name))
1713 return False
1714
1715 if self.public_key != pk_blob:
1716 sys.stderr.write('Expected public key blob does not match public '
1717 'key blob in descriptor for partition {}\n'.
1718 format(self.partition_name))
1719 return False
1720
1721 print ('{}: Successfully verified chain partition descriptor matches '
1722 'expected data'.format(self.partition_name))
1723
1724 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001725
1726DESCRIPTOR_CLASSES = [
1727 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1728 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1729]
1730
1731
1732def parse_descriptors(data):
1733 """Parses a blob of data into descriptors.
1734
1735 Arguments:
1736 data: A bytearray() with encoded descriptors.
1737
1738 Returns:
1739 A list of instances of objects derived from AvbDescriptor. For
1740 unknown descriptors, the class AvbDescriptor is used.
1741 """
1742 o = 0
1743 ret = []
1744 while o < len(data):
1745 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1746 if tag < len(DESCRIPTOR_CLASSES):
1747 c = DESCRIPTOR_CLASSES[tag]
1748 else:
1749 c = AvbDescriptor
1750 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1751 o += 16 + nb_following
1752 return ret
1753
1754
1755class AvbFooter(object):
1756 """A class for parsing and writing footers.
1757
1758 Footers are stored at the end of partitions and point to where the
1759 AvbVBMeta blob is located. They also contain the original size of
1760 the image before AVB information was added.
1761
1762 Attributes:
1763 magic: Magic for identifying the footer, see |MAGIC|.
1764 version_major: The major version of avbtool that wrote the footer.
1765 version_minor: The minor version of avbtool that wrote the footer.
1766 original_image_size: Original image size.
1767 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1768 vbmeta_size: Size of the AvbVBMeta blob.
1769 """
1770
1771 MAGIC = 'AVBf'
1772 SIZE = 64
1773 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001774 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1775 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001776 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1777 'Q' # Original image size.
1778 'Q' # Offset of VBMeta blob.
1779 'Q' + # Size of VBMeta blob.
1780 str(RESERVED) + 'x') # padding for reserved bytes
1781
1782 def __init__(self, data=None):
1783 """Initializes a new footer object.
1784
1785 Arguments:
1786 data: If not None, must be a bytearray of size 4096.
1787
1788 Raises:
1789 LookupError: If the given footer is malformed.
1790 struct.error: If the given data has no footer.
1791 """
1792 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1793
1794 if data:
1795 (self.magic, self.version_major, self.version_minor,
1796 self.original_image_size, self.vbmeta_offset,
1797 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1798 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001799 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001800 else:
1801 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001802 self.version_major = self.FOOTER_VERSION_MAJOR
1803 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001804 self.original_image_size = 0
1805 self.vbmeta_offset = 0
1806 self.vbmeta_size = 0
1807
David Zeuthena4fee8b2016-08-22 15:20:43 -04001808 def encode(self):
1809 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001810
David Zeuthena4fee8b2016-08-22 15:20:43 -04001811 Returns:
1812 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001813 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001814 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1815 self.version_minor, self.original_image_size,
1816 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001817
1818
1819class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001820 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001821
1822 Attributes:
1823 The attributes correspond to the |AvbVBMetaHeader| struct
1824 defined in avb_vbmeta_header.h.
1825 """
1826
1827 SIZE = 256
1828
David Zeuthene3cadca2017-02-22 21:25:46 -05001829 # Keep in sync with |reserved0| and |reserved| field of
1830 # |AvbVBMetaImageHeader|.
1831 RESERVED0 = 4
1832 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001833
1834 # Keep in sync with |AvbVBMetaImageHeader|.
1835 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1836 '2Q' # 2 x block size
1837 'L' # algorithm type
1838 '2Q' # offset, size (hash)
1839 '2Q' # offset, size (signature)
1840 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001841 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001842 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001843 'Q' # rollback_index
1844 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001845 str(RESERVED0) + 'x' + # padding for reserved bytes
1846 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001847 str(RESERVED) + 'x') # padding for reserved bytes
1848
1849 def __init__(self, data=None):
1850 """Initializes a new header object.
1851
1852 Arguments:
1853 data: If not None, must be a bytearray of size 8192.
1854
1855 Raises:
1856 Exception: If the given data is malformed.
1857 """
1858 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1859
1860 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001861 (self.magic, self.required_libavb_version_major,
1862 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001863 self.authentication_data_block_size, self.auxiliary_data_block_size,
1864 self.algorithm_type, self.hash_offset, self.hash_size,
1865 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001866 self.public_key_size, self.public_key_metadata_offset,
1867 self.public_key_metadata_size, self.descriptors_offset,
1868 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001869 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001870 self.flags,
1871 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001872 # Nuke NUL-bytes at the end of the string.
1873 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001874 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001875 else:
1876 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001877 # Start by just requiring version 1.0. Code that adds features
1878 # in a future version can use bump_required_libavb_version_minor() to
1879 # bump the minor.
1880 self.required_libavb_version_major = AVB_VERSION_MAJOR
1881 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001882 self.authentication_data_block_size = 0
1883 self.auxiliary_data_block_size = 0
1884 self.algorithm_type = 0
1885 self.hash_offset = 0
1886 self.hash_size = 0
1887 self.signature_offset = 0
1888 self.signature_size = 0
1889 self.public_key_offset = 0
1890 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001891 self.public_key_metadata_offset = 0
1892 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001893 self.descriptors_offset = 0
1894 self.descriptors_size = 0
1895 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001896 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001897 self.release_string = get_release_string()
1898
1899 def bump_required_libavb_version_minor(self, minor):
1900 """Function to bump required_libavb_version_minor.
1901
1902 Call this when writing data that requires a specific libavb
1903 version to parse it.
1904
1905 Arguments:
1906 minor: The minor version of libavb that has support for the feature.
1907 """
1908 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001909 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001910
1911 def save(self, output):
1912 """Serializes the header (256 bytes) to disk.
1913
1914 Arguments:
1915 output: The object to write the output to.
1916 """
1917 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001918 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1919 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001920 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1921 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001922 self.public_key_offset, self.public_key_size,
1923 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001924 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001925 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001926
1927 def encode(self):
1928 """Serializes the header (256) to a bytearray().
1929
1930 Returns:
1931 A bytearray() with the encoded header.
1932 """
1933 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001934 self.required_libavb_version_major,
1935 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001936 self.authentication_data_block_size,
1937 self.auxiliary_data_block_size, self.algorithm_type,
1938 self.hash_offset, self.hash_size, self.signature_offset,
1939 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001940 self.public_key_size, self.public_key_metadata_offset,
1941 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001942 self.descriptors_size, self.rollback_index, self.flags,
1943 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001944
1945
1946class Avb(object):
1947 """Business logic for avbtool command-line tool."""
1948
David Zeuthen8b6973b2016-09-20 12:39:49 -04001949 # Keep in sync with avb_ab_flow.h.
1950 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1951 AB_MAGIC = '\0AB0'
1952 AB_MAJOR_VERSION = 1
1953 AB_MINOR_VERSION = 0
1954 AB_MISC_METADATA_OFFSET = 2048
1955
David Zeuthen09692692016-09-30 16:16:40 -04001956 # Constants for maximum metadata size. These are used to give
1957 # meaningful errors if the value passed in via --partition_size is
1958 # too small and when --calc_max_image_size is used. We use
1959 # conservative figures.
1960 MAX_VBMETA_SIZE = 64 * 1024
1961 MAX_FOOTER_SIZE = 4096
1962
David Zeuthena4fee8b2016-08-22 15:20:43 -04001963 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001964 """Implements the 'erase_footer' command.
1965
1966 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001967 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001968 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001969
1970 Raises:
1971 AvbError: If there's no footer in the image.
1972 """
1973
David Zeuthena4fee8b2016-08-22 15:20:43 -04001974 image = ImageHandler(image_filename)
1975
David Zeuthen21e95262016-07-27 17:58:40 -04001976 (footer, _, descriptors, _) = self._parse_image(image)
1977
1978 if not footer:
1979 raise AvbError('Given image does not have a footer.')
1980
1981 new_image_size = None
1982 if not keep_hashtree:
1983 new_image_size = footer.original_image_size
1984 else:
1985 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001986 # descriptor to figure out the location and size of the hashtree
1987 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001988 for desc in descriptors:
1989 if isinstance(desc, AvbHashtreeDescriptor):
1990 # The hashtree is always just following the main data so the
1991 # new size is easily derived.
1992 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001993 # If the image has FEC codes, also keep those.
1994 if desc.fec_offset > 0:
1995 fec_end = desc.fec_offset + desc.fec_size
1996 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001997 break
1998 if not new_image_size:
1999 raise AvbError('Requested to keep hashtree but no hashtree '
2000 'descriptor was found.')
2001
2002 # And cut...
2003 image.truncate(new_image_size)
2004
David Zeuthen2bc232b2017-04-19 14:25:19 -04002005 def resize_image(self, image_filename, partition_size):
2006 """Implements the 'resize_image' command.
2007
2008 Arguments:
2009 image_filename: File with footer to resize.
2010 partition_size: The new size of the image.
2011
2012 Raises:
2013 AvbError: If there's no footer in the image.
2014 """
2015
2016 image = ImageHandler(image_filename)
2017
2018 if partition_size % image.block_size != 0:
2019 raise AvbError('Partition size of {} is not a multiple of the image '
2020 'block size {}.'.format(partition_size,
2021 image.block_size))
2022
2023 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
2024
2025 if not footer:
2026 raise AvbError('Given image does not have a footer.')
2027
2028 # The vbmeta blob is always at the end of the data so resizing an
2029 # image amounts to just moving the footer around.
2030
2031 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2032 if vbmeta_end_offset % image.block_size != 0:
2033 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
2034
2035 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002036 raise AvbError('Requested size of {} is too small for an image '
2037 'of size {}.'
2038 .format(partition_size,
2039 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002040
2041 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2042 # with enough bytes such that the final Footer block is at the end
2043 # of partition_size.
2044 image.truncate(vbmeta_end_offset)
2045 image.append_dont_care(partition_size - vbmeta_end_offset -
2046 1*image.block_size)
2047
2048 # Just reuse the same footer - only difference is that we're
2049 # writing it in a different place.
2050 footer_blob = footer.encode()
2051 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2052 footer_blob)
2053 image.append_raw(footer_blob_with_padding)
2054
David Zeuthen8b6973b2016-09-20 12:39:49 -04002055 def set_ab_metadata(self, misc_image, slot_data):
2056 """Implements the 'set_ab_metadata' command.
2057
2058 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2059 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2060
2061 Arguments:
2062 misc_image: The misc image to write to.
2063 slot_data: Slot data as a string
2064
2065 Raises:
2066 AvbError: If slot data is malformed.
2067 """
2068 tokens = slot_data.split(':')
2069 if len(tokens) != 6:
2070 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2071 a_priority = int(tokens[0])
2072 a_tries_remaining = int(tokens[1])
2073 a_success = True if int(tokens[2]) != 0 else False
2074 b_priority = int(tokens[3])
2075 b_tries_remaining = int(tokens[4])
2076 b_success = True if int(tokens[5]) != 0 else False
2077
2078 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2079 self.AB_MAGIC,
2080 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2081 a_priority, a_tries_remaining, a_success,
2082 b_priority, b_tries_remaining, b_success)
2083 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2084 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2085 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2086 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2087 misc_image.write(ab_data)
2088
David Zeuthena4fee8b2016-08-22 15:20:43 -04002089 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002090 """Implements the 'info_image' command.
2091
2092 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002093 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002094 output: Output file to write human-readable information to (file object).
2095 """
2096
David Zeuthena4fee8b2016-08-22 15:20:43 -04002097 image = ImageHandler(image_filename)
2098
David Zeuthen21e95262016-07-27 17:58:40 -04002099 o = output
2100
2101 (footer, header, descriptors, image_size) = self._parse_image(image)
2102
2103 if footer:
2104 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2105 footer.version_minor))
2106 o.write('Image size: {} bytes\n'.format(image_size))
2107 o.write('Original image size: {} bytes\n'.format(
2108 footer.original_image_size))
2109 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2110 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2111 o.write('--\n')
2112
2113 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2114
David Zeuthene3cadca2017-02-22 21:25:46 -05002115 o.write('Minimum libavb version: {}.{}{}\n'.format(
2116 header.required_libavb_version_major,
2117 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002118 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002119 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2120 o.write('Authentication Block: {} bytes\n'.format(
2121 header.authentication_data_block_size))
2122 o.write('Auxiliary Block: {} bytes\n'.format(
2123 header.auxiliary_data_block_size))
2124 o.write('Algorithm: {}\n'.format(alg_name))
2125 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002126 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002127 o.write('Release String: \'{}\'\n'.format(
2128 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002129
2130 # Print descriptors.
2131 num_printed = 0
2132 o.write('Descriptors:\n')
2133 for desc in descriptors:
2134 desc.print_desc(o)
2135 num_printed += 1
2136 if num_printed == 0:
2137 o.write(' (none)\n')
2138
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002139 def verify_image(self, image_filename, key_path, expected_chain_partitions):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002140 """Implements the 'verify_image' command.
2141
2142 Arguments:
2143 image_filename: Image file to get information from (file object).
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002144 key_path: None or check that embedded public key matches key at given path.
2145 expected_chain_partitions: List of chain partitions to check or None.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002146 """
2147
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002148 expected_chain_partitions_map = {}
2149 if expected_chain_partitions:
2150 used_locations = {}
2151 for cp in expected_chain_partitions:
2152 cp_tokens = cp.split(':')
2153 if len(cp_tokens) != 3:
2154 raise AvbError('Malformed chained partition "{}".'.format(cp))
2155 partition_name = cp_tokens[0]
2156 rollback_index_location = int(cp_tokens[1])
2157 file_path = cp_tokens[2]
2158 pk_blob = open(file_path).read()
2159 expected_chain_partitions_map[partition_name] = (rollback_index_location, pk_blob)
2160
2161 image_dir = os.path.dirname(image_filename)
2162 image_ext = os.path.splitext(image_filename)[1]
2163
2164 key_blob = None
2165 if key_path:
2166 print 'Verifying image {} using key at {}'.format(image_filename, key_path)
2167 key_blob = encode_rsa_key(key_path)
2168 else:
2169 print 'Verifying image {} using embedded public key'.format(image_filename)
2170
David Zeuthenb623d8b2017-04-04 16:05:53 -04002171 image = ImageHandler(image_filename)
2172 (footer, header, descriptors, image_size) = self._parse_image(image)
2173 offset = 0
2174 if footer:
2175 offset = footer.vbmeta_offset
2176 size = (header.SIZE + header.authentication_data_block_size +
2177 header.auxiliary_data_block_size)
2178 image.seek(offset)
2179 vbmeta_blob = image.read(size)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002180 h = AvbVBMetaHeader(vbmeta_blob[0:AvbVBMetaHeader.SIZE])
2181 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002182 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002183 raise AvbError('Signature check failed for {} vbmeta struct {}'
2184 .format(alg_name, image_filename))
2185
2186 if key_blob:
2187 # The embedded public key is in the auxiliary block at an offset.
2188 key_offset = AvbVBMetaHeader.SIZE
2189 key_offset += h.authentication_data_block_size
2190 key_offset += h.public_key_offset
2191 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + h.public_key_size]
2192 if key_blob != key_blob_in_vbmeta:
2193 raise AvbError('Embedded public key does not match given key.')
2194
2195 if footer:
2196 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2197 .format(alg_name, image_filename))
2198 else:
2199 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
2200 .format(alg_name, image_filename))
2201
2202 for desc in descriptors:
2203 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map):
2204 raise AvbError('Error verifying descriptor.')
2205
David Zeuthenb623d8b2017-04-04 16:05:53 -04002206
David Zeuthenb8643c02018-05-17 17:21:18 -04002207 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2208 """Implements the 'calculate_vbmeta_digest' command.
2209
2210 Arguments:
2211 image_filename: Image file to get information from (file object).
2212 hash_algorithm: Hash algorithm used.
2213 output: Output file to write human-readable information to (file object).
2214 """
2215
2216 image_dir = os.path.dirname(image_filename)
2217 image_ext = os.path.splitext(image_filename)[1]
2218
2219 image = ImageHandler(image_filename)
2220 (footer, header, descriptors, image_size) = self._parse_image(image)
2221 offset = 0
2222 if footer:
2223 offset = footer.vbmeta_offset
2224 size = (header.SIZE + header.authentication_data_block_size +
2225 header.auxiliary_data_block_size)
2226 image.seek(offset)
2227 vbmeta_blob = image.read(size)
2228
2229 hasher = hashlib.new(name=hash_algorithm)
2230 hasher.update(vbmeta_blob)
2231
2232 for desc in descriptors:
2233 if isinstance(desc, AvbChainPartitionDescriptor):
2234 ch_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
2235 ch_image = ImageHandler(ch_image_filename)
2236 (ch_footer, ch_header, ch_descriptors, ch_image_size) = self._parse_image(ch_image)
2237 ch_offset = 0
2238 if ch_footer:
2239 ch_offset = ch_footer.vbmeta_offset
2240 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2241 ch_header.auxiliary_data_block_size)
2242 ch_image.seek(ch_offset)
2243 ch_vbmeta_blob = ch_image.read(ch_size)
2244 hasher.update(ch_vbmeta_blob)
2245
2246 digest = hasher.digest()
2247 output.write('{}\n'.format(digest.encode('hex')))
2248
2249
David Zeuthen21e95262016-07-27 17:58:40 -04002250 def _parse_image(self, image):
2251 """Gets information about an image.
2252
2253 The image can either be a vbmeta or an image with a footer.
2254
2255 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002256 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002257
2258 Returns:
2259 A tuple where the first argument is a AvbFooter (None if there
2260 is no footer on the image), the second argument is a
2261 AvbVBMetaHeader, the third argument is a list of
2262 AvbDescriptor-derived instances, and the fourth argument is the
2263 size of |image|.
2264 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002265 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002266 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002267 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002268 try:
2269 footer = AvbFooter(image.read(AvbFooter.SIZE))
2270 except (LookupError, struct.error):
2271 # Nope, just seek back to the start.
2272 image.seek(0)
2273
2274 vbmeta_offset = 0
2275 if footer:
2276 vbmeta_offset = footer.vbmeta_offset
2277
2278 image.seek(vbmeta_offset)
2279 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2280
2281 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2282 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2283 desc_start_offset = aux_block_offset + h.descriptors_offset
2284 image.seek(desc_start_offset)
2285 descriptors = parse_descriptors(image.read(h.descriptors_size))
2286
David Zeuthen09692692016-09-30 16:16:40 -04002287 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002288
David Zeuthenb1b994d2017-03-06 18:01:31 -05002289 def _load_vbmeta_blob(self, image):
2290 """Gets the vbmeta struct and associated sections.
2291
2292 The image can either be a vbmeta.img or an image with a footer.
2293
2294 Arguments:
2295 image: An ImageHandler (vbmeta or footer).
2296
2297 Returns:
2298 A blob with the vbmeta struct and other sections.
2299 """
2300 assert isinstance(image, ImageHandler)
2301 footer = None
2302 image.seek(image.image_size - AvbFooter.SIZE)
2303 try:
2304 footer = AvbFooter(image.read(AvbFooter.SIZE))
2305 except (LookupError, struct.error):
2306 # Nope, just seek back to the start.
2307 image.seek(0)
2308
2309 vbmeta_offset = 0
2310 if footer:
2311 vbmeta_offset = footer.vbmeta_offset
2312
2313 image.seek(vbmeta_offset)
2314 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2315
2316 image.seek(vbmeta_offset)
2317 data_size = AvbVBMetaHeader.SIZE
2318 data_size += h.authentication_data_block_size
2319 data_size += h.auxiliary_data_block_size
2320 return image.read(data_size)
2321
David Zeuthen73f2afa2017-05-17 16:54:11 -04002322 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002323 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002324
2325 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002326 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002327
2328 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002329 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2330 instructions. There is one for when hashtree is not disabled and one for
2331 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002332
David Zeuthen21e95262016-07-27 17:58:40 -04002333 """
2334
David Zeuthen21e95262016-07-27 17:58:40 -04002335 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002336 c += '0' # start
2337 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2338 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2339 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2340 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2341 c += ' {}'.format(ht.data_block_size) # data_block
2342 c += ' {}'.format(ht.hash_block_size) # hash_block
2343 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2344 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2345 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2346 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2347 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2348 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002349 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002350 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002351 c += ' ignore_zero_blocks'
2352 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2353 c += ' fec_roots {}'.format(ht.fec_num_roots)
2354 # Note that fec_blocks is the size that FEC covers, *not* the
2355 # size of the FEC data. Since we use FEC for everything up until
2356 # the FEC data, it's the same as the offset.
2357 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2358 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2359 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002360 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002361 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002362 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002363 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002364
David Zeuthenfd41eb92016-11-17 12:24:47 -05002365 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002366 desc = AvbKernelCmdlineDescriptor()
2367 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002368 desc.flags = (
2369 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2370
2371 # The descriptor for when hashtree verification is disabled is a lot
2372 # simpler - we just set the root to the partition.
2373 desc_no_ht = AvbKernelCmdlineDescriptor()
2374 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2375 desc_no_ht.flags = (
2376 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2377
2378 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002379
David Zeuthen73f2afa2017-05-17 16:54:11 -04002380 def _get_cmdline_descriptors_for_dm_verity(self, image):
2381 """Generate kernel cmdline descriptors for dm-verity.
2382
2383 Arguments:
2384 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2385
2386 Returns:
2387 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2388 instructions. There is one for when hashtree is not disabled and one for
2389 when it is.
2390
2391 Raises:
2392 AvbError: If |image| doesn't have a hashtree descriptor.
2393
2394 """
2395
2396 (_, _, descriptors, _) = self._parse_image(image)
2397
2398 ht = None
2399 for desc in descriptors:
2400 if isinstance(desc, AvbHashtreeDescriptor):
2401 ht = desc
2402 break
2403
2404 if not ht:
2405 raise AvbError('No hashtree descriptor in given image')
2406
2407 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2408
David Zeuthen21e95262016-07-27 17:58:40 -04002409 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002410 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002411 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002412 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002413 include_descriptors_from_image,
2414 signing_helper,
2415 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002416 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002417 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002418 print_required_libavb_version,
2419 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002420 """Implements the 'make_vbmeta_image' command.
2421
2422 Arguments:
2423 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002424 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002425 algorithm_name: Name of algorithm to use.
2426 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002427 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002428 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002429 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002430 props: Properties to insert (list of strings of the form 'key:value').
2431 props_from_file: Properties to insert (list of strings 'key:<path>').
2432 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002433 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002434 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002435 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002436 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002437 release_string: None or avbtool release string to use instead of default.
2438 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002439 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002440 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002441
2442 Raises:
2443 AvbError: If a chained partition is malformed.
2444 """
2445
David Zeuthen1097a782017-05-31 15:53:17 -04002446 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002447 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002448 if include_descriptors_from_image:
2449 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2450 # version of all included descriptors.
2451 tmp_header = AvbVBMetaHeader()
2452 for image in include_descriptors_from_image:
2453 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2454 tmp_header.bump_required_libavb_version_minor(
2455 image_header.required_libavb_version_minor)
2456 print '1.{}'.format(tmp_header.required_libavb_version_minor)
2457 else:
2458 # Descriptors aside, all vbmeta features are supported in 1.0.
2459 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04002460 return
2461
2462 if not output:
2463 raise AvbError('No output file given')
2464
David Zeuthen21e95262016-07-27 17:58:40 -04002465 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002466 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002467 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002468 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002469 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002470 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002471 include_descriptors_from_image, signing_helper,
2472 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002473 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002474
2475 # Write entire vbmeta blob (header, authentication, auxiliary).
2476 output.seek(0)
2477 output.write(vbmeta_blob)
2478
David Zeuthen97cb5802017-06-01 16:14:05 -04002479 if padding_size > 0:
2480 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2481 padding_needed = padded_size - len(vbmeta_blob)
2482 output.write('\0' * padding_needed)
2483
David Zeuthen18666ab2016-11-15 11:18:05 -05002484 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2485 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002486 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002487 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002488 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002489 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002490 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002491 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002492 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002493 release_string, append_to_release_string,
2494 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002495 """Generates a VBMeta blob.
2496
2497 This blob contains the header (struct AvbVBMetaHeader), the
2498 authentication data block (which contains the hash and signature
2499 for the header and auxiliary block), and the auxiliary block
2500 (which contains descriptors, the public key used, and other data).
2501
2502 The |key| parameter can |None| only if the |algorithm_name| is
2503 'NONE'.
2504
2505 Arguments:
2506 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2507 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002508 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002509 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002510 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002511 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002512 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002513 props: Properties to insert (List of strings of the form 'key:value').
2514 props_from_file: Properties to insert (List of strings 'key:<path>').
2515 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002516 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002517 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002518 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2519 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002520 include_descriptors_from_image: List of file objects for which
2521 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002522 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002523 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002524 release_string: None or avbtool release string.
2525 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002526 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002527
2528 Returns:
2529 A bytearray() with the VBMeta blob.
2530
2531 Raises:
2532 Exception: If the |algorithm_name| is not found, if no key has
2533 been given and the given algorithm requires one, or the key is
2534 of the wrong size.
2535
2536 """
2537 try:
2538 alg = ALGORITHMS[algorithm_name]
2539 except KeyError:
2540 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2541
David Zeuthena5fd3a42017-02-27 16:38:54 -05002542 if not descriptors:
2543 descriptors = []
2544
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002545 h = AvbVBMetaHeader()
2546 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2547
David Zeuthena5fd3a42017-02-27 16:38:54 -05002548 # Insert chained partition descriptors, if any
2549 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002550 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002551 for cp in chain_partitions:
2552 cp_tokens = cp.split(':')
2553 if len(cp_tokens) != 3:
2554 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002555 partition_name = cp_tokens[0]
2556 rollback_index_location = int(cp_tokens[1])
2557 file_path = cp_tokens[2]
2558 # Check that the same rollback location isn't being used by
2559 # multiple chained partitions.
2560 if used_locations.get(rollback_index_location):
2561 raise AvbError('Rollback Index Location {} is already in use.'.format(
2562 rollback_index_location))
2563 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002564 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002565 desc.partition_name = partition_name
2566 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002567 if desc.rollback_index_location < 1:
2568 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002569 desc.public_key = open(file_path, 'rb').read()
2570 descriptors.append(desc)
2571
David Zeuthen21e95262016-07-27 17:58:40 -04002572 # Descriptors.
2573 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002574 for desc in descriptors:
2575 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002576
2577 # Add properties.
2578 if props:
2579 for prop in props:
2580 idx = prop.find(':')
2581 if idx == -1:
2582 raise AvbError('Malformed property "{}".'.format(prop))
2583 desc = AvbPropertyDescriptor()
2584 desc.key = prop[0:idx]
2585 desc.value = prop[(idx + 1):]
2586 encoded_descriptors.extend(desc.encode())
2587 if props_from_file:
2588 for prop in props_from_file:
2589 idx = prop.find(':')
2590 if idx == -1:
2591 raise AvbError('Malformed property "{}".'.format(prop))
2592 desc = AvbPropertyDescriptor()
2593 desc.key = prop[0:idx]
2594 desc.value = prop[(idx + 1):]
2595 file_path = prop[(idx + 1):]
2596 desc.value = open(file_path, 'rb').read()
2597 encoded_descriptors.extend(desc.encode())
2598
David Zeuthen73f2afa2017-05-17 16:54:11 -04002599 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002600 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002601 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002602 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002603 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2604 encoded_descriptors.extend(cmdline_desc[0].encode())
2605 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002606
David Zeuthen73f2afa2017-05-17 16:54:11 -04002607 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2608 if ht_desc_to_setup:
2609 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2610 ht_desc_to_setup)
2611 encoded_descriptors.extend(cmdline_desc[0].encode())
2612 encoded_descriptors.extend(cmdline_desc[1].encode())
2613
David Zeuthen21e95262016-07-27 17:58:40 -04002614 # Add kernel command-lines.
2615 if kernel_cmdlines:
2616 for i in kernel_cmdlines:
2617 desc = AvbKernelCmdlineDescriptor()
2618 desc.kernel_cmdline = i
2619 encoded_descriptors.extend(desc.encode())
2620
2621 # Add descriptors from other images.
2622 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002623 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002624 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002625 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002626 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2627 image_handler)
2628 # Bump the required libavb version to support all included descriptors.
2629 h.bump_required_libavb_version_minor(
2630 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002631 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002632 # The --include_descriptors_from_image option is used in some setups
2633 # with images A and B where both A and B contain a descriptor
2634 # for a partition with the same name. Since it's not meaningful
2635 # to include both descriptors, only include the last seen descriptor.
2636 # See bug 76386656 for details.
2637 if hasattr(desc, 'partition_name'):
2638 key = type(desc).__name__ + '_' + desc.partition_name
2639 descriptors_dict[key] = desc.encode()
2640 else:
2641 encoded_descriptors.extend(desc.encode())
2642 for key in sorted(descriptors_dict.keys()):
2643 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002644
David Zeuthen18666ab2016-11-15 11:18:05 -05002645 # Load public key metadata blob, if requested.
2646 pkmd_blob = []
2647 if public_key_metadata_path:
2648 with open(public_key_metadata_path) as f:
2649 pkmd_blob = f.read()
2650
David Zeuthen21e95262016-07-27 17:58:40 -04002651 key = None
2652 encoded_key = bytearray()
2653 if alg.public_key_num_bytes > 0:
2654 if not key_path:
2655 raise AvbError('Key is required for algorithm {}'.format(
2656 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002657 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002658 if len(encoded_key) != alg.public_key_num_bytes:
2659 raise AvbError('Key is wrong size for algorithm {}'.format(
2660 algorithm_name))
2661
David Zeuthene3cadca2017-02-22 21:25:46 -05002662 # Override release string, if requested.
2663 if isinstance(release_string, (str, unicode)):
2664 h.release_string = release_string
2665
2666 # Append to release string, if requested. Also insert a space before.
2667 if isinstance(append_to_release_string, (str, unicode)):
2668 h.release_string += ' ' + append_to_release_string
2669
David Zeuthen18666ab2016-11-15 11:18:05 -05002670 # For the Auxiliary data block, descriptors are stored at offset 0,
2671 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002672 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002673 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002674 h.descriptors_offset = 0
2675 h.descriptors_size = len(encoded_descriptors)
2676 h.public_key_offset = h.descriptors_size
2677 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002678 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2679 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002680
2681 # For the Authentication data block, the hash is first and then
2682 # the signature.
2683 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002684 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002685 h.algorithm_type = alg.algorithm_type
2686 h.hash_offset = 0
2687 h.hash_size = alg.hash_num_bytes
2688 # Signature offset and size - it's stored right after the hash
2689 # (in Authentication data block).
2690 h.signature_offset = alg.hash_num_bytes
2691 h.signature_size = alg.signature_num_bytes
2692
2693 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002694 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002695
2696 # Generate Header data block.
2697 header_data_blob = h.encode()
2698
2699 # Generate Auxiliary data block.
2700 aux_data_blob = bytearray()
2701 aux_data_blob.extend(encoded_descriptors)
2702 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002703 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002704 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2705 aux_data_blob.extend('\0' * padding_bytes)
2706
2707 # Calculate the hash.
2708 binary_hash = bytearray()
2709 binary_signature = bytearray()
2710 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002711 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002712 ha.update(header_data_blob)
2713 ha.update(aux_data_blob)
2714 binary_hash.extend(ha.digest())
2715
2716 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002717 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04002718 binary_signature.extend(raw_sign(signing_helper,
2719 signing_helper_with_files,
2720 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002721 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002722 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002723
2724 # Generate Authentication data block.
2725 auth_data_blob = bytearray()
2726 auth_data_blob.extend(binary_hash)
2727 auth_data_blob.extend(binary_signature)
2728 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2729 auth_data_blob.extend('\0' * padding_bytes)
2730
2731 return header_data_blob + auth_data_blob + aux_data_blob
2732
2733 def extract_public_key(self, key_path, output):
2734 """Implements the 'extract_public_key' command.
2735
2736 Arguments:
2737 key_path: The path to a RSA private key file.
2738 output: The file to write to.
2739 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002740 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002741
David Zeuthenb1b994d2017-03-06 18:01:31 -05002742 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2743 partition_size):
2744 """Implementation of the append_vbmeta_image command.
2745
2746 Arguments:
2747 image_filename: File to add the footer to.
2748 vbmeta_image_filename: File to get vbmeta struct from.
2749 partition_size: Size of partition.
2750
2751 Raises:
2752 AvbError: If an argument is incorrect.
2753 """
2754 image = ImageHandler(image_filename)
2755
2756 if partition_size % image.block_size != 0:
2757 raise AvbError('Partition size of {} is not a multiple of the image '
2758 'block size {}.'.format(partition_size,
2759 image.block_size))
2760
2761 # If there's already a footer, truncate the image to its original
2762 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002763 if image.image_size >= AvbFooter.SIZE:
2764 image.seek(image.image_size - AvbFooter.SIZE)
2765 try:
2766 footer = AvbFooter(image.read(AvbFooter.SIZE))
2767 # Existing footer found. Just truncate.
2768 original_image_size = footer.original_image_size
2769 image.truncate(footer.original_image_size)
2770 except (LookupError, struct.error):
2771 original_image_size = image.image_size
2772 else:
2773 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05002774 original_image_size = image.image_size
2775
2776 # If anything goes wrong from here-on, restore the image back to
2777 # its original size.
2778 try:
2779 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2780 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2781
2782 # If the image isn't sparse, its size might not be a multiple of
2783 # the block size. This will screw up padding later so just grow it.
2784 if image.image_size % image.block_size != 0:
2785 assert not image.is_sparse
2786 padding_needed = image.block_size - (image.image_size%image.block_size)
2787 image.truncate(image.image_size + padding_needed)
2788
2789 # The append_raw() method requires content with size being a
2790 # multiple of |block_size| so add padding as needed. Also record
2791 # where this is written to since we'll need to put that in the
2792 # footer.
2793 vbmeta_offset = image.image_size
2794 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2795 len(vbmeta_blob))
2796 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2797
2798 # Append vbmeta blob and footer
2799 image.append_raw(vbmeta_blob_with_padding)
2800 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2801
2802 # Now insert a DONT_CARE chunk with enough bytes such that the
2803 # final Footer block is at the end of partition_size..
2804 image.append_dont_care(partition_size - vbmeta_end_offset -
2805 1*image.block_size)
2806
2807 # Generate the Footer that tells where the VBMeta footer
2808 # is. Also put enough padding in the front of the footer since
2809 # we'll write out an entire block.
2810 footer = AvbFooter()
2811 footer.original_image_size = original_image_size
2812 footer.vbmeta_offset = vbmeta_offset
2813 footer.vbmeta_size = len(vbmeta_blob)
2814 footer_blob = footer.encode()
2815 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2816 footer_blob)
2817 image.append_raw(footer_blob_with_padding)
2818
2819 except:
2820 # Truncate back to original size, then re-raise
2821 image.truncate(original_image_size)
2822 raise
2823
David Zeuthena4fee8b2016-08-22 15:20:43 -04002824 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002825 hash_algorithm, salt, chain_partitions, algorithm_name,
2826 key_path,
2827 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002828 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002829 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04002830 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04002831 signing_helper, signing_helper_with_files,
2832 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002833 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002834 print_required_libavb_version, use_persistent_digest,
2835 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002836 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002837
2838 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002839 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002840 partition_size: Size of partition.
2841 partition_name: Name of partition (without A/B suffix).
2842 hash_algorithm: Hash algorithm to use.
2843 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002844 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002845 algorithm_name: Name of algorithm to use.
2846 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002847 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002848 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002849 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002850 props: Properties to insert (List of strings of the form 'key:value').
2851 props_from_file: Properties to insert (List of strings 'key:<path>').
2852 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002853 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002854 dm-verity kernel cmdline from.
2855 include_descriptors_from_image: List of file objects for which
2856 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04002857 calc_max_image_size: Don't store the footer - instead calculate the
2858 maximum image size leaving enough room for metadata with the
2859 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002860 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002861 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002862 release_string: None or avbtool release string.
2863 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002864 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2865 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04002866 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002867 use_persistent_digest: Use a persistent digest on device.
2868 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002869
2870 Raises:
2871 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002872 """
David Zeuthen1097a782017-05-31 15:53:17 -04002873
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002874 required_libavb_version_minor = 0
2875 if use_persistent_digest or do_not_use_ab:
2876 required_libavb_version_minor = 1
2877
David Zeuthen1097a782017-05-31 15:53:17 -04002878 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002879 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002880 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04002881 return
2882
David Zeuthenbf562452017-05-17 18:04:43 -04002883 # First, calculate the maximum image size such that an image
2884 # this size + metadata (footer + vbmeta struct) fits in
2885 # |partition_size|.
2886 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002887 if partition_size < max_metadata_size:
2888 raise AvbError('Parition size of {} is too small. '
2889 'Needs to be at least {}'.format(
2890 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04002891 max_image_size = partition_size - max_metadata_size
2892
2893 # If we're asked to only calculate the maximum image size, we're done.
2894 if calc_max_image_size:
2895 print '{}'.format(max_image_size)
2896 return
2897
David Zeuthena4fee8b2016-08-22 15:20:43 -04002898 image = ImageHandler(image_filename)
2899
2900 if partition_size % image.block_size != 0:
2901 raise AvbError('Partition size of {} is not a multiple of the image '
2902 'block size {}.'.format(partition_size,
2903 image.block_size))
2904
David Zeuthen21e95262016-07-27 17:58:40 -04002905 # If there's already a footer, truncate the image to its original
2906 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2907 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002908 if image.image_size >= AvbFooter.SIZE:
2909 image.seek(image.image_size - AvbFooter.SIZE)
2910 try:
2911 footer = AvbFooter(image.read(AvbFooter.SIZE))
2912 # Existing footer found. Just truncate.
2913 original_image_size = footer.original_image_size
2914 image.truncate(footer.original_image_size)
2915 except (LookupError, struct.error):
2916 original_image_size = image.image_size
2917 else:
2918 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04002919 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002920
2921 # If anything goes wrong from here-on, restore the image back to
2922 # its original size.
2923 try:
David Zeuthen09692692016-09-30 16:16:40 -04002924 # If image size exceeds the maximum image size, fail.
2925 if image.image_size > max_image_size:
2926 raise AvbError('Image size of {} exceeds maximum image '
2927 'size of {} in order to fit in a partition '
2928 'size of {}.'.format(image.image_size, max_image_size,
2929 partition_size))
2930
David Zeuthen21e95262016-07-27 17:58:40 -04002931 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2932 if salt:
2933 salt = salt.decode('hex')
2934 else:
2935 if salt is None:
2936 # If salt is not explicitly specified, choose a hash
2937 # that's the same size as the hash size.
2938 hash_size = digest_size
2939 salt = open('/dev/urandom').read(hash_size)
2940 else:
2941 salt = ''
2942
2943 hasher = hashlib.new(name=hash_algorithm, string=salt)
2944 # TODO(zeuthen): might want to read this in chunks to avoid
2945 # memory pressure, then again, this is only supposed to be used
2946 # on kernel/initramfs partitions. Possible optimization.
2947 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002948 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002949 digest = hasher.digest()
2950
2951 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002952 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002953 h_desc.hash_algorithm = hash_algorithm
2954 h_desc.partition_name = partition_name
2955 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002956 h_desc.flags = 0
2957 if do_not_use_ab:
2958 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
2959 if not use_persistent_digest:
2960 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04002961
2962 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002963 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002964 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002965 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002966 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002967 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002968 include_descriptors_from_image, signing_helper,
2969 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002970 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002971
David Zeuthend247fcb2017-02-16 12:09:27 -05002972 # Write vbmeta blob, if requested.
2973 if output_vbmeta_image:
2974 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002975
David Zeuthend247fcb2017-02-16 12:09:27 -05002976 # Append vbmeta blob and footer, unless requested not to.
2977 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002978 # If the image isn't sparse, its size might not be a multiple of
2979 # the block size. This will screw up padding later so just grow it.
2980 if image.image_size % image.block_size != 0:
2981 assert not image.is_sparse
2982 padding_needed = image.block_size - (
2983 image.image_size % image.block_size)
2984 image.truncate(image.image_size + padding_needed)
2985
2986 # The append_raw() method requires content with size being a
2987 # multiple of |block_size| so add padding as needed. Also record
2988 # where this is written to since we'll need to put that in the
2989 # footer.
2990 vbmeta_offset = image.image_size
2991 padding_needed = (
2992 round_to_multiple(len(vbmeta_blob), image.block_size) -
2993 len(vbmeta_blob))
2994 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
2995
David Zeuthend247fcb2017-02-16 12:09:27 -05002996 image.append_raw(vbmeta_blob_with_padding)
2997 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2998
2999 # Now insert a DONT_CARE chunk with enough bytes such that the
3000 # final Footer block is at the end of partition_size..
3001 image.append_dont_care(partition_size - vbmeta_end_offset -
3002 1*image.block_size)
3003
3004 # Generate the Footer that tells where the VBMeta footer
3005 # is. Also put enough padding in the front of the footer since
3006 # we'll write out an entire block.
3007 footer = AvbFooter()
3008 footer.original_image_size = original_image_size
3009 footer.vbmeta_offset = vbmeta_offset
3010 footer.vbmeta_size = len(vbmeta_blob)
3011 footer_blob = footer.encode()
3012 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3013 footer_blob)
3014 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003015
David Zeuthen21e95262016-07-27 17:58:40 -04003016 except:
3017 # Truncate back to original size, then re-raise
3018 image.truncate(original_image_size)
3019 raise
3020
David Zeuthena4fee8b2016-08-22 15:20:43 -04003021 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003022 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003023 block_size, salt, chain_partitions, algorithm_name,
3024 key_path,
3025 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003026 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003027 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003028 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003029 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003030 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003031 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003032 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003033 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003034 print_required_libavb_version,
3035 use_persistent_root_digest, do_not_use_ab):
David Zeuthen21e95262016-07-27 17:58:40 -04003036 """Implements the 'add_hashtree_footer' command.
3037
3038 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3039 more information about dm-verity and these hashes.
3040
3041 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003042 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003043 partition_size: Size of partition.
3044 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003045 generate_fec: If True, generate FEC codes.
3046 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003047 hash_algorithm: Hash algorithm to use.
3048 block_size: Block size to use.
3049 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003050 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003051 algorithm_name: Name of algorithm to use.
3052 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003053 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003054 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003055 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003056 props: Properties to insert (List of strings of the form 'key:value').
3057 props_from_file: Properties to insert (List of strings 'key:<path>').
3058 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003059 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003060 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003061 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3062 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003063 include_descriptors_from_image: List of file objects for which
3064 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003065 calc_max_image_size: Don't store the hashtree or footer - instead
3066 calculate the maximum image size leaving enough room for hashtree
3067 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003068 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003069 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003070 release_string: None or avbtool release string.
3071 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003072 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3073 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003074 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003075 use_persistent_root_digest: Use a persistent root digest on device.
3076 do_not_use_ab: The partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003077
3078 Raises:
3079 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003080 """
David Zeuthen1097a782017-05-31 15:53:17 -04003081
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003082 required_libavb_version_minor = 0
3083 if use_persistent_root_digest or do_not_use_ab:
3084 required_libavb_version_minor = 1
3085
David Zeuthen1097a782017-05-31 15:53:17 -04003086 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003087 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003088 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003089 return
3090
David Zeuthen09692692016-09-30 16:16:40 -04003091 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3092 digest_padding = round_to_pow2(digest_size) - digest_size
3093
3094 # First, calculate the maximum image size such that an image
3095 # this size + the hashtree + metadata (footer + vbmeta struct)
3096 # fits in |partition_size|. We use very conservative figures for
3097 # metadata.
3098 (_, max_tree_size) = calc_hash_level_offsets(
3099 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003100 max_fec_size = 0
3101 if generate_fec:
3102 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3103 max_metadata_size = (max_fec_size + max_tree_size +
3104 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04003105 self.MAX_FOOTER_SIZE)
3106 max_image_size = partition_size - max_metadata_size
3107
3108 # If we're asked to only calculate the maximum image size, we're done.
3109 if calc_max_image_size:
3110 print '{}'.format(max_image_size)
3111 return
3112
David Zeuthena4fee8b2016-08-22 15:20:43 -04003113 image = ImageHandler(image_filename)
3114
3115 if partition_size % image.block_size != 0:
3116 raise AvbError('Partition size of {} is not a multiple of the image '
3117 'block size {}.'.format(partition_size,
3118 image.block_size))
3119
David Zeuthen21e95262016-07-27 17:58:40 -04003120 # If there's already a footer, truncate the image to its original
3121 # size. This way 'avbtool add_hashtree_footer' is idempotent
3122 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003123 if image.image_size >= AvbFooter.SIZE:
3124 image.seek(image.image_size - AvbFooter.SIZE)
3125 try:
3126 footer = AvbFooter(image.read(AvbFooter.SIZE))
3127 # Existing footer found. Just truncate.
3128 original_image_size = footer.original_image_size
3129 image.truncate(footer.original_image_size)
3130 except (LookupError, struct.error):
3131 original_image_size = image.image_size
3132 else:
3133 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003134 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003135
3136 # If anything goes wrong from here-on, restore the image back to
3137 # its original size.
3138 try:
3139 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003140 rounded_image_size = round_to_multiple(image.image_size, block_size)
3141 if rounded_image_size > image.image_size:
3142 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003143
David Zeuthen09692692016-09-30 16:16:40 -04003144 # If image size exceeds the maximum image size, fail.
3145 if image.image_size > max_image_size:
3146 raise AvbError('Image size of {} exceeds maximum image '
3147 'size of {} in order to fit in a partition '
3148 'size of {}.'.format(image.image_size, max_image_size,
3149 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003150
3151 if salt:
3152 salt = salt.decode('hex')
3153 else:
3154 if salt is None:
3155 # If salt is not explicitly specified, choose a hash
3156 # that's the same size as the hash size.
3157 hash_size = digest_size
3158 salt = open('/dev/urandom').read(hash_size)
3159 else:
3160 salt = ''
3161
David Zeuthena4fee8b2016-08-22 15:20:43 -04003162 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003163 # offsets in advance.
3164 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003165 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003166
David Zeuthena4fee8b2016-08-22 15:20:43 -04003167 # If the image isn't sparse, its size might not be a multiple of
3168 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003169 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003170 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003171 padding_needed = image.block_size - (image.image_size%image.block_size)
3172 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003173
David Zeuthena4fee8b2016-08-22 15:20:43 -04003174 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003175 tree_offset = image.image_size
3176 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003177 block_size,
3178 hash_algorithm, salt,
3179 digest_padding,
3180 hash_level_offsets,
3181 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003182
3183 # Generate HashtreeDescriptor with details about the tree we
3184 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04003185 ht_desc = AvbHashtreeDescriptor()
3186 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003187 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003188 ht_desc.tree_offset = tree_offset
3189 ht_desc.tree_size = tree_size
3190 ht_desc.data_block_size = block_size
3191 ht_desc.hash_block_size = block_size
3192 ht_desc.hash_algorithm = hash_algorithm
3193 ht_desc.partition_name = partition_name
3194 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003195 if do_not_use_ab:
3196 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3197 if not use_persistent_root_digest:
3198 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003199
David Zeuthen09692692016-09-30 16:16:40 -04003200 # Write the hash tree
3201 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3202 len(hash_tree))
3203 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3204 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003205 len_hashtree_and_fec = len(hash_tree_with_padding)
3206
3207 # Generate FEC codes, if requested.
3208 if generate_fec:
3209 fec_data = generate_fec_data(image_filename, fec_num_roots)
3210 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3211 len(fec_data))
3212 fec_data_with_padding = fec_data + '\0'*padding_needed
3213 fec_offset = image.image_size
3214 image.append_raw(fec_data_with_padding)
3215 len_hashtree_and_fec += len(fec_data_with_padding)
3216 # Update the hashtree descriptor.
3217 ht_desc.fec_num_roots = fec_num_roots
3218 ht_desc.fec_offset = fec_offset
3219 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003220
David Zeuthen73f2afa2017-05-17 16:54:11 -04003221 ht_desc_to_setup = None
3222 if setup_as_rootfs_from_kernel:
3223 ht_desc_to_setup = ht_desc
3224
David Zeuthena4fee8b2016-08-22 15:20:43 -04003225 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003226 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003227 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003228 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003229 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003230 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003231 include_descriptors_from_image, signing_helper,
3232 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003233 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003234 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3235 len(vbmeta_blob))
3236 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003237
David Zeuthend247fcb2017-02-16 12:09:27 -05003238 # Write vbmeta blob, if requested.
3239 if output_vbmeta_image:
3240 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003241
David Zeuthend247fcb2017-02-16 12:09:27 -05003242 # Append vbmeta blob and footer, unless requested not to.
3243 if not do_not_append_vbmeta_image:
3244 image.append_raw(vbmeta_blob_with_padding)
3245
3246 # Now insert a DONT_CARE chunk with enough bytes such that the
3247 # final Footer block is at the end of partition_size..
3248 image.append_dont_care(partition_size - image.image_size -
3249 1*image.block_size)
3250
3251 # Generate the Footer that tells where the VBMeta footer
3252 # is. Also put enough padding in the front of the footer since
3253 # we'll write out an entire block.
3254 footer = AvbFooter()
3255 footer.original_image_size = original_image_size
3256 footer.vbmeta_offset = vbmeta_offset
3257 footer.vbmeta_size = len(vbmeta_blob)
3258 footer_blob = footer.encode()
3259 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3260 footer_blob)
3261 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003262
David Zeuthen21e95262016-07-27 17:58:40 -04003263 except:
David Zeuthen09692692016-09-30 16:16:40 -04003264 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003265 image.truncate(original_image_size)
3266 raise
3267
David Zeuthenc68f0822017-03-31 17:22:35 -04003268 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003269 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003270 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003271 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003272 """Implements the 'make_atx_certificate' command.
3273
3274 Android Things certificates are required for Android Things public key
3275 metadata. They chain the vbmeta signing key for a particular product back to
3276 a fused, permanent root key. These certificates are fixed-length and fixed-
3277 format with the explicit goal of not parsing ASN.1 in bootloader code.
3278
3279 Arguments:
3280 output: Certificate will be written to this file on success.
3281 authority_key_path: A PEM file path with the authority private key.
3282 If None, then a certificate will be created without a
3283 signature. The signature can be created out-of-band
3284 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003285 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003286 subject_key_version: A 64-bit version value. If this is None, the number
3287 of seconds since the epoch is used.
3288 subject: A subject identifier. For Product Signing Key certificates this
3289 should be the same Product ID found in the permanent attributes.
3290 is_intermediate_authority: True if the certificate is for an intermediate
3291 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003292 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003293 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003294 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003295 """
3296 signed_data = bytearray()
3297 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003298 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003299 hasher = hashlib.sha256()
3300 hasher.update(subject)
3301 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003302 if not usage:
3303 usage = 'com.google.android.things.vboot'
3304 if is_intermediate_authority:
3305 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003306 hasher = hashlib.sha256()
3307 hasher.update(usage)
3308 signed_data.extend(hasher.digest())
3309 if not subject_key_version:
3310 subject_key_version = int(time.time())
3311 signed_data.extend(struct.pack('<Q', subject_key_version))
3312 signature = bytearray()
3313 if authority_key_path:
3314 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003315 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003316 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08003317 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09003318 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003319 hasher.update(signed_data)
3320 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003321 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3322 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003323 alg.signature_num_bytes, authority_key_path,
3324 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003325 output.write(signed_data)
3326 output.write(signature)
3327
David Zeuthenc68f0822017-03-31 17:22:35 -04003328 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003329 product_id):
3330 """Implements the 'make_atx_permanent_attributes' command.
3331
3332 Android Things permanent attributes are designed to be permanent for a
3333 particular product and a hash of these attributes should be fused into
3334 hardware to enforce this.
3335
3336 Arguments:
3337 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003338 root_authority_key_path: Path to a PEM or DER public key for
3339 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003340 product_id: A 16-byte Product ID.
3341
3342 Raises:
3343 AvbError: If an argument is incorrect.
3344 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003345 EXPECTED_PRODUCT_ID_SIZE = 16
3346 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003347 raise AvbError('Invalid Product ID length.')
3348 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003349 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003350 output.write(product_id)
3351
3352 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003353 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003354 """Implements the 'make_atx_metadata' command.
3355
3356 Android Things metadata are included in vbmeta images to facilitate
3357 verification. The output of this command can be used as the
3358 public_key_metadata argument to other commands.
3359
3360 Arguments:
3361 output: Metadata will be written to this file on success.
3362 intermediate_key_certificate: A certificate file as output by
3363 make_atx_certificate with
3364 is_intermediate_authority set to true.
3365 product_key_certificate: A certificate file as output by
3366 make_atx_certificate with
3367 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003368
3369 Raises:
3370 AvbError: If an argument is incorrect.
3371 """
Darren Krahn43e12d82017-02-24 16:26:31 -08003372 EXPECTED_CERTIFICATE_SIZE = 1620
3373 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003374 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003375 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003376 raise AvbError('Invalid product key certificate length.')
3377 output.write(struct.pack('<I', 1)) # Format Version
3378 output.write(intermediate_key_certificate)
3379 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003380
Darren Krahnfccd64e2018-01-16 17:39:35 -08003381 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3382 unlock_key_certificate, challenge_path,
3383 unlock_key_path, signing_helper,
3384 signing_helper_with_files):
3385 """Implements the 'make_atx_unlock_credential' command.
3386
3387 Android Things unlock credentials can be used to authorize the unlock of AVB
3388 on a device. These credentials are presented to an Android Things bootloader
3389 via the fastboot interface in response to a 16-byte challenge. This method
3390 creates all fields of the credential except the challenge signature field
3391 (which is the last field) and can optionally create the challenge signature
3392 field as well if a challenge and the unlock_key_path is provided.
3393
3394 Arguments:
3395 output: The credential will be written to this file on success.
3396 intermediate_key_certificate: A certificate file as output by
3397 make_atx_certificate with
3398 is_intermediate_authority set to true.
3399 unlock_key_certificate: A certificate file as output by
3400 make_atx_certificate with
3401 is_intermediate_authority set to false and the
3402 usage set to
3403 'com.google.android.things.vboot.unlock'.
3404 challenge_path: [optional] A path to the challenge to sign.
3405 unlock_key_path: [optional] A PEM file path with the unlock private key.
3406 signing_helper: Program which signs a hash and returns the signature.
3407 signing_helper_with_files: Same as signing_helper but uses files instead.
3408
3409 Raises:
3410 AvbError: If an argument is incorrect.
3411 """
3412 EXPECTED_CERTIFICATE_SIZE = 1620
3413 EXPECTED_CHALLENGE_SIZE = 16
3414 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3415 raise AvbError('Invalid intermediate key certificate length.')
3416 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3417 raise AvbError('Invalid product key certificate length.')
3418 challenge = bytearray()
3419 if challenge_path:
3420 with open(challenge_path, 'r') as f:
3421 challenge = f.read()
3422 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3423 raise AvbError('Invalid unlock challenge length.')
3424 output.write(struct.pack('<I', 1)) # Format Version
3425 output.write(intermediate_key_certificate)
3426 output.write(unlock_key_certificate)
3427 if challenge_path and unlock_key_path:
3428 signature = bytearray()
3429 padding_and_hash = bytearray()
3430 algorithm_name = 'SHA512_RSA4096'
3431 alg = ALGORITHMS[algorithm_name]
3432 hasher = hashlib.sha512()
3433 padding_and_hash.extend(alg.padding)
3434 hasher.update(challenge)
3435 padding_and_hash.extend(hasher.digest())
3436 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3437 algorithm_name,
3438 alg.signature_num_bytes, unlock_key_path,
3439 padding_and_hash))
3440 output.write(signature)
3441
David Zeuthen21e95262016-07-27 17:58:40 -04003442
3443def calc_hash_level_offsets(image_size, block_size, digest_size):
3444 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3445
3446 Arguments:
3447 image_size: The size of the image to calculate a Merkle-tree for.
3448 block_size: The block size, e.g. 4096.
3449 digest_size: The size of each hash, e.g. 32 for SHA-256.
3450
3451 Returns:
3452 A tuple where the first argument is an array of offsets and the
3453 second is size of the tree, in bytes.
3454 """
3455 level_offsets = []
3456 level_sizes = []
3457 tree_size = 0
3458
3459 num_levels = 0
3460 size = image_size
3461 while size > block_size:
3462 num_blocks = (size + block_size - 1) / block_size
3463 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3464
3465 level_sizes.append(level_size)
3466 tree_size += level_size
3467 num_levels += 1
3468
3469 size = level_size
3470
3471 for n in range(0, num_levels):
3472 offset = 0
3473 for m in range(n + 1, num_levels):
3474 offset += level_sizes[m]
3475 level_offsets.append(offset)
3476
David Zeuthena4fee8b2016-08-22 15:20:43 -04003477 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003478
3479
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003480# See system/extras/libfec/include/fec/io.h for these definitions.
3481FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3482FEC_MAGIC = 0xfecfecfe
3483
3484
3485def calc_fec_data_size(image_size, num_roots):
3486 """Calculates how much space FEC data will take.
3487
3488 Args:
3489 image_size: The size of the image.
3490 num_roots: Number of roots.
3491
3492 Returns:
3493 The number of bytes needed for FEC for an image of the given size
3494 and with the requested number of FEC roots.
3495
3496 Raises:
3497 ValueError: If output from the 'fec' tool is invalid.
3498
3499 """
3500 p = subprocess.Popen(
3501 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3502 stdout=subprocess.PIPE,
3503 stderr=subprocess.PIPE)
3504 (pout, perr) = p.communicate()
3505 retcode = p.wait()
3506 if retcode != 0:
3507 raise ValueError('Error invoking fec: {}'.format(perr))
3508 return int(pout)
3509
3510
3511def generate_fec_data(image_filename, num_roots):
3512 """Generate FEC codes for an image.
3513
3514 Args:
3515 image_filename: The filename of the image.
3516 num_roots: Number of roots.
3517
3518 Returns:
3519 The FEC data blob.
3520
3521 Raises:
3522 ValueError: If output from the 'fec' tool is invalid.
3523 """
3524 fec_tmpfile = tempfile.NamedTemporaryFile()
3525 subprocess.check_call(
3526 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3527 fec_tmpfile.name],
3528 stderr=open(os.devnull))
3529 fec_data = fec_tmpfile.read()
3530 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3531 footer_data = fec_data[-footer_size:]
3532 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3533 footer_data)
3534 if magic != FEC_MAGIC:
3535 raise ValueError('Unexpected magic in FEC footer')
3536 return fec_data[0:fec_size]
3537
3538
David Zeuthen21e95262016-07-27 17:58:40 -04003539def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003540 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003541 """Generates a Merkle-tree for a file.
3542
3543 Args:
3544 image: The image, as a file.
3545 image_size: The size of the image.
3546 block_size: The block size, e.g. 4096.
3547 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3548 salt: The salt to use.
3549 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003550 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003551 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003552
3553 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003554 A tuple where the first element is the top-level hash and the
3555 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003556 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003557 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003558 hash_src_offset = 0
3559 hash_src_size = image_size
3560 level_num = 0
3561 while hash_src_size > block_size:
3562 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003563 remaining = hash_src_size
3564 while remaining > 0:
3565 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003566 # Only read from the file for the first level - for subsequent
3567 # levels, access the array we're building.
3568 if level_num == 0:
3569 image.seek(hash_src_offset + hash_src_size - remaining)
3570 data = image.read(min(remaining, block_size))
3571 else:
3572 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3573 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003574 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003575
3576 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003577 if len(data) < block_size:
3578 hasher.update('\0' * (block_size - len(data)))
3579 level_output += hasher.digest()
3580 if digest_padding > 0:
3581 level_output += '\0' * digest_padding
3582
3583 padding_needed = (round_to_multiple(
3584 len(level_output), block_size) - len(level_output))
3585 level_output += '\0' * padding_needed
3586
David Zeuthena4fee8b2016-08-22 15:20:43 -04003587 # Copy level-output into resulting tree.
3588 offset = hash_level_offsets[level_num]
3589 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003590
David Zeuthena4fee8b2016-08-22 15:20:43 -04003591 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003592 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003593 level_num += 1
3594
3595 hasher = hashlib.new(name=hash_alg_name, string=salt)
3596 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003597 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003598
3599
3600class AvbTool(object):
3601 """Object for avbtool command-line tool."""
3602
3603 def __init__(self):
3604 """Initializer method."""
3605 self.avb = Avb()
3606
3607 def _add_common_args(self, sub_parser):
3608 """Adds arguments used by several sub-commands.
3609
3610 Arguments:
3611 sub_parser: The parser to add arguments to.
3612 """
3613 sub_parser.add_argument('--algorithm',
3614 help='Algorithm to use (default: NONE)',
3615 metavar='ALGORITHM',
3616 default='NONE')
3617 sub_parser.add_argument('--key',
3618 help='Path to RSA private key file',
3619 metavar='KEY',
3620 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003621 sub_parser.add_argument('--signing_helper',
3622 help='Path to helper used for signing',
3623 metavar='APP',
3624 default=None,
3625 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003626 sub_parser.add_argument('--signing_helper_with_files',
3627 help='Path to helper used for signing using files',
3628 metavar='APP',
3629 default=None,
3630 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003631 sub_parser.add_argument('--public_key_metadata',
3632 help='Path to public key metadata file',
3633 metavar='KEY_METADATA',
3634 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003635 sub_parser.add_argument('--rollback_index',
3636 help='Rollback Index',
3637 type=parse_number,
3638 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003639 # This is used internally for unit tests. Do not include in --help output.
3640 sub_parser.add_argument('--internal_release_string',
3641 help=argparse.SUPPRESS)
3642 sub_parser.add_argument('--append_to_release_string',
3643 help='Text to append to release string',
3644 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003645 sub_parser.add_argument('--prop',
3646 help='Add property',
3647 metavar='KEY:VALUE',
3648 action='append')
3649 sub_parser.add_argument('--prop_from_file',
3650 help='Add property from file',
3651 metavar='KEY:PATH',
3652 action='append')
3653 sub_parser.add_argument('--kernel_cmdline',
3654 help='Add kernel cmdline',
3655 metavar='CMDLINE',
3656 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003657 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3658 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3659 # at some future point.
3660 sub_parser.add_argument('--setup_rootfs_from_kernel',
3661 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003662 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003663 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003664 type=argparse.FileType('rb'))
3665 sub_parser.add_argument('--include_descriptors_from_image',
3666 help='Include descriptors from image',
3667 metavar='IMAGE',
3668 action='append',
3669 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04003670 sub_parser.add_argument('--print_required_libavb_version',
3671 help=('Don\'t store the footer - '
3672 'instead calculate the required libavb '
3673 'version for the given options.'),
3674 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003675 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3676 sub_parser.add_argument('--chain_partition',
3677 help='Allow signed integrity-data for partition',
3678 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3679 action='append')
3680 sub_parser.add_argument('--flags',
3681 help='VBMeta flags',
3682 type=parse_number,
3683 default=0)
3684 sub_parser.add_argument('--set_hashtree_disabled_flag',
3685 help='Set the HASHTREE_DISABLED flag',
3686 action='store_true')
3687
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003688 def _add_common_footer_args(self, sub_parser):
3689 """Adds arguments used by add_*_footer sub-commands.
3690
3691 Arguments:
3692 sub_parser: The parser to add arguments to.
3693 """
3694 sub_parser.add_argument('--use_persistent_digest',
3695 help='Use a persistent digest on device instead of '
3696 'storing the digest in the descriptor. This '
3697 'cannot be used with A/B so must be combined '
3698 'with --do_not_use_ab when an A/B suffix is '
3699 'expected at runtime.',
3700 action='store_true')
3701 sub_parser.add_argument('--do_not_use_ab',
3702 help='The partition does not use A/B even when an '
3703 'A/B suffix is present. This must not be used '
3704 'for vbmeta or chained partitions.',
3705 action='store_true')
3706
David Zeuthena5fd3a42017-02-27 16:38:54 -05003707 def _fixup_common_args(self, args):
3708 """Common fixups needed by subcommands.
3709
3710 Arguments:
3711 args: Arguments to modify.
3712
3713 Returns:
3714 The modified arguments.
3715 """
3716 if args.set_hashtree_disabled_flag:
3717 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3718 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003719
3720 def run(self, argv):
3721 """Command-line processor.
3722
3723 Arguments:
3724 argv: Pass sys.argv from main.
3725 """
3726 parser = argparse.ArgumentParser()
3727 subparsers = parser.add_subparsers(title='subcommands')
3728
3729 sub_parser = subparsers.add_parser('version',
3730 help='Prints version of avbtool.')
3731 sub_parser.set_defaults(func=self.version)
3732
3733 sub_parser = subparsers.add_parser('extract_public_key',
3734 help='Extract public key.')
3735 sub_parser.add_argument('--key',
3736 help='Path to RSA private key file',
3737 required=True)
3738 sub_parser.add_argument('--output',
3739 help='Output file name',
3740 type=argparse.FileType('wb'),
3741 required=True)
3742 sub_parser.set_defaults(func=self.extract_public_key)
3743
3744 sub_parser = subparsers.add_parser('make_vbmeta_image',
3745 help='Makes a vbmeta image.')
3746 sub_parser.add_argument('--output',
3747 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04003748 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04003749 sub_parser.add_argument('--padding_size',
3750 metavar='NUMBER',
3751 help='If non-zero, pads output with NUL bytes so '
3752 'its size is a multiple of NUMBER (default: 0)',
3753 type=parse_number,
3754 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04003755 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003756 sub_parser.set_defaults(func=self.make_vbmeta_image)
3757
3758 sub_parser = subparsers.add_parser('add_hash_footer',
3759 help='Add hashes and footer to image.')
3760 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003761 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003762 type=argparse.FileType('rab+'))
3763 sub_parser.add_argument('--partition_size',
3764 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003765 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003766 sub_parser.add_argument('--partition_name',
3767 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04003768 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003769 sub_parser.add_argument('--hash_algorithm',
3770 help='Hash algorithm to use (default: sha256)',
3771 default='sha256')
3772 sub_parser.add_argument('--salt',
3773 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04003774 sub_parser.add_argument('--calc_max_image_size',
3775 help=('Don\'t store the footer - '
3776 'instead calculate the maximum image size '
3777 'leaving enough room for metadata with '
3778 'the given partition size.'),
3779 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003780 sub_parser.add_argument('--output_vbmeta_image',
3781 help='Also write vbmeta struct to file',
3782 type=argparse.FileType('wb'))
3783 sub_parser.add_argument('--do_not_append_vbmeta_image',
3784 help=('Do not append vbmeta struct or footer '
3785 'to the image'),
3786 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003787 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003788 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003789 sub_parser.set_defaults(func=self.add_hash_footer)
3790
David Zeuthenb1b994d2017-03-06 18:01:31 -05003791 sub_parser = subparsers.add_parser('append_vbmeta_image',
3792 help='Append vbmeta image to image.')
3793 sub_parser.add_argument('--image',
3794 help='Image to append vbmeta blob to',
3795 type=argparse.FileType('rab+'))
3796 sub_parser.add_argument('--partition_size',
3797 help='Partition size',
3798 type=parse_number,
3799 required=True)
3800 sub_parser.add_argument('--vbmeta_image',
3801 help='Image with vbmeta blob to append',
3802 type=argparse.FileType('rb'))
3803 sub_parser.set_defaults(func=self.append_vbmeta_image)
3804
David Zeuthen21e95262016-07-27 17:58:40 -04003805 sub_parser = subparsers.add_parser('add_hashtree_footer',
3806 help='Add hashtree and footer to image.')
3807 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003808 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003809 type=argparse.FileType('rab+'))
3810 sub_parser.add_argument('--partition_size',
3811 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04003812 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04003813 sub_parser.add_argument('--partition_name',
3814 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003815 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003816 sub_parser.add_argument('--hash_algorithm',
3817 help='Hash algorithm to use (default: sha1)',
3818 default='sha1')
3819 sub_parser.add_argument('--salt',
3820 help='Salt in hex (default: /dev/urandom)')
3821 sub_parser.add_argument('--block_size',
3822 help='Block size (default: 4096)',
3823 type=parse_number,
3824 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04003825 # TODO(zeuthen): The --generate_fec option was removed when we
3826 # moved to generating FEC by default. To avoid breaking existing
3827 # users needing to transition we simply just print a warning below
3828 # in add_hashtree_footer(). Remove this option and the warning at
3829 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003830 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04003831 help=argparse.SUPPRESS,
3832 action='store_true')
3833 sub_parser.add_argument('--do_not_generate_fec',
3834 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003835 action='store_true')
3836 sub_parser.add_argument('--fec_num_roots',
3837 help='Number of roots for FEC (default: 2)',
3838 type=parse_number,
3839 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003840 sub_parser.add_argument('--calc_max_image_size',
3841 help=('Don\'t store the hashtree or footer - '
3842 'instead calculate the maximum image size '
3843 'leaving enough room for hashtree '
3844 'and metadata with the given partition '
3845 'size.'),
3846 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003847 sub_parser.add_argument('--output_vbmeta_image',
3848 help='Also write vbmeta struct to file',
3849 type=argparse.FileType('wb'))
3850 sub_parser.add_argument('--do_not_append_vbmeta_image',
3851 help=('Do not append vbmeta struct or footer '
3852 'to the image'),
3853 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04003854 # This is different from --setup_rootfs_from_kernel insofar that
3855 # it doesn't take an IMAGE, the generated cmdline will be for the
3856 # hashtree we're adding.
3857 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
3858 action='store_true',
3859 help='Adds kernel cmdline for setting up rootfs')
David Zeuthen21e95262016-07-27 17:58:40 -04003860 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003861 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003862 sub_parser.set_defaults(func=self.add_hashtree_footer)
3863
3864 sub_parser = subparsers.add_parser('erase_footer',
3865 help='Erase footer from an image.')
3866 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003867 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003868 type=argparse.FileType('rwb+'),
3869 required=True)
3870 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003871 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003872 action='store_true')
3873 sub_parser.set_defaults(func=self.erase_footer)
3874
David Zeuthen2bc232b2017-04-19 14:25:19 -04003875 sub_parser = subparsers.add_parser('resize_image',
3876 help='Resize image with a footer.')
3877 sub_parser.add_argument('--image',
3878 help='Image with a footer',
3879 type=argparse.FileType('rwb+'),
3880 required=True)
3881 sub_parser.add_argument('--partition_size',
3882 help='New partition size',
3883 type=parse_number)
3884 sub_parser.set_defaults(func=self.resize_image)
3885
David Zeuthen21e95262016-07-27 17:58:40 -04003886 sub_parser = subparsers.add_parser(
3887 'info_image',
3888 help='Show information about vbmeta or footer.')
3889 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003890 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003891 type=argparse.FileType('rb'),
3892 required=True)
3893 sub_parser.add_argument('--output',
3894 help='Write info to file',
3895 type=argparse.FileType('wt'),
3896 default=sys.stdout)
3897 sub_parser.set_defaults(func=self.info_image)
3898
David Zeuthenb623d8b2017-04-04 16:05:53 -04003899 sub_parser = subparsers.add_parser(
3900 'verify_image',
3901 help='Verify an image.')
3902 sub_parser.add_argument('--image',
3903 help='Image to verify',
3904 type=argparse.FileType('rb'),
3905 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04003906 sub_parser.add_argument('--key',
3907 help='Check embedded public key matches KEY',
3908 metavar='KEY',
3909 required=False)
3910 sub_parser.add_argument('--expected_chain_partition',
3911 help='Expected chain partition',
3912 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3913 action='append')
David Zeuthenb623d8b2017-04-04 16:05:53 -04003914 sub_parser.set_defaults(func=self.verify_image)
3915
David Zeuthenb8643c02018-05-17 17:21:18 -04003916 sub_parser = subparsers.add_parser(
3917 'calculate_vbmeta_digest',
3918 help='Calculate vbmeta digest.')
3919 sub_parser.add_argument('--image',
3920 help='Image to calculate digest for',
3921 type=argparse.FileType('rb'),
3922 required=True)
3923 sub_parser.add_argument('--hash_algorithm',
3924 help='Hash algorithm to use (default: sha256)',
3925 default='sha256')
3926 sub_parser.add_argument('--output',
3927 help='Write hex digest to file (default: stdout)',
3928 type=argparse.FileType('wt'),
3929 default=sys.stdout)
3930 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
3931
David Zeuthen8b6973b2016-09-20 12:39:49 -04003932 sub_parser = subparsers.add_parser('set_ab_metadata',
3933 help='Set A/B metadata.')
3934 sub_parser.add_argument('--misc_image',
3935 help=('The misc image to modify. If the image does '
3936 'not exist, it will be created.'),
3937 type=argparse.FileType('r+b'),
3938 required=True)
3939 sub_parser.add_argument('--slot_data',
3940 help=('Slot data of the form "priority", '
3941 '"tries_remaining", "sucessful_boot" for '
3942 'slot A followed by the same for slot B, '
3943 'separated by colons. The default value '
3944 'is 15:7:0:14:7:0.'),
3945 default='15:7:0:14:7:0')
3946 sub_parser.set_defaults(func=self.set_ab_metadata)
3947
Darren Krahn147b08d2016-12-20 16:38:29 -08003948 sub_parser = subparsers.add_parser(
3949 'make_atx_certificate',
3950 help='Create an Android Things eXtension (ATX) certificate.')
3951 sub_parser.add_argument('--output',
3952 help='Write certificate to file',
3953 type=argparse.FileType('wb'),
3954 default=sys.stdout)
3955 sub_parser.add_argument('--subject',
3956 help=('Path to subject file'),
3957 type=argparse.FileType('rb'),
3958 required=True)
3959 sub_parser.add_argument('--subject_key',
3960 help=('Path to subject RSA public key file'),
3961 type=argparse.FileType('rb'),
3962 required=True)
3963 sub_parser.add_argument('--subject_key_version',
3964 help=('Version of the subject key'),
3965 type=parse_number,
3966 required=False)
3967 sub_parser.add_argument('--subject_is_intermediate_authority',
3968 help=('Generate an intermediate authority '
3969 'certificate'),
3970 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08003971 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07003972 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08003973 'string'),
3974 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08003975 sub_parser.add_argument('--authority_key',
3976 help='Path to authority RSA private key file',
3977 required=False)
3978 sub_parser.add_argument('--signing_helper',
3979 help='Path to helper used for signing',
3980 metavar='APP',
3981 default=None,
3982 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003983 sub_parser.add_argument('--signing_helper_with_files',
3984 help='Path to helper used for signing using files',
3985 metavar='APP',
3986 default=None,
3987 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08003988 sub_parser.set_defaults(func=self.make_atx_certificate)
3989
3990 sub_parser = subparsers.add_parser(
3991 'make_atx_permanent_attributes',
3992 help='Create Android Things eXtension (ATX) permanent attributes.')
3993 sub_parser.add_argument('--output',
3994 help='Write attributes to file',
3995 type=argparse.FileType('wb'),
3996 default=sys.stdout)
3997 sub_parser.add_argument('--root_authority_key',
3998 help='Path to authority RSA public key file',
3999 type=argparse.FileType('rb'),
4000 required=True)
4001 sub_parser.add_argument('--product_id',
4002 help=('Path to Product ID file'),
4003 type=argparse.FileType('rb'),
4004 required=True)
4005 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4006
4007 sub_parser = subparsers.add_parser(
4008 'make_atx_metadata',
4009 help='Create Android Things eXtension (ATX) metadata.')
4010 sub_parser.add_argument('--output',
4011 help='Write metadata to file',
4012 type=argparse.FileType('wb'),
4013 default=sys.stdout)
4014 sub_parser.add_argument('--intermediate_key_certificate',
4015 help='Path to intermediate key certificate file',
4016 type=argparse.FileType('rb'),
4017 required=True)
4018 sub_parser.add_argument('--product_key_certificate',
4019 help='Path to product key certificate file',
4020 type=argparse.FileType('rb'),
4021 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004022 sub_parser.set_defaults(func=self.make_atx_metadata)
4023
Darren Krahnfccd64e2018-01-16 17:39:35 -08004024 sub_parser = subparsers.add_parser(
4025 'make_atx_unlock_credential',
4026 help='Create an Android Things eXtension (ATX) unlock credential.')
4027 sub_parser.add_argument('--output',
4028 help='Write credential to file',
4029 type=argparse.FileType('wb'),
4030 default=sys.stdout)
4031 sub_parser.add_argument('--intermediate_key_certificate',
4032 help='Path to intermediate key certificate file',
4033 type=argparse.FileType('rb'),
4034 required=True)
4035 sub_parser.add_argument('--unlock_key_certificate',
4036 help='Path to unlock key certificate file',
4037 type=argparse.FileType('rb'),
4038 required=True)
4039 sub_parser.add_argument('--challenge',
4040 help='Path to the challenge to sign (optional). If '
4041 'this is not provided the challenge signature '
4042 'field is omitted and can be concatenated '
4043 'later.',
4044 required=False)
4045 sub_parser.add_argument('--unlock_key',
4046 help='Path to unlock key (optional). Must be '
4047 'provided if using --challenge.',
4048 required=False)
4049 sub_parser.add_argument('--signing_helper',
4050 help='Path to helper used for signing',
4051 metavar='APP',
4052 default=None,
4053 required=False)
4054 sub_parser.add_argument('--signing_helper_with_files',
4055 help='Path to helper used for signing using files',
4056 metavar='APP',
4057 default=None,
4058 required=False)
4059 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4060
David Zeuthen21e95262016-07-27 17:58:40 -04004061 args = parser.parse_args(argv[1:])
4062 try:
4063 args.func(args)
4064 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004065 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004066 sys.exit(1)
4067
4068 def version(self, _):
4069 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004070 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004071
4072 def extract_public_key(self, args):
4073 """Implements the 'extract_public_key' sub-command."""
4074 self.avb.extract_public_key(args.key, args.output)
4075
4076 def make_vbmeta_image(self, args):
4077 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004078 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004079 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004080 args.algorithm, args.key,
4081 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004082 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004083 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004084 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004085 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004086 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004087 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004088 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004089 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004090 args.print_required_libavb_version,
4091 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004092
David Zeuthenb1b994d2017-03-06 18:01:31 -05004093 def append_vbmeta_image(self, args):
4094 """Implements the 'append_vbmeta_image' sub-command."""
4095 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4096 args.partition_size)
4097
David Zeuthen21e95262016-07-27 17:58:40 -04004098 def add_hash_footer(self, args):
4099 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004100 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004101 self.avb.add_hash_footer(args.image.name if args.image else None,
4102 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004103 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004104 args.salt, args.chain_partition, args.algorithm,
4105 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004106 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004107 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004108 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004109 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004110 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004111 args.calc_max_image_size,
4112 args.signing_helper,
4113 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004114 args.internal_release_string,
4115 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004116 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004117 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004118 args.print_required_libavb_version,
4119 args.use_persistent_digest,
4120 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004121
4122 def add_hashtree_footer(self, args):
4123 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004124 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004125 # TODO(zeuthen): Remove when removing support for the
4126 # '--generate_fec' option above.
4127 if args.generate_fec:
4128 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4129 'is now generated by default. Use the option '
4130 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04004131 self.avb.add_hashtree_footer(args.image.name if args.image else None,
4132 args.partition_size,
4133 args.partition_name,
David Zeuthenbce9a292017-05-10 17:18:04 -04004134 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04004135 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004136 args.salt, args.chain_partition, args.algorithm,
4137 args.key, args.public_key_metadata,
4138 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04004139 args.prop_from_file,
4140 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004141 args.setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004142 args.setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04004143 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004144 args.calc_max_image_size,
4145 args.signing_helper,
4146 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004147 args.internal_release_string,
4148 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004149 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004150 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004151 args.print_required_libavb_version,
4152 args.use_persistent_digest,
4153 args.do_not_use_ab)
David Zeuthend247fcb2017-02-16 12:09:27 -05004154
David Zeuthen21e95262016-07-27 17:58:40 -04004155 def erase_footer(self, args):
4156 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004157 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004158
David Zeuthen2bc232b2017-04-19 14:25:19 -04004159 def resize_image(self, args):
4160 """Implements the 'resize_image' sub-command."""
4161 self.avb.resize_image(args.image.name, args.partition_size)
4162
David Zeuthen8b6973b2016-09-20 12:39:49 -04004163 def set_ab_metadata(self, args):
4164 """Implements the 'set_ab_metadata' sub-command."""
4165 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4166
David Zeuthen21e95262016-07-27 17:58:40 -04004167 def info_image(self, args):
4168 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004169 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004170
David Zeuthenb623d8b2017-04-04 16:05:53 -04004171 def verify_image(self, args):
4172 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004173 self.avb.verify_image(args.image.name, args.key,
4174 args.expected_chain_partition)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004175
David Zeuthenb8643c02018-05-17 17:21:18 -04004176 def calculate_vbmeta_digest(self, args):
4177 """Implements the 'calculate_vbmeta_digest' sub-command."""
4178 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4179 args.output)
4180
Darren Krahn147b08d2016-12-20 16:38:29 -08004181 def make_atx_certificate(self, args):
4182 """Implements the 'make_atx_certificate' sub-command."""
4183 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004184 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004185 args.subject_key_version,
4186 args.subject.read(),
4187 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004188 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004189 args.signing_helper,
4190 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004191
4192 def make_atx_permanent_attributes(self, args):
4193 """Implements the 'make_atx_permanent_attributes' sub-command."""
4194 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004195 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004196 args.product_id.read())
4197
4198 def make_atx_metadata(self, args):
4199 """Implements the 'make_atx_metadata' sub-command."""
4200 self.avb.make_atx_metadata(args.output,
4201 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004202 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004203
Darren Krahnfccd64e2018-01-16 17:39:35 -08004204 def make_atx_unlock_credential(self, args):
4205 """Implements the 'make_atx_unlock_credential' sub-command."""
4206 self.avb.make_atx_unlock_credential(
4207 args.output,
4208 args.intermediate_key_certificate.read(),
4209 args.unlock_key_certificate.read(),
4210 args.challenge,
4211 args.unlock_key,
4212 args.signing_helper,
4213 args.signing_helper_with_files)
4214
David Zeuthen21e95262016-07-27 17:58:40 -04004215
4216if __name__ == '__main__':
4217 tool = AvbTool()
4218 tool.run(sys.argv)