blob: 75c96539c8abdff921b7b08cb588ecce65365e1a [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
202 Args:
203 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
219 Args:
220 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)
535 if alg.hash_name == '':
536 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 """
1173 # Nothing to do.
1174 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001175
Jan Monscheeb28b62019-12-05 16:17:09 +01001176
David Zeuthen21e95262016-07-27 17:58:40 -04001177class AvbPropertyDescriptor(AvbDescriptor):
1178 """A class for property descriptors.
1179
1180 See the |AvbPropertyDescriptor| C struct for more information.
1181
1182 Attributes:
1183 key: The key.
1184 value: The key.
1185 """
1186
1187 TAG = 0
1188 SIZE = 32
1189 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1190 'Q' # key size (bytes)
1191 'Q') # value size (bytes)
1192
1193 def __init__(self, data=None):
1194 """Initializes a new property descriptor.
1195
1196 Arguments:
1197 data: If not None, must be a bytearray of size |SIZE|.
1198
1199 Raises:
1200 LookupError: If the given descriptor is malformed.
1201 """
1202 AvbDescriptor.__init__(self, None)
1203 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1204
1205 if data:
1206 (tag, num_bytes_following, key_size,
1207 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1208 expected_size = round_to_multiple(
1209 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1210 if tag != self.TAG or num_bytes_following != expected_size:
1211 raise LookupError('Given data does not look like a property '
1212 'descriptor.')
1213 self.key = data[self.SIZE:(self.SIZE + key_size)]
1214 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1215 value_size)]
1216 else:
1217 self.key = ''
1218 self.value = ''
1219
1220 def print_desc(self, o):
1221 """Print the descriptor.
1222
1223 Arguments:
1224 o: The object to write the output to.
1225 """
1226 if len(self.value) < 256:
1227 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1228 else:
1229 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1230
1231 def encode(self):
1232 """Serializes the descriptor.
1233
1234 Returns:
1235 A bytearray() with the descriptor data.
1236 """
1237 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1238 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1239 padding_size = nbf_with_padding - num_bytes_following
1240 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1241 len(self.key), len(self.value))
1242 padding = struct.pack(str(padding_size) + 'x')
1243 ret = desc + self.key + '\0' + self.value + '\0' + padding
1244 return bytearray(ret)
1245
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001246 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001247 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001248 """Verifies contents of the descriptor - used in verify_image sub-command.
1249
1250 Arguments:
1251 image_dir: The directory of the file being verified.
1252 image_ext: The extension of the file being verified (e.g. '.img').
1253 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001254 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001255 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001256 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1257 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001258
1259 Returns:
1260 True if the descriptor verifies, False otherwise.
1261 """
1262 # Nothing to do.
1263 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001264
Jan Monscheeb28b62019-12-05 16:17:09 +01001265
David Zeuthen21e95262016-07-27 17:58:40 -04001266class AvbHashtreeDescriptor(AvbDescriptor):
1267 """A class for hashtree descriptors.
1268
1269 See the |AvbHashtreeDescriptor| C struct for more information.
1270
1271 Attributes:
1272 dm_verity_version: dm-verity version used.
1273 image_size: Size of the image, after rounding up to |block_size|.
1274 tree_offset: Offset of the hash tree in the file.
1275 tree_size: Size of the tree.
1276 data_block_size: Data block size
1277 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001278 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1279 fec_offset: Offset of FEC data (0 if FEC is not used).
1280 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001281 hash_algorithm: Hash algorithm used.
1282 partition_name: Partition name.
1283 salt: Salt used.
1284 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001285 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001286 """
1287
1288 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001289 RESERVED = 60
1290 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001291 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1292 'L' # dm-verity version used
1293 'Q' # image size (bytes)
1294 'Q' # tree offset (bytes)
1295 'Q' # tree size (bytes)
1296 'L' # data block size (bytes)
1297 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001298 'L' # FEC number of roots
1299 'Q' # FEC offset (bytes)
1300 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001301 '32s' # hash algorithm used
1302 'L' # partition name (bytes)
1303 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001304 'L' # root digest length (bytes)
1305 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001306 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001307
1308 def __init__(self, data=None):
1309 """Initializes a new hashtree descriptor.
1310
1311 Arguments:
1312 data: If not None, must be a bytearray of size |SIZE|.
1313
1314 Raises:
1315 LookupError: If the given descriptor is malformed.
1316 """
1317 AvbDescriptor.__init__(self, None)
1318 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1319
1320 if data:
1321 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1322 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001323 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1324 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001325 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1326 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001327 expected_size = round_to_multiple(
1328 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1329 if tag != self.TAG or num_bytes_following != expected_size:
1330 raise LookupError('Given data does not look like a hashtree '
1331 'descriptor.')
1332 # Nuke NUL-bytes at the end.
1333 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1334 o = 0
1335 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1336 partition_name_len)])
1337 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1338 self.partition_name.decode('utf-8')
1339 o += partition_name_len
1340 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1341 o += salt_len
1342 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1343 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001344 if root_digest_len != 0:
1345 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001346
1347 else:
1348 self.dm_verity_version = 0
1349 self.image_size = 0
1350 self.tree_offset = 0
1351 self.tree_size = 0
1352 self.data_block_size = 0
1353 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001354 self.fec_num_roots = 0
1355 self.fec_offset = 0
1356 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001357 self.hash_algorithm = ''
1358 self.partition_name = ''
1359 self.salt = bytearray()
1360 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001361 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001362
1363 def print_desc(self, o):
1364 """Print the descriptor.
1365
1366 Arguments:
1367 o: The object to write the output to.
1368 """
1369 o.write(' Hashtree descriptor:\n')
1370 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1371 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1372 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1373 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1374 o.write(' Data Block Size: {} bytes\n'.format(
1375 self.data_block_size))
1376 o.write(' Hash Block Size: {} bytes\n'.format(
1377 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001378 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1379 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1380 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001381 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1382 o.write(' Partition Name: {}\n'.format(self.partition_name))
1383 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1384 'hex')))
1385 o.write(' Root Digest: {}\n'.format(str(
1386 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001387 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001388
1389 def encode(self):
1390 """Serializes the descriptor.
1391
1392 Returns:
1393 A bytearray() with the descriptor data.
1394 """
1395 encoded_name = self.partition_name.encode('utf-8')
1396 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1397 len(self.root_digest) - 16)
1398 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1399 padding_size = nbf_with_padding - num_bytes_following
1400 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1401 self.dm_verity_version, self.image_size,
1402 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001403 self.hash_block_size, self.fec_num_roots,
1404 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001405 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001406 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001407 padding = struct.pack(str(padding_size) + 'x')
1408 ret = desc + encoded_name + self.salt + self.root_digest + padding
1409 return bytearray(ret)
1410
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001411 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001412 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001413 """Verifies contents of the descriptor - used in verify_image sub-command.
1414
1415 Arguments:
1416 image_dir: The directory of the file being verified.
1417 image_ext: The extension of the file being verified (e.g. '.img').
1418 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001419 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001420 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001421 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1422 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001423
1424 Returns:
1425 True if the descriptor verifies, False otherwise.
1426 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001427 if self.partition_name == '':
Tao Bao558bd752019-09-18 18:18:34 -07001428 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001429 image = image_containing_descriptor
1430 else:
1431 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1432 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001433 # Generate the hashtree and checks that it matches what's in the file.
1434 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1435 digest_padding = round_to_pow2(digest_size) - digest_size
1436 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001437 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001438 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1439 self.data_block_size,
1440 self.hash_algorithm, self.salt,
1441 digest_padding,
1442 hash_level_offsets,
1443 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001444 # The root digest must match unless it is not embedded in the descriptor.
1445 if len(self.root_digest) != 0 and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001446 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1447 format(image_filename))
1448 return False
1449 # ... also check that the on-disk hashtree matches
1450 image.seek(self.tree_offset)
1451 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001452 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001453 if is_zeroed and accept_zeroed_hashtree:
Jan Monscheeb28b62019-12-05 16:17:09 +01001454 print ('{}: skipping verification since hashtree is zeroed and '
1455 '--accept_zeroed_hashtree was given'
David Zeuthen1394f762019-04-30 10:20:11 -04001456 .format(self.partition_name))
1457 else:
1458 if hash_tree != hash_tree_ondisk:
1459 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001460 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001461 return False
1462 print ('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1463 .format(self.partition_name, self.hash_algorithm, image.filename,
1464 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001465 # TODO: we could also verify that the FEC stored in the image is
1466 # correct but this a) currently requires the 'fec' binary; and b)
1467 # takes a long time; and c) is not strictly needed for
1468 # verification purposes as we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001469 return True
1470
David Zeuthen21e95262016-07-27 17:58:40 -04001471
1472class AvbHashDescriptor(AvbDescriptor):
1473 """A class for hash descriptors.
1474
1475 See the |AvbHashDescriptor| C struct for more information.
1476
1477 Attributes:
1478 image_size: Image size, in bytes.
1479 hash_algorithm: Hash algorithm used.
1480 partition_name: Partition name.
1481 salt: Salt used.
1482 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001483 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001484 """
1485
1486 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001487 RESERVED = 60
1488 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001489 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1490 'Q' # image size (bytes)
1491 '32s' # hash algorithm used
1492 'L' # partition name (bytes)
1493 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001494 'L' # digest length (bytes)
1495 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001496 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001497
1498 def __init__(self, data=None):
1499 """Initializes a new hash descriptor.
1500
1501 Arguments:
1502 data: If not None, must be a bytearray of size |SIZE|.
1503
1504 Raises:
1505 LookupError: If the given descriptor is malformed.
1506 """
1507 AvbDescriptor.__init__(self, None)
1508 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1509
1510 if data:
1511 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1512 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001513 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1514 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001515 expected_size = round_to_multiple(
1516 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1517 if tag != self.TAG or num_bytes_following != expected_size:
1518 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1519 # Nuke NUL-bytes at the end.
1520 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1521 o = 0
1522 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1523 partition_name_len)])
1524 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1525 self.partition_name.decode('utf-8')
1526 o += partition_name_len
1527 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1528 o += salt_len
1529 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1530 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001531 if digest_len != 0:
1532 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001533
1534 else:
1535 self.image_size = 0
1536 self.hash_algorithm = ''
1537 self.partition_name = ''
1538 self.salt = bytearray()
1539 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001540 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001541
1542 def print_desc(self, o):
1543 """Print the descriptor.
1544
1545 Arguments:
1546 o: The object to write the output to.
1547 """
1548 o.write(' Hash descriptor:\n')
1549 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1550 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1551 o.write(' Partition Name: {}\n'.format(self.partition_name))
1552 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1553 'hex')))
1554 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1555 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001556 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001557
1558 def encode(self):
1559 """Serializes the descriptor.
1560
1561 Returns:
1562 A bytearray() with the descriptor data.
1563 """
1564 encoded_name = self.partition_name.encode('utf-8')
1565 num_bytes_following = (
1566 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1567 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1568 padding_size = nbf_with_padding - num_bytes_following
1569 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1570 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001571 len(self.salt), len(self.digest), self.flags,
1572 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001573 padding = struct.pack(str(padding_size) + 'x')
1574 ret = desc + encoded_name + self.salt + self.digest + padding
1575 return bytearray(ret)
1576
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001577 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001578 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001579 """Verifies contents of the descriptor - used in verify_image sub-command.
1580
1581 Arguments:
1582 image_dir: The directory of the file being verified.
1583 image_ext: The extension of the file being verified (e.g. '.img').
1584 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001585 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001586 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001587 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1588 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001589
1590 Returns:
1591 True if the descriptor verifies, False otherwise.
1592 """
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001593 if self.partition_name == '':
Tao Bao558bd752019-09-18 18:18:34 -07001594 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001595 image = image_containing_descriptor
1596 else:
1597 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1598 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001599 data = image.read(self.image_size)
1600 ha = hashlib.new(self.hash_algorithm)
1601 ha.update(self.salt)
1602 ha.update(data)
1603 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001604 # The digest must match unless there is no digest in the descriptor.
1605 if len(self.digest) != 0 and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001606 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1607 format(self.hash_algorithm, image_filename))
1608 return False
1609 print ('{}: Successfully verified {} hash of {} for image of {} bytes'
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001610 .format(self.partition_name, self.hash_algorithm, image.filename,
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001611 self.image_size))
1612 return True
1613
David Zeuthen21e95262016-07-27 17:58:40 -04001614
1615class AvbKernelCmdlineDescriptor(AvbDescriptor):
1616 """A class for kernel command-line descriptors.
1617
1618 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1619
1620 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001621 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001622 kernel_cmdline: The kernel command-line.
1623 """
1624
1625 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001626 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001627 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001628 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001629 'L') # cmdline length (bytes)
1630
David Zeuthenfd41eb92016-11-17 12:24:47 -05001631 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1632 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1633
David Zeuthen21e95262016-07-27 17:58:40 -04001634 def __init__(self, data=None):
1635 """Initializes a new kernel cmdline descriptor.
1636
1637 Arguments:
1638 data: If not None, must be a bytearray of size |SIZE|.
1639
1640 Raises:
1641 LookupError: If the given descriptor is malformed.
1642 """
1643 AvbDescriptor.__init__(self, None)
1644 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1645
1646 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001647 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001648 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1649 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1650 8)
1651 if tag != self.TAG or num_bytes_following != expected_size:
1652 raise LookupError('Given data does not look like a kernel cmdline '
1653 'descriptor.')
1654 # Nuke NUL-bytes at the end.
1655 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1656 kernel_cmdline_length)])
1657 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1658 self.kernel_cmdline.decode('utf-8')
1659 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001660 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001661 self.kernel_cmdline = ''
1662
1663 def print_desc(self, o):
1664 """Print the descriptor.
1665
1666 Arguments:
1667 o: The object to write the output to.
1668 """
1669 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001670 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001671 o.write(' Kernel Cmdline: {}\n'.format(repr(
1672 self.kernel_cmdline)))
1673
1674 def encode(self):
1675 """Serializes the descriptor.
1676
1677 Returns:
1678 A bytearray() with the descriptor data.
1679 """
1680 encoded_str = self.kernel_cmdline.encode('utf-8')
1681 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1682 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1683 padding_size = nbf_with_padding - num_bytes_following
1684 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001685 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001686 padding = struct.pack(str(padding_size) + 'x')
1687 ret = desc + encoded_str + padding
1688 return bytearray(ret)
1689
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001690 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001691 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001692 """Verifies contents of the descriptor - used in verify_image sub-command.
1693
1694 Arguments:
1695 image_dir: The directory of the file being verified.
1696 image_ext: The extension of the file being verified (e.g. '.img').
1697 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001698 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001699 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001700 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1701 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001702
1703 Returns:
1704 True if the descriptor verifies, False otherwise.
1705 """
1706 # Nothing to verify.
1707 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001708
Jan Monscheeb28b62019-12-05 16:17:09 +01001709
David Zeuthen21e95262016-07-27 17:58:40 -04001710class AvbChainPartitionDescriptor(AvbDescriptor):
1711 """A class for chained partition descriptors.
1712
1713 See the |AvbChainPartitionDescriptor| C struct for more information.
1714
1715 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001716 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001717 partition_name: Partition name.
1718 public_key: Bytes for the public key.
1719 """
1720
1721 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001722 RESERVED = 64
1723 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001724 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001725 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001726 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001727 'L' + # public_key_size (bytes)
1728 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001729
1730 def __init__(self, data=None):
1731 """Initializes a new chain partition descriptor.
1732
1733 Arguments:
1734 data: If not None, must be a bytearray of size |SIZE|.
1735
1736 Raises:
1737 LookupError: If the given descriptor is malformed.
1738 """
1739 AvbDescriptor.__init__(self, None)
1740 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1741
1742 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001743 (tag, num_bytes_following, self.rollback_index_location,
1744 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001745 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001746 expected_size = round_to_multiple(
1747 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1748 if tag != self.TAG or num_bytes_following != expected_size:
1749 raise LookupError('Given data does not look like a chain partition '
1750 'descriptor.')
1751 o = 0
1752 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1753 partition_name_len)])
1754 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1755 self.partition_name.decode('utf-8')
1756 o += partition_name_len
1757 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1758
1759 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001760 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001761 self.partition_name = ''
1762 self.public_key = bytearray()
1763
1764 def print_desc(self, o):
1765 """Print the descriptor.
1766
1767 Arguments:
1768 o: The object to write the output to.
1769 """
1770 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001771 o.write(' Partition Name: {}\n'.format(self.partition_name))
1772 o.write(' Rollback Index Location: {}\n'.format(
1773 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001774 # Just show the SHA1 of the key, for size reasons.
1775 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001776 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001777
1778 def encode(self):
1779 """Serializes the descriptor.
1780
1781 Returns:
1782 A bytearray() with the descriptor data.
1783 """
1784 encoded_name = self.partition_name.encode('utf-8')
1785 num_bytes_following = (
1786 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1787 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1788 padding_size = nbf_with_padding - num_bytes_following
1789 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001790 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001791 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001792 padding = struct.pack(str(padding_size) + 'x')
1793 ret = desc + encoded_name + self.public_key + padding
1794 return bytearray(ret)
1795
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001796 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001797 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001798 """Verifies contents of the descriptor - used in verify_image sub-command.
1799
1800 Arguments:
1801 image_dir: The directory of the file being verified.
1802 image_ext: The extension of the file being verified (e.g. '.img').
1803 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001804 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001805 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001806 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1807 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001808
1809 Returns:
1810 True if the descriptor verifies, False otherwise.
1811 """
1812 value = expected_chain_partitions_map.get(self.partition_name)
1813 if not value:
1814 sys.stderr.write('No expected chain partition for partition {}. Use '
1815 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001816 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001817 format(self.partition_name))
1818 return False
1819 rollback_index_location, pk_blob = value
1820
1821 if self.rollback_index_location != rollback_index_location:
1822 sys.stderr.write('Expected rollback_index_location {} does not '
1823 'match {} in descriptor for partition {}\n'.
1824 format(rollback_index_location,
1825 self.rollback_index_location,
1826 self.partition_name))
1827 return False
1828
1829 if self.public_key != pk_blob:
1830 sys.stderr.write('Expected public key blob does not match public '
1831 'key blob in descriptor for partition {}\n'.
1832 format(self.partition_name))
1833 return False
1834
1835 print ('{}: Successfully verified chain partition descriptor matches '
1836 'expected data'.format(self.partition_name))
1837
1838 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001839
1840DESCRIPTOR_CLASSES = [
1841 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1842 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1843]
1844
1845
1846def parse_descriptors(data):
1847 """Parses a blob of data into descriptors.
1848
1849 Arguments:
1850 data: A bytearray() with encoded descriptors.
1851
1852 Returns:
1853 A list of instances of objects derived from AvbDescriptor. For
1854 unknown descriptors, the class AvbDescriptor is used.
1855 """
1856 o = 0
1857 ret = []
1858 while o < len(data):
1859 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1860 if tag < len(DESCRIPTOR_CLASSES):
1861 c = DESCRIPTOR_CLASSES[tag]
1862 else:
1863 c = AvbDescriptor
1864 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1865 o += 16 + nb_following
1866 return ret
1867
1868
1869class AvbFooter(object):
1870 """A class for parsing and writing footers.
1871
1872 Footers are stored at the end of partitions and point to where the
1873 AvbVBMeta blob is located. They also contain the original size of
1874 the image before AVB information was added.
1875
1876 Attributes:
1877 magic: Magic for identifying the footer, see |MAGIC|.
1878 version_major: The major version of avbtool that wrote the footer.
1879 version_minor: The minor version of avbtool that wrote the footer.
1880 original_image_size: Original image size.
1881 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1882 vbmeta_size: Size of the AvbVBMeta blob.
1883 """
1884
1885 MAGIC = 'AVBf'
1886 SIZE = 64
1887 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001888 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1889 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001890 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1891 'Q' # Original image size.
1892 'Q' # Offset of VBMeta blob.
1893 'Q' + # Size of VBMeta blob.
1894 str(RESERVED) + 'x') # padding for reserved bytes
1895
1896 def __init__(self, data=None):
1897 """Initializes a new footer object.
1898
1899 Arguments:
1900 data: If not None, must be a bytearray of size 4096.
1901
1902 Raises:
1903 LookupError: If the given footer is malformed.
1904 struct.error: If the given data has no footer.
1905 """
1906 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1907
1908 if data:
1909 (self.magic, self.version_major, self.version_minor,
1910 self.original_image_size, self.vbmeta_offset,
1911 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1912 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001913 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001914 else:
1915 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001916 self.version_major = self.FOOTER_VERSION_MAJOR
1917 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001918 self.original_image_size = 0
1919 self.vbmeta_offset = 0
1920 self.vbmeta_size = 0
1921
David Zeuthena4fee8b2016-08-22 15:20:43 -04001922 def encode(self):
1923 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001924
David Zeuthena4fee8b2016-08-22 15:20:43 -04001925 Returns:
1926 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001927 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001928 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1929 self.version_minor, self.original_image_size,
1930 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001931
1932
Dan Austina7bc4962019-12-02 13:26:08 -08001933# Android Firmware Transparency Log Data Structures
1934
Jan Monsch77cd2022019-12-10 17:18:04 +01001935
Dan Austina7bc4962019-12-02 13:26:08 -08001936class AvbIcpHeader(object):
1937 """A class for parsing and writing AVB vbmeta images with transparency
1938 log inclusion proof information.
1939
1940 Attributes:
1941 magic: Magic for identifying the ICP header.
1942 required_icp_version_major: The major version of AVB that wrote the entry.
1943 required_icp_version_minor: The minor version of AVB that wrote the entry.
1944 algorithm: Hash algorithm used. ID is defined in ALGORITHMS.
1945 icp_count: Number of inclusion proofs represented in this structure.
1946 """
1947
1948 SIZE = 18 # The size of the structure, in bytes
1949 MAGIC = 'AFTL'
Jan Monsch77cd2022019-12-10 17:18:04 +01001950 FORMAT_STRING = ('!4s2L' # magic, major & minor version
1951 'L' # algorithm type for transparency log
1952 'H') # number of inclusion proof entries
Dan Austina7bc4962019-12-02 13:26:08 -08001953
1954 def __init__(self, data=None):
1955 """Initializes a new transparency header object.
1956
1957 Arguments:
1958 data: If not None, must be a bytearray of size == 18.
1959 """
1960 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1961
1962 if data:
Jan Monsch77cd2022019-12-10 17:18:04 +01001963 (self.magic, self.required_icp_version_major,
1964 self.required_icp_version_minor, self.algorithm,
1965 self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08001966 else:
1967 self.magic = self.MAGIC
1968 self.required_icp_version_major = AVB_VERSION_MAJOR
1969 self.required_icp_version_minor = AVB_VERSION_MINOR
1970 self.algorithm = 0
1971 self.icp_count = 0
1972
1973 def save(self, output):
1974 """Serializes the transparency header (18) to disk.
1975
1976 Arguments:
1977 output: The object to write the header to.
1978 """
1979 output.write(self.encode())
1980
1981 def encode(self):
1982 """Serializes the header (18) to a bytearray().
1983
1984 Returns:
1985 A bytearray() with the encoded header.
1986 """
1987 return struct.pack(self.FORMAT_STRING, self.magic,
1988 self.required_icp_version_major,
1989 self.required_icp_version_minor,
1990 self.algorithm, self.icp_count)
Jan Monsch77cd2022019-12-10 17:18:04 +01001991
Dan Austina7bc4962019-12-02 13:26:08 -08001992 def is_valid(self):
1993 """Ensures that values in an AvbIcpHeader structure are sane.
1994
1995 Returns:
1996 True if the values in the AvbIcpHeader are sane, False otherwise.
1997 """
1998 if self.magic != AvbIcpHeader.MAGIC:
Jan Monsch77cd2022019-12-10 17:18:04 +01001999 sys.stderr.write(
2000 'ICP Header: magic value mismatch: {}\n'.format(self.magic))
Dan Austina7bc4962019-12-02 13:26:08 -08002001 return False
2002 if self.required_icp_version_major > AVB_VERSION_MAJOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002003 sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
2004 self.required_icp_version_major))
Dan Austina7bc4962019-12-02 13:26:08 -08002005 return False
2006 if self.required_icp_version_minor > AVB_VERSION_MINOR:
Jan Monsch77cd2022019-12-10 17:18:04 +01002007 sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
2008 self.required_icp_version_minor))
Dan Austina7bc4962019-12-02 13:26:08 -08002009 return False
2010 if self.algorithm < 0 or self.algorithm >= len(ALGORITHMS):
Jan Monsch77cd2022019-12-10 17:18:04 +01002011 sys.stderr.write(
2012 'ICP header: algorithm identifier out of range: {}\n'.format(
2013 self.algorithm))
Dan Austina7bc4962019-12-02 13:26:08 -08002014 return False
2015 if self.icp_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002016 sys.stderr.write(
2017 'ICP header: ICP entry count out of range: {}\n'.format(
2018 self.icp_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002019 return False
2020 return True
2021
Dan Austina7bc4962019-12-02 13:26:08 -08002022
2023class AvbIcpEntry(object):
2024 """A class for parsing and writing AFTL inclusion proof information.
2025
Jan Monsch77cd2022019-12-10 17:18:04 +01002026 The data that represents each of the components of the ICP entry are stored
2027 immediately following the ICP entry header. The format is log_url,
2028 SignedLogRoot, and inclusion proof hashes.
Dan Austina7bc4962019-12-02 13:26:08 -08002029
2030 Attributes:
2031 log_url_size: Length of the string representing the transparency log URL.
2032 leaf_index: Leaf index in the transparency log representing this entry.
Jan Monsch77cd2022019-12-10 17:18:04 +01002033 signed_root_blob_size: Size of the SignedLogRoot for the transparency log;
2034 treat as an opaque blob for now.
Dan Austina7bc4962019-12-02 13:26:08 -08002035 proof_hash_count: Number of hashes comprising the inclusion proof.
2036 proof_size: The total size of the inclusion proof, in bytes.
2037 next_entry: 1 if there is a next entry, 0 otherwise.
2038 """
2039 SIZE = 22 # The size of the structure, in bytes
2040 FORMAT_STRING = ('!L' # transparency log server url size
2041 'Q' # leaf index
2042 'L' # signed tree root blob size
2043 'B' # number of hashes in the inclusion proof
2044 'L' # size of the inclusion proof in bytes
2045 'B') # next entry marker
2046
2047 def __init__(self, data=None):
2048 """Initializes a new ICP entry object.
2049
2050 Arguments:
2051 data: If not None, must be a bytearray of size == 22.
2052 """
2053 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2054
2055 if data:
2056 (self.log_url_size, self.leaf_index, self.signed_root_blob_size,
Jan Monsch77cd2022019-12-10 17:18:04 +01002057 self.proof_hash_count, self.proof_size,
2058 self.next_entry) = struct.unpack(self.FORMAT_STRING, data)
Dan Austina7bc4962019-12-02 13:26:08 -08002059 else:
2060 self.log_url_size = 0
2061 self.leaf_index = 0
2062 self.signed_root_blob_size = 0
2063 self.proof_hash_count = 0
2064 self.proof_size = 0
2065 self.next_entry = 0
2066
2067 def save(self, output):
2068 """Serializes the transparency header (22) to disk.
2069
2070 Arguments:
2071 output: The object to write the header to.
2072 """
2073 output.write(self.encode())
2074
2075 def encode(self):
2076 """Serializes the header (22) to a bytearray().
2077
2078 Returns:
2079 A bytearray() with the encoded header.
2080 """
2081 return struct.pack(self.FORMAT_STRING, self.log_url_size,
2082 self.leaf_index, self.signed_root_blob_size,
2083 self.proof_hash_count, self.proof_size,
2084 self.next_entry)
2085
2086 def is_valid(self):
2087 """Ensures that values in an AvbIcpEntry structure are sane.
2088
2089 Returns:
2090 True if the values in the AvbIcpEntry are sane, False otherwise.
2091 """
2092 if self.log_url_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002093 sys.stderr.write(
2094 'ICP entry: invalid transparency log URL size: {}\n'.format(
2095 self.log_url_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002096 return False
2097 if self.leaf_index < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002098 sys.stderr.write('ICP entry: leaf index out of range: {}\n'.format(
2099 self.leaf_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002100 return False
2101 if self.signed_root_blob_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002102 sys.stderr.write('ICP entry: invalid signed root blob size: {}\n'.format(
2103 self.signed_root_blob_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002104 return False
2105 if self.proof_hash_count < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002106 sys.stderr.write('ICP entry: invalid proof count: {}\n'.format(
2107 self.proof_hash_count))
Dan Austina7bc4962019-12-02 13:26:08 -08002108 return False
2109 if self.proof_size < 0:
Jan Monsch77cd2022019-12-10 17:18:04 +01002110 sys.stderr.write(
2111 'ICP entry: invalid transparency log proof size: {}\n'.format(
2112 self.proof_size))
Dan Austina7bc4962019-12-02 13:26:08 -08002113 return False
2114 if self.next_entry != 0 and self.next_entry != 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002115 sys.stderr.write('ICP entry: invalid next entry value: {}\n'.format(
2116 self.next_entry))
Dan Austina7bc4962019-12-02 13:26:08 -08002117 return False
2118 return True
2119
Jan Monsch77cd2022019-12-10 17:18:04 +01002120
Dan Austina7bc4962019-12-02 13:26:08 -08002121def validate_icp_blob(icp_blob):
2122 """Ensures that values in an ICP section are structurally sane.
2123
2124 Arguments:
Jan Monsch77cd2022019-12-10 17:18:04 +01002125 icp_blob: A bytearray() that contains an ICP, with headers, entries and
2126 data.
Dan Austina7bc4962019-12-02 13:26:08 -08002127
2128 Returns:
2129 True if the values in the ICP section are sane, False otherwise.
2130 """
2131 icp_blob_size = len(icp_blob)
2132 icp_header_bytes = icp_blob[0:AvbIcpHeader.SIZE]
2133 icp_header = AvbIcpHeader(icp_header_bytes)
2134 if not icp_header.is_valid():
2135 sys.stderr.write('Validation of ICP header failed.\n')
2136 return False
2137
2138 icp_count = icp_header.icp_count
2139 algorithm_id = icp_header.algorithm
2140 proof_hash_size = lookup_hash_size_by_type(algorithm_id)
2141
2142 # jump past the header for entry processing
2143 icp_index = AvbIcpHeader.SIZE
2144 # Validate each entry
2145 for i in range(icp_count):
2146 # get the entry header from the ICP blob
2147 cur_icp_entry_blob = icp_blob[icp_index:(icp_index+AvbIcpEntry.SIZE)]
Jan Monsch77cd2022019-12-10 17:18:04 +01002148 cur_icp_entry = AvbIcpEntry(cur_icp_entry_blob)
Dan Austina7bc4962019-12-02 13:26:08 -08002149
2150 # now validate the entry structure
2151 if not cur_icp_entry.is_valid():
2152 sys.stderr.write('Validation of ICP entry {} failed.\n'.format(i))
2153 return False
2154 # jump past the entry header to the data blob
2155 icp_index += AvbIcpEntry.SIZE
2156 # extract each value from the data blob:
Jan Monsch77cd2022019-12-10 17:18:04 +01002157 transparency_log_url = icp_blob[icp_index:(icp_index
2158 + cur_icp_entry.log_url_size)]
Dan Austina7bc4962019-12-02 13:26:08 -08002159 icp_index += cur_icp_entry.log_url_size
2160 sth = icp_blob[icp_index:(icp_index+cur_icp_entry.signed_root_blob_size)]
2161 icp_index += cur_icp_entry.signed_root_blob_size
2162 if cur_icp_entry.proof_size / cur_icp_entry.proof_hash_count != proof_hash_size:
Jan Monsch77cd2022019-12-10 17:18:04 +01002163 sys.stderr.write('Bad proof size: {} in ICP entry {}'.format(
2164 cur_icp_entry.proof_size, i))
Dan Austina7bc4962019-12-02 13:26:08 -08002165 return False
2166 proof_size = icp_blob[icp_index:(icp_index+cur_icp_entry.proof_size)]
2167 icp_index += cur_icp_entry.proof_size
2168 # check if there is a next entry
2169 if cur_icp_entry.next_entry == 0:
2170 if i != icp_count - 1:
Jan Monsch77cd2022019-12-10 17:18:04 +01002171 sys.stderr.write('ICP entry count mismatch\n')
Dan Austina7bc4962019-12-02 13:26:08 -08002172 return False
2173 break
2174 if icp_index != len(icp_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +01002175 sys.stderr.write('ICP blob size mismatch, expected {}, got {}\n'.format(
2176 len(icp_blob), icp_index))
Dan Austina7bc4962019-12-02 13:26:08 -08002177 return False
2178 return True
2179
2180# AFTL structure test functions
2181
Jan Monsch77cd2022019-12-10 17:18:04 +01002182
Dan Austina7bc4962019-12-02 13:26:08 -08002183def test_default_icp_header():
2184 """Performs validation on a default ICP header structure.
2185
2186 Returns:
2187 True if the tests pass, False otherwise.
2188 """
2189
2190 icp_header = AvbIcpHeader()
2191 if icp_header.is_valid():
2192 sys.stdout.write('ICP header: validation passed\n')
2193 return True
2194 else:
2195 sys.stdout.write('ICP header: validation failed\n')
2196 return False
2197
Jan Monsch77cd2022019-12-10 17:18:04 +01002198
Dan Austina7bc4962019-12-02 13:26:08 -08002199def test_icp_header(algorithm, icp_count):
2200 """Create an ICP header structure and attempt to validate it.
2201
Jan Monsch77cd2022019-12-10 17:18:04 +01002202 Arguments:
2203 algorithm: The algorithm to be used.
2204 icp_count: Number of ICPs that follow the ICP header.
2205
Dan Austina7bc4962019-12-02 13:26:08 -08002206 Returns:
2207 True if the tests pass, False otherwise.
2208 """
2209
2210 icp_header = AvbIcpHeader()
2211 icp_header.algorithm = algorithm
2212 icp_header.icp_count = icp_count
2213 if icp_header.is_valid():
2214 sys.stdout.write('ICP header: validation passed\n')
2215 return True
2216 else:
2217 sys.stdout.write('ICP header: validation failed\n')
2218 return False
2219
Jan Monsch77cd2022019-12-10 17:18:04 +01002220
Dan Austina7bc4962019-12-02 13:26:08 -08002221def test_default_icp_entry():
2222 """Performs validation on an empty ICP entry structure.
2223
2224 Returns:
2225 True if the tests pass, False otherwise.
2226 """
2227
2228 icp_entry = AvbIcpEntry()
2229 if icp_entry.is_valid():
2230 sys.stdout.write('ICP entry validation passed\n')
2231 return True
2232 else:
2233 sys.stdout.write('ICP entry validation failed\n')
2234 return False
2235
Jan Monsch77cd2022019-12-10 17:18:04 +01002236
2237def test_icp_entry(log_url_size, leaf_index, signed_root_blob_size,
2238 proof_hash_count, proof_size, next_entry):
Dan Austina7bc4962019-12-02 13:26:08 -08002239 """Create an ICP entry structure and attempt to validate it.
2240
2241 Returns:
2242 True if the tests pass, False otherwise.
2243 """
2244
2245 icp_entry = AvbIcpEntry()
2246 icp_entry.log_url_size = log_url_size
2247 icp_entry.leaf_index = leaf_index
2248 icp_entry.signed_root_blob_size = signed_root_blob_size
2249 icp_entry.proof_hash_count = proof_hash_count
2250 icp_entry.proof_size = proof_size
2251 icp_entry.next_entry = next_entry
2252 if icp_entry.is_valid():
2253 sys.stdout.write('ICP entry validation passed\n')
2254 return True
2255 else:
2256 sys.stdout.write('ICP entry validation failed\n')
2257 return False
2258
Jan Monsch77cd2022019-12-10 17:18:04 +01002259
Dan Austina7bc4962019-12-02 13:26:08 -08002260def test_generate_icp_images():
2261 """Test cases for full AFTL ICP structure generation.
2262
2263 Returns:
2264 True if all tests return expected values, False otherwise.
2265 """
2266
2267 if not test_default_icp_header():
2268 return False
2269
2270 if not test_icp_header(1, 1):
2271 return False
2272
2273 icp_header = AvbIcpHeader()
2274 icp_header.algorithm = 1
2275 icp_header.icp_count = 1
2276
2277 tl_url = 'aftl-test-server.google.com'
2278 sth = bytearray()
2279 # fill each structure with an easily observable pattern for easy validation.
2280 sth.extend('a' * 160)
2281 proof_hashes = bytearray()
2282 proof_hashes.extend('b' * 32)
2283 proof_hashes.extend('c' * 32)
2284 proof_hashes.extend('d' * 32)
2285 proof_hashes.extend('e' * 32)
2286 if not test_icp_entry(len(tl_url), 1, len(sth), 4, len(proof_hashes), 0):
2287 return False
2288 icp_entry = AvbIcpEntry()
2289 icp_entry.log_url_size = len(tl_url)
2290 icp_entry.leaf_index = 1
2291 icp_entry.signed_root_blob_size = len(sth)
2292 icp_entry.proof_hash_count = 4
2293 icp_entry.proof_size = len(proof_hashes)
2294 icp_entry.next_entry = 0
2295 icp_data = bytearray()
2296 icp_data.extend(tl_url)
2297 icp_data.extend(sth)
2298 icp_data.extend(proof_hashes)
2299 icp_blob = bytearray()
2300 icp_blob.extend(icp_header.encode())
2301 icp_blob.extend(icp_entry.encode())
2302 icp_blob.extend(icp_data)
2303 if not validate_icp_blob(icp_blob):
2304 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002305 # Now add a 2nd entry (this should fail).
Dan Austina7bc4962019-12-02 13:26:08 -08002306 tl_url2 = 'aftl-test-server.google.ch'
2307 sth2 = bytearray()
2308 sth2.extend('f' * 192)
2309 proof_hashes2 = bytearray()
2310 proof_hashes2.extend('g' * 32)
2311 proof_hashes2.extend('h' * 32)
2312 if not test_icp_entry(len(tl_url2), 2, len(sth2), 2, len(proof_hashes2), 0):
2313 return False
2314 icp_entry2 = AvbIcpEntry()
2315 icp_entry2.log_url_size = len(tl_url2)
2316 icp_entry2.leaf_index = 2
2317 icp_entry2.signed_root_blob_size = len(sth2)
2318 icp_entry2.proof_hash_count = 2
2319 icp_entry2.proof_size = len(proof_hashes2)
2320 icp_entry2.next_entry = 0
2321 icp_data2 = bytearray()
2322 icp_data2.extend(tl_url2)
2323 icp_data2.extend(sth2)
2324 icp_data2.extend(proof_hashes2)
2325 icp_blob.extend(icp_entry2.encode())
2326 icp_blob.extend(icp_data2)
2327 if validate_icp_blob(icp_blob):
2328 return False
Jan Monsch77cd2022019-12-10 17:18:04 +01002329 # Now fix the entries so this passes.
Dan Austina7bc4962019-12-02 13:26:08 -08002330 icp_header.icp_count = 2
2331 icp_entry.next_entry = 1
2332 icp_blob2 = bytearray()
2333 icp_blob2.extend(icp_header.encode())
2334 icp_blob2.extend(icp_entry.encode())
2335 icp_blob2.extend(icp_data)
2336 icp_blob2.extend(icp_entry2.encode())
2337 icp_blob2.extend(icp_data2)
2338 if not validate_icp_blob(icp_blob2):
2339 return False
2340 return True
2341
Jan Monsch77cd2022019-12-10 17:18:04 +01002342
Dan Austina7bc4962019-12-02 13:26:08 -08002343def test_icp_image():
2344 """The main function that handles testing all AFTL related components.
Jan Monsch77cd2022019-12-10 17:18:04 +01002345
2346 Currently performs tests for validation of AFTL related structures.
Dan Austina7bc4962019-12-02 13:26:08 -08002347
2348 Returns:
2349 True if all tests pass, False otherwise.
2350 """
2351
2352 # Header related tests.
2353 default_icp = test_default_icp_header()
2354 # 1 is SHA256/RSA4096
2355 if not test_icp_header(1, 4):
2356 return False
2357 # Header related failure tests.
2358 if test_icp_header(-12, 4):
2359 return False
2360 if test_icp_header(4, -34):
2361 return False
2362 if test_icp_header(10, 10):
2363 return False
2364 # ICP entry related tests.
2365 if not test_default_icp_entry():
2366 return False
2367 if not test_icp_entry(28, 2, 92, 2, 64, 0):
2368 return False
2369 if not test_icp_entry(28, 4, 160, 4, 128, 1):
2370 return False
2371 # ICP entry related failure tests.
2372 if test_icp_entry(-2, 2, 92, 2, 64, 0):
2373 return False
2374 if test_icp_entry(28, -1, 92, 2, 64, 0):
2375 return False
2376 if test_icp_entry(28, 2, -2, 2, 64, 0):
2377 return False
2378 if test_icp_entry(28, 2, 92, 4, 128, 32234):
2379 return False
2380 # Tests for the full AFTL ICP structure.
2381 if not test_generate_icp_images():
2382 return False
2383 sys.stdout.write('All AFTL ICP tests passed.\n')
2384 return True
2385
Jan Monsch77cd2022019-12-10 17:18:04 +01002386
David Zeuthen21e95262016-07-27 17:58:40 -04002387class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04002388 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04002389
2390 Attributes:
Tao Bao80418a52018-07-20 11:41:22 -07002391 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2392 avb_vbmeta_image.h.
David Zeuthen21e95262016-07-27 17:58:40 -04002393 """
2394
2395 SIZE = 256
2396
David Zeuthene3cadca2017-02-22 21:25:46 -05002397 # Keep in sync with |reserved0| and |reserved| field of
2398 # |AvbVBMetaImageHeader|.
2399 RESERVED0 = 4
2400 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002401
2402 # Keep in sync with |AvbVBMetaImageHeader|.
2403 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2404 '2Q' # 2 x block size
2405 'L' # algorithm type
2406 '2Q' # offset, size (hash)
2407 '2Q' # offset, size (signature)
2408 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002409 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04002410 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002411 'Q' # rollback_index
2412 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002413 str(RESERVED0) + 'x' + # padding for reserved bytes
2414 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002415 str(RESERVED) + 'x') # padding for reserved bytes
2416
2417 def __init__(self, data=None):
2418 """Initializes a new header object.
2419
2420 Arguments:
2421 data: If not None, must be a bytearray of size 8192.
2422
2423 Raises:
2424 Exception: If the given data is malformed.
2425 """
2426 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2427
2428 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002429 (self.magic, self.required_libavb_version_major,
2430 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002431 self.authentication_data_block_size, self.auxiliary_data_block_size,
2432 self.algorithm_type, self.hash_offset, self.hash_size,
2433 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002434 self.public_key_size, self.public_key_metadata_offset,
2435 self.public_key_metadata_size, self.descriptors_offset,
2436 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002437 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002438 self.flags,
2439 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002440 # Nuke NUL-bytes at the end of the string.
2441 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002442 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002443 else:
2444 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002445 # Start by just requiring version 1.0. Code that adds features
2446 # in a future version can use bump_required_libavb_version_minor() to
2447 # bump the minor.
2448 self.required_libavb_version_major = AVB_VERSION_MAJOR
2449 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002450 self.authentication_data_block_size = 0
2451 self.auxiliary_data_block_size = 0
2452 self.algorithm_type = 0
2453 self.hash_offset = 0
2454 self.hash_size = 0
2455 self.signature_offset = 0
2456 self.signature_size = 0
2457 self.public_key_offset = 0
2458 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002459 self.public_key_metadata_offset = 0
2460 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002461 self.descriptors_offset = 0
2462 self.descriptors_size = 0
2463 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002464 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002465 self.release_string = get_release_string()
2466
2467 def bump_required_libavb_version_minor(self, minor):
2468 """Function to bump required_libavb_version_minor.
2469
2470 Call this when writing data that requires a specific libavb
2471 version to parse it.
2472
2473 Arguments:
2474 minor: The minor version of libavb that has support for the feature.
2475 """
2476 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002477 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002478
2479 def save(self, output):
2480 """Serializes the header (256 bytes) to disk.
2481
2482 Arguments:
2483 output: The object to write the output to.
2484 """
2485 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002486 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2487 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002488 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2489 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002490 self.public_key_offset, self.public_key_size,
2491 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002492 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002493 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002494
2495 def encode(self):
2496 """Serializes the header (256) to a bytearray().
2497
2498 Returns:
2499 A bytearray() with the encoded header.
2500 """
2501 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002502 self.required_libavb_version_major,
2503 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002504 self.authentication_data_block_size,
2505 self.auxiliary_data_block_size, self.algorithm_type,
2506 self.hash_offset, self.hash_size, self.signature_offset,
2507 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002508 self.public_key_size, self.public_key_metadata_offset,
2509 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002510 self.descriptors_size, self.rollback_index, self.flags,
2511 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002512
2513
2514class Avb(object):
2515 """Business logic for avbtool command-line tool."""
2516
David Zeuthen8b6973b2016-09-20 12:39:49 -04002517 # Keep in sync with avb_ab_flow.h.
2518 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2519 AB_MAGIC = '\0AB0'
2520 AB_MAJOR_VERSION = 1
2521 AB_MINOR_VERSION = 0
2522 AB_MISC_METADATA_OFFSET = 2048
2523
David Zeuthen09692692016-09-30 16:16:40 -04002524 # Constants for maximum metadata size. These are used to give
2525 # meaningful errors if the value passed in via --partition_size is
2526 # too small and when --calc_max_image_size is used. We use
2527 # conservative figures.
2528 MAX_VBMETA_SIZE = 64 * 1024
2529 MAX_FOOTER_SIZE = 4096
2530
David Zeuthen49936b42018-08-07 17:38:58 -04002531 def extract_vbmeta_image(self, output, image_filename, padding_size):
2532 """Implements the 'extract_vbmeta_image' command.
2533
2534 Arguments:
2535 output: Write vbmeta struct to this file.
2536 image_filename: File to extract vbmeta data from (with a footer).
2537 padding_size: If not 0, pads output so size is a multiple of the number.
2538
2539 Raises:
2540 AvbError: If there's no footer in the image.
2541 """
2542 image = ImageHandler(image_filename)
2543
2544 (footer, _, _, _) = self._parse_image(image)
2545
2546 if not footer:
2547 raise AvbError('Given image does not have a footer.')
2548
2549 image.seek(footer.vbmeta_offset)
2550 vbmeta_blob = image.read(footer.vbmeta_size)
2551 output.write(vbmeta_blob)
2552
2553 if padding_size > 0:
2554 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2555 padding_needed = padded_size - len(vbmeta_blob)
2556 output.write('\0' * padding_needed)
2557
David Zeuthena4fee8b2016-08-22 15:20:43 -04002558 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002559 """Implements the 'erase_footer' command.
2560
2561 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002562 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002563 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002564
2565 Raises:
2566 AvbError: If there's no footer in the image.
2567 """
2568
David Zeuthena4fee8b2016-08-22 15:20:43 -04002569 image = ImageHandler(image_filename)
2570
David Zeuthen21e95262016-07-27 17:58:40 -04002571 (footer, _, descriptors, _) = self._parse_image(image)
2572
2573 if not footer:
2574 raise AvbError('Given image does not have a footer.')
2575
2576 new_image_size = None
2577 if not keep_hashtree:
2578 new_image_size = footer.original_image_size
2579 else:
2580 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002581 # descriptor to figure out the location and size of the hashtree
2582 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002583 for desc in descriptors:
2584 if isinstance(desc, AvbHashtreeDescriptor):
2585 # The hashtree is always just following the main data so the
2586 # new size is easily derived.
2587 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002588 # If the image has FEC codes, also keep those.
2589 if desc.fec_offset > 0:
2590 fec_end = desc.fec_offset + desc.fec_size
2591 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002592 break
2593 if not new_image_size:
2594 raise AvbError('Requested to keep hashtree but no hashtree '
2595 'descriptor was found.')
2596
2597 # And cut...
2598 image.truncate(new_image_size)
2599
David Zeuthen1394f762019-04-30 10:20:11 -04002600 def zero_hashtree(self, image_filename):
2601 """Implements the 'zero_hashtree' command.
2602
2603 Arguments:
2604 image_filename: File to zero hashtree and FEC data from.
2605
2606 Raises:
2607 AvbError: If there's no footer in the image.
2608 """
2609
2610 image = ImageHandler(image_filename)
2611
2612 (footer, _, descriptors, _) = self._parse_image(image)
2613
2614 if not footer:
2615 raise AvbError('Given image does not have a footer.')
2616
2617 # Search for a hashtree descriptor to figure out the location and
2618 # size of the hashtree and FEC.
2619 ht_desc = None
2620 for desc in descriptors:
2621 if isinstance(desc, AvbHashtreeDescriptor):
2622 ht_desc = desc
2623 break
2624
2625 if not ht_desc:
2626 raise AvbError('No hashtree descriptor was found.')
2627
2628 zero_ht_start_offset = ht_desc.tree_offset
2629 zero_ht_num_bytes = ht_desc.tree_size
2630 zero_fec_start_offset = None
2631 zero_fec_num_bytes = 0
2632 if ht_desc.fec_offset > 0:
2633 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2634 raise AvbError('Hash-tree and FEC data must be adjacent.')
2635 zero_fec_start_offset = ht_desc.fec_offset
2636 zero_fec_num_bytes = ht_desc.fec_size
2637 zero_end_offset = zero_ht_start_offset + zero_ht_num_bytes + zero_fec_num_bytes
2638 image.seek(zero_end_offset)
2639 data = image.read(image.image_size - zero_end_offset)
2640
2641 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2642 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2643 # beginning of both hashtree and FEC. (That way, in the future we can add
2644 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2645 #
2646 # Applications can use these markers to detect that the hashtree and/or
2647 # FEC needs to be recomputed.
2648 image.truncate(zero_ht_start_offset)
2649 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2650 image.append_raw(data_zeroed_firstblock)
2651 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2652 if zero_fec_start_offset:
2653 image.append_raw(data_zeroed_firstblock)
2654 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2655 image.append_raw(data)
2656
David Zeuthen2bc232b2017-04-19 14:25:19 -04002657 def resize_image(self, image_filename, partition_size):
2658 """Implements the 'resize_image' command.
2659
2660 Arguments:
2661 image_filename: File with footer to resize.
2662 partition_size: The new size of the image.
2663
2664 Raises:
2665 AvbError: If there's no footer in the image.
2666 """
2667
2668 image = ImageHandler(image_filename)
2669
2670 if partition_size % image.block_size != 0:
2671 raise AvbError('Partition size of {} is not a multiple of the image '
2672 'block size {}.'.format(partition_size,
2673 image.block_size))
2674
Jan Monsch77cd2022019-12-10 17:18:04 +01002675 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002676
2677 if not footer:
2678 raise AvbError('Given image does not have a footer.')
2679
2680 # The vbmeta blob is always at the end of the data so resizing an
2681 # image amounts to just moving the footer around.
2682
2683 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2684 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002685 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2686 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002687
2688 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002689 raise AvbError('Requested size of {} is too small for an image '
2690 'of size {}.'
2691 .format(partition_size,
2692 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002693
2694 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2695 # with enough bytes such that the final Footer block is at the end
2696 # of partition_size.
2697 image.truncate(vbmeta_end_offset)
2698 image.append_dont_care(partition_size - vbmeta_end_offset -
2699 1*image.block_size)
2700
2701 # Just reuse the same footer - only difference is that we're
2702 # writing it in a different place.
2703 footer_blob = footer.encode()
2704 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2705 footer_blob)
2706 image.append_raw(footer_blob_with_padding)
2707
David Zeuthen8b6973b2016-09-20 12:39:49 -04002708 def set_ab_metadata(self, misc_image, slot_data):
2709 """Implements the 'set_ab_metadata' command.
2710
2711 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2712 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2713
2714 Arguments:
2715 misc_image: The misc image to write to.
2716 slot_data: Slot data as a string
2717
2718 Raises:
2719 AvbError: If slot data is malformed.
2720 """
2721 tokens = slot_data.split(':')
2722 if len(tokens) != 6:
2723 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2724 a_priority = int(tokens[0])
2725 a_tries_remaining = int(tokens[1])
2726 a_success = True if int(tokens[2]) != 0 else False
2727 b_priority = int(tokens[3])
2728 b_tries_remaining = int(tokens[4])
2729 b_success = True if int(tokens[5]) != 0 else False
2730
2731 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2732 self.AB_MAGIC,
2733 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2734 a_priority, a_tries_remaining, a_success,
2735 b_priority, b_tries_remaining, b_success)
2736 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2737 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2738 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2739 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2740 misc_image.write(ab_data)
2741
David Zeuthena4fee8b2016-08-22 15:20:43 -04002742 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002743 """Implements the 'info_image' command.
2744
2745 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002746 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002747 output: Output file to write human-readable information to (file object).
2748 """
2749
David Zeuthena4fee8b2016-08-22 15:20:43 -04002750 image = ImageHandler(image_filename)
2751
David Zeuthen21e95262016-07-27 17:58:40 -04002752 o = output
2753
2754 (footer, header, descriptors, image_size) = self._parse_image(image)
2755
2756 if footer:
2757 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2758 footer.version_minor))
2759 o.write('Image size: {} bytes\n'.format(image_size))
2760 o.write('Original image size: {} bytes\n'.format(
2761 footer.original_image_size))
2762 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2763 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2764 o.write('--\n')
2765
2766 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2767
David Zeuthene3cadca2017-02-22 21:25:46 -05002768 o.write('Minimum libavb version: {}.{}{}\n'.format(
2769 header.required_libavb_version_major,
2770 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002771 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002772 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2773 o.write('Authentication Block: {} bytes\n'.format(
2774 header.authentication_data_block_size))
2775 o.write('Auxiliary Block: {} bytes\n'.format(
2776 header.auxiliary_data_block_size))
2777 o.write('Algorithm: {}\n'.format(alg_name))
2778 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002779 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002780 o.write('Release String: \'{}\'\n'.format(
2781 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002782
2783 # Print descriptors.
2784 num_printed = 0
2785 o.write('Descriptors:\n')
2786 for desc in descriptors:
2787 desc.print_desc(o)
2788 num_printed += 1
2789 if num_printed == 0:
2790 o.write(' (none)\n')
2791
Jan Monscheeb28b62019-12-05 16:17:09 +01002792 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2793 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002794 """Implements the 'verify_image' command.
2795
2796 Arguments:
2797 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002798 key_path: None or check that embedded public key matches key at given
2799 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002800 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002801 follow_chain_partitions:
2802 If True, will follows chain partitions even when not specified with
2803 the --expected_chain_partition option
2804 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2805 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002806
2807 Raises:
2808 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002809 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002810 expected_chain_partitions_map = {}
2811 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002812 for cp in expected_chain_partitions:
2813 cp_tokens = cp.split(':')
2814 if len(cp_tokens) != 3:
2815 raise AvbError('Malformed chained partition "{}".'.format(cp))
2816 partition_name = cp_tokens[0]
2817 rollback_index_location = int(cp_tokens[1])
2818 file_path = cp_tokens[2]
2819 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002820 expected_chain_partitions_map[partition_name] = (
2821 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002822
2823 image_dir = os.path.dirname(image_filename)
2824 image_ext = os.path.splitext(image_filename)[1]
2825
2826 key_blob = None
2827 if key_path:
Jan Monscheeb28b62019-12-05 16:17:09 +01002828 print 'Verifying image {} using key at {}'.format(image_filename,
2829 key_path)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002830 key_blob = encode_rsa_key(key_path)
2831 else:
Jan Monscheeb28b62019-12-05 16:17:09 +01002832 print 'Verifying image {} using embedded public key'.format(
2833 image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002834
David Zeuthenb623d8b2017-04-04 16:05:53 -04002835 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002836 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002837 offset = 0
2838 if footer:
2839 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002840
David Zeuthenb623d8b2017-04-04 16:05:53 -04002841 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002842 vbmeta_blob = image.read(header.SIZE
2843 + header.authentication_data_block_size
2844 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002845
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002846 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002847 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002848 raise AvbError('Signature check failed for {} vbmeta struct {}'
2849 .format(alg_name, image_filename))
2850
2851 if key_blob:
2852 # The embedded public key is in the auxiliary block at an offset.
2853 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002854 key_offset += header.authentication_data_block_size
2855 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002856 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2857 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002858 if key_blob != key_blob_in_vbmeta:
2859 raise AvbError('Embedded public key does not match given key.')
2860
2861 if footer:
2862 print ('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002863 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002864 else:
2865 print ('vbmeta: Successfully verified {} vbmeta struct in {}'
David Zeuthen49936b42018-08-07 17:38:58 -04002866 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002867
2868 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002869 if (isinstance(desc, AvbChainPartitionDescriptor)
2870 and follow_chain_partitions
2871 and expected_chain_partitions_map.get(desc.partition_name) == None):
David Zeuthene947cb62019-01-25 15:27:08 -05002872 # In this case we're processing a chain descriptor but don't have a
2873 # --expect_chain_partition ... however --follow_chain_partitions was
2874 # specified so we shouldn't error out in desc.verify().
Jan Monscheeb28b62019-12-05 16:17:09 +01002875 print ('{}: Chained but ROLLBACK_SLOT (which is {}) '
2876 'and KEY (which has sha1 {}) not specified'
2877 .format(desc.partition_name, desc.rollback_index_location,
2878 hashlib.sha1(desc.public_key).hexdigest()))
David Zeuthene947cb62019-01-25 15:27:08 -05002879 else:
Jan Monscheeb28b62019-12-05 16:17:09 +01002880 if not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
2881 image, accept_zeroed_hashtree):
David Zeuthene947cb62019-01-25 15:27:08 -05002882 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002883 # Honor --follow_chain_partitions - add '--' to make the output more
2884 # readable.
2885 if (isinstance(desc, AvbChainPartitionDescriptor)
2886 and follow_chain_partitions):
David Zeuthene947cb62019-01-25 15:27:08 -05002887 print '--'
Jan Monscheeb28b62019-12-05 16:17:09 +01002888 chained_image_filename = os.path.join(image_dir,
2889 desc.partition_name + image_ext)
2890 self.verify_image(chained_image_filename, key_path, None, False,
2891 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002892
David Zeuthenb8643c02018-05-17 17:21:18 -04002893 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2894 """Implements the 'calculate_vbmeta_digest' command.
2895
2896 Arguments:
2897 image_filename: Image file to get information from (file object).
2898 hash_algorithm: Hash algorithm used.
2899 output: Output file to write human-readable information to (file object).
2900 """
2901
2902 image_dir = os.path.dirname(image_filename)
2903 image_ext = os.path.splitext(image_filename)[1]
2904
2905 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002906 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002907 offset = 0
2908 if footer:
2909 offset = footer.vbmeta_offset
2910 size = (header.SIZE + header.authentication_data_block_size +
2911 header.auxiliary_data_block_size)
2912 image.seek(offset)
2913 vbmeta_blob = image.read(size)
2914
2915 hasher = hashlib.new(name=hash_algorithm)
2916 hasher.update(vbmeta_blob)
2917
2918 for desc in descriptors:
2919 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002920 ch_image_filename = os.path.join(image_dir,
2921 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002922 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002923 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002924 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002925 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2926 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002927 if ch_footer:
2928 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002929 ch_image.seek(ch_offset)
2930 ch_vbmeta_blob = ch_image.read(ch_size)
2931 hasher.update(ch_vbmeta_blob)
2932
2933 digest = hasher.digest()
2934 output.write('{}\n'.format(digest.encode('hex')))
2935
David Zeuthenf7d2e752018-09-20 13:30:41 -04002936 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2937 """Implements the 'calculate_kernel_cmdline' command.
2938
2939 Arguments:
2940 image_filename: Image file to get information from (file object).
2941 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2942 output: Output file to write human-readable information to (file object).
2943 """
2944
2945 image = ImageHandler(image_filename)
2946 _, _, descriptors, _ = self._parse_image(image)
2947
2948 image_dir = os.path.dirname(image_filename)
2949 image_ext = os.path.splitext(image_filename)[1]
2950
2951 cmdline_descriptors = []
2952 for desc in descriptors:
2953 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002954 ch_image_filename = os.path.join(image_dir,
2955 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002956 ch_image = ImageHandler(ch_image_filename)
2957 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2958 for ch_desc in ch_descriptors:
2959 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2960 cmdline_descriptors.append(ch_desc)
2961 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2962 cmdline_descriptors.append(desc)
2963
2964 kernel_cmdline_snippets = []
2965 for desc in cmdline_descriptors:
2966 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002967 if ((desc.flags &
2968 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2969 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002970 if hashtree_disabled:
2971 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002972 if (desc.flags &
2973 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002974 if not hashtree_disabled:
2975 use_cmdline = False
2976 if use_cmdline:
2977 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2978 output.write(' '.join(kernel_cmdline_snippets))
2979
David Zeuthen21e95262016-07-27 17:58:40 -04002980 def _parse_image(self, image):
2981 """Gets information about an image.
2982
2983 The image can either be a vbmeta or an image with a footer.
2984
2985 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002986 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002987
2988 Returns:
2989 A tuple where the first argument is a AvbFooter (None if there
2990 is no footer on the image), the second argument is a
2991 AvbVBMetaHeader, the third argument is a list of
2992 AvbDescriptor-derived instances, and the fourth argument is the
2993 size of |image|.
2994 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002995 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002996 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002997 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002998 try:
2999 footer = AvbFooter(image.read(AvbFooter.SIZE))
3000 except (LookupError, struct.error):
3001 # Nope, just seek back to the start.
3002 image.seek(0)
3003
3004 vbmeta_offset = 0
3005 if footer:
3006 vbmeta_offset = footer.vbmeta_offset
3007
3008 image.seek(vbmeta_offset)
3009 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3010
3011 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
3012 aux_block_offset = auth_block_offset + h.authentication_data_block_size
3013 desc_start_offset = aux_block_offset + h.descriptors_offset
3014 image.seek(desc_start_offset)
3015 descriptors = parse_descriptors(image.read(h.descriptors_size))
3016
David Zeuthen09692692016-09-30 16:16:40 -04003017 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003018
David Zeuthenb1b994d2017-03-06 18:01:31 -05003019 def _load_vbmeta_blob(self, image):
3020 """Gets the vbmeta struct and associated sections.
3021
3022 The image can either be a vbmeta.img or an image with a footer.
3023
3024 Arguments:
3025 image: An ImageHandler (vbmeta or footer).
3026
3027 Returns:
3028 A blob with the vbmeta struct and other sections.
3029 """
3030 assert isinstance(image, ImageHandler)
3031 footer = None
3032 image.seek(image.image_size - AvbFooter.SIZE)
3033 try:
3034 footer = AvbFooter(image.read(AvbFooter.SIZE))
3035 except (LookupError, struct.error):
3036 # Nope, just seek back to the start.
3037 image.seek(0)
3038
3039 vbmeta_offset = 0
3040 if footer:
3041 vbmeta_offset = footer.vbmeta_offset
3042
3043 image.seek(vbmeta_offset)
3044 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
3045
3046 image.seek(vbmeta_offset)
3047 data_size = AvbVBMetaHeader.SIZE
3048 data_size += h.authentication_data_block_size
3049 data_size += h.auxiliary_data_block_size
3050 return image.read(data_size)
3051
David Zeuthen73f2afa2017-05-17 16:54:11 -04003052 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05003053 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04003054
3055 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04003056 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04003057
3058 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05003059 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3060 instructions. There is one for when hashtree is not disabled and one for
3061 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04003062
David Zeuthen21e95262016-07-27 17:58:40 -04003063 """
3064
David Zeuthen21e95262016-07-27 17:58:40 -04003065 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003066 c += '0' # start
3067 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
3068 c += ' verity {}'.format(ht.dm_verity_version) # type and version
3069 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
3070 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
3071 c += ' {}'.format(ht.data_block_size) # data_block
3072 c += ' {}'.format(ht.hash_block_size) # hash_block
3073 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
3074 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
3075 c += ' {}'.format(ht.hash_algorithm) # hash_alg
3076 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
3077 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
3078 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05003079 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003080 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003081 c += ' ignore_zero_blocks'
3082 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3083 c += ' fec_roots {}'.format(ht.fec_num_roots)
3084 # Note that fec_blocks is the size that FEC covers, *not* the
3085 # size of the FEC data. Since we use FEC for everything up until
3086 # the FEC data, it's the same as the offset.
3087 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
3088 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
3089 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05003090 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04003091 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003092 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04003093 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04003094
David Zeuthenfd41eb92016-11-17 12:24:47 -05003095 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04003096 desc = AvbKernelCmdlineDescriptor()
3097 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05003098 desc.flags = (
3099 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
3100
3101 # The descriptor for when hashtree verification is disabled is a lot
3102 # simpler - we just set the root to the partition.
3103 desc_no_ht = AvbKernelCmdlineDescriptor()
3104 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
3105 desc_no_ht.flags = (
3106 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
3107
3108 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04003109
David Zeuthen73f2afa2017-05-17 16:54:11 -04003110 def _get_cmdline_descriptors_for_dm_verity(self, image):
3111 """Generate kernel cmdline descriptors for dm-verity.
3112
3113 Arguments:
3114 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
3115
3116 Returns:
3117 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
3118 instructions. There is one for when hashtree is not disabled and one for
3119 when it is.
3120
3121 Raises:
3122 AvbError: If |image| doesn't have a hashtree descriptor.
3123
3124 """
3125
3126 (_, _, descriptors, _) = self._parse_image(image)
3127
3128 ht = None
3129 for desc in descriptors:
3130 if isinstance(desc, AvbHashtreeDescriptor):
3131 ht = desc
3132 break
3133
3134 if not ht:
3135 raise AvbError('No hashtree descriptor in given image')
3136
3137 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
3138
Dan Austina7bc4962019-12-02 13:26:08 -08003139 def icp_test_suite(self):
Jan Monsch77cd2022019-12-10 17:18:04 +01003140 """Implements a self-contained test suite for AFTL related code.
Dan Austina7bc4962019-12-02 13:26:08 -08003141
3142 Returns:
3143 True if all tests pass, False otherwise.
3144 """
3145 return test_icp_image()
3146
David Zeuthen21e95262016-07-27 17:58:40 -04003147 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05003148 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003149 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003150 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04003151 include_descriptors_from_image,
3152 signing_helper,
3153 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003154 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003155 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04003156 print_required_libavb_version,
3157 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003158 """Implements the 'make_vbmeta_image' command.
3159
3160 Arguments:
3161 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003162 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003163 algorithm_name: Name of algorithm to use.
3164 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003165 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003166 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003167 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003168 props: Properties to insert (list of strings of the form 'key:value').
3169 props_from_file: Properties to insert (list of strings 'key:<path>').
3170 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003171 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04003172 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003173 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003174 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003175 release_string: None or avbtool release string to use instead of default.
3176 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04003177 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04003178 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04003179
3180 Raises:
3181 AvbError: If a chained partition is malformed.
3182 """
3183
David Zeuthen1097a782017-05-31 15:53:17 -04003184 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003185 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003186 if include_descriptors_from_image:
3187 # Use the bump logic in AvbVBMetaHeader to calculate the max required
3188 # version of all included descriptors.
3189 tmp_header = AvbVBMetaHeader()
3190 for image in include_descriptors_from_image:
3191 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
3192 tmp_header.bump_required_libavb_version_minor(
3193 image_header.required_libavb_version_minor)
3194 print '1.{}'.format(tmp_header.required_libavb_version_minor)
3195 else:
3196 # Descriptors aside, all vbmeta features are supported in 1.0.
3197 print '1.0'
David Zeuthen1097a782017-05-31 15:53:17 -04003198 return
3199
3200 if not output:
3201 raise AvbError('No output file given')
3202
David Zeuthen21e95262016-07-27 17:58:40 -04003203 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04003204 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003205 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003206 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003207 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003208 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003209 include_descriptors_from_image, signing_helper,
3210 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003211 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04003212
3213 # Write entire vbmeta blob (header, authentication, auxiliary).
3214 output.seek(0)
3215 output.write(vbmeta_blob)
3216
David Zeuthen97cb5802017-06-01 16:14:05 -04003217 if padding_size > 0:
3218 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
3219 padding_needed = padded_size - len(vbmeta_blob)
3220 output.write('\0' * padding_needed)
3221
David Zeuthen18666ab2016-11-15 11:18:05 -05003222 def _generate_vbmeta_blob(self, algorithm_name, key_path,
3223 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003224 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003225 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003226 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003227 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003228 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05003229 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003230 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003231 release_string, append_to_release_string,
3232 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04003233 """Generates a VBMeta blob.
3234
3235 This blob contains the header (struct AvbVBMetaHeader), the
3236 authentication data block (which contains the hash and signature
3237 for the header and auxiliary block), and the auxiliary block
3238 (which contains descriptors, the public key used, and other data).
3239
3240 The |key| parameter can |None| only if the |algorithm_name| is
3241 'NONE'.
3242
3243 Arguments:
3244 algorithm_name: The algorithm name as per the ALGORITHMS dict.
3245 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05003246 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003247 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003248 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003249 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05003250 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003251 props: Properties to insert (List of strings of the form 'key:value').
3252 props_from_file: Properties to insert (List of strings 'key:<path>').
3253 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003254 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003255 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003256 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3257 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04003258 include_descriptors_from_image: List of file objects for which
3259 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003260 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003261 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003262 release_string: None or avbtool release string.
3263 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003264 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04003265
3266 Returns:
3267 A bytearray() with the VBMeta blob.
3268
3269 Raises:
3270 Exception: If the |algorithm_name| is not found, if no key has
3271 been given and the given algorithm requires one, or the key is
3272 of the wrong size.
3273
3274 """
3275 try:
3276 alg = ALGORITHMS[algorithm_name]
3277 except KeyError:
3278 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3279
David Zeuthena5fd3a42017-02-27 16:38:54 -05003280 if not descriptors:
3281 descriptors = []
3282
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003283 h = AvbVBMetaHeader()
3284 h.bump_required_libavb_version_minor(required_libavb_version_minor)
3285
David Zeuthena5fd3a42017-02-27 16:38:54 -05003286 # Insert chained partition descriptors, if any
3287 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04003288 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05003289 for cp in chain_partitions:
3290 cp_tokens = cp.split(':')
3291 if len(cp_tokens) != 3:
3292 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04003293 partition_name = cp_tokens[0]
3294 rollback_index_location = int(cp_tokens[1])
3295 file_path = cp_tokens[2]
3296 # Check that the same rollback location isn't being used by
3297 # multiple chained partitions.
3298 if used_locations.get(rollback_index_location):
3299 raise AvbError('Rollback Index Location {} is already in use.'.format(
3300 rollback_index_location))
3301 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05003302 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04003303 desc.partition_name = partition_name
3304 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05003305 if desc.rollback_index_location < 1:
3306 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05003307 desc.public_key = open(file_path, 'rb').read()
3308 descriptors.append(desc)
3309
David Zeuthen21e95262016-07-27 17:58:40 -04003310 # Descriptors.
3311 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05003312 for desc in descriptors:
3313 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003314
3315 # Add properties.
3316 if props:
3317 for prop in props:
3318 idx = prop.find(':')
3319 if idx == -1:
3320 raise AvbError('Malformed property "{}".'.format(prop))
3321 desc = AvbPropertyDescriptor()
3322 desc.key = prop[0:idx]
3323 desc.value = prop[(idx + 1):]
3324 encoded_descriptors.extend(desc.encode())
3325 if props_from_file:
3326 for prop in props_from_file:
3327 idx = prop.find(':')
3328 if idx == -1:
3329 raise AvbError('Malformed property "{}".'.format(prop))
3330 desc = AvbPropertyDescriptor()
3331 desc.key = prop[0:idx]
3332 desc.value = prop[(idx + 1):]
3333 file_path = prop[(idx + 1):]
3334 desc.value = open(file_path, 'rb').read()
3335 encoded_descriptors.extend(desc.encode())
3336
David Zeuthen73f2afa2017-05-17 16:54:11 -04003337 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003338 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003339 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003340 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05003341 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3342 encoded_descriptors.extend(cmdline_desc[0].encode())
3343 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04003344
David Zeuthen73f2afa2017-05-17 16:54:11 -04003345 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3346 if ht_desc_to_setup:
3347 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3348 ht_desc_to_setup)
3349 encoded_descriptors.extend(cmdline_desc[0].encode())
3350 encoded_descriptors.extend(cmdline_desc[1].encode())
3351
David Zeuthen21e95262016-07-27 17:58:40 -04003352 # Add kernel command-lines.
3353 if kernel_cmdlines:
3354 for i in kernel_cmdlines:
3355 desc = AvbKernelCmdlineDescriptor()
3356 desc.kernel_cmdline = i
3357 encoded_descriptors.extend(desc.encode())
3358
3359 # Add descriptors from other images.
3360 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003361 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04003362 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003363 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003364 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3365 image_handler)
3366 # Bump the required libavb version to support all included descriptors.
3367 h.bump_required_libavb_version_minor(
3368 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003369 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003370 # The --include_descriptors_from_image option is used in some setups
3371 # with images A and B where both A and B contain a descriptor
3372 # for a partition with the same name. Since it's not meaningful
3373 # to include both descriptors, only include the last seen descriptor.
3374 # See bug 76386656 for details.
3375 if hasattr(desc, 'partition_name'):
3376 key = type(desc).__name__ + '_' + desc.partition_name
3377 descriptors_dict[key] = desc.encode()
3378 else:
3379 encoded_descriptors.extend(desc.encode())
3380 for key in sorted(descriptors_dict.keys()):
3381 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003382
David Zeuthen18666ab2016-11-15 11:18:05 -05003383 # Load public key metadata blob, if requested.
3384 pkmd_blob = []
3385 if public_key_metadata_path:
3386 with open(public_key_metadata_path) as f:
3387 pkmd_blob = f.read()
3388
David Zeuthen21e95262016-07-27 17:58:40 -04003389 key = None
3390 encoded_key = bytearray()
3391 if alg.public_key_num_bytes > 0:
3392 if not key_path:
3393 raise AvbError('Key is required for algorithm {}'.format(
3394 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003395 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003396 if len(encoded_key) != alg.public_key_num_bytes:
3397 raise AvbError('Key is wrong size for algorithm {}'.format(
3398 algorithm_name))
3399
David Zeuthene3cadca2017-02-22 21:25:46 -05003400 # Override release string, if requested.
3401 if isinstance(release_string, (str, unicode)):
3402 h.release_string = release_string
3403
3404 # Append to release string, if requested. Also insert a space before.
3405 if isinstance(append_to_release_string, (str, unicode)):
3406 h.release_string += ' ' + append_to_release_string
3407
David Zeuthen18666ab2016-11-15 11:18:05 -05003408 # For the Auxiliary data block, descriptors are stored at offset 0,
3409 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003410 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003411 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003412 h.descriptors_offset = 0
3413 h.descriptors_size = len(encoded_descriptors)
3414 h.public_key_offset = h.descriptors_size
3415 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003416 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3417 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003418
3419 # For the Authentication data block, the hash is first and then
3420 # the signature.
3421 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003422 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003423 h.algorithm_type = alg.algorithm_type
3424 h.hash_offset = 0
3425 h.hash_size = alg.hash_num_bytes
3426 # Signature offset and size - it's stored right after the hash
3427 # (in Authentication data block).
3428 h.signature_offset = alg.hash_num_bytes
3429 h.signature_size = alg.signature_num_bytes
3430
3431 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003432 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003433
3434 # Generate Header data block.
3435 header_data_blob = h.encode()
3436
3437 # Generate Auxiliary data block.
3438 aux_data_blob = bytearray()
3439 aux_data_blob.extend(encoded_descriptors)
3440 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003441 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003442 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3443 aux_data_blob.extend('\0' * padding_bytes)
3444
3445 # Calculate the hash.
3446 binary_hash = bytearray()
3447 binary_signature = bytearray()
3448 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003449 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003450 ha.update(header_data_blob)
3451 ha.update(aux_data_blob)
3452 binary_hash.extend(ha.digest())
3453
3454 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003455 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003456 binary_signature.extend(raw_sign(signing_helper,
3457 signing_helper_with_files,
3458 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003459 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003460 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003461
3462 # Generate Authentication data block.
3463 auth_data_blob = bytearray()
3464 auth_data_blob.extend(binary_hash)
3465 auth_data_blob.extend(binary_signature)
3466 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3467 auth_data_blob.extend('\0' * padding_bytes)
3468
3469 return header_data_blob + auth_data_blob + aux_data_blob
3470
3471 def extract_public_key(self, key_path, output):
3472 """Implements the 'extract_public_key' command.
3473
3474 Arguments:
3475 key_path: The path to a RSA private key file.
3476 output: The file to write to.
3477 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003478 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003479
David Zeuthenb1b994d2017-03-06 18:01:31 -05003480 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3481 partition_size):
3482 """Implementation of the append_vbmeta_image command.
3483
3484 Arguments:
3485 image_filename: File to add the footer to.
3486 vbmeta_image_filename: File to get vbmeta struct from.
3487 partition_size: Size of partition.
3488
3489 Raises:
3490 AvbError: If an argument is incorrect.
3491 """
3492 image = ImageHandler(image_filename)
3493
3494 if partition_size % image.block_size != 0:
3495 raise AvbError('Partition size of {} is not a multiple of the image '
3496 'block size {}.'.format(partition_size,
3497 image.block_size))
3498
3499 # If there's already a footer, truncate the image to its original
3500 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003501 if image.image_size >= AvbFooter.SIZE:
3502 image.seek(image.image_size - AvbFooter.SIZE)
3503 try:
3504 footer = AvbFooter(image.read(AvbFooter.SIZE))
3505 # Existing footer found. Just truncate.
3506 original_image_size = footer.original_image_size
3507 image.truncate(footer.original_image_size)
3508 except (LookupError, struct.error):
3509 original_image_size = image.image_size
3510 else:
3511 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003512 original_image_size = image.image_size
3513
3514 # If anything goes wrong from here-on, restore the image back to
3515 # its original size.
3516 try:
3517 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3518 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3519
3520 # If the image isn't sparse, its size might not be a multiple of
3521 # the block size. This will screw up padding later so just grow it.
3522 if image.image_size % image.block_size != 0:
3523 assert not image.is_sparse
3524 padding_needed = image.block_size - (image.image_size%image.block_size)
3525 image.truncate(image.image_size + padding_needed)
3526
3527 # The append_raw() method requires content with size being a
3528 # multiple of |block_size| so add padding as needed. Also record
3529 # where this is written to since we'll need to put that in the
3530 # footer.
3531 vbmeta_offset = image.image_size
3532 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3533 len(vbmeta_blob))
3534 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3535
3536 # Append vbmeta blob and footer
3537 image.append_raw(vbmeta_blob_with_padding)
3538 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3539
3540 # Now insert a DONT_CARE chunk with enough bytes such that the
3541 # final Footer block is at the end of partition_size..
3542 image.append_dont_care(partition_size - vbmeta_end_offset -
3543 1*image.block_size)
3544
3545 # Generate the Footer that tells where the VBMeta footer
3546 # is. Also put enough padding in the front of the footer since
3547 # we'll write out an entire block.
3548 footer = AvbFooter()
3549 footer.original_image_size = original_image_size
3550 footer.vbmeta_offset = vbmeta_offset
3551 footer.vbmeta_size = len(vbmeta_blob)
3552 footer_blob = footer.encode()
3553 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3554 footer_blob)
3555 image.append_raw(footer_blob_with_padding)
3556
3557 except:
3558 # Truncate back to original size, then re-raise
3559 image.truncate(original_image_size)
3560 raise
3561
David Zeuthena4fee8b2016-08-22 15:20:43 -04003562 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003563 hash_algorithm, salt, chain_partitions, algorithm_name,
3564 key_path,
3565 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003566 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003567 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003568 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003569 signing_helper, signing_helper_with_files,
3570 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003571 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003572 print_required_libavb_version, use_persistent_digest,
3573 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003574 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003575
3576 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003577 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003578 partition_size: Size of partition.
3579 partition_name: Name of partition (without A/B suffix).
3580 hash_algorithm: Hash algorithm to use.
3581 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003582 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003583 algorithm_name: Name of algorithm to use.
3584 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003585 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003586 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003587 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003588 props: Properties to insert (List of strings of the form 'key:value').
3589 props_from_file: Properties to insert (List of strings 'key:<path>').
3590 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003591 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003592 dm-verity kernel cmdline from.
3593 include_descriptors_from_image: List of file objects for which
3594 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003595 calc_max_image_size: Don't store the footer - instead calculate the
3596 maximum image size leaving enough room for metadata with the
3597 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003598 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003599 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003600 release_string: None or avbtool release string.
3601 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003602 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3603 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003604 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003605 use_persistent_digest: Use a persistent digest on device.
3606 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003607
3608 Raises:
3609 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003610 """
David Zeuthen1097a782017-05-31 15:53:17 -04003611
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003612 required_libavb_version_minor = 0
3613 if use_persistent_digest or do_not_use_ab:
3614 required_libavb_version_minor = 1
3615
David Zeuthen1097a782017-05-31 15:53:17 -04003616 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003617 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003618 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003619 return
3620
David Zeuthenbf562452017-05-17 18:04:43 -04003621 # First, calculate the maximum image size such that an image
3622 # this size + metadata (footer + vbmeta struct) fits in
3623 # |partition_size|.
3624 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003625 if partition_size < max_metadata_size:
3626 raise AvbError('Parition size of {} is too small. '
3627 'Needs to be at least {}'.format(
3628 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003629 max_image_size = partition_size - max_metadata_size
3630
3631 # If we're asked to only calculate the maximum image size, we're done.
3632 if calc_max_image_size:
3633 print '{}'.format(max_image_size)
3634 return
3635
David Zeuthena4fee8b2016-08-22 15:20:43 -04003636 image = ImageHandler(image_filename)
3637
3638 if partition_size % image.block_size != 0:
3639 raise AvbError('Partition size of {} is not a multiple of the image '
3640 'block size {}.'.format(partition_size,
3641 image.block_size))
3642
David Zeuthen21e95262016-07-27 17:58:40 -04003643 # If there's already a footer, truncate the image to its original
3644 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3645 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003646 if image.image_size >= AvbFooter.SIZE:
3647 image.seek(image.image_size - AvbFooter.SIZE)
3648 try:
3649 footer = AvbFooter(image.read(AvbFooter.SIZE))
3650 # Existing footer found. Just truncate.
3651 original_image_size = footer.original_image_size
3652 image.truncate(footer.original_image_size)
3653 except (LookupError, struct.error):
3654 original_image_size = image.image_size
3655 else:
3656 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003657 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003658
3659 # If anything goes wrong from here-on, restore the image back to
3660 # its original size.
3661 try:
David Zeuthen09692692016-09-30 16:16:40 -04003662 # If image size exceeds the maximum image size, fail.
3663 if image.image_size > max_image_size:
3664 raise AvbError('Image size of {} exceeds maximum image '
3665 'size of {} in order to fit in a partition '
3666 'size of {}.'.format(image.image_size, max_image_size,
3667 partition_size))
3668
David Zeuthen21e95262016-07-27 17:58:40 -04003669 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3670 if salt:
3671 salt = salt.decode('hex')
3672 else:
Bryan Henry45354282018-10-25 18:37:27 -07003673 if salt is None and not use_persistent_digest:
3674 # If salt is not explicitly specified, choose a hash that's the same
3675 # size as the hash size. Don't populate a random salt if this
3676 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003677 hash_size = digest_size
3678 salt = open('/dev/urandom').read(hash_size)
3679 else:
3680 salt = ''
3681
3682 hasher = hashlib.new(name=hash_algorithm, string=salt)
3683 # TODO(zeuthen): might want to read this in chunks to avoid
3684 # memory pressure, then again, this is only supposed to be used
3685 # on kernel/initramfs partitions. Possible optimization.
3686 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003687 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003688 digest = hasher.digest()
3689
3690 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003691 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003692 h_desc.hash_algorithm = hash_algorithm
3693 h_desc.partition_name = partition_name
3694 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003695 h_desc.flags = 0
3696 if do_not_use_ab:
3697 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3698 if not use_persistent_digest:
3699 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003700
3701 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003702 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003703 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003704 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003705 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003706 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003707 include_descriptors_from_image, signing_helper,
3708 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003709 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003710
David Zeuthend247fcb2017-02-16 12:09:27 -05003711 # Write vbmeta blob, if requested.
3712 if output_vbmeta_image:
3713 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003714
David Zeuthend247fcb2017-02-16 12:09:27 -05003715 # Append vbmeta blob and footer, unless requested not to.
3716 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003717 # If the image isn't sparse, its size might not be a multiple of
3718 # the block size. This will screw up padding later so just grow it.
3719 if image.image_size % image.block_size != 0:
3720 assert not image.is_sparse
3721 padding_needed = image.block_size - (
3722 image.image_size % image.block_size)
3723 image.truncate(image.image_size + padding_needed)
3724
3725 # The append_raw() method requires content with size being a
3726 # multiple of |block_size| so add padding as needed. Also record
3727 # where this is written to since we'll need to put that in the
3728 # footer.
3729 vbmeta_offset = image.image_size
3730 padding_needed = (
3731 round_to_multiple(len(vbmeta_blob), image.block_size) -
3732 len(vbmeta_blob))
3733 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3734
David Zeuthend247fcb2017-02-16 12:09:27 -05003735 image.append_raw(vbmeta_blob_with_padding)
3736 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3737
3738 # Now insert a DONT_CARE chunk with enough bytes such that the
3739 # final Footer block is at the end of partition_size..
3740 image.append_dont_care(partition_size - vbmeta_end_offset -
3741 1*image.block_size)
3742
3743 # Generate the Footer that tells where the VBMeta footer
3744 # is. Also put enough padding in the front of the footer since
3745 # we'll write out an entire block.
3746 footer = AvbFooter()
3747 footer.original_image_size = original_image_size
3748 footer.vbmeta_offset = vbmeta_offset
3749 footer.vbmeta_size = len(vbmeta_blob)
3750 footer_blob = footer.encode()
3751 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3752 footer_blob)
3753 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003754
David Zeuthen21e95262016-07-27 17:58:40 -04003755 except:
3756 # Truncate back to original size, then re-raise
3757 image.truncate(original_image_size)
3758 raise
3759
David Zeuthena4fee8b2016-08-22 15:20:43 -04003760 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003761 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003762 block_size, salt, chain_partitions, algorithm_name,
3763 key_path,
3764 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003765 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003766 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003767 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003768 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003769 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003770 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003771 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003772 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003773 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003774 use_persistent_root_digest, do_not_use_ab,
3775 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003776 """Implements the 'add_hashtree_footer' command.
3777
3778 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3779 more information about dm-verity and these hashes.
3780
3781 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003782 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003783 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003784 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003785 generate_fec: If True, generate FEC codes.
3786 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003787 hash_algorithm: Hash algorithm to use.
3788 block_size: Block size to use.
3789 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003790 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003791 algorithm_name: Name of algorithm to use.
3792 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003793 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003794 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003795 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003796 props: Properties to insert (List of strings of the form 'key:value').
3797 props_from_file: Properties to insert (List of strings 'key:<path>').
3798 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003799 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003800 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003801 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3802 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003803 include_descriptors_from_image: List of file objects for which
3804 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003805 calc_max_image_size: Don't store the hashtree or footer - instead
3806 calculate the maximum image size leaving enough room for hashtree
3807 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003808 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003809 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003810 release_string: None or avbtool release string.
3811 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003812 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3813 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003814 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003815 use_persistent_root_digest: Use a persistent root digest on device.
3816 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003817 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003818
3819 Raises:
3820 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003821 """
David Zeuthen1097a782017-05-31 15:53:17 -04003822
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003823 required_libavb_version_minor = 0
3824 if use_persistent_root_digest or do_not_use_ab:
3825 required_libavb_version_minor = 1
3826
David Zeuthen1097a782017-05-31 15:53:17 -04003827 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003828 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003829 print '1.{}'.format(required_libavb_version_minor)
David Zeuthen1097a782017-05-31 15:53:17 -04003830 return
3831
David Zeuthen09692692016-09-30 16:16:40 -04003832 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3833 digest_padding = round_to_pow2(digest_size) - digest_size
3834
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003835 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3836 # size such that an image this size + the hashtree + metadata (footer +
3837 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3838 # for metadata.
3839 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003840 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003841 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003842 if not no_hashtree:
3843 (_, max_tree_size) = calc_hash_level_offsets(
3844 partition_size, block_size, digest_size + digest_padding)
3845 if generate_fec:
3846 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003847 max_metadata_size = (max_fec_size + max_tree_size +
3848 self.MAX_VBMETA_SIZE +
3849 self.MAX_FOOTER_SIZE)
3850 max_image_size = partition_size - max_metadata_size
3851 else:
3852 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003853
3854 # If we're asked to only calculate the maximum image size, we're done.
3855 if calc_max_image_size:
3856 print '{}'.format(max_image_size)
3857 return
3858
David Zeuthena4fee8b2016-08-22 15:20:43 -04003859 image = ImageHandler(image_filename)
3860
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003861 if partition_size > 0:
3862 if partition_size % image.block_size != 0:
3863 raise AvbError('Partition size of {} is not a multiple of the image '
3864 'block size {}.'.format(partition_size,
3865 image.block_size))
3866 else:
3867 if image.image_size % image.block_size != 0:
3868 raise AvbError('File size of {} is not a multiple of the image '
3869 'block size {}.'.format(image.image_size,
3870 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003871
David Zeuthen21e95262016-07-27 17:58:40 -04003872 # If there's already a footer, truncate the image to its original
3873 # size. This way 'avbtool add_hashtree_footer' is idempotent
3874 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003875 if image.image_size >= AvbFooter.SIZE:
3876 image.seek(image.image_size - AvbFooter.SIZE)
3877 try:
3878 footer = AvbFooter(image.read(AvbFooter.SIZE))
3879 # Existing footer found. Just truncate.
3880 original_image_size = footer.original_image_size
3881 image.truncate(footer.original_image_size)
3882 except (LookupError, struct.error):
3883 original_image_size = image.image_size
3884 else:
3885 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003886 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003887
3888 # If anything goes wrong from here-on, restore the image back to
3889 # its original size.
3890 try:
3891 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003892 rounded_image_size = round_to_multiple(image.image_size, block_size)
3893 if rounded_image_size > image.image_size:
3894 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003895
David Zeuthen09692692016-09-30 16:16:40 -04003896 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003897 if partition_size > 0:
3898 if image.image_size > max_image_size:
3899 raise AvbError('Image size of {} exceeds maximum image '
3900 'size of {} in order to fit in a partition '
3901 'size of {}.'.format(image.image_size, max_image_size,
3902 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003903
3904 if salt:
3905 salt = salt.decode('hex')
3906 else:
Bryan Henry45354282018-10-25 18:37:27 -07003907 if salt is None and not use_persistent_root_digest:
3908 # If salt is not explicitly specified, choose a hash that's the same
3909 # size as the hash size. Don't populate a random salt if this
3910 # descriptor is being created to use a persistent digest on device.
David Zeuthen21e95262016-07-27 17:58:40 -04003911 hash_size = digest_size
3912 salt = open('/dev/urandom').read(hash_size)
3913 else:
3914 salt = ''
3915
David Zeuthena4fee8b2016-08-22 15:20:43 -04003916 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003917 # offsets in advance.
3918 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003919 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003920
David Zeuthena4fee8b2016-08-22 15:20:43 -04003921 # If the image isn't sparse, its size might not be a multiple of
3922 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003923 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003924 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003925 padding_needed = image.block_size - (image.image_size%image.block_size)
3926 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003927
David Zeuthena4fee8b2016-08-22 15:20:43 -04003928 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003929 tree_offset = image.image_size
3930 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003931 block_size,
3932 hash_algorithm, salt,
3933 digest_padding,
3934 hash_level_offsets,
3935 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003936
3937 # Generate HashtreeDescriptor with details about the tree we
3938 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003939 if no_hashtree:
3940 tree_size = 0
3941 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003942 ht_desc = AvbHashtreeDescriptor()
3943 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003944 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003945 ht_desc.tree_offset = tree_offset
3946 ht_desc.tree_size = tree_size
3947 ht_desc.data_block_size = block_size
3948 ht_desc.hash_block_size = block_size
3949 ht_desc.hash_algorithm = hash_algorithm
3950 ht_desc.partition_name = partition_name
3951 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003952 if do_not_use_ab:
3953 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3954 if not use_persistent_root_digest:
3955 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003956
David Zeuthen09692692016-09-30 16:16:40 -04003957 # Write the hash tree
3958 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3959 len(hash_tree))
3960 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3961 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003962 len_hashtree_and_fec = len(hash_tree_with_padding)
3963
3964 # Generate FEC codes, if requested.
3965 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003966 if no_hashtree:
3967 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003968 else:
3969 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003970 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3971 len(fec_data))
3972 fec_data_with_padding = fec_data + '\0'*padding_needed
3973 fec_offset = image.image_size
3974 image.append_raw(fec_data_with_padding)
3975 len_hashtree_and_fec += len(fec_data_with_padding)
3976 # Update the hashtree descriptor.
3977 ht_desc.fec_num_roots = fec_num_roots
3978 ht_desc.fec_offset = fec_offset
3979 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003980
David Zeuthen73f2afa2017-05-17 16:54:11 -04003981 ht_desc_to_setup = None
3982 if setup_as_rootfs_from_kernel:
3983 ht_desc_to_setup = ht_desc
3984
David Zeuthena4fee8b2016-08-22 15:20:43 -04003985 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003986 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003987 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003988 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003989 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003990 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003991 include_descriptors_from_image, signing_helper,
3992 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003993 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003994 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3995 len(vbmeta_blob))
3996 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003997
David Zeuthend247fcb2017-02-16 12:09:27 -05003998 # Write vbmeta blob, if requested.
3999 if output_vbmeta_image:
4000 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04004001
David Zeuthend247fcb2017-02-16 12:09:27 -05004002 # Append vbmeta blob and footer, unless requested not to.
4003 if not do_not_append_vbmeta_image:
4004 image.append_raw(vbmeta_blob_with_padding)
4005
4006 # Now insert a DONT_CARE chunk with enough bytes such that the
4007 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004008 if partition_size > 0:
4009 image.append_dont_care(partition_size - image.image_size -
4010 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05004011
4012 # Generate the Footer that tells where the VBMeta footer
4013 # is. Also put enough padding in the front of the footer since
4014 # we'll write out an entire block.
4015 footer = AvbFooter()
4016 footer.original_image_size = original_image_size
4017 footer.vbmeta_offset = vbmeta_offset
4018 footer.vbmeta_size = len(vbmeta_blob)
4019 footer_blob = footer.encode()
4020 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
4021 footer_blob)
4022 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004023
David Zeuthen21e95262016-07-27 17:58:40 -04004024 except:
David Zeuthen09692692016-09-30 16:16:40 -04004025 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04004026 image.truncate(original_image_size)
4027 raise
4028
David Zeuthenc68f0822017-03-31 17:22:35 -04004029 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004030 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004031 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004032 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08004033 """Implements the 'make_atx_certificate' command.
4034
4035 Android Things certificates are required for Android Things public key
4036 metadata. They chain the vbmeta signing key for a particular product back to
4037 a fused, permanent root key. These certificates are fixed-length and fixed-
4038 format with the explicit goal of not parsing ASN.1 in bootloader code.
4039
4040 Arguments:
4041 output: Certificate will be written to this file on success.
4042 authority_key_path: A PEM file path with the authority private key.
4043 If None, then a certificate will be created without a
4044 signature. The signature can be created out-of-band
4045 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04004046 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08004047 subject_key_version: A 64-bit version value. If this is None, the number
4048 of seconds since the epoch is used.
4049 subject: A subject identifier. For Product Signing Key certificates this
4050 should be the same Product ID found in the permanent attributes.
4051 is_intermediate_authority: True if the certificate is for an intermediate
4052 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08004053 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08004054 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04004055 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08004056 """
4057 signed_data = bytearray()
4058 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004059 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004060 hasher = hashlib.sha256()
4061 hasher.update(subject)
4062 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08004063 if not usage:
4064 usage = 'com.google.android.things.vboot'
4065 if is_intermediate_authority:
4066 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08004067 hasher = hashlib.sha256()
4068 hasher.update(usage)
4069 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07004070 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08004071 subject_key_version = int(time.time())
4072 signed_data.extend(struct.pack('<Q', subject_key_version))
4073 signature = bytearray()
4074 if authority_key_path:
4075 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08004076 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09004077 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08004078 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09004079 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08004080 hasher.update(signed_data)
4081 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04004082 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4083 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09004084 alg.signature_num_bytes, authority_key_path,
4085 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08004086 output.write(signed_data)
4087 output.write(signature)
4088
David Zeuthenc68f0822017-03-31 17:22:35 -04004089 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08004090 product_id):
4091 """Implements the 'make_atx_permanent_attributes' command.
4092
4093 Android Things permanent attributes are designed to be permanent for a
4094 particular product and a hash of these attributes should be fused into
4095 hardware to enforce this.
4096
4097 Arguments:
4098 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04004099 root_authority_key_path: Path to a PEM or DER public key for
4100 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08004101 product_id: A 16-byte Product ID.
4102
4103 Raises:
4104 AvbError: If an argument is incorrect.
4105 """
Darren Krahn43e12d82017-02-24 16:26:31 -08004106 EXPECTED_PRODUCT_ID_SIZE = 16
4107 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004108 raise AvbError('Invalid Product ID length.')
4109 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04004110 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08004111 output.write(product_id)
4112
4113 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08004114 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08004115 """Implements the 'make_atx_metadata' command.
4116
4117 Android Things metadata are included in vbmeta images to facilitate
4118 verification. The output of this command can be used as the
4119 public_key_metadata argument to other commands.
4120
4121 Arguments:
4122 output: Metadata will be written to this file on success.
4123 intermediate_key_certificate: A certificate file as output by
4124 make_atx_certificate with
4125 is_intermediate_authority set to true.
4126 product_key_certificate: A certificate file as output by
4127 make_atx_certificate with
4128 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08004129
4130 Raises:
4131 AvbError: If an argument is incorrect.
4132 """
Darren Krahn43e12d82017-02-24 16:26:31 -08004133 EXPECTED_CERTIFICATE_SIZE = 1620
4134 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004135 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08004136 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08004137 raise AvbError('Invalid product key certificate length.')
4138 output.write(struct.pack('<I', 1)) # Format Version
4139 output.write(intermediate_key_certificate)
4140 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08004141
Darren Krahnfccd64e2018-01-16 17:39:35 -08004142 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
4143 unlock_key_certificate, challenge_path,
4144 unlock_key_path, signing_helper,
4145 signing_helper_with_files):
4146 """Implements the 'make_atx_unlock_credential' command.
4147
4148 Android Things unlock credentials can be used to authorize the unlock of AVB
4149 on a device. These credentials are presented to an Android Things bootloader
4150 via the fastboot interface in response to a 16-byte challenge. This method
4151 creates all fields of the credential except the challenge signature field
4152 (which is the last field) and can optionally create the challenge signature
4153 field as well if a challenge and the unlock_key_path is provided.
4154
4155 Arguments:
4156 output: The credential will be written to this file on success.
4157 intermediate_key_certificate: A certificate file as output by
4158 make_atx_certificate with
4159 is_intermediate_authority set to true.
4160 unlock_key_certificate: A certificate file as output by
4161 make_atx_certificate with
4162 is_intermediate_authority set to false and the
4163 usage set to
4164 'com.google.android.things.vboot.unlock'.
4165 challenge_path: [optional] A path to the challenge to sign.
4166 unlock_key_path: [optional] A PEM file path with the unlock private key.
4167 signing_helper: Program which signs a hash and returns the signature.
4168 signing_helper_with_files: Same as signing_helper but uses files instead.
4169
4170 Raises:
4171 AvbError: If an argument is incorrect.
4172 """
4173 EXPECTED_CERTIFICATE_SIZE = 1620
4174 EXPECTED_CHALLENGE_SIZE = 16
4175 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4176 raise AvbError('Invalid intermediate key certificate length.')
4177 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
4178 raise AvbError('Invalid product key certificate length.')
4179 challenge = bytearray()
4180 if challenge_path:
4181 with open(challenge_path, 'r') as f:
4182 challenge = f.read()
4183 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
4184 raise AvbError('Invalid unlock challenge length.')
4185 output.write(struct.pack('<I', 1)) # Format Version
4186 output.write(intermediate_key_certificate)
4187 output.write(unlock_key_certificate)
4188 if challenge_path and unlock_key_path:
4189 signature = bytearray()
4190 padding_and_hash = bytearray()
4191 algorithm_name = 'SHA512_RSA4096'
4192 alg = ALGORITHMS[algorithm_name]
4193 hasher = hashlib.sha512()
4194 padding_and_hash.extend(alg.padding)
4195 hasher.update(challenge)
4196 padding_and_hash.extend(hasher.digest())
4197 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
4198 algorithm_name,
4199 alg.signature_num_bytes, unlock_key_path,
4200 padding_and_hash))
4201 output.write(signature)
4202
David Zeuthen21e95262016-07-27 17:58:40 -04004203
4204def calc_hash_level_offsets(image_size, block_size, digest_size):
4205 """Calculate the offsets of all the hash-levels in a Merkle-tree.
4206
4207 Arguments:
4208 image_size: The size of the image to calculate a Merkle-tree for.
4209 block_size: The block size, e.g. 4096.
4210 digest_size: The size of each hash, e.g. 32 for SHA-256.
4211
4212 Returns:
4213 A tuple where the first argument is an array of offsets and the
4214 second is size of the tree, in bytes.
4215 """
4216 level_offsets = []
4217 level_sizes = []
4218 tree_size = 0
4219
4220 num_levels = 0
4221 size = image_size
4222 while size > block_size:
4223 num_blocks = (size + block_size - 1) / block_size
4224 level_size = round_to_multiple(num_blocks * digest_size, block_size)
4225
4226 level_sizes.append(level_size)
4227 tree_size += level_size
4228 num_levels += 1
4229
4230 size = level_size
4231
4232 for n in range(0, num_levels):
4233 offset = 0
4234 for m in range(n + 1, num_levels):
4235 offset += level_sizes[m]
4236 level_offsets.append(offset)
4237
David Zeuthena4fee8b2016-08-22 15:20:43 -04004238 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04004239
4240
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004241# See system/extras/libfec/include/fec/io.h for these definitions.
4242FEC_FOOTER_FORMAT = '<LLLLLQ32s'
4243FEC_MAGIC = 0xfecfecfe
4244
4245
4246def calc_fec_data_size(image_size, num_roots):
4247 """Calculates how much space FEC data will take.
4248
4249 Args:
4250 image_size: The size of the image.
4251 num_roots: Number of roots.
4252
4253 Returns:
4254 The number of bytes needed for FEC for an image of the given size
4255 and with the requested number of FEC roots.
4256
4257 Raises:
4258 ValueError: If output from the 'fec' tool is invalid.
4259
4260 """
4261 p = subprocess.Popen(
4262 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4263 stdout=subprocess.PIPE,
4264 stderr=subprocess.PIPE)
4265 (pout, perr) = p.communicate()
4266 retcode = p.wait()
4267 if retcode != 0:
4268 raise ValueError('Error invoking fec: {}'.format(perr))
4269 return int(pout)
4270
4271
4272def generate_fec_data(image_filename, num_roots):
4273 """Generate FEC codes for an image.
4274
4275 Args:
4276 image_filename: The filename of the image.
4277 num_roots: Number of roots.
4278
4279 Returns:
4280 The FEC data blob.
4281
4282 Raises:
4283 ValueError: If output from the 'fec' tool is invalid.
4284 """
4285 fec_tmpfile = tempfile.NamedTemporaryFile()
4286 subprocess.check_call(
4287 ['fec', '--encode', '--roots', str(num_roots), image_filename,
4288 fec_tmpfile.name],
4289 stderr=open(os.devnull))
4290 fec_data = fec_tmpfile.read()
4291 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4292 footer_data = fec_data[-footer_size:]
4293 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4294 footer_data)
4295 if magic != FEC_MAGIC:
4296 raise ValueError('Unexpected magic in FEC footer')
4297 return fec_data[0:fec_size]
4298
4299
David Zeuthen21e95262016-07-27 17:58:40 -04004300def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04004301 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04004302 """Generates a Merkle-tree for a file.
4303
4304 Args:
4305 image: The image, as a file.
4306 image_size: The size of the image.
4307 block_size: The block size, e.g. 4096.
4308 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4309 salt: The salt to use.
4310 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04004311 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04004312 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04004313
4314 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004315 A tuple where the first element is the top-level hash and the
4316 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04004317 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04004318 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004319 hash_src_offset = 0
4320 hash_src_size = image_size
4321 level_num = 0
4322 while hash_src_size > block_size:
4323 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04004324 remaining = hash_src_size
4325 while remaining > 0:
4326 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004327 # Only read from the file for the first level - for subsequent
4328 # levels, access the array we're building.
4329 if level_num == 0:
4330 image.seek(hash_src_offset + hash_src_size - remaining)
4331 data = image.read(min(remaining, block_size))
4332 else:
4333 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4334 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04004335 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004336
4337 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04004338 if len(data) < block_size:
4339 hasher.update('\0' * (block_size - len(data)))
4340 level_output += hasher.digest()
4341 if digest_padding > 0:
4342 level_output += '\0' * digest_padding
4343
4344 padding_needed = (round_to_multiple(
4345 len(level_output), block_size) - len(level_output))
4346 level_output += '\0' * padding_needed
4347
David Zeuthena4fee8b2016-08-22 15:20:43 -04004348 # Copy level-output into resulting tree.
4349 offset = hash_level_offsets[level_num]
4350 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04004351
David Zeuthena4fee8b2016-08-22 15:20:43 -04004352 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04004353 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04004354 level_num += 1
4355
4356 hasher = hashlib.new(name=hash_alg_name, string=salt)
4357 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04004358 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04004359
4360
4361class AvbTool(object):
4362 """Object for avbtool command-line tool."""
4363
4364 def __init__(self):
4365 """Initializer method."""
4366 self.avb = Avb()
4367
4368 def _add_common_args(self, sub_parser):
4369 """Adds arguments used by several sub-commands.
4370
4371 Arguments:
4372 sub_parser: The parser to add arguments to.
4373 """
4374 sub_parser.add_argument('--algorithm',
4375 help='Algorithm to use (default: NONE)',
4376 metavar='ALGORITHM',
4377 default='NONE')
4378 sub_parser.add_argument('--key',
4379 help='Path to RSA private key file',
4380 metavar='KEY',
4381 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004382 sub_parser.add_argument('--signing_helper',
4383 help='Path to helper used for signing',
4384 metavar='APP',
4385 default=None,
4386 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004387 sub_parser.add_argument('--signing_helper_with_files',
4388 help='Path to helper used for signing using files',
4389 metavar='APP',
4390 default=None,
4391 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004392 sub_parser.add_argument('--public_key_metadata',
4393 help='Path to public key metadata file',
4394 metavar='KEY_METADATA',
4395 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004396 sub_parser.add_argument('--rollback_index',
4397 help='Rollback Index',
4398 type=parse_number,
4399 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004400 # This is used internally for unit tests. Do not include in --help output.
4401 sub_parser.add_argument('--internal_release_string',
4402 help=argparse.SUPPRESS)
4403 sub_parser.add_argument('--append_to_release_string',
4404 help='Text to append to release string',
4405 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004406 sub_parser.add_argument('--prop',
4407 help='Add property',
4408 metavar='KEY:VALUE',
4409 action='append')
4410 sub_parser.add_argument('--prop_from_file',
4411 help='Add property from file',
4412 metavar='KEY:PATH',
4413 action='append')
4414 sub_parser.add_argument('--kernel_cmdline',
4415 help='Add kernel cmdline',
4416 metavar='CMDLINE',
4417 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004418 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4419 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4420 # at some future point.
4421 sub_parser.add_argument('--setup_rootfs_from_kernel',
4422 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004423 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004424 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004425 type=argparse.FileType('rb'))
4426 sub_parser.add_argument('--include_descriptors_from_image',
4427 help='Include descriptors from image',
4428 metavar='IMAGE',
4429 action='append',
4430 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004431 sub_parser.add_argument('--print_required_libavb_version',
4432 help=('Don\'t store the footer - '
4433 'instead calculate the required libavb '
4434 'version for the given options.'),
4435 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004436 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4437 sub_parser.add_argument('--chain_partition',
4438 help='Allow signed integrity-data for partition',
4439 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4440 action='append')
4441 sub_parser.add_argument('--flags',
4442 help='VBMeta flags',
4443 type=parse_number,
4444 default=0)
4445 sub_parser.add_argument('--set_hashtree_disabled_flag',
4446 help='Set the HASHTREE_DISABLED flag',
4447 action='store_true')
4448
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004449 def _add_common_footer_args(self, sub_parser):
4450 """Adds arguments used by add_*_footer sub-commands.
4451
4452 Arguments:
4453 sub_parser: The parser to add arguments to.
4454 """
4455 sub_parser.add_argument('--use_persistent_digest',
4456 help='Use a persistent digest on device instead of '
4457 'storing the digest in the descriptor. This '
4458 'cannot be used with A/B so must be combined '
4459 'with --do_not_use_ab when an A/B suffix is '
4460 'expected at runtime.',
4461 action='store_true')
4462 sub_parser.add_argument('--do_not_use_ab',
4463 help='The partition does not use A/B even when an '
4464 'A/B suffix is present. This must not be used '
4465 'for vbmeta or chained partitions.',
4466 action='store_true')
4467
David Zeuthena5fd3a42017-02-27 16:38:54 -05004468 def _fixup_common_args(self, args):
4469 """Common fixups needed by subcommands.
4470
4471 Arguments:
4472 args: Arguments to modify.
4473
4474 Returns:
4475 The modified arguments.
4476 """
4477 if args.set_hashtree_disabled_flag:
4478 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4479 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004480
4481 def run(self, argv):
4482 """Command-line processor.
4483
4484 Arguments:
4485 argv: Pass sys.argv from main.
4486 """
4487 parser = argparse.ArgumentParser()
4488 subparsers = parser.add_subparsers(title='subcommands')
4489
4490 sub_parser = subparsers.add_parser('version',
4491 help='Prints version of avbtool.')
4492 sub_parser.set_defaults(func=self.version)
4493
4494 sub_parser = subparsers.add_parser('extract_public_key',
4495 help='Extract public key.')
4496 sub_parser.add_argument('--key',
4497 help='Path to RSA private key file',
4498 required=True)
4499 sub_parser.add_argument('--output',
4500 help='Output file name',
4501 type=argparse.FileType('wb'),
4502 required=True)
4503 sub_parser.set_defaults(func=self.extract_public_key)
4504
Jan Monsch77cd2022019-12-10 17:18:04 +01004505 sub_parser = subparsers.add_parser(
4506 'icp_test_suite',
4507 help='Test suite for ICP related functionality.')
Dan Austina7bc4962019-12-02 13:26:08 -08004508 sub_parser.set_defaults(func=self.icp_test_suite)
4509
David Zeuthen21e95262016-07-27 17:58:40 -04004510 sub_parser = subparsers.add_parser('make_vbmeta_image',
4511 help='Makes a vbmeta image.')
4512 sub_parser.add_argument('--output',
4513 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004514 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004515 sub_parser.add_argument('--padding_size',
4516 metavar='NUMBER',
4517 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004518 'its size is a multiple of NUMBER '
4519 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004520 type=parse_number,
4521 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004522 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004523 sub_parser.set_defaults(func=self.make_vbmeta_image)
4524
4525 sub_parser = subparsers.add_parser('add_hash_footer',
4526 help='Add hashes and footer to image.')
4527 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004528 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004529 type=argparse.FileType('rab+'))
4530 sub_parser.add_argument('--partition_size',
4531 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004532 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004533 sub_parser.add_argument('--partition_name',
4534 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004535 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004536 sub_parser.add_argument('--hash_algorithm',
4537 help='Hash algorithm to use (default: sha256)',
4538 default='sha256')
4539 sub_parser.add_argument('--salt',
4540 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004541 sub_parser.add_argument('--calc_max_image_size',
4542 help=('Don\'t store the footer - '
4543 'instead calculate the maximum image size '
4544 'leaving enough room for metadata with '
4545 'the given partition size.'),
4546 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004547 sub_parser.add_argument('--output_vbmeta_image',
4548 help='Also write vbmeta struct to file',
4549 type=argparse.FileType('wb'))
4550 sub_parser.add_argument('--do_not_append_vbmeta_image',
4551 help=('Do not append vbmeta struct or footer '
4552 'to the image'),
4553 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004554 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004555 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004556 sub_parser.set_defaults(func=self.add_hash_footer)
4557
David Zeuthenb1b994d2017-03-06 18:01:31 -05004558 sub_parser = subparsers.add_parser('append_vbmeta_image',
4559 help='Append vbmeta image to image.')
4560 sub_parser.add_argument('--image',
4561 help='Image to append vbmeta blob to',
4562 type=argparse.FileType('rab+'))
4563 sub_parser.add_argument('--partition_size',
4564 help='Partition size',
4565 type=parse_number,
4566 required=True)
4567 sub_parser.add_argument('--vbmeta_image',
4568 help='Image with vbmeta blob to append',
4569 type=argparse.FileType('rb'))
4570 sub_parser.set_defaults(func=self.append_vbmeta_image)
4571
Jan Monscheeb28b62019-12-05 16:17:09 +01004572 sub_parser = subparsers.add_parser(
4573 'add_hashtree_footer',
4574 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004575 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004576 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004577 type=argparse.FileType('rab+'))
4578 sub_parser.add_argument('--partition_size',
4579 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004580 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004581 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004582 sub_parser.add_argument('--partition_name',
4583 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004584 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004585 sub_parser.add_argument('--hash_algorithm',
4586 help='Hash algorithm to use (default: sha1)',
4587 default='sha1')
4588 sub_parser.add_argument('--salt',
4589 help='Salt in hex (default: /dev/urandom)')
4590 sub_parser.add_argument('--block_size',
4591 help='Block size (default: 4096)',
4592 type=parse_number,
4593 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004594 # TODO(zeuthen): The --generate_fec option was removed when we
4595 # moved to generating FEC by default. To avoid breaking existing
4596 # users needing to transition we simply just print a warning below
4597 # in add_hashtree_footer(). Remove this option and the warning at
4598 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004599 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004600 help=argparse.SUPPRESS,
4601 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004602 sub_parser.add_argument(
4603 '--do_not_generate_fec',
4604 help='Do not generate forward-error-correction codes',
4605 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004606 sub_parser.add_argument('--fec_num_roots',
4607 help='Number of roots for FEC (default: 2)',
4608 type=parse_number,
4609 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004610 sub_parser.add_argument('--calc_max_image_size',
4611 help=('Don\'t store the hashtree or footer - '
4612 'instead calculate the maximum image size '
4613 'leaving enough room for hashtree '
4614 'and metadata with the given partition '
4615 'size.'),
4616 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004617 sub_parser.add_argument('--output_vbmeta_image',
4618 help='Also write vbmeta struct to file',
4619 type=argparse.FileType('wb'))
4620 sub_parser.add_argument('--do_not_append_vbmeta_image',
4621 help=('Do not append vbmeta struct or footer '
4622 'to the image'),
4623 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004624 # This is different from --setup_rootfs_from_kernel insofar that
4625 # it doesn't take an IMAGE, the generated cmdline will be for the
4626 # hashtree we're adding.
4627 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4628 action='store_true',
4629 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004630 sub_parser.add_argument('--no_hashtree',
4631 action='store_true',
4632 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004633 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004634 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004635 sub_parser.set_defaults(func=self.add_hashtree_footer)
4636
4637 sub_parser = subparsers.add_parser('erase_footer',
4638 help='Erase footer from an image.')
4639 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004640 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004641 type=argparse.FileType('rwb+'),
4642 required=True)
4643 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004644 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004645 action='store_true')
4646 sub_parser.set_defaults(func=self.erase_footer)
4647
David Zeuthen1394f762019-04-30 10:20:11 -04004648 sub_parser = subparsers.add_parser('zero_hashtree',
4649 help='Zero out hashtree and FEC data.')
4650 sub_parser.add_argument('--image',
4651 help='Image with a footer',
4652 type=argparse.FileType('rwb+'),
4653 required=True)
4654 sub_parser.set_defaults(func=self.zero_hashtree)
4655
Jan Monscheeb28b62019-12-05 16:17:09 +01004656 sub_parser = subparsers.add_parser(
4657 'extract_vbmeta_image',
4658 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004659 sub_parser.add_argument('--image',
4660 help='Image with footer',
4661 type=argparse.FileType('rb'),
4662 required=True)
4663 sub_parser.add_argument('--output',
4664 help='Output file name',
4665 type=argparse.FileType('wb'))
4666 sub_parser.add_argument('--padding_size',
4667 metavar='NUMBER',
4668 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004669 'its size is a multiple of NUMBER '
4670 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004671 type=parse_number,
4672 default=0)
4673 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4674
David Zeuthen2bc232b2017-04-19 14:25:19 -04004675 sub_parser = subparsers.add_parser('resize_image',
4676 help='Resize image with a footer.')
4677 sub_parser.add_argument('--image',
4678 help='Image with a footer',
4679 type=argparse.FileType('rwb+'),
4680 required=True)
4681 sub_parser.add_argument('--partition_size',
4682 help='New partition size',
4683 type=parse_number)
4684 sub_parser.set_defaults(func=self.resize_image)
4685
David Zeuthen21e95262016-07-27 17:58:40 -04004686 sub_parser = subparsers.add_parser(
4687 'info_image',
4688 help='Show information about vbmeta or footer.')
4689 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004690 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004691 type=argparse.FileType('rb'),
4692 required=True)
4693 sub_parser.add_argument('--output',
4694 help='Write info to file',
4695 type=argparse.FileType('wt'),
4696 default=sys.stdout)
4697 sub_parser.set_defaults(func=self.info_image)
4698
David Zeuthenb623d8b2017-04-04 16:05:53 -04004699 sub_parser = subparsers.add_parser(
4700 'verify_image',
4701 help='Verify an image.')
4702 sub_parser.add_argument('--image',
4703 help='Image to verify',
4704 type=argparse.FileType('rb'),
4705 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004706 sub_parser.add_argument('--key',
4707 help='Check embedded public key matches KEY',
4708 metavar='KEY',
4709 required=False)
4710 sub_parser.add_argument('--expected_chain_partition',
4711 help='Expected chain partition',
4712 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4713 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004714 sub_parser.add_argument(
4715 '--follow_chain_partitions',
4716 help=('Follows chain partitions even when not '
4717 'specified with the --expected_chain_partition option'),
4718 action='store_true')
4719 sub_parser.add_argument(
4720 '--accept_zeroed_hashtree',
4721 help=('Accept images where the hashtree or FEC data is zeroed out'),
4722 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004723 sub_parser.set_defaults(func=self.verify_image)
4724
David Zeuthenb8643c02018-05-17 17:21:18 -04004725 sub_parser = subparsers.add_parser(
4726 'calculate_vbmeta_digest',
4727 help='Calculate vbmeta digest.')
4728 sub_parser.add_argument('--image',
4729 help='Image to calculate digest for',
4730 type=argparse.FileType('rb'),
4731 required=True)
4732 sub_parser.add_argument('--hash_algorithm',
4733 help='Hash algorithm to use (default: sha256)',
4734 default='sha256')
4735 sub_parser.add_argument('--output',
4736 help='Write hex digest to file (default: stdout)',
4737 type=argparse.FileType('wt'),
4738 default=sys.stdout)
4739 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4740
David Zeuthenf7d2e752018-09-20 13:30:41 -04004741 sub_parser = subparsers.add_parser(
4742 'calculate_kernel_cmdline',
4743 help='Calculate kernel cmdline.')
4744 sub_parser.add_argument('--image',
4745 help='Image to calculate kernel cmdline for',
4746 type=argparse.FileType('rb'),
4747 required=True)
4748 sub_parser.add_argument('--hashtree_disabled',
4749 help='Return the cmdline for hashtree disabled',
4750 action='store_true')
4751 sub_parser.add_argument('--output',
4752 help='Write cmdline to file (default: stdout)',
4753 type=argparse.FileType('wt'),
4754 default=sys.stdout)
4755 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4756
David Zeuthen8b6973b2016-09-20 12:39:49 -04004757 sub_parser = subparsers.add_parser('set_ab_metadata',
4758 help='Set A/B metadata.')
4759 sub_parser.add_argument('--misc_image',
4760 help=('The misc image to modify. If the image does '
4761 'not exist, it will be created.'),
4762 type=argparse.FileType('r+b'),
4763 required=True)
4764 sub_parser.add_argument('--slot_data',
4765 help=('Slot data of the form "priority", '
4766 '"tries_remaining", "sucessful_boot" for '
4767 'slot A followed by the same for slot B, '
4768 'separated by colons. The default value '
4769 'is 15:7:0:14:7:0.'),
4770 default='15:7:0:14:7:0')
4771 sub_parser.set_defaults(func=self.set_ab_metadata)
4772
Darren Krahn147b08d2016-12-20 16:38:29 -08004773 sub_parser = subparsers.add_parser(
4774 'make_atx_certificate',
4775 help='Create an Android Things eXtension (ATX) certificate.')
4776 sub_parser.add_argument('--output',
4777 help='Write certificate to file',
4778 type=argparse.FileType('wb'),
4779 default=sys.stdout)
4780 sub_parser.add_argument('--subject',
4781 help=('Path to subject file'),
4782 type=argparse.FileType('rb'),
4783 required=True)
4784 sub_parser.add_argument('--subject_key',
4785 help=('Path to subject RSA public key file'),
4786 type=argparse.FileType('rb'),
4787 required=True)
4788 sub_parser.add_argument('--subject_key_version',
4789 help=('Version of the subject key'),
4790 type=parse_number,
4791 required=False)
4792 sub_parser.add_argument('--subject_is_intermediate_authority',
4793 help=('Generate an intermediate authority '
4794 'certificate'),
4795 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004796 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004797 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004798 'string'),
4799 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004800 sub_parser.add_argument('--authority_key',
4801 help='Path to authority RSA private key file',
4802 required=False)
4803 sub_parser.add_argument('--signing_helper',
4804 help='Path to helper used for signing',
4805 metavar='APP',
4806 default=None,
4807 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004808 sub_parser.add_argument('--signing_helper_with_files',
4809 help='Path to helper used for signing using files',
4810 metavar='APP',
4811 default=None,
4812 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004813 sub_parser.set_defaults(func=self.make_atx_certificate)
4814
4815 sub_parser = subparsers.add_parser(
4816 'make_atx_permanent_attributes',
4817 help='Create Android Things eXtension (ATX) permanent attributes.')
4818 sub_parser.add_argument('--output',
4819 help='Write attributes to file',
4820 type=argparse.FileType('wb'),
4821 default=sys.stdout)
4822 sub_parser.add_argument('--root_authority_key',
4823 help='Path to authority RSA public key file',
4824 type=argparse.FileType('rb'),
4825 required=True)
4826 sub_parser.add_argument('--product_id',
4827 help=('Path to Product ID file'),
4828 type=argparse.FileType('rb'),
4829 required=True)
4830 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4831
4832 sub_parser = subparsers.add_parser(
4833 'make_atx_metadata',
4834 help='Create Android Things eXtension (ATX) metadata.')
4835 sub_parser.add_argument('--output',
4836 help='Write metadata to file',
4837 type=argparse.FileType('wb'),
4838 default=sys.stdout)
4839 sub_parser.add_argument('--intermediate_key_certificate',
4840 help='Path to intermediate key certificate file',
4841 type=argparse.FileType('rb'),
4842 required=True)
4843 sub_parser.add_argument('--product_key_certificate',
4844 help='Path to product key certificate file',
4845 type=argparse.FileType('rb'),
4846 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004847 sub_parser.set_defaults(func=self.make_atx_metadata)
4848
Darren Krahnfccd64e2018-01-16 17:39:35 -08004849 sub_parser = subparsers.add_parser(
4850 'make_atx_unlock_credential',
4851 help='Create an Android Things eXtension (ATX) unlock credential.')
4852 sub_parser.add_argument('--output',
4853 help='Write credential to file',
4854 type=argparse.FileType('wb'),
4855 default=sys.stdout)
4856 sub_parser.add_argument('--intermediate_key_certificate',
4857 help='Path to intermediate key certificate file',
4858 type=argparse.FileType('rb'),
4859 required=True)
4860 sub_parser.add_argument('--unlock_key_certificate',
4861 help='Path to unlock key certificate file',
4862 type=argparse.FileType('rb'),
4863 required=True)
4864 sub_parser.add_argument('--challenge',
4865 help='Path to the challenge to sign (optional). If '
4866 'this is not provided the challenge signature '
4867 'field is omitted and can be concatenated '
4868 'later.',
4869 required=False)
4870 sub_parser.add_argument('--unlock_key',
4871 help='Path to unlock key (optional). Must be '
4872 'provided if using --challenge.',
4873 required=False)
4874 sub_parser.add_argument('--signing_helper',
4875 help='Path to helper used for signing',
4876 metavar='APP',
4877 default=None,
4878 required=False)
4879 sub_parser.add_argument('--signing_helper_with_files',
4880 help='Path to helper used for signing using files',
4881 metavar='APP',
4882 default=None,
4883 required=False)
4884 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4885
David Zeuthen21e95262016-07-27 17:58:40 -04004886 args = parser.parse_args(argv[1:])
4887 try:
4888 args.func(args)
4889 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04004890 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04004891 sys.exit(1)
4892
4893 def version(self, _):
4894 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05004895 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04004896
4897 def extract_public_key(self, args):
4898 """Implements the 'extract_public_key' sub-command."""
4899 self.avb.extract_public_key(args.key, args.output)
4900
Dan Austina7bc4962019-12-02 13:26:08 -08004901 def icp_test_suite(self, _):
4902 """Implements the 'icp_test_suite' sub-command."""
4903 return self.avb.icp_test_suite()
4904
David Zeuthen21e95262016-07-27 17:58:40 -04004905 def make_vbmeta_image(self, args):
4906 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004907 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004908 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004909 args.algorithm, args.key,
4910 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004911 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004912 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004913 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004914 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004915 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004916 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004917 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004918 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004919 args.print_required_libavb_version,
4920 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004921
David Zeuthenb1b994d2017-03-06 18:01:31 -05004922 def append_vbmeta_image(self, args):
4923 """Implements the 'append_vbmeta_image' sub-command."""
4924 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4925 args.partition_size)
4926
David Zeuthen21e95262016-07-27 17:58:40 -04004927 def add_hash_footer(self, args):
4928 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004929 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004930 self.avb.add_hash_footer(args.image.name if args.image else None,
4931 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004932 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004933 args.salt, args.chain_partition, args.algorithm,
4934 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004935 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004936 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004937 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004938 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004939 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004940 args.calc_max_image_size,
4941 args.signing_helper,
4942 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004943 args.internal_release_string,
4944 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004945 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004946 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004947 args.print_required_libavb_version,
4948 args.use_persistent_digest,
4949 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004950
4951 def add_hashtree_footer(self, args):
4952 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004953 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004954 # TODO(zeuthen): Remove when removing support for the
4955 # '--generate_fec' option above.
4956 if args.generate_fec:
4957 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4958 'is now generated by default. Use the option '
4959 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004960 self.avb.add_hashtree_footer(
4961 args.image.name if args.image else None,
4962 args.partition_size,
4963 args.partition_name,
4964 not args.do_not_generate_fec, args.fec_num_roots,
4965 args.hash_algorithm, args.block_size,
4966 args.salt, args.chain_partition, args.algorithm,
4967 args.key, args.public_key_metadata,
4968 args.rollback_index, args.flags, args.prop,
4969 args.prop_from_file,
4970 args.kernel_cmdline,
4971 args.setup_rootfs_from_kernel,
4972 args.setup_as_rootfs_from_kernel,
4973 args.include_descriptors_from_image,
4974 args.calc_max_image_size,
4975 args.signing_helper,
4976 args.signing_helper_with_files,
4977 args.internal_release_string,
4978 args.append_to_release_string,
4979 args.output_vbmeta_image,
4980 args.do_not_append_vbmeta_image,
4981 args.print_required_libavb_version,
4982 args.use_persistent_digest,
4983 args.do_not_use_ab,
4984 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004985
David Zeuthen21e95262016-07-27 17:58:40 -04004986 def erase_footer(self, args):
4987 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004988 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004989
David Zeuthen1394f762019-04-30 10:20:11 -04004990 def zero_hashtree(self, args):
4991 """Implements the 'zero_hashtree' sub-command."""
4992 self.avb.zero_hashtree(args.image.name)
4993
David Zeuthen49936b42018-08-07 17:38:58 -04004994 def extract_vbmeta_image(self, args):
4995 """Implements the 'extract_vbmeta_image' sub-command."""
4996 self.avb.extract_vbmeta_image(args.output, args.image.name,
4997 args.padding_size)
4998
David Zeuthen2bc232b2017-04-19 14:25:19 -04004999 def resize_image(self, args):
5000 """Implements the 'resize_image' sub-command."""
5001 self.avb.resize_image(args.image.name, args.partition_size)
5002
David Zeuthen8b6973b2016-09-20 12:39:49 -04005003 def set_ab_metadata(self, args):
5004 """Implements the 'set_ab_metadata' sub-command."""
5005 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
5006
David Zeuthen21e95262016-07-27 17:58:40 -04005007 def info_image(self, args):
5008 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04005009 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04005010
David Zeuthenb623d8b2017-04-04 16:05:53 -04005011 def verify_image(self, args):
5012 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04005013 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05005014 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04005015 args.follow_chain_partitions,
5016 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04005017
David Zeuthenb8643c02018-05-17 17:21:18 -04005018 def calculate_vbmeta_digest(self, args):
5019 """Implements the 'calculate_vbmeta_digest' sub-command."""
5020 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
5021 args.output)
5022
David Zeuthenf7d2e752018-09-20 13:30:41 -04005023 def calculate_kernel_cmdline(self, args):
5024 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01005025 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
5026 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04005027
Darren Krahn147b08d2016-12-20 16:38:29 -08005028 def make_atx_certificate(self, args):
5029 """Implements the 'make_atx_certificate' sub-command."""
5030 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04005031 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005032 args.subject_key_version,
5033 args.subject.read(),
5034 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08005035 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04005036 args.signing_helper,
5037 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08005038
5039 def make_atx_permanent_attributes(self, args):
5040 """Implements the 'make_atx_permanent_attributes' sub-command."""
5041 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04005042 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08005043 args.product_id.read())
5044
5045 def make_atx_metadata(self, args):
5046 """Implements the 'make_atx_metadata' sub-command."""
5047 self.avb.make_atx_metadata(args.output,
5048 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08005049 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08005050
Darren Krahnfccd64e2018-01-16 17:39:35 -08005051 def make_atx_unlock_credential(self, args):
5052 """Implements the 'make_atx_unlock_credential' sub-command."""
5053 self.avb.make_atx_unlock_credential(
5054 args.output,
5055 args.intermediate_key_certificate.read(),
5056 args.unlock_key_certificate.read(),
5057 args.challenge,
5058 args.unlock_key,
5059 args.signing_helper,
5060 args.signing_helper_with_files)
5061
David Zeuthen21e95262016-07-27 17:58:40 -04005062
5063if __name__ == '__main__':
5064 tool = AvbTool()
5065 tool.run(sys.argv)