blob: f365e80ce496b87268264b2de1e079198beae851 [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
Tao Bao80418a52018-07-20 11:41:22 -070068 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040069
70 The constant |ALGORITHMS| is a dictionary from human-readable
71 names (e.g 'SHA256_RSA2048') to instances of this class.
72
73 Attributes:
74 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040075 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040076 hash_num_bytes: Number of bytes used to store the hash.
77 signature_num_bytes: Number of bytes used to store the signature.
78 public_key_num_bytes: Number of bytes used to store the public key.
79 padding: Padding used for signature, if any.
80 """
81
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
83 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040084 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040085 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.hash_num_bytes = hash_num_bytes
87 self.signature_num_bytes = signature_num_bytes
88 self.public_key_num_bytes = public_key_num_bytes
89 self.padding = padding
90
David Zeuthenb623d8b2017-04-04 16:05:53 -040091
David Zeuthen21e95262016-07-27 17:58:40 -040092# This must be kept in sync with the avb_crypto.h file.
93#
94# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
95# obtained from section 5.2.2 of RFC 4880.
96ALGORITHMS = {
97 'NONE': Algorithm(
98 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040099 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400100 hash_num_bytes=0,
101 signature_num_bytes=0,
102 public_key_num_bytes=0,
103 padding=[]),
104 'SHA256_RSA2048': Algorithm(
105 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400106 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400107 hash_num_bytes=32,
108 signature_num_bytes=256,
109 public_key_num_bytes=8 + 2*2048/8,
110 padding=[
111 # PKCS1-v1_5 padding
112 0x00, 0x01] + [0xff]*202 + [0x00] + [
113 # ASN.1 header
114 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
115 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
116 0x00, 0x04, 0x20,
117 ]),
118 'SHA256_RSA4096': Algorithm(
119 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400120 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400121 hash_num_bytes=32,
122 signature_num_bytes=512,
123 public_key_num_bytes=8 + 2*4096/8,
124 padding=[
125 # PKCS1-v1_5 padding
126 0x00, 0x01] + [0xff]*458 + [0x00] + [
127 # ASN.1 header
128 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
129 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
130 0x00, 0x04, 0x20,
131 ]),
132 'SHA256_RSA8192': Algorithm(
133 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400134 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400135 hash_num_bytes=32,
136 signature_num_bytes=1024,
137 public_key_num_bytes=8 + 2*8192/8,
138 padding=[
139 # PKCS1-v1_5 padding
140 0x00, 0x01] + [0xff]*970 + [0x00] + [
141 # ASN.1 header
142 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
143 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
144 0x00, 0x04, 0x20,
145 ]),
146 'SHA512_RSA2048': Algorithm(
147 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400148 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400149 hash_num_bytes=64,
150 signature_num_bytes=256,
151 public_key_num_bytes=8 + 2*2048/8,
152 padding=[
153 # PKCS1-v1_5 padding
154 0x00, 0x01] + [0xff]*170 + [0x00] + [
155 # ASN.1 header
156 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
157 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
158 0x00, 0x04, 0x40
159 ]),
160 'SHA512_RSA4096': Algorithm(
161 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400162 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400163 hash_num_bytes=64,
164 signature_num_bytes=512,
165 public_key_num_bytes=8 + 2*4096/8,
166 padding=[
167 # PKCS1-v1_5 padding
168 0x00, 0x01] + [0xff]*426 + [0x00] + [
169 # ASN.1 header
170 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
171 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
172 0x00, 0x04, 0x40
173 ]),
174 'SHA512_RSA8192': Algorithm(
175 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400176 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400177 hash_num_bytes=64,
178 signature_num_bytes=1024,
179 public_key_num_bytes=8 + 2*8192/8,
180 padding=[
181 # PKCS1-v1_5 padding
182 0x00, 0x01] + [0xff]*938 + [0x00] + [
183 # ASN.1 header
184 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
185 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
186 0x00, 0x04, 0x40
187 ]),
188}
189
190
David Zeuthene3cadca2017-02-22 21:25:46 -0500191def get_release_string():
192 """Calculates the release string to use in the VBMeta struct."""
193 # Keep in sync with libavb/avb_version.c:avb_version_string().
194 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
195 AVB_VERSION_MINOR,
196 AVB_VERSION_SUB)
197
198
David Zeuthen21e95262016-07-27 17:58:40 -0400199def round_to_multiple(number, size):
200 """Rounds a number up to nearest multiple of another number.
201
Jan Monschfe00c0a2019-12-11 11:19:40 +0100202 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400203 number: The number to round up.
204 size: The multiple to round up to.
205
206 Returns:
207 If |number| is a multiple of |size|, returns |number|, otherwise
208 returns |number| + |size|.
209 """
210 remainder = number % size
211 if remainder == 0:
212 return number
213 return number + size - remainder
214
215
216def round_to_pow2(number):
217 """Rounds a number up to the next power of 2.
218
Jan Monschfe00c0a2019-12-11 11:19:40 +0100219 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400220 number: The number to round up.
221
222 Returns:
223 If |number| is already a power of 2 then |number| is
224 returned. Otherwise the smallest power of 2 greater than |number|
225 is returned.
226 """
227 return 2**((number - 1).bit_length())
228
229
David Zeuthen21e95262016-07-27 17:58:40 -0400230def encode_long(num_bits, value):
231 """Encodes a long to a bytearray() using a given amount of bits.
232
233 This number is written big-endian, e.g. with the most significant
234 bit first.
235
David Zeuthenb623d8b2017-04-04 16:05:53 -0400236 This is the reverse of decode_long().
237
David Zeuthen21e95262016-07-27 17:58:40 -0400238 Arguments:
239 num_bits: The number of bits to write, e.g. 2048.
240 value: The value to write.
241
242 Returns:
243 A bytearray() with the encoded long.
244 """
245 ret = bytearray()
246 for bit_pos in range(num_bits, 0, -8):
247 octet = (value >> (bit_pos - 8)) & 0xff
248 ret.extend(struct.pack('!B', octet))
249 return ret
250
251
David Zeuthenb623d8b2017-04-04 16:05:53 -0400252def decode_long(blob):
253 """Decodes a long from a bytearray() using a given amount of bits.
254
255 This number is expected to be in big-endian, e.g. with the most
256 significant bit first.
257
258 This is the reverse of encode_long().
259
260 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100261 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400262
263 Returns:
264 The decoded value.
265 """
266 ret = 0
267 for b in bytearray(blob):
268 ret *= 256
269 ret += b
270 return ret
271
272
David Zeuthen21e95262016-07-27 17:58:40 -0400273def egcd(a, b):
274 """Calculate greatest common divisor of two numbers.
275
276 This implementation uses a recursive version of the extended
277 Euclidian algorithm.
278
279 Arguments:
280 a: First number.
281 b: Second number.
282
283 Returns:
284 A tuple (gcd, x, y) that where |gcd| is the greatest common
285 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
286 """
287 if a == 0:
288 return (b, 0, 1)
289 else:
290 g, y, x = egcd(b % a, a)
291 return (g, x - (b // a) * y, y)
292
293
294def modinv(a, m):
295 """Calculate modular multiplicative inverse of |a| modulo |m|.
296
297 This calculates the number |x| such that |a| * |x| == 1 (modulo
298 |m|). This number only exists if |a| and |m| are co-prime - |None|
299 is returned if this isn't true.
300
301 Arguments:
302 a: The number to calculate a modular inverse of.
303 m: The modulo to use.
304
305 Returns:
306 The modular multiplicative inverse of |a| and |m| or |None| if
307 these numbers are not co-prime.
308 """
309 gcd, x, _ = egcd(a, m)
310 if gcd != 1:
311 return None # modular inverse does not exist
312 else:
313 return x % m
314
315
316def parse_number(string):
317 """Parse a string as a number.
318
319 This is just a short-hand for int(string, 0) suitable for use in the
320 |type| parameter of |ArgumentParser|'s add_argument() function. An
321 improvement to just using type=int is that this function supports
322 numbers in other bases, e.g. "0x1234".
323
324 Arguments:
325 string: The string to parse.
326
327 Returns:
328 The parsed integer.
329
330 Raises:
331 ValueError: If the number could not be parsed.
332 """
333 return int(string, 0)
334
335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336class RSAPublicKey(object):
337 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339 Attributes:
340 exponent: The key exponent.
341 modulus: The key modulus.
342 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400343 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400344
345 MODULUS_PREFIX = 'modulus='
346
347 def __init__(self, key_path):
348 """Loads and parses an RSA key from either a private or public key file.
349
350 Arguments:
351 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100352
353 Raises:
354 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400355 """
356 # We used to have something as simple as this:
357 #
358 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
359 # self.exponent = key.e
360 # self.modulus = key.n
361 # self.num_bits = key.size() + 1
362 #
363 # but unfortunately PyCrypto is not available in the builder. So
364 # instead just parse openssl(1) output to get this
365 # information. It's ugly but...
366 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
367 p = subprocess.Popen(args,
368 stdin=subprocess.PIPE,
369 stdout=subprocess.PIPE,
370 stderr=subprocess.PIPE)
371 (pout, perr) = p.communicate()
372 if p.wait() != 0:
373 # Could be just a public key is passed, try that.
374 args.append('-pubin')
375 p = subprocess.Popen(args,
376 stdin=subprocess.PIPE,
377 stdout=subprocess.PIPE,
378 stderr=subprocess.PIPE)
379 (pout, perr) = p.communicate()
380 if p.wait() != 0:
381 raise AvbError('Error getting public key: {}'.format(perr))
382
383 if not pout.lower().startswith(self.MODULUS_PREFIX):
384 raise AvbError('Unexpected modulus output')
385
386 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
387
388 # The exponent is assumed to always be 65537 and the number of
389 # bits can be derived from the modulus by rounding up to the
390 # nearest power of 2.
391 self.modulus = int(modulus_hexstr, 16)
392 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
393 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400394
395
David Zeuthenc68f0822017-03-31 17:22:35 -0400396def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400397 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
398
399 This creates a |AvbRSAPublicKeyHeader| as well as the two large
400 numbers (|key_num_bits| bits long) following it.
401
402 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400403 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400404
405 Returns:
406 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100407
408 Raises:
409 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400410 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400411 key = RSAPublicKey(key_path)
412 if key.exponent != 65537:
413 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400414 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400415 # Calculate n0inv = -1/n[0] (mod 2^32)
416 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400417 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400419 r = 2L**key.modulus.bit_length()
420 rrmodn = r * r % key.modulus
421 ret.extend(struct.pack('!II', key.num_bits, n0inv))
422 ret.extend(encode_long(key.num_bits, key.modulus))
423 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400424 return ret
425
426
427def lookup_algorithm_by_type(alg_type):
428 """Looks up algorithm by type.
429
430 Arguments:
431 alg_type: The integer representing the type.
432
433 Returns:
434 A tuple with the algorithm name and an |Algorithm| instance.
435
436 Raises:
437 Exception: If the algorithm cannot be found
438 """
439 for alg_name in ALGORITHMS:
440 alg_data = ALGORITHMS[alg_name]
441 if alg_data.algorithm_type == alg_type:
442 return (alg_name, alg_data)
443 raise AvbError('Unknown algorithm type {}'.format(alg_type))
444
Jan Monsch77cd2022019-12-10 17:18:04 +0100445
Dan Austina7bc4962019-12-02 13:26:08 -0800446def lookup_hash_size_by_type(alg_type):
447 """Looks up hash size by type.
448
449 Arguments:
450 alg_type: The integer representing the type.
451
452 Returns:
453 The corresponding hash size.
454
455 Raises:
456 AvbError: If the algorithm cannot be found.
457 """
458 for alg_name in ALGORITHMS:
459 alg_data = ALGORITHMS[alg_name]
460 if alg_data.algorithm_type == alg_type:
461 return alg_data.hash_num_bytes
462 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400463
Jan Monsch77cd2022019-12-10 17:18:04 +0100464
David Zeuthena156d3d2017-06-01 12:08:09 -0400465def raw_sign(signing_helper, signing_helper_with_files,
466 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900467 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800468 """Computes a raw RSA signature using |signing_helper| or openssl.
469
470 Arguments:
471 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400472 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800473 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900474 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800475 key_path: Path to the private key file. Must be PEM format.
476 raw_data_to_sign: Data to sign (bytearray or str expected).
477
478 Returns:
479 A bytearray containing the signature.
480
481 Raises:
482 Exception: If an error occurs.
483 """
484 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400485 if signing_helper_with_files is not None:
486 signing_file = tempfile.NamedTemporaryFile()
487 signing_file.write(str(raw_data_to_sign))
488 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100489 p = subprocess.Popen([
490 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400491 retcode = p.wait()
492 if retcode != 0:
493 raise AvbError('Error signing')
494 signing_file.seek(0)
495 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800496 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400497 if signing_helper is not None:
498 p = subprocess.Popen(
499 [signing_helper, algorithm_name, key_path],
500 stdin=subprocess.PIPE,
501 stdout=subprocess.PIPE,
502 stderr=subprocess.PIPE)
503 else:
504 p = subprocess.Popen(
505 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
506 stdin=subprocess.PIPE,
507 stdout=subprocess.PIPE,
508 stderr=subprocess.PIPE)
509 (pout, perr) = p.communicate(str(raw_data_to_sign))
510 retcode = p.wait()
511 if retcode != 0:
512 raise AvbError('Error signing: {}'.format(perr))
513 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900514 if len(signature) != signature_num_bytes:
515 raise AvbError('Error signing: Invalid length of signature')
516 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800517
518
David Zeuthenb623d8b2017-04-04 16:05:53 -0400519def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100520 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400521
522 Arguments:
523 vbmeta_header: A AvbVBMetaHeader.
524 vbmeta_blob: The whole vbmeta blob, including the header.
525
526 Returns:
527 True if the signature is valid and corresponds to the embedded
528 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100529
530 Raises:
531 AvbError: If there errors calling out to openssl command during
532 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400533 """
534 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100535 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536 return True
537 header_blob = vbmeta_blob[0:256]
538 auth_offset = 256
539 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
540 aux_size = vbmeta_header.auxiliary_data_block_size
541 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
542 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
543 pubkey_size = vbmeta_header.public_key_size
544 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
545
546 digest_offset = auth_offset + vbmeta_header.hash_offset
547 digest_size = vbmeta_header.hash_size
548 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
549
550 sig_offset = auth_offset + vbmeta_header.signature_offset
551 sig_size = vbmeta_header.signature_size
552 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
553
554 # Now that we've got the stored digest, public key, and signature
555 # all we need to do is to verify. This is the exactly the same
556 # steps as performed in the avb_vbmeta_image_verify() function in
557 # libavb/avb_vbmeta_image.c.
558
559 ha = hashlib.new(alg.hash_name)
560 ha.update(header_blob)
561 ha.update(aux_blob)
562 computed_digest = ha.digest()
563
564 if computed_digest != digest_blob:
565 return False
566
567 padding_and_digest = bytearray(alg.padding)
568 padding_and_digest.extend(computed_digest)
569
570 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
571 modulus_blob = pubkey_blob[8:8 + num_bits/8]
572 modulus = decode_long(modulus_blob)
573 exponent = 65537
574
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500575 # We used to have this:
576 #
577 # import Crypto.PublicKey.RSA
578 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
579 # if not key.verify(decode_long(padding_and_digest),
580 # (decode_long(sig_blob), None)):
581 # return False
582 # return True
583 #
584 # but since 'avbtool verify_image' is used on the builders we don't want
585 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
586 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
587 '\n'
588 '[pubkeyinfo]\n'
589 'algorithm=SEQUENCE:rsa_alg\n'
590 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
591 '\n'
592 '[rsa_alg]\n'
593 'algorithm=OID:rsaEncryption\n'
594 'parameter=NULL\n'
595 '\n'
596 '[rsapubkey]\n'
597 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100598 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
599 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500600 asn1_tmpfile = tempfile.NamedTemporaryFile()
601 asn1_tmpfile.write(asn1_str)
602 asn1_tmpfile.flush()
603 der_tmpfile = tempfile.NamedTemporaryFile()
604 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100605 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
606 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500607 retcode = p.wait()
608 if retcode != 0:
609 raise AvbError('Error generating DER file')
610
611 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100612 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
613 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500614 stdin=subprocess.PIPE,
615 stdout=subprocess.PIPE,
616 stderr=subprocess.PIPE)
617 (pout, perr) = p.communicate(str(sig_blob))
618 retcode = p.wait()
619 if retcode != 0:
620 raise AvbError('Error verifying data: {}'.format(perr))
621 recovered_data = bytearray(pout)
622 if recovered_data != padding_and_digest:
623 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400624 return False
625 return True
626
627
David Zeuthena4fee8b2016-08-22 15:20:43 -0400628class ImageChunk(object):
629 """Data structure used for representing chunks in Android sparse files.
630
631 Attributes:
632 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
633 chunk_offset: Offset in the sparse file where this chunk begins.
634 output_offset: Offset in de-sparsified file where output begins.
635 output_size: Number of bytes in output.
636 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
637 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
638 """
639
640 FORMAT = '<2H2I'
641 TYPE_RAW = 0xcac1
642 TYPE_FILL = 0xcac2
643 TYPE_DONT_CARE = 0xcac3
644 TYPE_CRC32 = 0xcac4
645
646 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
647 input_offset, fill_data):
648 """Initializes an ImageChunk object.
649
650 Arguments:
651 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
652 chunk_offset: Offset in the sparse file where this chunk begins.
653 output_offset: Offset in de-sparsified file.
654 output_size: Number of bytes in output.
655 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
656 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
657
658 Raises:
659 ValueError: If data is not well-formed.
660 """
661 self.chunk_type = chunk_type
662 self.chunk_offset = chunk_offset
663 self.output_offset = output_offset
664 self.output_size = output_size
665 self.input_offset = input_offset
666 self.fill_data = fill_data
667 # Check invariants.
668 if self.chunk_type == self.TYPE_RAW:
669 if self.fill_data is not None:
670 raise ValueError('RAW chunk cannot have fill_data set.')
671 if not self.input_offset:
672 raise ValueError('RAW chunk must have input_offset set.')
673 elif self.chunk_type == self.TYPE_FILL:
674 if self.fill_data is None:
675 raise ValueError('FILL chunk must have fill_data set.')
676 if self.input_offset:
677 raise ValueError('FILL chunk cannot have input_offset set.')
678 elif self.chunk_type == self.TYPE_DONT_CARE:
679 if self.fill_data is not None:
680 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
681 if self.input_offset:
682 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
683 else:
684 raise ValueError('Invalid chunk type')
685
686
687class ImageHandler(object):
688 """Abstraction for image I/O with support for Android sparse images.
689
690 This class provides an interface for working with image files that
691 may be using the Android Sparse Image format. When an instance is
692 constructed, we test whether it's an Android sparse file. If so,
693 operations will be on the sparse file by interpreting the sparse
694 format, otherwise they will be directly on the file. Either way the
695 operations do the same.
696
697 For reading, this interface mimics a file object - it has seek(),
698 tell(), and read() methods. For writing, only truncation
699 (truncate()) and appending is supported (append_raw() and
700 append_dont_care()). Additionally, data can only be written in units
701 of the block size.
702
703 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400704 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 is_sparse: Whether the file being operated on is sparse.
706 block_size: The block size, typically 4096.
707 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400708 """
709 # See system/core/libsparse/sparse_format.h for details.
710 MAGIC = 0xed26ff3a
711 HEADER_FORMAT = '<I4H4I'
712
713 # These are formats and offset of just the |total_chunks| and
714 # |total_blocks| fields.
715 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
716 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
717
718 def __init__(self, image_filename):
719 """Initializes an image handler.
720
721 Arguments:
722 image_filename: The name of the file to operate on.
723
724 Raises:
725 ValueError: If data in the file is invalid.
726 """
David Zeuthen49936b42018-08-07 17:38:58 -0400727 self.filename = image_filename
David Zeuthena4fee8b2016-08-22 15:20:43 -0400728 self._read_header()
729
730 def _read_header(self):
731 """Initializes internal data structures used for reading file.
732
733 This may be called multiple times and is typically called after
734 modifying the file (e.g. appending, truncation).
735
736 Raises:
737 ValueError: If data in the file is invalid.
738 """
739 self.is_sparse = False
740 self.block_size = 4096
741 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400742 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400743 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400744 self.image_size = self._image.tell()
745
746 self._image.seek(0, os.SEEK_SET)
747 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
748 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
749 block_size, self._num_total_blocks, self._num_total_chunks,
750 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
751 if magic != self.MAGIC:
752 # Not a sparse image, our job here is done.
753 return
754 if not (major_version == 1 and minor_version == 0):
755 raise ValueError('Encountered sparse image format version {}.{} but '
756 'only 1.0 is supported'.format(major_version,
757 minor_version))
758 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
759 raise ValueError('Unexpected file_hdr_sz value {}.'.
760 format(file_hdr_sz))
761 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
762 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
763 format(chunk_hdr_sz))
764
765 self.block_size = block_size
766
767 # Build an list of chunks by parsing the file.
768 self._chunks = []
769
770 # Find the smallest offset where only "Don't care" chunks
771 # follow. This will be the size of the content in the sparse
772 # image.
773 offset = 0
774 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400775 for _ in xrange(1, self._num_total_chunks + 1):
776 chunk_offset = self._image.tell()
777
778 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
779 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
780 header_bin)
781 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
782
David Zeuthena4fee8b2016-08-22 15:20:43 -0400783 if chunk_type == ImageChunk.TYPE_RAW:
784 if data_sz != (chunk_sz * self.block_size):
785 raise ValueError('Raw chunk input size ({}) does not match output '
786 'size ({})'.
787 format(data_sz, chunk_sz*self.block_size))
788 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
789 chunk_offset,
790 output_offset,
791 chunk_sz*self.block_size,
792 self._image.tell(),
793 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700794 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400795
796 elif chunk_type == ImageChunk.TYPE_FILL:
797 if data_sz != 4:
798 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
799 'has {}'.format(data_sz))
800 fill_data = self._image.read(4)
801 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
802 chunk_offset,
803 output_offset,
804 chunk_sz*self.block_size,
805 None,
806 fill_data))
807 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
808 if data_sz != 0:
809 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
810 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400811 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
812 chunk_offset,
813 output_offset,
814 chunk_sz*self.block_size,
815 None,
816 None))
817 elif chunk_type == ImageChunk.TYPE_CRC32:
818 if data_sz != 4:
819 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
820 'this has {}'.format(data_sz))
821 self._image.read(4)
822 else:
823 raise ValueError('Unknown chunk type {}'.format(chunk_type))
824
825 offset += chunk_sz
826 output_offset += chunk_sz*self.block_size
827
828 # Record where sparse data end.
829 self._sparse_end = self._image.tell()
830
831 # Now that we've traversed all chunks, sanity check.
832 if self._num_total_blocks != offset:
833 raise ValueError('The header said we should have {} output blocks, '
834 'but we saw {}'.format(self._num_total_blocks, offset))
835 junk_len = len(self._image.read())
836 if junk_len > 0:
837 raise ValueError('There were {} bytes of extra data at the end of the '
838 'file.'.format(junk_len))
839
David Zeuthen09692692016-09-30 16:16:40 -0400840 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400841 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400842
843 # This is used when bisecting in read() to find the initial slice.
844 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
845
846 self.is_sparse = True
847
848 def _update_chunks_and_blocks(self):
849 """Helper function to update the image header.
850
851 The the |total_chunks| and |total_blocks| fields in the header
852 will be set to value of the |_num_total_blocks| and
853 |_num_total_chunks| attributes.
854
855 """
856 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
857 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
858 self._num_total_blocks,
859 self._num_total_chunks))
860
861 def append_dont_care(self, num_bytes):
862 """Appends a DONT_CARE chunk to the sparse file.
863
864 The given number of bytes must be a multiple of the block size.
865
866 Arguments:
867 num_bytes: Size in number of bytes of the DONT_CARE chunk.
868 """
869 assert num_bytes % self.block_size == 0
870
871 if not self.is_sparse:
872 self._image.seek(0, os.SEEK_END)
873 # This is more efficient that writing NUL bytes since it'll add
874 # a hole on file systems that support sparse files (native
875 # sparse, not Android sparse).
876 self._image.truncate(self._image.tell() + num_bytes)
877 self._read_header()
878 return
879
880 self._num_total_chunks += 1
881 self._num_total_blocks += num_bytes / self.block_size
882 self._update_chunks_and_blocks()
883
884 self._image.seek(self._sparse_end, os.SEEK_SET)
885 self._image.write(struct.pack(ImageChunk.FORMAT,
886 ImageChunk.TYPE_DONT_CARE,
887 0, # Reserved
888 num_bytes / self.block_size,
889 struct.calcsize(ImageChunk.FORMAT)))
890 self._read_header()
891
892 def append_raw(self, data):
893 """Appends a RAW chunk to the sparse file.
894
895 The length of the given data must be a multiple of the block size.
896
897 Arguments:
898 data: Data to append.
899 """
900 assert len(data) % self.block_size == 0
901
902 if not self.is_sparse:
903 self._image.seek(0, os.SEEK_END)
904 self._image.write(data)
905 self._read_header()
906 return
907
908 self._num_total_chunks += 1
909 self._num_total_blocks += len(data) / self.block_size
910 self._update_chunks_and_blocks()
911
912 self._image.seek(self._sparse_end, os.SEEK_SET)
913 self._image.write(struct.pack(ImageChunk.FORMAT,
914 ImageChunk.TYPE_RAW,
915 0, # Reserved
916 len(data) / self.block_size,
917 len(data) +
918 struct.calcsize(ImageChunk.FORMAT)))
919 self._image.write(data)
920 self._read_header()
921
922 def append_fill(self, fill_data, size):
923 """Appends a fill chunk to the sparse file.
924
925 The total length of the fill data must be a multiple of the block size.
926
927 Arguments:
928 fill_data: Fill data to append - must be four bytes.
929 size: Number of chunk - must be a multiple of four and the block size.
930 """
931 assert len(fill_data) == 4
932 assert size % 4 == 0
933 assert size % self.block_size == 0
934
935 if not self.is_sparse:
936 self._image.seek(0, os.SEEK_END)
937 self._image.write(fill_data * (size/4))
938 self._read_header()
939 return
940
941 self._num_total_chunks += 1
942 self._num_total_blocks += size / self.block_size
943 self._update_chunks_and_blocks()
944
945 self._image.seek(self._sparse_end, os.SEEK_SET)
946 self._image.write(struct.pack(ImageChunk.FORMAT,
947 ImageChunk.TYPE_FILL,
948 0, # Reserved
949 size / self.block_size,
950 4 + struct.calcsize(ImageChunk.FORMAT)))
951 self._image.write(fill_data)
952 self._read_header()
953
954 def seek(self, offset):
955 """Sets the cursor position for reading from unsparsified file.
956
957 Arguments:
958 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100959
960 Raises:
961 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400962 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700963 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100964 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400965 self._file_pos = offset
966
967 def read(self, size):
968 """Reads data from the unsparsified file.
969
970 This method may return fewer than |size| bytes of data if the end
971 of the file was encountered.
972
973 The file cursor for reading is advanced by the number of bytes
974 read.
975
976 Arguments:
977 size: Number of bytes to read.
978
979 Returns:
980 The data.
981
982 """
983 if not self.is_sparse:
984 self._image.seek(self._file_pos)
985 data = self._image.read(size)
986 self._file_pos += len(data)
987 return data
988
989 # Iterate over all chunks.
990 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
991 self._file_pos) - 1
992 data = bytearray()
993 to_go = size
994 while to_go > 0:
995 chunk = self._chunks[chunk_idx]
996 chunk_pos_offset = self._file_pos - chunk.output_offset
997 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
998
999 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1000 self._image.seek(chunk.input_offset + chunk_pos_offset)
1001 data.extend(self._image.read(chunk_pos_to_go))
1002 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1003 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
1004 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1005 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1006 else:
1007 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1008 data.extend('\0' * chunk_pos_to_go)
1009
1010 to_go -= chunk_pos_to_go
1011 self._file_pos += chunk_pos_to_go
1012 chunk_idx += 1
1013 # Generate partial read in case of EOF.
1014 if chunk_idx >= len(self._chunks):
1015 break
1016
1017 return data
1018
1019 def tell(self):
1020 """Returns the file cursor position for reading from unsparsified file.
1021
1022 Returns:
1023 The file cursor position for reading.
1024 """
1025 return self._file_pos
1026
1027 def truncate(self, size):
1028 """Truncates the unsparsified file.
1029
1030 Arguments:
1031 size: Desired size of unsparsified file.
1032
1033 Raises:
1034 ValueError: If desired size isn't a multiple of the block size.
1035 """
1036 if not self.is_sparse:
1037 self._image.truncate(size)
1038 self._read_header()
1039 return
1040
1041 if size % self.block_size != 0:
1042 raise ValueError('Cannot truncate to a size which is not a multiple '
1043 'of the block size')
1044
1045 if size == self.image_size:
1046 # Trivial where there's nothing to do.
1047 return
1048 elif size < self.image_size:
1049 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1050 chunk = self._chunks[chunk_idx]
1051 if chunk.output_offset != size:
1052 # Truncation in the middle of a trunk - need to keep the chunk
1053 # and modify it.
1054 chunk_idx_for_update = chunk_idx + 1
1055 num_to_keep = size - chunk.output_offset
1056 assert num_to_keep % self.block_size == 0
1057 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1058 truncate_at = (chunk.chunk_offset +
1059 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1060 data_sz = num_to_keep
1061 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1062 truncate_at = (chunk.chunk_offset +
1063 struct.calcsize(ImageChunk.FORMAT) + 4)
1064 data_sz = 4
1065 else:
1066 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1067 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1068 data_sz = 0
1069 chunk_sz = num_to_keep/self.block_size
1070 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1071 self._image.seek(chunk.chunk_offset)
1072 self._image.write(struct.pack(ImageChunk.FORMAT,
1073 chunk.chunk_type,
1074 0, # Reserved
1075 chunk_sz,
1076 total_sz))
1077 chunk.output_size = num_to_keep
1078 else:
1079 # Truncation at trunk boundary.
1080 truncate_at = chunk.chunk_offset
1081 chunk_idx_for_update = chunk_idx
1082
1083 self._num_total_chunks = chunk_idx_for_update
1084 self._num_total_blocks = 0
1085 for i in range(0, chunk_idx_for_update):
1086 self._num_total_blocks += self._chunks[i].output_size / self.block_size
1087 self._update_chunks_and_blocks()
1088 self._image.truncate(truncate_at)
1089
1090 # We've modified the file so re-read all data.
1091 self._read_header()
1092 else:
1093 # Truncating to grow - just add a DONT_CARE section.
1094 self.append_dont_care(size - self.image_size)
1095
1096
David Zeuthen21e95262016-07-27 17:58:40 -04001097class AvbDescriptor(object):
1098 """Class for AVB descriptor.
1099
1100 See the |AvbDescriptor| C struct for more information.
1101
1102 Attributes:
1103 tag: The tag identifying what kind of descriptor this is.
1104 data: The data in the descriptor.
1105 """
1106
1107 SIZE = 16
1108 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1109
1110 def __init__(self, data):
1111 """Initializes a new property descriptor.
1112
1113 Arguments:
1114 data: If not None, must be a bytearray().
1115
1116 Raises:
1117 LookupError: If the given descriptor is malformed.
1118 """
1119 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1120
1121 if data:
1122 (self.tag, num_bytes_following) = (
1123 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1124 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1125 else:
1126 self.tag = None
1127 self.data = None
1128
1129 def print_desc(self, o):
1130 """Print the descriptor.
1131
1132 Arguments:
1133 o: The object to write the output to.
1134 """
1135 o.write(' Unknown descriptor:\n')
1136 o.write(' Tag: {}\n'.format(self.tag))
1137 if len(self.data) < 256:
1138 o.write(' Data: {} ({} bytes)\n'.format(
1139 repr(str(self.data)), len(self.data)))
1140 else:
1141 o.write(' Data: {} bytes\n'.format(len(self.data)))
1142
1143 def encode(self):
1144 """Serializes the descriptor.
1145
1146 Returns:
1147 A bytearray() with the descriptor data.
1148 """
1149 num_bytes_following = len(self.data)
1150 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1151 padding_size = nbf_with_padding - num_bytes_following
1152 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1153 padding = struct.pack(str(padding_size) + 'x')
1154 ret = desc + self.data + padding
1155 return bytearray(ret)
1156
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001157 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001158 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001159 """Verifies contents of the descriptor - used in verify_image sub-command.
1160
1161 Arguments:
1162 image_dir: The directory of the file being verified.
1163 image_ext: The extension of the file being verified (e.g. '.img').
1164 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001165 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001166 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001167 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1168 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001169
1170 Returns:
1171 True if the descriptor verifies, False otherwise.
1172 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001173 # Deletes unused parameters to prevent pylint warning unused-argument.
1174 del image_dir, image_ext, expected_chain_partitions_map
1175 del image_containing_descriptor, accept_zeroed_hashtree
1176
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001177 # Nothing to do.
1178 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001179
Jan Monscheeb28b62019-12-05 16:17:09 +01001180
David Zeuthen21e95262016-07-27 17:58:40 -04001181class AvbPropertyDescriptor(AvbDescriptor):
1182 """A class for property descriptors.
1183
1184 See the |AvbPropertyDescriptor| C struct for more information.
1185
1186 Attributes:
1187 key: The key.
1188 value: The key.
1189 """
1190
1191 TAG = 0
1192 SIZE = 32
1193 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1194 'Q' # key size (bytes)
1195 'Q') # value size (bytes)
1196
1197 def __init__(self, data=None):
1198 """Initializes a new property descriptor.
1199
1200 Arguments:
1201 data: If not None, must be a bytearray of size |SIZE|.
1202
1203 Raises:
1204 LookupError: If the given descriptor is malformed.
1205 """
1206 AvbDescriptor.__init__(self, None)
1207 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1208
1209 if data:
1210 (tag, num_bytes_following, key_size,
1211 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1212 expected_size = round_to_multiple(
1213 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1214 if tag != self.TAG or num_bytes_following != expected_size:
1215 raise LookupError('Given data does not look like a property '
1216 'descriptor.')
1217 self.key = data[self.SIZE:(self.SIZE + key_size)]
1218 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1219 value_size)]
1220 else:
1221 self.key = ''
1222 self.value = ''
1223
1224 def print_desc(self, o):
1225 """Print the descriptor.
1226
1227 Arguments:
1228 o: The object to write the output to.
1229 """
1230 if len(self.value) < 256:
1231 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1232 else:
1233 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1234
1235 def encode(self):
1236 """Serializes the descriptor.
1237
1238 Returns:
1239 A bytearray() with the descriptor data.
1240 """
1241 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1242 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1243 padding_size = nbf_with_padding - num_bytes_following
1244 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1245 len(self.key), len(self.value))
1246 padding = struct.pack(str(padding_size) + 'x')
1247 ret = desc + self.key + '\0' + self.value + '\0' + padding
1248 return bytearray(ret)
1249
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001250 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001251 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001252 """Verifies contents of the descriptor - used in verify_image sub-command.
1253
1254 Arguments:
1255 image_dir: The directory of the file being verified.
1256 image_ext: The extension of the file being verified (e.g. '.img').
1257 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001258 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001259 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001260 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1261 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001262
1263 Returns:
1264 True if the descriptor verifies, False otherwise.
1265 """
1266 # Nothing to do.
1267 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001268
Jan Monscheeb28b62019-12-05 16:17:09 +01001269
David Zeuthen21e95262016-07-27 17:58:40 -04001270class AvbHashtreeDescriptor(AvbDescriptor):
1271 """A class for hashtree descriptors.
1272
1273 See the |AvbHashtreeDescriptor| C struct for more information.
1274
1275 Attributes:
1276 dm_verity_version: dm-verity version used.
1277 image_size: Size of the image, after rounding up to |block_size|.
1278 tree_offset: Offset of the hash tree in the file.
1279 tree_size: Size of the tree.
1280 data_block_size: Data block size
1281 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001282 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1283 fec_offset: Offset of FEC data (0 if FEC is not used).
1284 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001285 hash_algorithm: Hash algorithm used.
1286 partition_name: Partition name.
1287 salt: Salt used.
1288 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001289 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001290 """
1291
1292 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001293 RESERVED = 60
1294 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001295 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1296 'L' # dm-verity version used
1297 'Q' # image size (bytes)
1298 'Q' # tree offset (bytes)
1299 'Q' # tree size (bytes)
1300 'L' # data block size (bytes)
1301 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001302 'L' # FEC number of roots
1303 'Q' # FEC offset (bytes)
1304 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001305 '32s' # hash algorithm used
1306 'L' # partition name (bytes)
1307 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001308 'L' # root digest length (bytes)
1309 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001310 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001311
1312 def __init__(self, data=None):
1313 """Initializes a new hashtree descriptor.
1314
1315 Arguments:
1316 data: If not None, must be a bytearray of size |SIZE|.
1317
1318 Raises:
1319 LookupError: If the given descriptor is malformed.
1320 """
1321 AvbDescriptor.__init__(self, None)
1322 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1323
1324 if data:
1325 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1326 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001327 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1328 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001329 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1330 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001331 expected_size = round_to_multiple(
1332 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1333 if tag != self.TAG or num_bytes_following != expected_size:
1334 raise LookupError('Given data does not look like a hashtree '
1335 'descriptor.')
1336 # Nuke NUL-bytes at the end.
1337 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1338 o = 0
1339 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1340 partition_name_len)])
1341 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1342 self.partition_name.decode('utf-8')
1343 o += partition_name_len
1344 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1345 o += salt_len
1346 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1347 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001348 if root_digest_len != 0:
1349 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001350
1351 else:
1352 self.dm_verity_version = 0
1353 self.image_size = 0
1354 self.tree_offset = 0
1355 self.tree_size = 0
1356 self.data_block_size = 0
1357 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001358 self.fec_num_roots = 0
1359 self.fec_offset = 0
1360 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001361 self.hash_algorithm = ''
1362 self.partition_name = ''
1363 self.salt = bytearray()
1364 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001365 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001366
1367 def print_desc(self, o):
1368 """Print the descriptor.
1369
1370 Arguments:
1371 o: The object to write the output to.
1372 """
1373 o.write(' Hashtree descriptor:\n')
1374 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1375 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1376 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1377 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1378 o.write(' Data Block Size: {} bytes\n'.format(
1379 self.data_block_size))
1380 o.write(' Hash Block Size: {} bytes\n'.format(
1381 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001382 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1383 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1384 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001385 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1386 o.write(' Partition Name: {}\n'.format(self.partition_name))
1387 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1388 'hex')))
1389 o.write(' Root Digest: {}\n'.format(str(
1390 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001391 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001392
1393 def encode(self):
1394 """Serializes the descriptor.
1395
1396 Returns:
1397 A bytearray() with the descriptor data.
1398 """
1399 encoded_name = self.partition_name.encode('utf-8')
1400 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1401 len(self.root_digest) - 16)
1402 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1403 padding_size = nbf_with_padding - num_bytes_following
1404 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1405 self.dm_verity_version, self.image_size,
1406 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001407 self.hash_block_size, self.fec_num_roots,
1408 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001409 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001410 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001411 padding = struct.pack(str(padding_size) + 'x')
1412 ret = desc + encoded_name + self.salt + self.root_digest + padding
1413 return bytearray(ret)
1414
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001415 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001416 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001417 """Verifies contents of the descriptor - used in verify_image sub-command.
1418
1419 Arguments:
1420 image_dir: The directory of the file being verified.
1421 image_ext: The extension of the file being verified (e.g. '.img').
1422 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001423 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001424 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001425 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1426 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001427
1428 Returns:
1429 True if the descriptor verifies, False otherwise.
1430 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001431 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001432 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001433 image = image_containing_descriptor
1434 else:
1435 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1436 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001437 # Generate the hashtree and checks that it matches what's in the file.
1438 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1439 digest_padding = round_to_pow2(digest_size) - digest_size
1440 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001441 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001442 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1443 self.data_block_size,
1444 self.hash_algorithm, self.salt,
1445 digest_padding,
1446 hash_level_offsets,
1447 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001448 # The root digest must match unless it is not embedded in the descriptor.
Jan Monschfe00c0a2019-12-11 11:19:40 +01001449 if len(self.root_digest) and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001450 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1451 format(image_filename))
1452 return False
1453 # ... also check that the on-disk hashtree matches
1454 image.seek(self.tree_offset)
1455 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001456 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001457 if is_zeroed and accept_zeroed_hashtree:
Jan Monscheeb28b62019-12-05 16:17:09 +01001458 print ('{}: skipping verification since hashtree is zeroed and '
1459 '--accept_zeroed_hashtree was given'
David Zeuthen1394f762019-04-30 10:20:11 -04001460 .format(self.partition_name))
1461 else:
1462 if hash_tree != hash_tree_ondisk:
1463 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001464 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001465 return False
1466 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1467 .format(self.partition_name, self.hash_algorithm, image.filename,
1468 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001469 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1470 # correct but this a) currently requires the 'fec' binary; and b) takes a
1471 # long time; and c) is not strictly needed for verification purposes as
1472 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001473 return True
1474
David Zeuthen21e95262016-07-27 17:58:40 -04001475
1476class AvbHashDescriptor(AvbDescriptor):
1477 """A class for hash descriptors.
1478
1479 See the |AvbHashDescriptor| C struct for more information.
1480
1481 Attributes:
1482 image_size: Image size, in bytes.
1483 hash_algorithm: Hash algorithm used.
1484 partition_name: Partition name.
1485 salt: Salt used.
1486 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001487 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001488 """
1489
1490 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001491 RESERVED = 60
1492 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001493 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1494 'Q' # image size (bytes)
1495 '32s' # hash algorithm used
1496 'L' # partition name (bytes)
1497 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001498 'L' # digest length (bytes)
1499 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001500 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001501
1502 def __init__(self, data=None):
1503 """Initializes a new hash descriptor.
1504
1505 Arguments:
1506 data: If not None, must be a bytearray of size |SIZE|.
1507
1508 Raises:
1509 LookupError: If the given descriptor is malformed.
1510 """
1511 AvbDescriptor.__init__(self, None)
1512 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1513
1514 if data:
1515 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1516 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001517 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1518 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001519 expected_size = round_to_multiple(
1520 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1521 if tag != self.TAG or num_bytes_following != expected_size:
1522 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1523 # Nuke NUL-bytes at the end.
1524 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1525 o = 0
1526 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1527 partition_name_len)])
1528 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1529 self.partition_name.decode('utf-8')
1530 o += partition_name_len
1531 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1532 o += salt_len
1533 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1534 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001535 if digest_len != 0:
1536 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001537
1538 else:
1539 self.image_size = 0
1540 self.hash_algorithm = ''
1541 self.partition_name = ''
1542 self.salt = bytearray()
1543 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001544 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001545
1546 def print_desc(self, o):
1547 """Print the descriptor.
1548
1549 Arguments:
1550 o: The object to write the output to.
1551 """
1552 o.write(' Hash descriptor:\n')
1553 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1554 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1555 o.write(' Partition Name: {}\n'.format(self.partition_name))
1556 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1557 'hex')))
1558 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1559 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001560 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001561
1562 def encode(self):
1563 """Serializes the descriptor.
1564
1565 Returns:
1566 A bytearray() with the descriptor data.
1567 """
1568 encoded_name = self.partition_name.encode('utf-8')
1569 num_bytes_following = (
1570 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1571 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1572 padding_size = nbf_with_padding - num_bytes_following
1573 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1574 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001575 len(self.salt), len(self.digest), self.flags,
1576 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001577 padding = struct.pack(str(padding_size) + 'x')
1578 ret = desc + encoded_name + self.salt + self.digest + padding
1579 return bytearray(ret)
1580
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001581 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001582 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001583 """Verifies contents of the descriptor - used in verify_image sub-command.
1584
1585 Arguments:
1586 image_dir: The directory of the file being verified.
1587 image_ext: The extension of the file being verified (e.g. '.img').
1588 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001589 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001590 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001591 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1592 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001593
1594 Returns:
1595 True if the descriptor verifies, False otherwise.
1596 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001597 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001598 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001599 image = image_containing_descriptor
1600 else:
1601 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1602 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001603 data = image.read(self.image_size)
1604 ha = hashlib.new(self.hash_algorithm)
1605 ha.update(self.salt)
1606 ha.update(data)
1607 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001608 # The digest must match unless there is no digest in the descriptor.
Jan Monschfe00c0a2019-12-11 11:19:40 +01001609 if len(self.digest) and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001610 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1611 format(self.hash_algorithm, image_filename))
1612 return False
1613 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001614 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001615 self.image_size))
1616 return True
1617
David Zeuthen21e95262016-07-27 17:58:40 -04001618
1619class AvbKernelCmdlineDescriptor(AvbDescriptor):
1620 """A class for kernel command-line descriptors.
1621
1622 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1623
1624 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001625 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001626 kernel_cmdline: The kernel command-line.
1627 """
1628
1629 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001630 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001631 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001632 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001633 'L') # cmdline length (bytes)
1634
David Zeuthenfd41eb92016-11-17 12:24:47 -05001635 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1636 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1637
David Zeuthen21e95262016-07-27 17:58:40 -04001638 def __init__(self, data=None):
1639 """Initializes a new kernel cmdline descriptor.
1640
1641 Arguments:
1642 data: If not None, must be a bytearray of size |SIZE|.
1643
1644 Raises:
1645 LookupError: If the given descriptor is malformed.
1646 """
1647 AvbDescriptor.__init__(self, None)
1648 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1649
1650 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001651 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001652 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1653 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1654 8)
1655 if tag != self.TAG or num_bytes_following != expected_size:
1656 raise LookupError('Given data does not look like a kernel cmdline '
1657 'descriptor.')
1658 # Nuke NUL-bytes at the end.
1659 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1660 kernel_cmdline_length)])
1661 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1662 self.kernel_cmdline.decode('utf-8')
1663 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001664 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001665 self.kernel_cmdline = ''
1666
1667 def print_desc(self, o):
1668 """Print the descriptor.
1669
1670 Arguments:
1671 o: The object to write the output to.
1672 """
1673 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001674 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001675 o.write(' Kernel Cmdline: {}\n'.format(repr(
1676 self.kernel_cmdline)))
1677
1678 def encode(self):
1679 """Serializes the descriptor.
1680
1681 Returns:
1682 A bytearray() with the descriptor data.
1683 """
1684 encoded_str = self.kernel_cmdline.encode('utf-8')
1685 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1686 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1687 padding_size = nbf_with_padding - num_bytes_following
1688 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001689 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001690 padding = struct.pack(str(padding_size) + 'x')
1691 ret = desc + encoded_str + padding
1692 return bytearray(ret)
1693
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001694 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001695 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001696 """Verifies contents of the descriptor - used in verify_image sub-command.
1697
1698 Arguments:
1699 image_dir: The directory of the file being verified.
1700 image_ext: The extension of the file being verified (e.g. '.img').
1701 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001702 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001703 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001704 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1705 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001706
1707 Returns:
1708 True if the descriptor verifies, False otherwise.
1709 """
1710 # Nothing to verify.
1711 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001712
Jan Monscheeb28b62019-12-05 16:17:09 +01001713
David Zeuthen21e95262016-07-27 17:58:40 -04001714class AvbChainPartitionDescriptor(AvbDescriptor):
1715 """A class for chained partition descriptors.
1716
1717 See the |AvbChainPartitionDescriptor| C struct for more information.
1718
1719 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001720 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001721 partition_name: Partition name.
1722 public_key: Bytes for the public key.
1723 """
1724
1725 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001726 RESERVED = 64
1727 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001728 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001729 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001730 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001731 'L' + # public_key_size (bytes)
1732 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001733
1734 def __init__(self, data=None):
1735 """Initializes a new chain partition descriptor.
1736
1737 Arguments:
1738 data: If not None, must be a bytearray of size |SIZE|.
1739
1740 Raises:
1741 LookupError: If the given descriptor is malformed.
1742 """
1743 AvbDescriptor.__init__(self, None)
1744 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1745
1746 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001747 (tag, num_bytes_following, self.rollback_index_location,
1748 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001749 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001750 expected_size = round_to_multiple(
1751 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1752 if tag != self.TAG or num_bytes_following != expected_size:
1753 raise LookupError('Given data does not look like a chain partition '
1754 'descriptor.')
1755 o = 0
1756 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1757 partition_name_len)])
1758 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1759 self.partition_name.decode('utf-8')
1760 o += partition_name_len
1761 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1762
1763 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001764 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001765 self.partition_name = ''
1766 self.public_key = bytearray()
1767
1768 def print_desc(self, o):
1769 """Print the descriptor.
1770
1771 Arguments:
1772 o: The object to write the output to.
1773 """
1774 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001775 o.write(' Partition Name: {}\n'.format(self.partition_name))
1776 o.write(' Rollback Index Location: {}\n'.format(
1777 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001778 # Just show the SHA1 of the key, for size reasons.
1779 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001780 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001781
1782 def encode(self):
1783 """Serializes the descriptor.
1784
1785 Returns:
1786 A bytearray() with the descriptor data.
1787 """
1788 encoded_name = self.partition_name.encode('utf-8')
1789 num_bytes_following = (
1790 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1791 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1792 padding_size = nbf_with_padding - num_bytes_following
1793 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001794 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001795 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001796 padding = struct.pack(str(padding_size) + 'x')
1797 ret = desc + encoded_name + self.public_key + padding
1798 return bytearray(ret)
1799
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001800 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001801 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001802 """Verifies contents of the descriptor - used in verify_image sub-command.
1803
1804 Arguments:
1805 image_dir: The directory of the file being verified.
1806 image_ext: The extension of the file being verified (e.g. '.img').
1807 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001808 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001809 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001810 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1811 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001812
1813 Returns:
1814 True if the descriptor verifies, False otherwise.
1815 """
1816 value = expected_chain_partitions_map.get(self.partition_name)
1817 if not value:
1818 sys.stderr.write('No expected chain partition for partition {}. Use '
1819 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001820 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001821 format(self.partition_name))
1822 return False
1823 rollback_index_location, pk_blob = value
1824
1825 if self.rollback_index_location != rollback_index_location:
1826 sys.stderr.write('Expected rollback_index_location {} does not '
1827 'match {} in descriptor for partition {}\n'.
1828 format(rollback_index_location,
1829 self.rollback_index_location,
1830 self.partition_name))
1831 return False
1832
1833 if self.public_key != pk_blob:
1834 sys.stderr.write('Expected public key blob does not match public '
1835 'key blob in descriptor for partition {}\n'.
1836 format(self.partition_name))
1837 return False
1838
1839 print ('{}: Successfully verified chain partition descriptor matches '
1840 'expected data'.format(self.partition_name))
1841
1842 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001843
1844DESCRIPTOR_CLASSES = [
1845 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1846 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1847]
1848
1849
1850def parse_descriptors(data):
1851 """Parses a blob of data into descriptors.
1852
1853 Arguments:
1854 data: A bytearray() with encoded descriptors.
1855
1856 Returns:
1857 A list of instances of objects derived from AvbDescriptor. For
1858 unknown descriptors, the class AvbDescriptor is used.
1859 """
1860 o = 0
1861 ret = []
1862 while o < len(data):
1863 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1864 if tag < len(DESCRIPTOR_CLASSES):
1865 c = DESCRIPTOR_CLASSES[tag]
1866 else:
1867 c = AvbDescriptor
1868 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1869 o += 16 + nb_following
1870 return ret
1871
1872
1873class AvbFooter(object):
1874 """A class for parsing and writing footers.
1875
1876 Footers are stored at the end of partitions and point to where the
1877 AvbVBMeta blob is located. They also contain the original size of
1878 the image before AVB information was added.
1879
1880 Attributes:
1881 magic: Magic for identifying the footer, see |MAGIC|.
1882 version_major: The major version of avbtool that wrote the footer.
1883 version_minor: The minor version of avbtool that wrote the footer.
1884 original_image_size: Original image size.
1885 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1886 vbmeta_size: Size of the AvbVBMeta blob.
1887 """
1888
1889 MAGIC = 'AVBf'
1890 SIZE = 64
1891 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001892 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1893 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001894 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1895 'Q' # Original image size.
1896 'Q' # Offset of VBMeta blob.
1897 'Q' + # Size of VBMeta blob.
1898 str(RESERVED) + 'x') # padding for reserved bytes
1899
1900 def __init__(self, data=None):
1901 """Initializes a new footer object.
1902
1903 Arguments:
1904 data: If not None, must be a bytearray of size 4096.
1905
1906 Raises:
1907 LookupError: If the given footer is malformed.
1908 struct.error: If the given data has no footer.
1909 """
1910 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1911
1912 if data:
1913 (self.magic, self.version_major, self.version_minor,
1914 self.original_image_size, self.vbmeta_offset,
1915 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1916 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001917 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001918 else:
1919 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001920 self.version_major = self.FOOTER_VERSION_MAJOR
1921 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001922 self.original_image_size = 0
1923 self.vbmeta_offset = 0
1924 self.vbmeta_size = 0
1925
David Zeuthena4fee8b2016-08-22 15:20:43 -04001926 def encode(self):
1927 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001928
David Zeuthena4fee8b2016-08-22 15:20:43 -04001929 Returns:
1930 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001931 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001932 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1933 self.version_minor, self.original_image_size,
1934 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001935
1936
Dan Austina7bc4962019-12-02 13:26:08 -08001937# Android Firmware Transparency Log Data Structures
1938
Jan Monsch77cd2022019-12-10 17:18:04 +01001939
Dan Austina7bc4962019-12-02 13:26:08 -08001940class AvbIcpHeader(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01001941 """A class for the transparency log inclusion proof header.
Dan Austina7bc4962019-12-02 13:26:08 -08001942
1943 Attributes:
1944 magic: Magic for identifying the ICP header.
1945 required_icp_version_major: The major version of AVB that wrote the entry.
1946 required_icp_version_minor: The minor version of AVB that wrote the entry.
1947 algorithm: Hash algorithm used. ID is defined in ALGORITHMS.
1948 icp_count: Number of inclusion proofs represented in this structure.
1949 """
1950
1951 SIZE = 18 # The size of the structure, in bytes
1952 MAGIC = 'AFTL'
Jan Monsch77cd2022019-12-10 17:18:04 +01001953 FORMAT_STRING = ('!4s2L' # magic, major & minor version
1954 'L' # algorithm type for transparency log
1955 'H') # number of inclusion proof entries
Dan Austina7bc4962019-12-02 13:26:08 -08001956
1957 def __init__(self, data=None):
1958 """Initializes a new transparency header object.
1959
1960 Arguments:
1961 data: If not None, must be a bytearray of size == 18.
1962 """
1963 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1964
1965 if data:
Jan Monsch77cd2022019-12-10 17:18:04 +01001966 (self.magic, self.required_icp_version_major,
1967 self.required_icp_version_minor, self.algorithm,
1968 self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08001969 else:
1970 self.magic = self.MAGIC
1971 self.required_icp_version_major = AVB_VERSION_MAJOR
1972 self.required_icp_version_minor = AVB_VERSION_MINOR
1973 self.algorithm = 0
1974 self.icp_count = 0
1975
1976 def save(self, output):
1977 """Serializes the transparency header (18) to disk.
1978
1979 Arguments:
1980 output: The object to write the header to.
1981 """
1982 output.write(self.encode())
1983
1984 def encode(self):
1985 """Serializes the header (18) to a bytearray().
1986
1987 Returns:
1988 A bytearray() with the encoded header.
1989 """
1990 return struct.pack(self.FORMAT_STRING, self.magic,
1991 self.required_icp_version_major,
1992 self.required_icp_version_minor,
1993 self.algorithm, self.icp_count)
Jan Monsch77cd2022019-12-10 17:18:04 +01001994
Dan Austina7bc4962019-12-02 13:26:08 -08001995 def is_valid(self):
1996 """Ensures that values in an AvbIcpHeader structure are sane.
1997
1998 Returns:
1999 True if the values in the AvbIcpHeader are sane, False otherwise.
2000 """
2001 if self.magic != AvbIcpHeader.MAGIC:
Jan Monsch77cd2022019-12-10 17:18:04 +01002002 sys.stderr.write(
2003 'ICP Header: magic value mismatch: {}\n'.format(self.magic))
Dan Austina7bc4962019-12-02 13:26:08 -08002004 return False
2005 if self.required_icp_version_major > AVB_VERSION_MAJOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002006 sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
2007 self.required_icp_version_major))
Dan Austina7bc4962019-12-02 13:26:08 -08002008 return False
2009 if self.required_icp_version_minor > AVB_VERSION_MINOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002010 sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
2011 self.required_icp_version_minor))
Dan Austina7bc4962019-12-02 13:26:08 -08002012 return False
2013 if self.algorithm < 0 or self.algorithm >= len(ALGORITHMS):
Jan Monsch77cd2022019-12-10 17:18:04 +01002014 sys.stderr.write(
2015 'ICP header: algorithm identifier out of range: {}\n'.format(
2016 self.algorithm))
Dan Austina7bc4962019-12-02 13:26:08 -08002017 return False
2018 if self.icp_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002019 sys.stderr.write(
2020 'ICP header: ICP entry count out of range: {}\n'.format(
2021 self.icp_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002022 return False
2023 return True
2024
Dan Austina7bc4962019-12-02 13:26:08 -08002025
2026class AvbIcpEntry(object):
Jan Monschfe00c0a2019-12-11 11:19:40 +01002027 """A class for the transparency log inclusion proof entries.
Dan Austina7bc4962019-12-02 13:26:08 -08002028
Jan Monsch77cd2022019-12-10 17:18:04 +01002029 The data that represents each of the components of the ICP entry are stored
2030 immediately following the ICP entry header. The format is log_url,
2031 SignedLogRoot, and inclusion proof hashes.
Dan Austina7bc4962019-12-02 13:26:08 -08002032
2033 Attributes:
2034 log_url_size: Length of the string representing the transparency log URL.
2035 leaf_index: Leaf index in the transparency log representing this entry.
Jan Monsch77cd2022019-12-10 17:18:04 +01002036 signed_root_blob_size: Size of the SignedLogRoot for the transparency log;
2037 treat as an opaque blob for now.
Dan Austina7bc4962019-12-02 13:26:08 -08002038 proof_hash_count: Number of hashes comprising the inclusion proof.
2039 proof_size: The total size of the inclusion proof, in bytes.
2040 next_entry: 1 if there is a next entry, 0 otherwise.
2041 """
2042 SIZE = 22 # The size of the structure, in bytes
2043 FORMAT_STRING = ('!L' # transparency log server url size
2044 'Q' # leaf index
2045 'L' # signed tree root blob size
2046 'B' # number of hashes in the inclusion proof
2047 'L' # size of the inclusion proof in bytes
2048 'B') # next entry marker
2049
2050 def __init__(self, data=None):
2051 """Initializes a new ICP entry object.
2052
2053 Arguments:
2054 data: If not None, must be a bytearray of size == 22.
2055 """
2056 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2057
2058 if data:
2059 (self.log_url_size, self.leaf_index, self.signed_root_blob_size,
Jan Monsch77cd2022019-12-10 17:18:04 +01002060 self.proof_hash_count, self.proof_size,
2061 self.next_entry) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08002062 else:
2063 self.log_url_size = 0
2064 self.leaf_index = 0
2065 self.signed_root_blob_size = 0
2066 self.proof_hash_count = 0
2067 self.proof_size = 0
2068 self.next_entry = 0
2069
2070 def save(self, output):
2071 """Serializes the transparency header (22) to disk.
2072
2073 Arguments:
2074 output: The object to write the header to.
2075 """
2076 output.write(self.encode())
2077
2078 def encode(self):
2079 """Serializes the header (22) to a bytearray().
2080
2081 Returns:
2082 A bytearray() with the encoded header.
2083 """
2084 return struct.pack(self.FORMAT_STRING, self.log_url_size,
2085 self.leaf_index, self.signed_root_blob_size,
2086 self.proof_hash_count, self.proof_size,
2087 self.next_entry)
2088
2089 def is_valid(self):
2090 """Ensures that values in an AvbIcpEntry structure are sane.
2091
2092 Returns:
2093 True if the values in the AvbIcpEntry are sane, False otherwise.
2094 """
2095 if self.log_url_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002096 sys.stderr.write(
2097 'ICP entry: invalid transparency log URL size: {}\n'.format(
2098 self.log_url_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002099 return False
2100 if self.leaf_index < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002101 sys.stderr.write('ICP entry: leaf index out of range: {}\n'.format(
2102 self.leaf_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002103 return False
2104 if self.signed_root_blob_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002105 sys.stderr.write('ICP entry: invalid signed root blob size: {}\n'.format(
2106 self.signed_root_blob_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002107 return False
2108 if self.proof_hash_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002109 sys.stderr.write('ICP entry: invalid proof count: {}\n'.format(
2110 self.proof_hash_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002111 return False
2112 if self.proof_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002113 sys.stderr.write(
2114 'ICP entry: invalid transparency log proof size: {}\n'.format(
2115 self.proof_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002116 return False
2117 if self.next_entry != 0 and self.next_entry != 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002118 sys.stderr.write('ICP entry: invalid next entry value: {}\n'.format(
2119 self.next_entry))
Dan Austina7bc4962019-12-02 13:26:08 -08002120 return False
2121 return True
2122
Jan Monsch77cd2022019-12-10 17:18:04 +01002123
Dan Austina7bc4962019-12-02 13:26:08 -08002124def validate_icp_blob(icp_blob):
2125 """Ensures that values in an ICP section are structurally sane.
2126
2127 Arguments:
Jan Monsch77cd2022019-12-10 17:18:04 +01002128 icp_blob: A bytearray() that contains an ICP, with headers, entries and
2129 data.
Dan Austina7bc4962019-12-02 13:26:08 -08002130
2131 Returns:
2132 True if the values in the ICP section are sane, False otherwise.
2133 """
2134 icp_blob_size = len(icp_blob)
2135 icp_header_bytes = icp_blob[0:AvbIcpHeader.SIZE]
2136 icp_header = AvbIcpHeader(icp_header_bytes)
2137 if not icp_header.is_valid():
2138 sys.stderr.write('Validation of ICP header failed.\n')
2139 return False
2140
2141 icp_count = icp_header.icp_count
2142 algorithm_id = icp_header.algorithm
2143 proof_hash_size = lookup_hash_size_by_type(algorithm_id)
2144
2145 # jump past the header for entry processing
2146 icp_index = AvbIcpHeader.SIZE
2147 # Validate each entry
2148 for i in range(icp_count):
2149 # get the entry header from the ICP blob
2150 cur_icp_entry_blob = icp_blob[icp_index:(icp_index+AvbIcpEntry.SIZE)]
Jan Monsch77cd2022019-12-10 17:18:04 +01002151 cur_icp_entry = AvbIcpEntry(cur_icp_entry_blob)
Dan Austina7bc4962019-12-02 13:26:08 -08002152
2153 # now validate the entry structure
2154 if not cur_icp_entry.is_valid():
2155 sys.stderr.write('Validation of ICP entry {} failed.\n'.format(i))
2156 return False
2157 # jump past the entry header to the data blob
2158 icp_index += AvbIcpEntry.SIZE
2159 # extract each value from the data blob:
Jan Monsch77cd2022019-12-10 17:18:04 +01002160 transparency_log_url = icp_blob[icp_index:(icp_index
2161 + cur_icp_entry.log_url_size)]
Dan Austina7bc4962019-12-02 13:26:08 -08002162 icp_index += cur_icp_entry.log_url_size
2163 sth = icp_blob[icp_index:(icp_index+cur_icp_entry.signed_root_blob_size)]
2164 icp_index += cur_icp_entry.signed_root_blob_size
2165 if cur_icp_entry.proof_size / cur_icp_entry.proof_hash_count != proof_hash_size:
Jan Monsch77cd2022019-12-10 17:18:04 +01002166 sys.stderr.write('Bad proof size: {} in ICP entry {}'.format(
2167 cur_icp_entry.proof_size, i))
Dan Austina7bc4962019-12-02 13:26:08 -08002168 return False
2169 proof_size = icp_blob[icp_index:(icp_index+cur_icp_entry.proof_size)]
2170 icp_index += cur_icp_entry.proof_size
2171 # check if there is a next entry
2172 if cur_icp_entry.next_entry == 0:
2173 if i != icp_count - 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002174 sys.stderr.write('ICP entry count mismatch\n')
Dan Austina7bc4962019-12-02 13:26:08 -08002175 return False
2176 break
2177 if icp_index != len(icp_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +01002178 sys.stderr.write('ICP blob size mismatch, expected {}, got {}\n'.format(
2179 len(icp_blob), icp_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002180 return False
2181 return True
2182
2183# AFTL structure test functions
2184
Jan Monsch77cd2022019-12-10 17:18:04 +01002185
Dan Austina7bc4962019-12-02 13:26:08 -08002186def test_default_icp_header():
2187 """Performs validation on a default ICP header structure.
2188
2189 Returns:
2190 True if the tests pass, False otherwise.
2191 """
2192
2193 icp_header = AvbIcpHeader()
2194 if icp_header.is_valid():
2195 sys.stdout.write('ICP header: validation passed\n')
2196 return True
2197 else:
2198 sys.stdout.write('ICP header: validation failed\n')
2199 return False
2200
Jan Monsch77cd2022019-12-10 17:18:04 +01002201
Dan Austina7bc4962019-12-02 13:26:08 -08002202def test_icp_header(algorithm, icp_count):
2203 """Create an ICP header structure and attempt to validate it.
2204
Jan Monsch77cd2022019-12-10 17:18:04 +01002205 Arguments:
2206 algorithm: The algorithm to be used.
2207 icp_count: Number of ICPs that follow the ICP header.
2208
Dan Austina7bc4962019-12-02 13:26:08 -08002209 Returns:
2210 True if the tests pass, False otherwise.
2211 """
2212
2213 icp_header = AvbIcpHeader()
2214 icp_header.algorithm = algorithm
2215 icp_header.icp_count = icp_count
2216 if icp_header.is_valid():
2217 sys.stdout.write('ICP header: validation passed\n')
2218 return True
2219 else:
2220 sys.stdout.write('ICP header: validation failed\n')
2221 return False
2222
Jan Monsch77cd2022019-12-10 17:18:04 +01002223
Dan Austina7bc4962019-12-02 13:26:08 -08002224def test_default_icp_entry():
2225 """Performs validation on an empty ICP entry structure.
2226
2227 Returns:
2228 True if the tests pass, False otherwise.
2229 """
2230
2231 icp_entry = AvbIcpEntry()
2232 if icp_entry.is_valid():
2233 sys.stdout.write('ICP entry validation passed\n')
2234 return True
2235 else:
2236 sys.stdout.write('ICP entry validation failed\n')
2237 return False
2238
Jan Monsch77cd2022019-12-10 17:18:04 +01002239
2240def test_icp_entry(log_url_size, leaf_index, signed_root_blob_size,
2241 proof_hash_count, proof_size, next_entry):
Dan Austina7bc4962019-12-02 13:26:08 -08002242 """Create an ICP entry structure and attempt to validate it.
2243
2244 Returns:
2245 True if the tests pass, False otherwise.
2246 """
2247
2248 icp_entry = AvbIcpEntry()
2249 icp_entry.log_url_size = log_url_size
2250 icp_entry.leaf_index = leaf_index
2251 icp_entry.signed_root_blob_size = signed_root_blob_size
2252 icp_entry.proof_hash_count = proof_hash_count
2253 icp_entry.proof_size = proof_size
2254 icp_entry.next_entry = next_entry
2255 if icp_entry.is_valid():
2256 sys.stdout.write('ICP entry validation passed\n')
2257 return True
2258 else:
2259 sys.stdout.write('ICP entry validation failed\n')
2260 return False
2261
Jan Monsch77cd2022019-12-10 17:18:04 +01002262
Dan Austina7bc4962019-12-02 13:26:08 -08002263def test_generate_icp_images():
2264 """Test cases for full AFTL ICP structure generation.
2265
2266 Returns:
2267 True if all tests return expected values, False otherwise.
2268 """
2269
2270 if not test_default_icp_header():
2271 return False
2272
2273 if not test_icp_header(1, 1):
2274 return False
2275
2276 icp_header = AvbIcpHeader()
2277 icp_header.algorithm = 1
2278 icp_header.icp_count = 1
2279
2280 tl_url = 'aftl-test-server.google.com'
2281 sth = bytearray()
2282 # fill each structure with an easily observable pattern for easy validation.
2283 sth.extend('a' * 160)
2284 proof_hashes = bytearray()
2285 proof_hashes.extend('b' * 32)
2286 proof_hashes.extend('c' * 32)
2287 proof_hashes.extend('d' * 32)
2288 proof_hashes.extend('e' * 32)
2289 if not test_icp_entry(len(tl_url), 1, len(sth), 4, len(proof_hashes), 0):
2290 return False
2291 icp_entry = AvbIcpEntry()
2292 icp_entry.log_url_size = len(tl_url)
2293 icp_entry.leaf_index = 1
2294 icp_entry.signed_root_blob_size = len(sth)
2295 icp_entry.proof_hash_count = 4
2296 icp_entry.proof_size = len(proof_hashes)
2297 icp_entry.next_entry = 0
2298 icp_data = bytearray()
2299 icp_data.extend(tl_url)
2300 icp_data.extend(sth)
2301 icp_data.extend(proof_hashes)
2302 icp_blob = bytearray()
2303 icp_blob.extend(icp_header.encode())
2304 icp_blob.extend(icp_entry.encode())
2305 icp_blob.extend(icp_data)
2306 if not validate_icp_blob(icp_blob):
2307 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002308 # Now add a 2nd entry (this should fail).
Dan Austina7bc4962019-12-02 13:26:08 -08002309 tl_url2 = 'aftl-test-server.google.ch'
2310 sth2 = bytearray()
2311 sth2.extend('f' * 192)
2312 proof_hashes2 = bytearray()
2313 proof_hashes2.extend('g' * 32)
2314 proof_hashes2.extend('h' * 32)
2315 if not test_icp_entry(len(tl_url2), 2, len(sth2), 2, len(proof_hashes2), 0):
2316 return False
2317 icp_entry2 = AvbIcpEntry()
2318 icp_entry2.log_url_size = len(tl_url2)
2319 icp_entry2.leaf_index = 2
2320 icp_entry2.signed_root_blob_size = len(sth2)
2321 icp_entry2.proof_hash_count = 2
2322 icp_entry2.proof_size = len(proof_hashes2)
2323 icp_entry2.next_entry = 0
2324 icp_data2 = bytearray()
2325 icp_data2.extend(tl_url2)
2326 icp_data2.extend(sth2)
2327 icp_data2.extend(proof_hashes2)
2328 icp_blob.extend(icp_entry2.encode())
2329 icp_blob.extend(icp_data2)
2330 if validate_icp_blob(icp_blob):
2331 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002332 # Now fix the entries so this passes.
Dan Austina7bc4962019-12-02 13:26:08 -08002333 icp_header.icp_count = 2
2334 icp_entry.next_entry = 1
2335 icp_blob2 = bytearray()
2336 icp_blob2.extend(icp_header.encode())
2337 icp_blob2.extend(icp_entry.encode())
2338 icp_blob2.extend(icp_data)
2339 icp_blob2.extend(icp_entry2.encode())
2340 icp_blob2.extend(icp_data2)
2341 if not validate_icp_blob(icp_blob2):
2342 return False
2343 return True
2344
Jan Monsch77cd2022019-12-10 17:18:04 +01002345
Dan Austina7bc4962019-12-02 13:26:08 -08002346def test_icp_image():
2347 """The main function that handles testing all AFTL related components.
Jan Monsch77cd2022019-12-10 17:18:04 +01002348
2349 Currently performs tests for validation of AFTL related structures.
Dan Austina7bc4962019-12-02 13:26:08 -08002350
2351 Returns:
2352 True if all tests pass, False otherwise.
2353 """
2354
2355 # Header related tests.
2356 default_icp = test_default_icp_header()
2357 # 1 is SHA256/RSA4096
2358 if not test_icp_header(1, 4):
2359 return False
2360 # Header related failure tests.
2361 if test_icp_header(-12, 4):
2362 return False
2363 if test_icp_header(4, -34):
2364 return False
2365 if test_icp_header(10, 10):
2366 return False
2367 # ICP entry related tests.
2368 if not test_default_icp_entry():
2369 return False
2370 if not test_icp_entry(28, 2, 92, 2, 64, 0):
2371 return False
2372 if not test_icp_entry(28, 4, 160, 4, 128, 1):
2373 return False
2374 # ICP entry related failure tests.
2375 if test_icp_entry(-2, 2, 92, 2, 64, 0):
2376 return False
2377 if test_icp_entry(28, -1, 92, 2, 64, 0):
2378 return False
2379 if test_icp_entry(28, 2, -2, 2, 64, 0):
2380 return False
2381 if test_icp_entry(28, 2, 92, 4, 128, 32234):
2382 return False
2383 # Tests for the full AFTL ICP structure.
2384 if not test_generate_icp_images():
2385 return False
2386 sys.stdout.write('All AFTL ICP tests passed.\n')
2387 return True
2388
Jan Monsch77cd2022019-12-10 17:18:04 +01002389
David Zeuthen21e95262016-07-27 17:58:40 -04002390class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002391 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002392
Jan Monschfe00c0a2019-12-11 11:19:40 +01002393 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2394 avb_vbmeta_image.h.
2395
David Zeuthen21e95262016-07-27 17:58:40 -04002396 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01002397 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2398 required_libavb_version_major: The major version of libavb required for this
2399 header.
2400 required_libavb_version_minor: The minor version of libavb required for this
2401 header.
2402 authentication_data_block_size: The size of the signature block.
2403 auxiliary_data_block_size: The size of the auxiliary data block.
2404 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2405 enum.
2406 hash_offset: Offset into the "Authentication data" block of hash data.
2407 hash_size: Length of the hash data.
2408 signature_offset: Offset into the "Authentication data" block of signature
2409 data.
2410 signature_size: Length of the signature data.
2411 public_key_offset: Offset into the "Auxiliary data" block of public key
2412 data.
2413 public_key_size: Length of the public key data.
2414 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2415 key metadata.
2416 public_key_metadata_size: Length of the public key metadata. Must be set to
2417 zero if there is no public key metadata.
2418 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2419 data.
2420 descriptors_size: Length of descriptor data.
2421 rollback_index: The rollback index which can be used to prevent rollback to
2422 older versions.
2423 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2424 zero if the vbmeta image is not a top-level image.
2425 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2426 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2427 terminated. Applications must not make assumptions about how this
2428 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002429 """
2430
2431 SIZE = 256
2432
David Zeuthene3cadca2017-02-22 21:25:46 -05002433 # Keep in sync with |reserved0| and |reserved| field of
2434 # |AvbVBMetaImageHeader|.
2435 RESERVED0 = 4
2436 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002437
2438 # Keep in sync with |AvbVBMetaImageHeader|.
2439 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2440 '2Q' # 2 x block size
2441 'L' # algorithm type
2442 '2Q' # offset, size (hash)
2443 '2Q' # offset, size (signature)
2444 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002445 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002446 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002447 'Q' # rollback_index
2448 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002449 str(RESERVED0) + 'x' + # padding for reserved bytes
2450 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002451 str(RESERVED) + 'x') # padding for reserved bytes
2452
2453 def __init__(self, data=None):
2454 """Initializes a new header object.
2455
2456 Arguments:
2457 data: If not None, must be a bytearray of size 8192.
2458
2459 Raises:
2460 Exception: If the given data is malformed.
2461 """
2462 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2463
2464 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002465 (self.magic, self.required_libavb_version_major,
2466 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002467 self.authentication_data_block_size, self.auxiliary_data_block_size,
2468 self.algorithm_type, self.hash_offset, self.hash_size,
2469 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002470 self.public_key_size, self.public_key_metadata_offset,
2471 self.public_key_metadata_size, self.descriptors_offset,
2472 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002473 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002474 self.flags,
2475 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002476 # Nuke NUL-bytes at the end of the string.
2477 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002478 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002479 else:
2480 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002481 # Start by just requiring version 1.0. Code that adds features
2482 # in a future version can use bump_required_libavb_version_minor() to
2483 # bump the minor.
2484 self.required_libavb_version_major = AVB_VERSION_MAJOR
2485 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002486 self.authentication_data_block_size = 0
2487 self.auxiliary_data_block_size = 0
2488 self.algorithm_type = 0
2489 self.hash_offset = 0
2490 self.hash_size = 0
2491 self.signature_offset = 0
2492 self.signature_size = 0
2493 self.public_key_offset = 0
2494 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002495 self.public_key_metadata_offset = 0
2496 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002497 self.descriptors_offset = 0
2498 self.descriptors_size = 0
2499 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002500 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002501 self.release_string = get_release_string()
2502
2503 def bump_required_libavb_version_minor(self, minor):
2504 """Function to bump required_libavb_version_minor.
2505
2506 Call this when writing data that requires a specific libavb
2507 version to parse it.
2508
2509 Arguments:
2510 minor: The minor version of libavb that has support for the feature.
2511 """
2512 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002513 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002514
2515 def save(self, output):
2516 """Serializes the header (256 bytes) to disk.
2517
2518 Arguments:
2519 output: The object to write the output to.
2520 """
2521 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002522 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2523 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002524 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2525 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002526 self.public_key_offset, self.public_key_size,
2527 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002528 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002529 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002530
2531 def encode(self):
2532 """Serializes the header (256) to a bytearray().
2533
2534 Returns:
2535 A bytearray() with the encoded header.
2536 """
2537 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002538 self.required_libavb_version_major,
2539 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002540 self.authentication_data_block_size,
2541 self.auxiliary_data_block_size, self.algorithm_type,
2542 self.hash_offset, self.hash_size, self.signature_offset,
2543 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002544 self.public_key_size, self.public_key_metadata_offset,
2545 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002546 self.descriptors_size, self.rollback_index, self.flags,
2547 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002548
2549
2550class Avb(object):
2551 """Business logic for avbtool command-line tool."""
2552
David Zeuthen8b6973b2016-09-20 12:39:49 -04002553 # Keep in sync with avb_ab_flow.h.
2554 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2555 AB_MAGIC = '\0AB0'
2556 AB_MAJOR_VERSION = 1
2557 AB_MINOR_VERSION = 0
2558 AB_MISC_METADATA_OFFSET = 2048
2559
David Zeuthen09692692016-09-30 16:16:40 -04002560 # Constants for maximum metadata size. These are used to give
2561 # meaningful errors if the value passed in via --partition_size is
2562 # too small and when --calc_max_image_size is used. We use
2563 # conservative figures.
2564 MAX_VBMETA_SIZE = 64 * 1024
2565 MAX_FOOTER_SIZE = 4096
2566
David Zeuthen49936b42018-08-07 17:38:58 -04002567 def extract_vbmeta_image(self, output, image_filename, padding_size):
2568 """Implements the 'extract_vbmeta_image' command.
2569
2570 Arguments:
2571 output: Write vbmeta struct to this file.
2572 image_filename: File to extract vbmeta data from (with a footer).
2573 padding_size: If not 0, pads output so size is a multiple of the number.
2574
2575 Raises:
2576 AvbError: If there's no footer in the image.
2577 """
2578 image = ImageHandler(image_filename)
2579
2580 (footer, _, _, _) = self._parse_image(image)
2581
2582 if not footer:
2583 raise AvbError('Given image does not have a footer.')
2584
2585 image.seek(footer.vbmeta_offset)
2586 vbmeta_blob = image.read(footer.vbmeta_size)
2587 output.write(vbmeta_blob)
2588
2589 if padding_size > 0:
2590 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2591 padding_needed = padded_size - len(vbmeta_blob)
2592 output.write('\0' * padding_needed)
2593
David Zeuthena4fee8b2016-08-22 15:20:43 -04002594 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002595 """Implements the 'erase_footer' command.
2596
2597 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002598 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002599 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002600
2601 Raises:
2602 AvbError: If there's no footer in the image.
2603 """
2604
David Zeuthena4fee8b2016-08-22 15:20:43 -04002605 image = ImageHandler(image_filename)
2606
David Zeuthen21e95262016-07-27 17:58:40 -04002607 (footer, _, descriptors, _) = self._parse_image(image)
2608
2609 if not footer:
2610 raise AvbError('Given image does not have a footer.')
2611
2612 new_image_size = None
2613 if not keep_hashtree:
2614 new_image_size = footer.original_image_size
2615 else:
2616 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002617 # descriptor to figure out the location and size of the hashtree
2618 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002619 for desc in descriptors:
2620 if isinstance(desc, AvbHashtreeDescriptor):
2621 # The hashtree is always just following the main data so the
2622 # new size is easily derived.
2623 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002624 # If the image has FEC codes, also keep those.
2625 if desc.fec_offset > 0:
2626 fec_end = desc.fec_offset + desc.fec_size
2627 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002628 break
2629 if not new_image_size:
2630 raise AvbError('Requested to keep hashtree but no hashtree '
2631 'descriptor was found.')
2632
2633 # And cut...
2634 image.truncate(new_image_size)
2635
David Zeuthen1394f762019-04-30 10:20:11 -04002636 def zero_hashtree(self, image_filename):
2637 """Implements the 'zero_hashtree' command.
2638
2639 Arguments:
2640 image_filename: File to zero hashtree and FEC data from.
2641
2642 Raises:
2643 AvbError: If there's no footer in the image.
2644 """
2645
2646 image = ImageHandler(image_filename)
2647
2648 (footer, _, descriptors, _) = self._parse_image(image)
2649
2650 if not footer:
2651 raise AvbError('Given image does not have a footer.')
2652
2653 # Search for a hashtree descriptor to figure out the location and
2654 # size of the hashtree and FEC.
2655 ht_desc = None
2656 for desc in descriptors:
2657 if isinstance(desc, AvbHashtreeDescriptor):
2658 ht_desc = desc
2659 break
2660
2661 if not ht_desc:
2662 raise AvbError('No hashtree descriptor was found.')
2663
2664 zero_ht_start_offset = ht_desc.tree_offset
2665 zero_ht_num_bytes = ht_desc.tree_size
2666 zero_fec_start_offset = None
2667 zero_fec_num_bytes = 0
2668 if ht_desc.fec_offset > 0:
2669 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2670 raise AvbError('Hash-tree and FEC data must be adjacent.')
2671 zero_fec_start_offset = ht_desc.fec_offset
2672 zero_fec_num_bytes = ht_desc.fec_size
2673 zero_end_offset = zero_ht_start_offset + zero_ht_num_bytes + zero_fec_num_bytes
2674 image.seek(zero_end_offset)
2675 data = image.read(image.image_size - zero_end_offset)
2676
2677 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2678 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2679 # beginning of both hashtree and FEC. (That way, in the future we can add
2680 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2681 #
2682 # Applications can use these markers to detect that the hashtree and/or
2683 # FEC needs to be recomputed.
2684 image.truncate(zero_ht_start_offset)
2685 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2686 image.append_raw(data_zeroed_firstblock)
2687 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2688 if zero_fec_start_offset:
2689 image.append_raw(data_zeroed_firstblock)
2690 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2691 image.append_raw(data)
2692
David Zeuthen2bc232b2017-04-19 14:25:19 -04002693 def resize_image(self, image_filename, partition_size):
2694 """Implements the 'resize_image' command.
2695
2696 Arguments:
2697 image_filename: File with footer to resize.
2698 partition_size: The new size of the image.
2699
2700 Raises:
2701 AvbError: If there's no footer in the image.
2702 """
2703
2704 image = ImageHandler(image_filename)
2705
2706 if partition_size % image.block_size != 0:
2707 raise AvbError('Partition size of {} is not a multiple of the image '
2708 'block size {}.'.format(partition_size,
2709 image.block_size))
2710
Jan Monsch77cd2022019-12-10 17:18:04 +01002711 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002712
2713 if not footer:
2714 raise AvbError('Given image does not have a footer.')
2715
2716 # The vbmeta blob is always at the end of the data so resizing an
2717 # image amounts to just moving the footer around.
2718
2719 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2720 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002721 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2722 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002723
2724 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002725 raise AvbError('Requested size of {} is too small for an image '
2726 'of size {}.'
2727 .format(partition_size,
2728 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002729
2730 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2731 # with enough bytes such that the final Footer block is at the end
2732 # of partition_size.
2733 image.truncate(vbmeta_end_offset)
2734 image.append_dont_care(partition_size - vbmeta_end_offset -
2735 1*image.block_size)
2736
2737 # Just reuse the same footer - only difference is that we're
2738 # writing it in a different place.
2739 footer_blob = footer.encode()
2740 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2741 footer_blob)
2742 image.append_raw(footer_blob_with_padding)
2743
David Zeuthen8b6973b2016-09-20 12:39:49 -04002744 def set_ab_metadata(self, misc_image, slot_data):
2745 """Implements the 'set_ab_metadata' command.
2746
2747 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2748 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2749
2750 Arguments:
2751 misc_image: The misc image to write to.
2752 slot_data: Slot data as a string
2753
2754 Raises:
2755 AvbError: If slot data is malformed.
2756 """
2757 tokens = slot_data.split(':')
2758 if len(tokens) != 6:
2759 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2760 a_priority = int(tokens[0])
2761 a_tries_remaining = int(tokens[1])
2762 a_success = True if int(tokens[2]) != 0 else False
2763 b_priority = int(tokens[3])
2764 b_tries_remaining = int(tokens[4])
2765 b_success = True if int(tokens[5]) != 0 else False
2766
2767 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2768 self.AB_MAGIC,
2769 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2770 a_priority, a_tries_remaining, a_success,
2771 b_priority, b_tries_remaining, b_success)
2772 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2773 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2774 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2775 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2776 misc_image.write(ab_data)
2777
David Zeuthena4fee8b2016-08-22 15:20:43 -04002778 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002779 """Implements the 'info_image' command.
2780
2781 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002782 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002783 output: Output file to write human-readable information to (file object).
2784 """
2785
David Zeuthena4fee8b2016-08-22 15:20:43 -04002786 image = ImageHandler(image_filename)
2787
David Zeuthen21e95262016-07-27 17:58:40 -04002788 o = output
2789
2790 (footer, header, descriptors, image_size) = self._parse_image(image)
2791
2792 if footer:
2793 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2794 footer.version_minor))
2795 o.write('Image size: {} bytes\n'.format(image_size))
2796 o.write('Original image size: {} bytes\n'.format(
2797 footer.original_image_size))
2798 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2799 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2800 o.write('--\n')
2801
2802 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2803
David Zeuthene3cadca2017-02-22 21:25:46 -05002804 o.write('Minimum libavb version: {}.{}{}\n'.format(
2805 header.required_libavb_version_major,
2806 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002807 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002808 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2809 o.write('Authentication Block: {} bytes\n'.format(
2810 header.authentication_data_block_size))
2811 o.write('Auxiliary Block: {} bytes\n'.format(
2812 header.auxiliary_data_block_size))
2813 o.write('Algorithm: {}\n'.format(alg_name))
2814 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002815 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002816 o.write('Release String: \'{}\'\n'.format(
2817 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002818
2819 # Print descriptors.
2820 num_printed = 0
2821 o.write('Descriptors:\n')
2822 for desc in descriptors:
2823 desc.print_desc(o)
2824 num_printed += 1
2825 if num_printed == 0:
2826 o.write(' (none)\n')
2827
Jan Monscheeb28b62019-12-05 16:17:09 +01002828 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2829 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002830 """Implements the 'verify_image' command.
2831
2832 Arguments:
2833 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002834 key_path: None or check that embedded public key matches key at given
2835 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002836 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002837 follow_chain_partitions:
2838 If True, will follows chain partitions even when not specified with
2839 the --expected_chain_partition option
2840 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2841 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002842
2843 Raises:
2844 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002845 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002846 expected_chain_partitions_map = {}
2847 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002848 for cp in expected_chain_partitions:
2849 cp_tokens = cp.split(':')
2850 if len(cp_tokens) != 3:
2851 raise AvbError('Malformed chained partition "{}".'.format(cp))
2852 partition_name = cp_tokens[0]
2853 rollback_index_location = int(cp_tokens[1])
2854 file_path = cp_tokens[2]
2855 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002856 expected_chain_partitions_map[partition_name] = (
2857 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002858
2859 image_dir = os.path.dirname(image_filename)
2860 image_ext = os.path.splitext(image_filename)[1]
2861
2862 key_blob = None
2863 if key_path:
Jan Monscheeb28b62019-12-05 16:17:09 +01002864 print 'Verifying image {} using key at {}'.format(image_filename,
2865 key_path)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002866 key_blob = encode_rsa_key(key_path)
2867 else:
Jan Monscheeb28b62019-12-05 16:17:09 +01002868 print 'Verifying image {} using embedded public key'.format(
2869 image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002870
David Zeuthenb623d8b2017-04-04 16:05:53 -04002871 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002872 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002873 offset = 0
2874 if footer:
2875 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002876
David Zeuthenb623d8b2017-04-04 16:05:53 -04002877 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002878 vbmeta_blob = image.read(header.SIZE
2879 + header.authentication_data_block_size
2880 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002881
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002882 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002883 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002884 raise AvbError('Signature check failed for {} vbmeta struct {}'
2885 .format(alg_name, image_filename))
2886
2887 if key_blob:
2888 # The embedded public key is in the auxiliary block at an offset.
2889 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002890 key_offset += header.authentication_data_block_size
2891 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002892 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2893 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002894 if key_blob != key_blob_in_vbmeta:
2895 raise AvbError('Embedded public key does not match given key.')
2896
2897 if footer:
2898 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002899 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002900 else:
2901 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002902 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002903
2904 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002905 if (isinstance(desc, AvbChainPartitionDescriptor)
2906 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002907 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002908 # In this case we're processing a chain descriptor but don't have a
2909 # --expect_chain_partition ... however --follow_chain_partitions was
2910 # specified so we shouldn't error out in desc.verify().
Jan Monscheeb28b62019-12-05 16:17:09 +01002911 print ('{}: Chained but ROLLBACK_SLOT (which is {}) '
2912 'and KEY (which has sha1 {}) not specified'
2913 .format(desc.partition_name, desc.rollback_index_location,
2914 hashlib.sha1(desc.public_key).hexdigest()))
David Zeuthene947cb62019-01-25 15:27:08 -05002915 else:
Jan Monscheeb28b62019-12-05 16:17:09 +01002916 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
2917 image, accept_zeroed_hashtree):
David Zeuthene947cb62019-01-25 15:27:08 -05002918 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002919 # Honor --follow_chain_partitions - add '--' to make the output more
2920 # readable.
2921 if (isinstance(desc, AvbChainPartitionDescriptor)
2922 and follow_chain_partitions):
David Zeuthene947cb62019-01-25 15:27:08 -05002923 print '--'
Jan Monscheeb28b62019-12-05 16:17:09 +01002924 chained_image_filename = os.path.join(image_dir,
2925 desc.partition_name + image_ext)
2926 self.verify_image(chained_image_filename, key_path, None, False,
2927 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002928
David Zeuthenb8643c02018-05-17 17:21:18 -04002929 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2930 """Implements the 'calculate_vbmeta_digest' command.
2931
2932 Arguments:
2933 image_filename: Image file to get information from (file object).
2934 hash_algorithm: Hash algorithm used.
2935 output: Output file to write human-readable information to (file object).
2936 """
2937
2938 image_dir = os.path.dirname(image_filename)
2939 image_ext = os.path.splitext(image_filename)[1]
2940
2941 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002942 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002943 offset = 0
2944 if footer:
2945 offset = footer.vbmeta_offset
2946 size = (header.SIZE + header.authentication_data_block_size +
2947 header.auxiliary_data_block_size)
2948 image.seek(offset)
2949 vbmeta_blob = image.read(size)
2950
2951 hasher = hashlib.new(name=hash_algorithm)
2952 hasher.update(vbmeta_blob)
2953
2954 for desc in descriptors:
2955 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002956 ch_image_filename = os.path.join(image_dir,
2957 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002958 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002959 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002960 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002961 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2962 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002963 if ch_footer:
2964 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002965 ch_image.seek(ch_offset)
2966 ch_vbmeta_blob = ch_image.read(ch_size)
2967 hasher.update(ch_vbmeta_blob)
2968
2969 digest = hasher.digest()
2970 output.write('{}\n'.format(digest.encode('hex')))
2971
David Zeuthenf7d2e752018-09-20 13:30:41 -04002972 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2973 """Implements the 'calculate_kernel_cmdline' command.
2974
2975 Arguments:
2976 image_filename: Image file to get information from (file object).
2977 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2978 output: Output file to write human-readable information to (file object).
2979 """
2980
2981 image = ImageHandler(image_filename)
2982 _, _, descriptors, _ = self._parse_image(image)
2983
2984 image_dir = os.path.dirname(image_filename)
2985 image_ext = os.path.splitext(image_filename)[1]
2986
2987 cmdline_descriptors = []
2988 for desc in descriptors:
2989 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002990 ch_image_filename = os.path.join(image_dir,
2991 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002992 ch_image = ImageHandler(ch_image_filename)
2993 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2994 for ch_desc in ch_descriptors:
2995 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2996 cmdline_descriptors.append(ch_desc)
2997 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2998 cmdline_descriptors.append(desc)
2999
3000 kernel_cmdline_snippets = []
3001 for desc in cmdline_descriptors:
3002 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01003003 if ((desc.flags &
3004 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3005 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04003006 if hashtree_disabled:
3007 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01003008 if (desc.flags &
3009 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04003010 if not hashtree_disabled:
3011 use_cmdline = False
3012 if use_cmdline:
3013 kernel_cmdline_snippets.append(desc.kernel_cmdline)
3014 output.write(' '.join(kernel_cmdline_snippets))
3015
David Zeuthen21e95262016-07-27 17:58:40 -04003016 def _parse_image(self, image):
3017 """Gets information about an image.
3018
3019 The image can either be a vbmeta or an image with a footer.
3020
3021 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003022 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003023
3024 Returns:
3025 A tuple where the first argument is a AvbFooter (None if there
3026 is no footer on the image), the second argument is a
3027 AvbVBMetaHeader, the third argument is a list of
3028 AvbDescriptor-derived instances, and the fourth argument is the
3029 size of |image|.
3030 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003031 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04003032 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04003033 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04003034 try:
3035 footer = AvbFooter(image.read(AvbFooter.SIZE))
3036 except (LookupError, struct.error):
3037 # Nope, just seek back to the start.
3038 image.seek(0)
3039
3040 vbmeta_offset = 0
3041 if footer:
3042 vbmeta_offset = footer.vbmeta_offset
3043
3044 image.seek(vbmeta_offset)
3045 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3046
3047 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
3048 aux_block_offset = auth_block_offset + h.authentication_data_block_size
3049 desc_start_offset = aux_block_offset + h.descriptors_offset
3050 image.seek(desc_start_offset)
3051 descriptors = parse_descriptors(image.read(h.descriptors_size))
3052
David Zeuthen09692692016-09-30 16:16:40 -04003053 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003054
David Zeuthenb1b994d2017-03-06 18:01:31 -05003055 def _load_vbmeta_blob(self, image):
3056 """Gets the vbmeta struct and associated sections.
3057
3058 The image can either be a vbmeta.img or an image with a footer.
3059
3060 Arguments:
3061 image: An ImageHandler (vbmeta or footer).
3062
3063 Returns:
3064 A blob with the vbmeta struct and other sections.
3065 """
3066 assert isinstance(image, ImageHandler)
3067 footer = None
3068 image.seek(image.image_size - AvbFooter.SIZE)
3069 try:
3070 footer = AvbFooter(image.read(AvbFooter.SIZE))
3071 except (LookupError, struct.error):
3072 # Nope, just seek back to the start.
3073 image.seek(0)
3074
3075 vbmeta_offset = 0
3076 if footer:
3077 vbmeta_offset = footer.vbmeta_offset
3078
3079 image.seek(vbmeta_offset)
3080 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3081
3082 image.seek(vbmeta_offset)
3083 data_size = AvbVBMetaHeader.SIZE
3084 data_size += h.authentication_data_block_size
3085 data_size += h.auxiliary_data_block_size
3086 return image.read(data_size)
3087
David Zeuthen73f2afa2017-05-17 16:54:11 -04003088 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05003089 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04003090
3091 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04003092 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04003093
3094 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05003095 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3096 instructions. There is one for when hashtree is not disabled and one for
3097 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04003098
David Zeuthen21e95262016-07-27 17:58:40 -04003099 """
3100
David Zeuthen21e95262016-07-27 17:58:40 -04003101 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003102 c += '0' # start
3103 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
3104 c += ' verity {}'.format(ht.dm_verity_version) # type and version
3105 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
3106 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
3107 c += ' {}'.format(ht.data_block_size) # data_block
3108 c += ' {}'.format(ht.hash_block_size) # hash_block
3109 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
3110 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
3111 c += ' {}'.format(ht.hash_algorithm) # hash_alg
3112 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
3113 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
3114 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05003115 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003116 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003117 c += ' ignore_zero_blocks'
3118 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3119 c += ' fec_roots {}'.format(ht.fec_num_roots)
3120 # Note that fec_blocks is the size that FEC covers, *not* the
3121 # size of the FEC data. Since we use FEC for everything up until
3122 # the FEC data, it's the same as the offset.
3123 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
3124 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
3125 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05003126 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003127 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003128 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04003129 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04003130
David Zeuthenfd41eb92016-11-17 12:24:47 -05003131 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003132 desc = AvbKernelCmdlineDescriptor()
3133 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05003134 desc.flags = (
3135 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3136
3137 # The descriptor for when hashtree verification is disabled is a lot
3138 # simpler - we just set the root to the partition.
3139 desc_no_ht = AvbKernelCmdlineDescriptor()
3140 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3141 desc_no_ht.flags = (
3142 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
3143
3144 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04003145
David Zeuthen73f2afa2017-05-17 16:54:11 -04003146 def _get_cmdline_descriptors_for_dm_verity(self, image):
3147 """Generate kernel cmdline descriptors for dm-verity.
3148
3149 Arguments:
3150 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
3151
3152 Returns:
3153 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3154 instructions. There is one for when hashtree is not disabled and one for
3155 when it is.
3156
3157 Raises:
3158 AvbError: If |image| doesn't have a hashtree descriptor.
3159
3160 """
3161
3162 (_, _, descriptors, _) = self._parse_image(image)
3163
3164 ht = None
3165 for desc in descriptors:
3166 if isinstance(desc, AvbHashtreeDescriptor):
3167 ht = desc
3168 break
3169
3170 if not ht:
3171 raise AvbError('No hashtree descriptor in given image')
3172
3173 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
3174
Dan Austina7bc4962019-12-02 13:26:08 -08003175 def icp_test_suite(self):
Jan Monsch77cd2022019-12-10 17:18:04 +01003176 """Implements a self-contained test suite for AFTL related code.
Dan Austina7bc4962019-12-02 13:26:08 -08003177
3178 Returns:
3179 True if all tests pass, False otherwise.
3180 """
3181 return test_icp_image()
3182
David Zeuthen21e95262016-07-27 17:58:40 -04003183 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05003184 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003185 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003186 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04003187 include_descriptors_from_image,
3188 signing_helper,
3189 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003190 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003191 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003192 print_required_libavb_version,
3193 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003194 """Implements the 'make_vbmeta_image' command.
3195
3196 Arguments:
3197 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003198 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003199 algorithm_name: Name of algorithm to use.
3200 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003201 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003202 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003203 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003204 props: Properties to insert (list of strings of the form 'key:value').
3205 props_from_file: Properties to insert (list of strings 'key:<path>').
3206 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003207 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04003208 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003209 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003210 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003211 release_string: None or avbtool release string to use instead of default.
3212 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04003213 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04003214 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04003215
3216 Raises:
3217 AvbError: If a chained partition is malformed.
3218 """
3219
David Zeuthen1097a782017-05-31 15:53:17 -04003220 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003221 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003222 if include_descriptors_from_image:
3223 # Use the bump logic in AvbVBMetaHeader to calculate the max required
3224 # version of all included descriptors.
3225 tmp_header = AvbVBMetaHeader()
3226 for image in include_descriptors_from_image:
3227 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
3228 tmp_header.bump_required_libavb_version_minor(
3229 image_header.required_libavb_version_minor)
3230 print '1.{}'.format(tmp_header.required_libavb_version_minor)
3231 else:
3232 # Descriptors aside, all vbmeta features are supported in 1.0.
3233 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04003234 return
3235
3236 if not output:
3237 raise AvbError('No output file given')
3238
David Zeuthen21e95262016-07-27 17:58:40 -04003239 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04003240 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003241 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003242 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003243 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003244 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003245 include_descriptors_from_image, signing_helper,
3246 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003247 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04003248
3249 # Write entire vbmeta blob (header, authentication, auxiliary).
3250 output.seek(0)
3251 output.write(vbmeta_blob)
3252
David Zeuthen97cb5802017-06-01 16:14:05 -04003253 if padding_size > 0:
3254 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
3255 padding_needed = padded_size - len(vbmeta_blob)
3256 output.write('\0' * padding_needed)
3257
David Zeuthen18666ab2016-11-15 11:18:05 -05003258 def _generate_vbmeta_blob(self, algorithm_name, key_path,
3259 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003260 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003261 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003262 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003263 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003264 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003265 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003266 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003267 release_string, append_to_release_string,
3268 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04003269 """Generates a VBMeta blob.
3270
3271 This blob contains the header (struct AvbVBMetaHeader), the
3272 authentication data block (which contains the hash and signature
3273 for the header and auxiliary block), and the auxiliary block
3274 (which contains descriptors, the public key used, and other data).
3275
3276 The |key| parameter can |None| only if the |algorithm_name| is
3277 'NONE'.
3278
3279 Arguments:
3280 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3281 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003282 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003283 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003284 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003285 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003286 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003287 props: Properties to insert (List of strings of the form 'key:value').
3288 props_from_file: Properties to insert (List of strings 'key:<path>').
3289 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003290 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003291 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003292 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3293 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003294 include_descriptors_from_image: List of file objects for which
3295 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003296 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003297 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003298 release_string: None or avbtool release string.
3299 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003300 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003301
3302 Returns:
3303 A bytearray() with the VBMeta blob.
3304
3305 Raises:
3306 Exception: If the |algorithm_name| is not found, if no key has
3307 been given and the given algorithm requires one, or the key is
3308 of the wrong size.
3309
3310 """
3311 try:
3312 alg = ALGORITHMS[algorithm_name]
3313 except KeyError:
3314 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3315
David Zeuthena5fd3a42017-02-27 16:38:54 -05003316 if not descriptors:
3317 descriptors = []
3318
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003319 h = AvbVBMetaHeader()
3320 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3321
David Zeuthena5fd3a42017-02-27 16:38:54 -05003322 # Insert chained partition descriptors, if any
3323 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04003324 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003325 for cp in chain_partitions:
3326 cp_tokens = cp.split(':')
3327 if len(cp_tokens) != 3:
3328 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003329 partition_name = cp_tokens[0]
3330 rollback_index_location = int(cp_tokens[1])
3331 file_path = cp_tokens[2]
3332 # Check that the same rollback location isn't being used by
3333 # multiple chained partitions.
3334 if used_locations.get(rollback_index_location):
3335 raise AvbError('Rollback Index Location {} is already in use.'.format(
3336 rollback_index_location))
3337 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003338 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003339 desc.partition_name = partition_name
3340 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003341 if desc.rollback_index_location < 1:
3342 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003343 desc.public_key = open(file_path, 'rb').read()
3344 descriptors.append(desc)
3345
David Zeuthen21e95262016-07-27 17:58:40 -04003346 # Descriptors.
3347 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003348 for desc in descriptors:
3349 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003350
3351 # Add properties.
3352 if props:
3353 for prop in props:
3354 idx = prop.find(':')
3355 if idx == -1:
3356 raise AvbError('Malformed property "{}".'.format(prop))
3357 desc = AvbPropertyDescriptor()
3358 desc.key = prop[0:idx]
3359 desc.value = prop[(idx + 1):]
3360 encoded_descriptors.extend(desc.encode())
3361 if props_from_file:
3362 for prop in props_from_file:
3363 idx = prop.find(':')
3364 if idx == -1:
3365 raise AvbError('Malformed property "{}".'.format(prop))
3366 desc = AvbPropertyDescriptor()
3367 desc.key = prop[0:idx]
3368 desc.value = prop[(idx + 1):]
3369 file_path = prop[(idx + 1):]
3370 desc.value = open(file_path, 'rb').read()
3371 encoded_descriptors.extend(desc.encode())
3372
David Zeuthen73f2afa2017-05-17 16:54:11 -04003373 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003374 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003375 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003376 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003377 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3378 encoded_descriptors.extend(cmdline_desc[0].encode())
3379 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003380
David Zeuthen73f2afa2017-05-17 16:54:11 -04003381 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3382 if ht_desc_to_setup:
3383 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3384 ht_desc_to_setup)
3385 encoded_descriptors.extend(cmdline_desc[0].encode())
3386 encoded_descriptors.extend(cmdline_desc[1].encode())
3387
David Zeuthen21e95262016-07-27 17:58:40 -04003388 # Add kernel command-lines.
3389 if kernel_cmdlines:
3390 for i in kernel_cmdlines:
3391 desc = AvbKernelCmdlineDescriptor()
3392 desc.kernel_cmdline = i
3393 encoded_descriptors.extend(desc.encode())
3394
3395 # Add descriptors from other images.
3396 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003397 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003398 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003399 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003400 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3401 image_handler)
3402 # Bump the required libavb version to support all included descriptors.
3403 h.bump_required_libavb_version_minor(
3404 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003405 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003406 # The --include_descriptors_from_image option is used in some setups
3407 # with images A and B where both A and B contain a descriptor
3408 # for a partition with the same name. Since it's not meaningful
3409 # to include both descriptors, only include the last seen descriptor.
3410 # See bug 76386656 for details.
3411 if hasattr(desc, 'partition_name'):
3412 key = type(desc).__name__ + '_' + desc.partition_name
3413 descriptors_dict[key] = desc.encode()
3414 else:
3415 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003416 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003417 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003418
David Zeuthen18666ab2016-11-15 11:18:05 -05003419 # Load public key metadata blob, if requested.
3420 pkmd_blob = []
3421 if public_key_metadata_path:
3422 with open(public_key_metadata_path) as f:
3423 pkmd_blob = f.read()
3424
David Zeuthen21e95262016-07-27 17:58:40 -04003425 key = None
3426 encoded_key = bytearray()
3427 if alg.public_key_num_bytes > 0:
3428 if not key_path:
3429 raise AvbError('Key is required for algorithm {}'.format(
3430 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003431 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003432 if len(encoded_key) != alg.public_key_num_bytes:
3433 raise AvbError('Key is wrong size for algorithm {}'.format(
3434 algorithm_name))
3435
David Zeuthene3cadca2017-02-22 21:25:46 -05003436 # Override release string, if requested.
3437 if isinstance(release_string, (str, unicode)):
3438 h.release_string = release_string
3439
3440 # Append to release string, if requested. Also insert a space before.
3441 if isinstance(append_to_release_string, (str, unicode)):
3442 h.release_string += ' ' + append_to_release_string
3443
David Zeuthen18666ab2016-11-15 11:18:05 -05003444 # For the Auxiliary data block, descriptors are stored at offset 0,
3445 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003446 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003447 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003448 h.descriptors_offset = 0
3449 h.descriptors_size = len(encoded_descriptors)
3450 h.public_key_offset = h.descriptors_size
3451 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003452 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3453 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003454
3455 # For the Authentication data block, the hash is first and then
3456 # the signature.
3457 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003458 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003459 h.algorithm_type = alg.algorithm_type
3460 h.hash_offset = 0
3461 h.hash_size = alg.hash_num_bytes
3462 # Signature offset and size - it's stored right after the hash
3463 # (in Authentication data block).
3464 h.signature_offset = alg.hash_num_bytes
3465 h.signature_size = alg.signature_num_bytes
3466
3467 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003468 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003469
3470 # Generate Header data block.
3471 header_data_blob = h.encode()
3472
3473 # Generate Auxiliary data block.
3474 aux_data_blob = bytearray()
3475 aux_data_blob.extend(encoded_descriptors)
3476 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003477 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003478 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3479 aux_data_blob.extend('\0' * padding_bytes)
3480
3481 # Calculate the hash.
3482 binary_hash = bytearray()
3483 binary_signature = bytearray()
3484 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003485 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003486 ha.update(header_data_blob)
3487 ha.update(aux_data_blob)
3488 binary_hash.extend(ha.digest())
3489
3490 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003491 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003492 binary_signature.extend(raw_sign(signing_helper,
3493 signing_helper_with_files,
3494 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003495 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003496 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003497
3498 # Generate Authentication data block.
3499 auth_data_blob = bytearray()
3500 auth_data_blob.extend(binary_hash)
3501 auth_data_blob.extend(binary_signature)
3502 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3503 auth_data_blob.extend('\0' * padding_bytes)
3504
3505 return header_data_blob + auth_data_blob + aux_data_blob
3506
3507 def extract_public_key(self, key_path, output):
3508 """Implements the 'extract_public_key' command.
3509
3510 Arguments:
3511 key_path: The path to a RSA private key file.
3512 output: The file to write to.
3513 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003514 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003515
David Zeuthenb1b994d2017-03-06 18:01:31 -05003516 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3517 partition_size):
3518 """Implementation of the append_vbmeta_image command.
3519
3520 Arguments:
3521 image_filename: File to add the footer to.
3522 vbmeta_image_filename: File to get vbmeta struct from.
3523 partition_size: Size of partition.
3524
3525 Raises:
3526 AvbError: If an argument is incorrect.
3527 """
3528 image = ImageHandler(image_filename)
3529
3530 if partition_size % image.block_size != 0:
3531 raise AvbError('Partition size of {} is not a multiple of the image '
3532 'block size {}.'.format(partition_size,
3533 image.block_size))
3534
3535 # If there's already a footer, truncate the image to its original
3536 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003537 if image.image_size >= AvbFooter.SIZE:
3538 image.seek(image.image_size - AvbFooter.SIZE)
3539 try:
3540 footer = AvbFooter(image.read(AvbFooter.SIZE))
3541 # Existing footer found. Just truncate.
3542 original_image_size = footer.original_image_size
3543 image.truncate(footer.original_image_size)
3544 except (LookupError, struct.error):
3545 original_image_size = image.image_size
3546 else:
3547 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003548 original_image_size = image.image_size
3549
3550 # If anything goes wrong from here-on, restore the image back to
3551 # its original size.
3552 try:
3553 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3554 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3555
3556 # If the image isn't sparse, its size might not be a multiple of
3557 # the block size. This will screw up padding later so just grow it.
3558 if image.image_size % image.block_size != 0:
3559 assert not image.is_sparse
3560 padding_needed = image.block_size - (image.image_size%image.block_size)
3561 image.truncate(image.image_size + padding_needed)
3562
3563 # The append_raw() method requires content with size being a
3564 # multiple of |block_size| so add padding as needed. Also record
3565 # where this is written to since we'll need to put that in the
3566 # footer.
3567 vbmeta_offset = image.image_size
3568 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3569 len(vbmeta_blob))
3570 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3571
3572 # Append vbmeta blob and footer
3573 image.append_raw(vbmeta_blob_with_padding)
3574 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3575
3576 # Now insert a DONT_CARE chunk with enough bytes such that the
3577 # final Footer block is at the end of partition_size..
3578 image.append_dont_care(partition_size - vbmeta_end_offset -
3579 1*image.block_size)
3580
3581 # Generate the Footer that tells where the VBMeta footer
3582 # is. Also put enough padding in the front of the footer since
3583 # we'll write out an entire block.
3584 footer = AvbFooter()
3585 footer.original_image_size = original_image_size
3586 footer.vbmeta_offset = vbmeta_offset
3587 footer.vbmeta_size = len(vbmeta_blob)
3588 footer_blob = footer.encode()
3589 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3590 footer_blob)
3591 image.append_raw(footer_blob_with_padding)
3592
3593 except:
3594 # Truncate back to original size, then re-raise
3595 image.truncate(original_image_size)
3596 raise
3597
David Zeuthena4fee8b2016-08-22 15:20:43 -04003598 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003599 hash_algorithm, salt, chain_partitions, algorithm_name,
3600 key_path,
3601 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003602 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003603 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003604 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003605 signing_helper, signing_helper_with_files,
3606 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003607 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003608 print_required_libavb_version, use_persistent_digest,
3609 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003610 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003611
3612 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003613 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003614 partition_size: Size of partition.
3615 partition_name: Name of partition (without A/B suffix).
3616 hash_algorithm: Hash algorithm to use.
3617 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003618 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003619 algorithm_name: Name of algorithm to use.
3620 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003621 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003622 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003623 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003624 props: Properties to insert (List of strings of the form 'key:value').
3625 props_from_file: Properties to insert (List of strings 'key:<path>').
3626 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003627 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003628 dm-verity kernel cmdline from.
3629 include_descriptors_from_image: List of file objects for which
3630 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003631 calc_max_image_size: Don't store the footer - instead calculate the
3632 maximum image size leaving enough room for metadata with the
3633 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003634 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003635 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003636 release_string: None or avbtool release string.
3637 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003638 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3639 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003640 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003641 use_persistent_digest: Use a persistent digest on device.
3642 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003643
3644 Raises:
3645 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003646 """
David Zeuthen1097a782017-05-31 15:53:17 -04003647
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003648 required_libavb_version_minor = 0
3649 if use_persistent_digest or do_not_use_ab:
3650 required_libavb_version_minor = 1
3651
David Zeuthen1097a782017-05-31 15:53:17 -04003652 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003653 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003654 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003655 return
3656
David Zeuthenbf562452017-05-17 18:04:43 -04003657 # First, calculate the maximum image size such that an image
3658 # this size + metadata (footer + vbmeta struct) fits in
3659 # |partition_size|.
3660 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003661 if partition_size < max_metadata_size:
3662 raise AvbError('Parition size of {} is too small. '
3663 'Needs to be at least {}'.format(
3664 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003665 max_image_size = partition_size - max_metadata_size
3666
3667 # If we're asked to only calculate the maximum image size, we're done.
3668 if calc_max_image_size:
3669 print '{}'.format(max_image_size)
3670 return
3671
David Zeuthena4fee8b2016-08-22 15:20:43 -04003672 image = ImageHandler(image_filename)
3673
3674 if partition_size % image.block_size != 0:
3675 raise AvbError('Partition size of {} is not a multiple of the image '
3676 'block size {}.'.format(partition_size,
3677 image.block_size))
3678
David Zeuthen21e95262016-07-27 17:58:40 -04003679 # If there's already a footer, truncate the image to its original
3680 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3681 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003682 if image.image_size >= AvbFooter.SIZE:
3683 image.seek(image.image_size - AvbFooter.SIZE)
3684 try:
3685 footer = AvbFooter(image.read(AvbFooter.SIZE))
3686 # Existing footer found. Just truncate.
3687 original_image_size = footer.original_image_size
3688 image.truncate(footer.original_image_size)
3689 except (LookupError, struct.error):
3690 original_image_size = image.image_size
3691 else:
3692 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003693 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003694
3695 # If anything goes wrong from here-on, restore the image back to
3696 # its original size.
3697 try:
David Zeuthen09692692016-09-30 16:16:40 -04003698 # If image size exceeds the maximum image size, fail.
3699 if image.image_size > max_image_size:
3700 raise AvbError('Image size of {} exceeds maximum image '
3701 'size of {} in order to fit in a partition '
3702 'size of {}.'.format(image.image_size, max_image_size,
3703 partition_size))
3704
David Zeuthen21e95262016-07-27 17:58:40 -04003705 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3706 if salt:
3707 salt = salt.decode('hex')
3708 else:
Bryan Henry45354282018-10-25 18:37:27 -07003709 if salt is None and not use_persistent_digest:
3710 # If salt is not explicitly specified, choose a hash that's the same
3711 # size as the hash size. Don't populate a random salt if this
3712 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003713 hash_size = digest_size
3714 salt = open('/dev/urandom').read(hash_size)
3715 else:
3716 salt = ''
3717
3718 hasher = hashlib.new(name=hash_algorithm, string=salt)
3719 # TODO(zeuthen): might want to read this in chunks to avoid
3720 # memory pressure, then again, this is only supposed to be used
3721 # on kernel/initramfs partitions. Possible optimization.
3722 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003723 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003724 digest = hasher.digest()
3725
3726 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003727 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003728 h_desc.hash_algorithm = hash_algorithm
3729 h_desc.partition_name = partition_name
3730 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003731 h_desc.flags = 0
3732 if do_not_use_ab:
3733 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3734 if not use_persistent_digest:
3735 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003736
3737 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003738 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003739 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003740 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003741 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003742 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003743 include_descriptors_from_image, signing_helper,
3744 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003745 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003746
David Zeuthend247fcb2017-02-16 12:09:27 -05003747 # Write vbmeta blob, if requested.
3748 if output_vbmeta_image:
3749 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003750
David Zeuthend247fcb2017-02-16 12:09:27 -05003751 # Append vbmeta blob and footer, unless requested not to.
3752 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003753 # If the image isn't sparse, its size might not be a multiple of
3754 # the block size. This will screw up padding later so just grow it.
3755 if image.image_size % image.block_size != 0:
3756 assert not image.is_sparse
3757 padding_needed = image.block_size - (
3758 image.image_size % image.block_size)
3759 image.truncate(image.image_size + padding_needed)
3760
3761 # The append_raw() method requires content with size being a
3762 # multiple of |block_size| so add padding as needed. Also record
3763 # where this is written to since we'll need to put that in the
3764 # footer.
3765 vbmeta_offset = image.image_size
3766 padding_needed = (
3767 round_to_multiple(len(vbmeta_blob), image.block_size) -
3768 len(vbmeta_blob))
3769 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3770
David Zeuthend247fcb2017-02-16 12:09:27 -05003771 image.append_raw(vbmeta_blob_with_padding)
3772 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3773
3774 # Now insert a DONT_CARE chunk with enough bytes such that the
3775 # final Footer block is at the end of partition_size..
3776 image.append_dont_care(partition_size - vbmeta_end_offset -
3777 1*image.block_size)
3778
3779 # Generate the Footer that tells where the VBMeta footer
3780 # is. Also put enough padding in the front of the footer since
3781 # we'll write out an entire block.
3782 footer = AvbFooter()
3783 footer.original_image_size = original_image_size
3784 footer.vbmeta_offset = vbmeta_offset
3785 footer.vbmeta_size = len(vbmeta_blob)
3786 footer_blob = footer.encode()
3787 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3788 footer_blob)
3789 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003790
David Zeuthen21e95262016-07-27 17:58:40 -04003791 except:
3792 # Truncate back to original size, then re-raise
3793 image.truncate(original_image_size)
3794 raise
3795
David Zeuthena4fee8b2016-08-22 15:20:43 -04003796 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003797 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003798 block_size, salt, chain_partitions, algorithm_name,
3799 key_path,
3800 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003801 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003802 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003803 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003804 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003805 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003806 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003807 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003808 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003809 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003810 use_persistent_root_digest, do_not_use_ab,
3811 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003812 """Implements the 'add_hashtree_footer' command.
3813
3814 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3815 more information about dm-verity and these hashes.
3816
3817 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003818 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003819 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003820 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003821 generate_fec: If True, generate FEC codes.
3822 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003823 hash_algorithm: Hash algorithm to use.
3824 block_size: Block size to use.
3825 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003826 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003827 algorithm_name: Name of algorithm to use.
3828 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003829 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003830 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003831 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003832 props: Properties to insert (List of strings of the form 'key:value').
3833 props_from_file: Properties to insert (List of strings 'key:<path>').
3834 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003835 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003836 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003837 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3838 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003839 include_descriptors_from_image: List of file objects for which
3840 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003841 calc_max_image_size: Don't store the hashtree or footer - instead
3842 calculate the maximum image size leaving enough room for hashtree
3843 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003844 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003845 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003846 release_string: None or avbtool release string.
3847 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003848 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3849 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003850 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003851 use_persistent_root_digest: Use a persistent root digest on device.
3852 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003853 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003854
3855 Raises:
3856 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003857 """
David Zeuthen1097a782017-05-31 15:53:17 -04003858
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003859 required_libavb_version_minor = 0
3860 if use_persistent_root_digest or do_not_use_ab:
3861 required_libavb_version_minor = 1
3862
David Zeuthen1097a782017-05-31 15:53:17 -04003863 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003864 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003865 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003866 return
3867
David Zeuthen09692692016-09-30 16:16:40 -04003868 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3869 digest_padding = round_to_pow2(digest_size) - digest_size
3870
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003871 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3872 # size such that an image this size + the hashtree + metadata (footer +
3873 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3874 # for metadata.
3875 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003876 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003877 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003878 if not no_hashtree:
3879 (_, max_tree_size) = calc_hash_level_offsets(
3880 partition_size, block_size, digest_size + digest_padding)
3881 if generate_fec:
3882 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003883 max_metadata_size = (max_fec_size + max_tree_size +
3884 self.MAX_VBMETA_SIZE +
3885 self.MAX_FOOTER_SIZE)
3886 max_image_size = partition_size - max_metadata_size
3887 else:
3888 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003889
3890 # If we're asked to only calculate the maximum image size, we're done.
3891 if calc_max_image_size:
3892 print '{}'.format(max_image_size)
3893 return
3894
David Zeuthena4fee8b2016-08-22 15:20:43 -04003895 image = ImageHandler(image_filename)
3896
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003897 if partition_size > 0:
3898 if partition_size % image.block_size != 0:
3899 raise AvbError('Partition size of {} is not a multiple of the image '
3900 'block size {}.'.format(partition_size,
3901 image.block_size))
3902 else:
3903 if image.image_size % image.block_size != 0:
3904 raise AvbError('File size of {} is not a multiple of the image '
3905 'block size {}.'.format(image.image_size,
3906 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003907
David Zeuthen21e95262016-07-27 17:58:40 -04003908 # If there's already a footer, truncate the image to its original
3909 # size. This way 'avbtool add_hashtree_footer' is idempotent
3910 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003911 if image.image_size >= AvbFooter.SIZE:
3912 image.seek(image.image_size - AvbFooter.SIZE)
3913 try:
3914 footer = AvbFooter(image.read(AvbFooter.SIZE))
3915 # Existing footer found. Just truncate.
3916 original_image_size = footer.original_image_size
3917 image.truncate(footer.original_image_size)
3918 except (LookupError, struct.error):
3919 original_image_size = image.image_size
3920 else:
3921 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003922 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003923
3924 # If anything goes wrong from here-on, restore the image back to
3925 # its original size.
3926 try:
3927 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003928 rounded_image_size = round_to_multiple(image.image_size, block_size)
3929 if rounded_image_size > image.image_size:
3930 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003931
David Zeuthen09692692016-09-30 16:16:40 -04003932 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003933 if partition_size > 0:
3934 if image.image_size > max_image_size:
3935 raise AvbError('Image size of {} exceeds maximum image '
3936 'size of {} in order to fit in a partition '
3937 'size of {}.'.format(image.image_size, max_image_size,
3938 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003939
3940 if salt:
3941 salt = salt.decode('hex')
3942 else:
Bryan Henry45354282018-10-25 18:37:27 -07003943 if salt is None and not use_persistent_root_digest:
3944 # If salt is not explicitly specified, choose a hash that's the same
3945 # size as the hash size. Don't populate a random salt if this
3946 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003947 hash_size = digest_size
3948 salt = open('/dev/urandom').read(hash_size)
3949 else:
3950 salt = ''
3951
David Zeuthena4fee8b2016-08-22 15:20:43 -04003952 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003953 # offsets in advance.
3954 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003955 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003956
David Zeuthena4fee8b2016-08-22 15:20:43 -04003957 # If the image isn't sparse, its size might not be a multiple of
3958 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003959 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003960 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003961 padding_needed = image.block_size - (image.image_size%image.block_size)
3962 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003963
David Zeuthena4fee8b2016-08-22 15:20:43 -04003964 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003965 tree_offset = image.image_size
3966 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003967 block_size,
3968 hash_algorithm, salt,
3969 digest_padding,
3970 hash_level_offsets,
3971 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003972
3973 # Generate HashtreeDescriptor with details about the tree we
3974 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003975 if no_hashtree:
3976 tree_size = 0
3977 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003978 ht_desc = AvbHashtreeDescriptor()
3979 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003980 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003981 ht_desc.tree_offset = tree_offset
3982 ht_desc.tree_size = tree_size
3983 ht_desc.data_block_size = block_size
3984 ht_desc.hash_block_size = block_size
3985 ht_desc.hash_algorithm = hash_algorithm
3986 ht_desc.partition_name = partition_name
3987 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003988 if do_not_use_ab:
3989 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3990 if not use_persistent_root_digest:
3991 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003992
David Zeuthen09692692016-09-30 16:16:40 -04003993 # Write the hash tree
3994 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3995 len(hash_tree))
3996 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3997 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003998 len_hashtree_and_fec = len(hash_tree_with_padding)
3999
4000 # Generate FEC codes, if requested.
4001 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09004002 if no_hashtree:
4003 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07004004 else:
4005 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004006 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
4007 len(fec_data))
4008 fec_data_with_padding = fec_data + '\0'*padding_needed
4009 fec_offset = image.image_size
4010 image.append_raw(fec_data_with_padding)
4011 len_hashtree_and_fec += len(fec_data_with_padding)
4012 # Update the hashtree descriptor.
4013 ht_desc.fec_num_roots = fec_num_roots
4014 ht_desc.fec_offset = fec_offset
4015 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04004016
David Zeuthen73f2afa2017-05-17 16:54:11 -04004017 ht_desc_to_setup = None
4018 if setup_as_rootfs_from_kernel:
4019 ht_desc_to_setup = ht_desc
4020
David Zeuthena4fee8b2016-08-22 15:20:43 -04004021 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004022 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04004023 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05004024 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05004025 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04004026 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04004027 include_descriptors_from_image, signing_helper,
4028 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004029 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004030 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
4031 len(vbmeta_blob))
4032 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04004033
David Zeuthend247fcb2017-02-16 12:09:27 -05004034 # Write vbmeta blob, if requested.
4035 if output_vbmeta_image:
4036 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04004037
David Zeuthend247fcb2017-02-16 12:09:27 -05004038 # Append vbmeta blob and footer, unless requested not to.
4039 if not do_not_append_vbmeta_image:
4040 image.append_raw(vbmeta_blob_with_padding)
4041
4042 # Now insert a DONT_CARE chunk with enough bytes such that the
4043 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004044 if partition_size > 0:
4045 image.append_dont_care(partition_size - image.image_size -
4046 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05004047
4048 # Generate the Footer that tells where the VBMeta footer
4049 # is. Also put enough padding in the front of the footer since
4050 # we'll write out an entire block.
4051 footer = AvbFooter()
4052 footer.original_image_size = original_image_size
4053 footer.vbmeta_offset = vbmeta_offset
4054 footer.vbmeta_size = len(vbmeta_blob)
4055 footer_blob = footer.encode()
4056 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4057 footer_blob)
4058 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004059
David Zeuthen21e95262016-07-27 17:58:40 -04004060 except:
David Zeuthen09692692016-09-30 16:16:40 -04004061 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04004062 image.truncate(original_image_size)
4063 raise
4064
David Zeuthenc68f0822017-03-31 17:22:35 -04004065 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004066 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004067 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004068 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08004069 """Implements the 'make_atx_certificate' command.
4070
4071 Android Things certificates are required for Android Things public key
4072 metadata. They chain the vbmeta signing key for a particular product back to
4073 a fused, permanent root key. These certificates are fixed-length and fixed-
4074 format with the explicit goal of not parsing ASN.1 in bootloader code.
4075
4076 Arguments:
4077 output: Certificate will be written to this file on success.
4078 authority_key_path: A PEM file path with the authority private key.
4079 If None, then a certificate will be created without a
4080 signature. The signature can be created out-of-band
4081 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04004082 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08004083 subject_key_version: A 64-bit version value. If this is None, the number
4084 of seconds since the epoch is used.
4085 subject: A subject identifier. For Product Signing Key certificates this
4086 should be the same Product ID found in the permanent attributes.
4087 is_intermediate_authority: True if the certificate is for an intermediate
4088 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08004089 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08004090 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004091 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08004092 """
4093 signed_data = bytearray()
4094 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004095 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004096 hasher = hashlib.sha256()
4097 hasher.update(subject)
4098 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08004099 if not usage:
4100 usage = 'com.google.android.things.vboot'
4101 if is_intermediate_authority:
4102 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08004103 hasher = hashlib.sha256()
4104 hasher.update(usage)
4105 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07004106 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08004107 subject_key_version = int(time.time())
4108 signed_data.extend(struct.pack('<Q', subject_key_version))
4109 signature = bytearray()
4110 if authority_key_path:
4111 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08004112 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09004113 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08004114 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09004115 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08004116 hasher.update(signed_data)
4117 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04004118 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4119 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004120 alg.signature_num_bytes, authority_key_path,
4121 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08004122 output.write(signed_data)
4123 output.write(signature)
4124
David Zeuthenc68f0822017-03-31 17:22:35 -04004125 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004126 product_id):
4127 """Implements the 'make_atx_permanent_attributes' command.
4128
4129 Android Things permanent attributes are designed to be permanent for a
4130 particular product and a hash of these attributes should be fused into
4131 hardware to enforce this.
4132
4133 Arguments:
4134 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04004135 root_authority_key_path: Path to a PEM or DER public key for
4136 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08004137 product_id: A 16-byte Product ID.
4138
4139 Raises:
4140 AvbError: If an argument is incorrect.
4141 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004142 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004143 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004144 raise AvbError('Invalid Product ID length.')
4145 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004146 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004147 output.write(product_id)
4148
4149 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08004150 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08004151 """Implements the 'make_atx_metadata' command.
4152
4153 Android Things metadata are included in vbmeta images to facilitate
4154 verification. The output of this command can be used as the
4155 public_key_metadata argument to other commands.
4156
4157 Arguments:
4158 output: Metadata will be written to this file on success.
4159 intermediate_key_certificate: A certificate file as output by
4160 make_atx_certificate with
4161 is_intermediate_authority set to true.
4162 product_key_certificate: A certificate file as output by
4163 make_atx_certificate with
4164 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08004165
4166 Raises:
4167 AvbError: If an argument is incorrect.
4168 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004169 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08004170 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004171 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08004172 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004173 raise AvbError('Invalid product key certificate length.')
4174 output.write(struct.pack('<I', 1)) # Format Version
4175 output.write(intermediate_key_certificate)
4176 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08004177
Darren Krahnfccd64e2018-01-16 17:39:35 -08004178 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
4179 unlock_key_certificate, challenge_path,
4180 unlock_key_path, signing_helper,
4181 signing_helper_with_files):
4182 """Implements the 'make_atx_unlock_credential' command.
4183
4184 Android Things unlock credentials can be used to authorize the unlock of AVB
4185 on a device. These credentials are presented to an Android Things bootloader
4186 via the fastboot interface in response to a 16-byte challenge. This method
4187 creates all fields of the credential except the challenge signature field
4188 (which is the last field) and can optionally create the challenge signature
4189 field as well if a challenge and the unlock_key_path is provided.
4190
4191 Arguments:
4192 output: The credential will be written to this file on success.
4193 intermediate_key_certificate: A certificate file as output by
4194 make_atx_certificate with
4195 is_intermediate_authority set to true.
4196 unlock_key_certificate: A certificate file as output by
4197 make_atx_certificate with
4198 is_intermediate_authority set to false and the
4199 usage set to
4200 'com.google.android.things.vboot.unlock'.
4201 challenge_path: [optional] A path to the challenge to sign.
4202 unlock_key_path: [optional] A PEM file path with the unlock private key.
4203 signing_helper: Program which signs a hash and returns the signature.
4204 signing_helper_with_files: Same as signing_helper but uses files instead.
4205
4206 Raises:
4207 AvbError: If an argument is incorrect.
4208 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01004209 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
4210 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08004211 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4212 raise AvbError('Invalid intermediate key certificate length.')
4213 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4214 raise AvbError('Invalid product key certificate length.')
4215 challenge = bytearray()
4216 if challenge_path:
4217 with open(challenge_path, 'r') as f:
4218 challenge = f.read()
4219 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
4220 raise AvbError('Invalid unlock challenge length.')
4221 output.write(struct.pack('<I', 1)) # Format Version
4222 output.write(intermediate_key_certificate)
4223 output.write(unlock_key_certificate)
4224 if challenge_path and unlock_key_path:
4225 signature = bytearray()
4226 padding_and_hash = bytearray()
4227 algorithm_name = 'SHA512_RSA4096'
4228 alg = ALGORITHMS[algorithm_name]
4229 hasher = hashlib.sha512()
4230 padding_and_hash.extend(alg.padding)
4231 hasher.update(challenge)
4232 padding_and_hash.extend(hasher.digest())
4233 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4234 algorithm_name,
4235 alg.signature_num_bytes, unlock_key_path,
4236 padding_and_hash))
4237 output.write(signature)
4238
David Zeuthen21e95262016-07-27 17:58:40 -04004239
4240def calc_hash_level_offsets(image_size, block_size, digest_size):
4241 """Calculate the offsets of all the hash-levels in a Merkle-tree.
4242
4243 Arguments:
4244 image_size: The size of the image to calculate a Merkle-tree for.
4245 block_size: The block size, e.g. 4096.
4246 digest_size: The size of each hash, e.g. 32 for SHA-256.
4247
4248 Returns:
4249 A tuple where the first argument is an array of offsets and the
4250 second is size of the tree, in bytes.
4251 """
4252 level_offsets = []
4253 level_sizes = []
4254 tree_size = 0
4255
4256 num_levels = 0
4257 size = image_size
4258 while size > block_size:
4259 num_blocks = (size + block_size - 1) / block_size
4260 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4261
4262 level_sizes.append(level_size)
4263 tree_size += level_size
4264 num_levels += 1
4265
4266 size = level_size
4267
4268 for n in range(0, num_levels):
4269 offset = 0
4270 for m in range(n + 1, num_levels):
4271 offset += level_sizes[m]
4272 level_offsets.append(offset)
4273
David Zeuthena4fee8b2016-08-22 15:20:43 -04004274 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004275
4276
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004277# See system/extras/libfec/include/fec/io.h for these definitions.
4278FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4279FEC_MAGIC = 0xfecfecfe
4280
4281
4282def calc_fec_data_size(image_size, num_roots):
4283 """Calculates how much space FEC data will take.
4284
Jan Monschfe00c0a2019-12-11 11:19:40 +01004285 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004286 image_size: The size of the image.
4287 num_roots: Number of roots.
4288
4289 Returns:
4290 The number of bytes needed for FEC for an image of the given size
4291 and with the requested number of FEC roots.
4292
4293 Raises:
4294 ValueError: If output from the 'fec' tool is invalid.
4295
4296 """
4297 p = subprocess.Popen(
4298 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4299 stdout=subprocess.PIPE,
4300 stderr=subprocess.PIPE)
4301 (pout, perr) = p.communicate()
4302 retcode = p.wait()
4303 if retcode != 0:
4304 raise ValueError('Error invoking fec: {}'.format(perr))
4305 return int(pout)
4306
4307
4308def generate_fec_data(image_filename, num_roots):
4309 """Generate FEC codes for an image.
4310
Jan Monschfe00c0a2019-12-11 11:19:40 +01004311 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004312 image_filename: The filename of the image.
4313 num_roots: Number of roots.
4314
4315 Returns:
4316 The FEC data blob.
4317
4318 Raises:
4319 ValueError: If output from the 'fec' tool is invalid.
4320 """
4321 fec_tmpfile = tempfile.NamedTemporaryFile()
4322 subprocess.check_call(
4323 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4324 fec_tmpfile.name],
4325 stderr=open(os.devnull))
4326 fec_data = fec_tmpfile.read()
4327 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4328 footer_data = fec_data[-footer_size:]
4329 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4330 footer_data)
4331 if magic != FEC_MAGIC:
4332 raise ValueError('Unexpected magic in FEC footer')
4333 return fec_data[0:fec_size]
4334
4335
David Zeuthen21e95262016-07-27 17:58:40 -04004336def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004337 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004338 """Generates a Merkle-tree for a file.
4339
Jan Monschfe00c0a2019-12-11 11:19:40 +01004340 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04004341 image: The image, as a file.
4342 image_size: The size of the image.
4343 block_size: The block size, e.g. 4096.
4344 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4345 salt: The salt to use.
4346 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004347 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004348 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004349
4350 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004351 A tuple where the first element is the top-level hash and the
4352 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04004353 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004354 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004355 hash_src_offset = 0
4356 hash_src_size = image_size
4357 level_num = 0
4358 while hash_src_size > block_size:
4359 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004360 remaining = hash_src_size
4361 while remaining > 0:
4362 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004363 # Only read from the file for the first level - for subsequent
4364 # levels, access the array we're building.
4365 if level_num == 0:
4366 image.seek(hash_src_offset + hash_src_size - remaining)
4367 data = image.read(min(remaining, block_size))
4368 else:
4369 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4370 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004371 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004372
4373 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004374 if len(data) < block_size:
4375 hasher.update('\0' * (block_size - len(data)))
4376 level_output += hasher.digest()
4377 if digest_padding > 0:
4378 level_output += '\0' * digest_padding
4379
4380 padding_needed = (round_to_multiple(
4381 len(level_output), block_size) - len(level_output))
4382 level_output += '\0' * padding_needed
4383
David Zeuthena4fee8b2016-08-22 15:20:43 -04004384 # Copy level-output into resulting tree.
4385 offset = hash_level_offsets[level_num]
4386 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004387
David Zeuthena4fee8b2016-08-22 15:20:43 -04004388 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004389 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004390 level_num += 1
4391
4392 hasher = hashlib.new(name=hash_alg_name, string=salt)
4393 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004394 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04004395
4396
4397class AvbTool(object):
4398 """Object for avbtool command-line tool."""
4399
4400 def __init__(self):
4401 """Initializer method."""
4402 self.avb = Avb()
4403
4404 def _add_common_args(self, sub_parser):
4405 """Adds arguments used by several sub-commands.
4406
4407 Arguments:
4408 sub_parser: The parser to add arguments to.
4409 """
4410 sub_parser.add_argument('--algorithm',
4411 help='Algorithm to use (default: NONE)',
4412 metavar='ALGORITHM',
4413 default='NONE')
4414 sub_parser.add_argument('--key',
4415 help='Path to RSA private key file',
4416 metavar='KEY',
4417 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004418 sub_parser.add_argument('--signing_helper',
4419 help='Path to helper used for signing',
4420 metavar='APP',
4421 default=None,
4422 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004423 sub_parser.add_argument('--signing_helper_with_files',
4424 help='Path to helper used for signing using files',
4425 metavar='APP',
4426 default=None,
4427 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004428 sub_parser.add_argument('--public_key_metadata',
4429 help='Path to public key metadata file',
4430 metavar='KEY_METADATA',
4431 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004432 sub_parser.add_argument('--rollback_index',
4433 help='Rollback Index',
4434 type=parse_number,
4435 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004436 # This is used internally for unit tests. Do not include in --help output.
4437 sub_parser.add_argument('--internal_release_string',
4438 help=argparse.SUPPRESS)
4439 sub_parser.add_argument('--append_to_release_string',
4440 help='Text to append to release string',
4441 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004442 sub_parser.add_argument('--prop',
4443 help='Add property',
4444 metavar='KEY:VALUE',
4445 action='append')
4446 sub_parser.add_argument('--prop_from_file',
4447 help='Add property from file',
4448 metavar='KEY:PATH',
4449 action='append')
4450 sub_parser.add_argument('--kernel_cmdline',
4451 help='Add kernel cmdline',
4452 metavar='CMDLINE',
4453 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004454 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4455 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4456 # at some future point.
4457 sub_parser.add_argument('--setup_rootfs_from_kernel',
4458 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004459 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004460 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004461 type=argparse.FileType('rb'))
4462 sub_parser.add_argument('--include_descriptors_from_image',
4463 help='Include descriptors from image',
4464 metavar='IMAGE',
4465 action='append',
4466 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004467 sub_parser.add_argument('--print_required_libavb_version',
4468 help=('Don\'t store the footer - '
4469 'instead calculate the required libavb '
4470 'version for the given options.'),
4471 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004472 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4473 sub_parser.add_argument('--chain_partition',
4474 help='Allow signed integrity-data for partition',
4475 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4476 action='append')
4477 sub_parser.add_argument('--flags',
4478 help='VBMeta flags',
4479 type=parse_number,
4480 default=0)
4481 sub_parser.add_argument('--set_hashtree_disabled_flag',
4482 help='Set the HASHTREE_DISABLED flag',
4483 action='store_true')
4484
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004485 def _add_common_footer_args(self, sub_parser):
4486 """Adds arguments used by add_*_footer sub-commands.
4487
4488 Arguments:
4489 sub_parser: The parser to add arguments to.
4490 """
4491 sub_parser.add_argument('--use_persistent_digest',
4492 help='Use a persistent digest on device instead of '
4493 'storing the digest in the descriptor. This '
4494 'cannot be used with A/B so must be combined '
4495 'with --do_not_use_ab when an A/B suffix is '
4496 'expected at runtime.',
4497 action='store_true')
4498 sub_parser.add_argument('--do_not_use_ab',
4499 help='The partition does not use A/B even when an '
4500 'A/B suffix is present. This must not be used '
4501 'for vbmeta or chained partitions.',
4502 action='store_true')
4503
David Zeuthena5fd3a42017-02-27 16:38:54 -05004504 def _fixup_common_args(self, args):
4505 """Common fixups needed by subcommands.
4506
4507 Arguments:
4508 args: Arguments to modify.
4509
4510 Returns:
4511 The modified arguments.
4512 """
4513 if args.set_hashtree_disabled_flag:
4514 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4515 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004516
4517 def run(self, argv):
4518 """Command-line processor.
4519
4520 Arguments:
4521 argv: Pass sys.argv from main.
4522 """
4523 parser = argparse.ArgumentParser()
4524 subparsers = parser.add_subparsers(title='subcommands')
4525
4526 sub_parser = subparsers.add_parser('version',
4527 help='Prints version of avbtool.')
4528 sub_parser.set_defaults(func=self.version)
4529
4530 sub_parser = subparsers.add_parser('extract_public_key',
4531 help='Extract public key.')
4532 sub_parser.add_argument('--key',
4533 help='Path to RSA private key file',
4534 required=True)
4535 sub_parser.add_argument('--output',
4536 help='Output file name',
4537 type=argparse.FileType('wb'),
4538 required=True)
4539 sub_parser.set_defaults(func=self.extract_public_key)
4540
Jan Monsch77cd2022019-12-10 17:18:04 +01004541 sub_parser = subparsers.add_parser(
4542 'icp_test_suite',
4543 help='Test suite for ICP related functionality.')
Dan Austina7bc4962019-12-02 13:26:08 -08004544 sub_parser.set_defaults(func=self.icp_test_suite)
4545
David Zeuthen21e95262016-07-27 17:58:40 -04004546 sub_parser = subparsers.add_parser('make_vbmeta_image',
4547 help='Makes a vbmeta image.')
4548 sub_parser.add_argument('--output',
4549 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004550 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004551 sub_parser.add_argument('--padding_size',
4552 metavar='NUMBER',
4553 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004554 'its size is a multiple of NUMBER '
4555 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004556 type=parse_number,
4557 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004558 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004559 sub_parser.set_defaults(func=self.make_vbmeta_image)
4560
4561 sub_parser = subparsers.add_parser('add_hash_footer',
4562 help='Add hashes and footer to image.')
4563 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004564 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004565 type=argparse.FileType('rab+'))
4566 sub_parser.add_argument('--partition_size',
4567 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004568 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004569 sub_parser.add_argument('--partition_name',
4570 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004571 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004572 sub_parser.add_argument('--hash_algorithm',
4573 help='Hash algorithm to use (default: sha256)',
4574 default='sha256')
4575 sub_parser.add_argument('--salt',
4576 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004577 sub_parser.add_argument('--calc_max_image_size',
4578 help=('Don\'t store the footer - '
4579 'instead calculate the maximum image size '
4580 'leaving enough room for metadata with '
4581 'the given partition size.'),
4582 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004583 sub_parser.add_argument('--output_vbmeta_image',
4584 help='Also write vbmeta struct to file',
4585 type=argparse.FileType('wb'))
4586 sub_parser.add_argument('--do_not_append_vbmeta_image',
4587 help=('Do not append vbmeta struct or footer '
4588 'to the image'),
4589 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004590 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004591 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004592 sub_parser.set_defaults(func=self.add_hash_footer)
4593
David Zeuthenb1b994d2017-03-06 18:01:31 -05004594 sub_parser = subparsers.add_parser('append_vbmeta_image',
4595 help='Append vbmeta image to image.')
4596 sub_parser.add_argument('--image',
4597 help='Image to append vbmeta blob to',
4598 type=argparse.FileType('rab+'))
4599 sub_parser.add_argument('--partition_size',
4600 help='Partition size',
4601 type=parse_number,
4602 required=True)
4603 sub_parser.add_argument('--vbmeta_image',
4604 help='Image with vbmeta blob to append',
4605 type=argparse.FileType('rb'))
4606 sub_parser.set_defaults(func=self.append_vbmeta_image)
4607
Jan Monscheeb28b62019-12-05 16:17:09 +01004608 sub_parser = subparsers.add_parser(
4609 'add_hashtree_footer',
4610 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004611 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004612 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004613 type=argparse.FileType('rab+'))
4614 sub_parser.add_argument('--partition_size',
4615 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004616 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004617 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004618 sub_parser.add_argument('--partition_name',
4619 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004620 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004621 sub_parser.add_argument('--hash_algorithm',
4622 help='Hash algorithm to use (default: sha1)',
4623 default='sha1')
4624 sub_parser.add_argument('--salt',
4625 help='Salt in hex (default: /dev/urandom)')
4626 sub_parser.add_argument('--block_size',
4627 help='Block size (default: 4096)',
4628 type=parse_number,
4629 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004630 # TODO(zeuthen): The --generate_fec option was removed when we
4631 # moved to generating FEC by default. To avoid breaking existing
4632 # users needing to transition we simply just print a warning below
4633 # in add_hashtree_footer(). Remove this option and the warning at
4634 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004635 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004636 help=argparse.SUPPRESS,
4637 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004638 sub_parser.add_argument(
4639 '--do_not_generate_fec',
4640 help='Do not generate forward-error-correction codes',
4641 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004642 sub_parser.add_argument('--fec_num_roots',
4643 help='Number of roots for FEC (default: 2)',
4644 type=parse_number,
4645 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004646 sub_parser.add_argument('--calc_max_image_size',
4647 help=('Don\'t store the hashtree or footer - '
4648 'instead calculate the maximum image size '
4649 'leaving enough room for hashtree '
4650 'and metadata with the given partition '
4651 'size.'),
4652 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004653 sub_parser.add_argument('--output_vbmeta_image',
4654 help='Also write vbmeta struct to file',
4655 type=argparse.FileType('wb'))
4656 sub_parser.add_argument('--do_not_append_vbmeta_image',
4657 help=('Do not append vbmeta struct or footer '
4658 'to the image'),
4659 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004660 # This is different from --setup_rootfs_from_kernel insofar that
4661 # it doesn't take an IMAGE, the generated cmdline will be for the
4662 # hashtree we're adding.
4663 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4664 action='store_true',
4665 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004666 sub_parser.add_argument('--no_hashtree',
4667 action='store_true',
4668 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004669 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004670 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004671 sub_parser.set_defaults(func=self.add_hashtree_footer)
4672
4673 sub_parser = subparsers.add_parser('erase_footer',
4674 help='Erase footer from an image.')
4675 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004676 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004677 type=argparse.FileType('rwb+'),
4678 required=True)
4679 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004680 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004681 action='store_true')
4682 sub_parser.set_defaults(func=self.erase_footer)
4683
David Zeuthen1394f762019-04-30 10:20:11 -04004684 sub_parser = subparsers.add_parser('zero_hashtree',
4685 help='Zero out hashtree and FEC data.')
4686 sub_parser.add_argument('--image',
4687 help='Image with a footer',
4688 type=argparse.FileType('rwb+'),
4689 required=True)
4690 sub_parser.set_defaults(func=self.zero_hashtree)
4691
Jan Monscheeb28b62019-12-05 16:17:09 +01004692 sub_parser = subparsers.add_parser(
4693 'extract_vbmeta_image',
4694 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004695 sub_parser.add_argument('--image',
4696 help='Image with footer',
4697 type=argparse.FileType('rb'),
4698 required=True)
4699 sub_parser.add_argument('--output',
4700 help='Output file name',
4701 type=argparse.FileType('wb'))
4702 sub_parser.add_argument('--padding_size',
4703 metavar='NUMBER',
4704 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004705 'its size is a multiple of NUMBER '
4706 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004707 type=parse_number,
4708 default=0)
4709 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4710
David Zeuthen2bc232b2017-04-19 14:25:19 -04004711 sub_parser = subparsers.add_parser('resize_image',
4712 help='Resize image with a footer.')
4713 sub_parser.add_argument('--image',
4714 help='Image with a footer',
4715 type=argparse.FileType('rwb+'),
4716 required=True)
4717 sub_parser.add_argument('--partition_size',
4718 help='New partition size',
4719 type=parse_number)
4720 sub_parser.set_defaults(func=self.resize_image)
4721
David Zeuthen21e95262016-07-27 17:58:40 -04004722 sub_parser = subparsers.add_parser(
4723 'info_image',
4724 help='Show information about vbmeta or footer.')
4725 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004726 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004727 type=argparse.FileType('rb'),
4728 required=True)
4729 sub_parser.add_argument('--output',
4730 help='Write info to file',
4731 type=argparse.FileType('wt'),
4732 default=sys.stdout)
4733 sub_parser.set_defaults(func=self.info_image)
4734
David Zeuthenb623d8b2017-04-04 16:05:53 -04004735 sub_parser = subparsers.add_parser(
4736 'verify_image',
4737 help='Verify an image.')
4738 sub_parser.add_argument('--image',
4739 help='Image to verify',
4740 type=argparse.FileType('rb'),
4741 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004742 sub_parser.add_argument('--key',
4743 help='Check embedded public key matches KEY',
4744 metavar='KEY',
4745 required=False)
4746 sub_parser.add_argument('--expected_chain_partition',
4747 help='Expected chain partition',
4748 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4749 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004750 sub_parser.add_argument(
4751 '--follow_chain_partitions',
4752 help=('Follows chain partitions even when not '
4753 'specified with the --expected_chain_partition option'),
4754 action='store_true')
4755 sub_parser.add_argument(
4756 '--accept_zeroed_hashtree',
4757 help=('Accept images where the hashtree or FEC data is zeroed out'),
4758 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004759 sub_parser.set_defaults(func=self.verify_image)
4760
David Zeuthenb8643c02018-05-17 17:21:18 -04004761 sub_parser = subparsers.add_parser(
4762 'calculate_vbmeta_digest',
4763 help='Calculate vbmeta digest.')
4764 sub_parser.add_argument('--image',
4765 help='Image to calculate digest for',
4766 type=argparse.FileType('rb'),
4767 required=True)
4768 sub_parser.add_argument('--hash_algorithm',
4769 help='Hash algorithm to use (default: sha256)',
4770 default='sha256')
4771 sub_parser.add_argument('--output',
4772 help='Write hex digest to file (default: stdout)',
4773 type=argparse.FileType('wt'),
4774 default=sys.stdout)
4775 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4776
David Zeuthenf7d2e752018-09-20 13:30:41 -04004777 sub_parser = subparsers.add_parser(
4778 'calculate_kernel_cmdline',
4779 help='Calculate kernel cmdline.')
4780 sub_parser.add_argument('--image',
4781 help='Image to calculate kernel cmdline for',
4782 type=argparse.FileType('rb'),
4783 required=True)
4784 sub_parser.add_argument('--hashtree_disabled',
4785 help='Return the cmdline for hashtree disabled',
4786 action='store_true')
4787 sub_parser.add_argument('--output',
4788 help='Write cmdline to file (default: stdout)',
4789 type=argparse.FileType('wt'),
4790 default=sys.stdout)
4791 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4792
David Zeuthen8b6973b2016-09-20 12:39:49 -04004793 sub_parser = subparsers.add_parser('set_ab_metadata',
4794 help='Set A/B metadata.')
4795 sub_parser.add_argument('--misc_image',
4796 help=('The misc image to modify. If the image does '
4797 'not exist, it will be created.'),
4798 type=argparse.FileType('r+b'),
4799 required=True)
4800 sub_parser.add_argument('--slot_data',
4801 help=('Slot data of the form "priority", '
4802 '"tries_remaining", "sucessful_boot" for '
4803 'slot A followed by the same for slot B, '
4804 'separated by colons. The default value '
4805 'is 15:7:0:14:7:0.'),
4806 default='15:7:0:14:7:0')
4807 sub_parser.set_defaults(func=self.set_ab_metadata)
4808
Darren Krahn147b08d2016-12-20 16:38:29 -08004809 sub_parser = subparsers.add_parser(
4810 'make_atx_certificate',
4811 help='Create an Android Things eXtension (ATX) certificate.')
4812 sub_parser.add_argument('--output',
4813 help='Write certificate to file',
4814 type=argparse.FileType('wb'),
4815 default=sys.stdout)
4816 sub_parser.add_argument('--subject',
4817 help=('Path to subject file'),
4818 type=argparse.FileType('rb'),
4819 required=True)
4820 sub_parser.add_argument('--subject_key',
4821 help=('Path to subject RSA public key file'),
4822 type=argparse.FileType('rb'),
4823 required=True)
4824 sub_parser.add_argument('--subject_key_version',
4825 help=('Version of the subject key'),
4826 type=parse_number,
4827 required=False)
4828 sub_parser.add_argument('--subject_is_intermediate_authority',
4829 help=('Generate an intermediate authority '
4830 'certificate'),
4831 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004832 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004833 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004834 'string'),
4835 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004836 sub_parser.add_argument('--authority_key',
4837 help='Path to authority RSA private key file',
4838 required=False)
4839 sub_parser.add_argument('--signing_helper',
4840 help='Path to helper used for signing',
4841 metavar='APP',
4842 default=None,
4843 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004844 sub_parser.add_argument('--signing_helper_with_files',
4845 help='Path to helper used for signing using files',
4846 metavar='APP',
4847 default=None,
4848 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004849 sub_parser.set_defaults(func=self.make_atx_certificate)
4850
4851 sub_parser = subparsers.add_parser(
4852 'make_atx_permanent_attributes',
4853 help='Create Android Things eXtension (ATX) permanent attributes.')
4854 sub_parser.add_argument('--output',
4855 help='Write attributes to file',
4856 type=argparse.FileType('wb'),
4857 default=sys.stdout)
4858 sub_parser.add_argument('--root_authority_key',
4859 help='Path to authority RSA public key file',
4860 type=argparse.FileType('rb'),
4861 required=True)
4862 sub_parser.add_argument('--product_id',
4863 help=('Path to Product ID file'),
4864 type=argparse.FileType('rb'),
4865 required=True)
4866 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4867
4868 sub_parser = subparsers.add_parser(
4869 'make_atx_metadata',
4870 help='Create Android Things eXtension (ATX) metadata.')
4871 sub_parser.add_argument('--output',
4872 help='Write metadata to file',
4873 type=argparse.FileType('wb'),
4874 default=sys.stdout)
4875 sub_parser.add_argument('--intermediate_key_certificate',
4876 help='Path to intermediate key certificate file',
4877 type=argparse.FileType('rb'),
4878 required=True)
4879 sub_parser.add_argument('--product_key_certificate',
4880 help='Path to product key certificate file',
4881 type=argparse.FileType('rb'),
4882 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004883 sub_parser.set_defaults(func=self.make_atx_metadata)
4884
Darren Krahnfccd64e2018-01-16 17:39:35 -08004885 sub_parser = subparsers.add_parser(
4886 'make_atx_unlock_credential',
4887 help='Create an Android Things eXtension (ATX) unlock credential.')
4888 sub_parser.add_argument('--output',
4889 help='Write credential to file',
4890 type=argparse.FileType('wb'),
4891 default=sys.stdout)
4892 sub_parser.add_argument('--intermediate_key_certificate',
4893 help='Path to intermediate key certificate file',
4894 type=argparse.FileType('rb'),
4895 required=True)
4896 sub_parser.add_argument('--unlock_key_certificate',
4897 help='Path to unlock key certificate file',
4898 type=argparse.FileType('rb'),
4899 required=True)
4900 sub_parser.add_argument('--challenge',
4901 help='Path to the challenge to sign (optional). If '
4902 'this is not provided the challenge signature '
4903 'field is omitted and can be concatenated '
4904 'later.',
4905 required=False)
4906 sub_parser.add_argument('--unlock_key',
4907 help='Path to unlock key (optional). Must be '
4908 'provided if using --challenge.',
4909 required=False)
4910 sub_parser.add_argument('--signing_helper',
4911 help='Path to helper used for signing',
4912 metavar='APP',
4913 default=None,
4914 required=False)
4915 sub_parser.add_argument('--signing_helper_with_files',
4916 help='Path to helper used for signing using files',
4917 metavar='APP',
4918 default=None,
4919 required=False)
4920 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4921
David Zeuthen21e95262016-07-27 17:58:40 -04004922 args = parser.parse_args(argv[1:])
4923 try:
4924 args.func(args)
4925 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004926 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004927 sys.exit(1)
4928
4929 def version(self, _):
4930 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004931 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004932
4933 def extract_public_key(self, args):
4934 """Implements the 'extract_public_key' sub-command."""
4935 self.avb.extract_public_key(args.key, args.output)
4936
Dan Austina7bc4962019-12-02 13:26:08 -08004937 def icp_test_suite(self, _):
4938 """Implements the 'icp_test_suite' sub-command."""
4939 return self.avb.icp_test_suite()
4940
David Zeuthen21e95262016-07-27 17:58:40 -04004941 def make_vbmeta_image(self, args):
4942 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004943 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004944 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004945 args.algorithm, args.key,
4946 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004947 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004948 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004949 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004950 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004951 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004952 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004953 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004954 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004955 args.print_required_libavb_version,
4956 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004957
David Zeuthenb1b994d2017-03-06 18:01:31 -05004958 def append_vbmeta_image(self, args):
4959 """Implements the 'append_vbmeta_image' sub-command."""
4960 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4961 args.partition_size)
4962
David Zeuthen21e95262016-07-27 17:58:40 -04004963 def add_hash_footer(self, args):
4964 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004965 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004966 self.avb.add_hash_footer(args.image.name if args.image else None,
4967 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004968 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004969 args.salt, args.chain_partition, args.algorithm,
4970 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004971 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004972 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004973 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004974 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004975 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004976 args.calc_max_image_size,
4977 args.signing_helper,
4978 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004979 args.internal_release_string,
4980 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004981 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004982 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004983 args.print_required_libavb_version,
4984 args.use_persistent_digest,
4985 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004986
4987 def add_hashtree_footer(self, args):
4988 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004989 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004990 # TODO(zeuthen): Remove when removing support for the
4991 # '--generate_fec' option above.
4992 if args.generate_fec:
4993 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4994 'is now generated by default. Use the option '
4995 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004996 self.avb.add_hashtree_footer(
4997 args.image.name if args.image else None,
4998 args.partition_size,
4999 args.partition_name,
5000 not args.do_not_generate_fec, args.fec_num_roots,
5001 args.hash_algorithm, args.block_size,
5002 args.salt, args.chain_partition, args.algorithm,
5003 args.key, args.public_key_metadata,
5004 args.rollback_index, args.flags, args.prop,
5005 args.prop_from_file,
5006 args.kernel_cmdline,
5007 args.setup_rootfs_from_kernel,
5008 args.setup_as_rootfs_from_kernel,
5009 args.include_descriptors_from_image,
5010 args.calc_max_image_size,
5011 args.signing_helper,
5012 args.signing_helper_with_files,
5013 args.internal_release_string,
5014 args.append_to_release_string,
5015 args.output_vbmeta_image,
5016 args.do_not_append_vbmeta_image,
5017 args.print_required_libavb_version,
5018 args.use_persistent_digest,
5019 args.do_not_use_ab,
5020 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05005021
David Zeuthen21e95262016-07-27 17:58:40 -04005022 def erase_footer(self, args):
5023 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005024 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04005025
David Zeuthen1394f762019-04-30 10:20:11 -04005026 def zero_hashtree(self, args):
5027 """Implements the 'zero_hashtree' sub-command."""
5028 self.avb.zero_hashtree(args.image.name)
5029
David Zeuthen49936b42018-08-07 17:38:58 -04005030 def extract_vbmeta_image(self, args):
5031 """Implements the 'extract_vbmeta_image' sub-command."""
5032 self.avb.extract_vbmeta_image(args.output, args.image.name,
5033 args.padding_size)
5034
David Zeuthen2bc232b2017-04-19 14:25:19 -04005035 def resize_image(self, args):
5036 """Implements the 'resize_image' sub-command."""
5037 self.avb.resize_image(args.image.name, args.partition_size)
5038
David Zeuthen8b6973b2016-09-20 12:39:49 -04005039 def set_ab_metadata(self, args):
5040 """Implements the 'set_ab_metadata' sub-command."""
5041 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
5042
David Zeuthen21e95262016-07-27 17:58:40 -04005043 def info_image(self, args):
5044 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005045 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04005046
David Zeuthenb623d8b2017-04-04 16:05:53 -04005047 def verify_image(self, args):
5048 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04005049 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05005050 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04005051 args.follow_chain_partitions,
5052 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04005053
David Zeuthenb8643c02018-05-17 17:21:18 -04005054 def calculate_vbmeta_digest(self, args):
5055 """Implements the 'calculate_vbmeta_digest' sub-command."""
5056 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
5057 args.output)
5058
David Zeuthenf7d2e752018-09-20 13:30:41 -04005059 def calculate_kernel_cmdline(self, args):
5060 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01005061 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
5062 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04005063
Darren Krahn147b08d2016-12-20 16:38:29 -08005064 def make_atx_certificate(self, args):
5065 """Implements the 'make_atx_certificate' sub-command."""
5066 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04005067 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005068 args.subject_key_version,
5069 args.subject.read(),
5070 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08005071 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04005072 args.signing_helper,
5073 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08005074
5075 def make_atx_permanent_attributes(self, args):
5076 """Implements the 'make_atx_permanent_attributes' sub-command."""
5077 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04005078 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005079 args.product_id.read())
5080
5081 def make_atx_metadata(self, args):
5082 """Implements the 'make_atx_metadata' sub-command."""
5083 self.avb.make_atx_metadata(args.output,
5084 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08005085 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08005086
Darren Krahnfccd64e2018-01-16 17:39:35 -08005087 def make_atx_unlock_credential(self, args):
5088 """Implements the 'make_atx_unlock_credential' sub-command."""
5089 self.avb.make_atx_unlock_credential(
5090 args.output,
5091 args.intermediate_key_certificate.read(),
5092 args.unlock_key_certificate.read(),
5093 args.challenge,
5094 args.unlock_key,
5095 args.signing_helper,
5096 args.signing_helper_with_files)
5097
David Zeuthen21e95262016-07-27 17:58:40 -04005098
5099if __name__ == '__main__':
5100 tool = AvbTool()
5101 tool.run(sys.argv)