blob: 0bc62ad20f343168bffef66027ad9956b7e87cce [file] [log] [blame]
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Verifying the integrity of a Chrome OS update payload.
6
7This module is used internally by the main Payload class for verifying the
8integrity of an update payload. The interface for invoking the checks is as
9follows:
10
11 checker = PayloadChecker(payload)
12 checker.Run(...)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080013"""
14
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080015from __future__ import print_function
16
Gilad Arnold553b0ec2013-01-26 01:00:39 -080017import array
18import base64
19import hashlib
Gilad Arnoldcb638912013-06-24 04:57:11 -070020import itertools
Gilad Arnold9b90c932013-05-22 17:12:56 -070021import os
Gilad Arnold553b0ec2013-01-26 01:00:39 -080022import subprocess
23
24import common
Gilad Arnoldcb638912013-06-24 04:57:11 -070025import error
Gilad Arnold553b0ec2013-01-26 01:00:39 -080026import format_utils
27import histogram
28import update_metadata_pb2
29
30
31#
Gilad Arnold9b90c932013-05-22 17:12:56 -070032# Constants.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080033#
Gilad Arnoldcb638912013-06-24 04:57:11 -070034
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070035_CHECK_DST_PSEUDO_EXTENTS = 'dst-pseudo-extents'
36_CHECK_MOVE_SAME_SRC_DST_BLOCK = 'move-same-src-dst-block'
37_CHECK_PAYLOAD_SIG = 'payload-sig'
38CHECKS_TO_DISABLE = (
Gilad Arnold382df5c2013-05-03 12:49:28 -070039 _CHECK_DST_PSEUDO_EXTENTS,
40 _CHECK_MOVE_SAME_SRC_DST_BLOCK,
41 _CHECK_PAYLOAD_SIG,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070042)
43
Gilad Arnold553b0ec2013-01-26 01:00:39 -080044_TYPE_FULL = 'full'
45_TYPE_DELTA = 'delta'
46
47_DEFAULT_BLOCK_SIZE = 4096
48
Gilad Arnold9b90c932013-05-22 17:12:56 -070049_DEFAULT_PUBKEY_BASE_NAME = 'update-payload-key.pub.pem'
50_DEFAULT_PUBKEY_FILE_NAME = os.path.join(os.path.dirname(__file__),
51 _DEFAULT_PUBKEY_BASE_NAME)
52
Gilad Arnold553b0ec2013-01-26 01:00:39 -080053
54#
55# Helper functions.
56#
Gilad Arnoldcb638912013-06-24 04:57:11 -070057
Gilad Arnold553b0ec2013-01-26 01:00:39 -080058def _IsPowerOfTwo(val):
59 """Returns True iff val is a power of two."""
60 return val > 0 and (val & (val - 1)) == 0
61
62
63def _AddFormat(format_func, value):
64 """Adds a custom formatted representation to ordinary string representation.
65
66 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -070067 format_func: A value formatter.
68 value: Value to be formatted and returned.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080069
Gilad Arnold553b0ec2013-01-26 01:00:39 -080070 Returns:
71 A string 'x (y)' where x = str(value) and y = format_func(value).
Gilad Arnold553b0ec2013-01-26 01:00:39 -080072 """
Gilad Arnold6a3a3872013-10-04 18:18:45 -070073 ret = str(value)
74 formatted_str = format_func(value)
75 if formatted_str:
76 ret += ' (%s)' % formatted_str
77 return ret
Gilad Arnold553b0ec2013-01-26 01:00:39 -080078
79
80def _AddHumanReadableSize(size):
81 """Adds a human readable representation to a byte size value."""
82 return _AddFormat(format_utils.BytesToHumanReadable, size)
83
84
85#
86# Payload report generator.
87#
Gilad Arnoldcb638912013-06-24 04:57:11 -070088
Gilad Arnold553b0ec2013-01-26 01:00:39 -080089class _PayloadReport(object):
90 """A payload report generator.
91
92 A report is essentially a sequence of nodes, which represent data points. It
93 is initialized to have a "global", untitled section. A node may be a
94 sub-report itself.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080095 """
96
Gilad Arnoldcb638912013-06-24 04:57:11 -070097 # Report nodes: Field, sub-report, section.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080098 class Node(object):
99 """A report node interface."""
100
101 @staticmethod
102 def _Indent(indent, line):
103 """Indents a line by a given indentation amount.
104
105 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700106 indent: The indentation amount.
107 line: The line content (string).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800108
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800109 Returns:
110 The properly indented line (string).
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800111 """
112 return '%*s%s' % (indent, '', line)
113
114 def GenerateLines(self, base_indent, sub_indent, curr_section):
115 """Generates the report lines for this node.
116
117 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700118 base_indent: Base indentation for each line.
119 sub_indent: Additional indentation for sub-nodes.
120 curr_section: The current report section object.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800121
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800122 Returns:
123 A pair consisting of a list of properly indented report lines and a new
124 current section object.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800125 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700126 raise NotImplementedError
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800127
128 class FieldNode(Node):
129 """A field report node, representing a (name, value) pair."""
130
131 def __init__(self, name, value, linebreak, indent):
132 super(_PayloadReport.FieldNode, self).__init__()
133 self.name = name
134 self.value = value
135 self.linebreak = linebreak
136 self.indent = indent
137
138 def GenerateLines(self, base_indent, sub_indent, curr_section):
139 """Generates a properly formatted 'name : value' entry."""
140 report_output = ''
141 if self.name:
142 report_output += self.name.ljust(curr_section.max_field_name_len) + ' :'
143 value_lines = str(self.value).splitlines()
144 if self.linebreak and self.name:
145 report_output += '\n' + '\n'.join(
146 ['%*s%s' % (self.indent, '', line) for line in value_lines])
147 else:
148 if self.name:
149 report_output += ' '
150 report_output += '%*s' % (self.indent, '')
151 cont_line_indent = len(report_output)
152 indented_value_lines = [value_lines[0]]
153 indented_value_lines.extend(['%*s%s' % (cont_line_indent, '', line)
154 for line in value_lines[1:]])
155 report_output += '\n'.join(indented_value_lines)
156
157 report_lines = [self._Indent(base_indent, line + '\n')
158 for line in report_output.split('\n')]
159 return report_lines, curr_section
160
161 class SubReportNode(Node):
162 """A sub-report node, representing a nested report."""
163
164 def __init__(self, title, report):
165 super(_PayloadReport.SubReportNode, self).__init__()
166 self.title = title
167 self.report = report
168
169 def GenerateLines(self, base_indent, sub_indent, curr_section):
170 """Recurse with indentation."""
171 report_lines = [self._Indent(base_indent, self.title + ' =>\n')]
172 report_lines.extend(self.report.GenerateLines(base_indent + sub_indent,
173 sub_indent))
174 return report_lines, curr_section
175
176 class SectionNode(Node):
177 """A section header node."""
178
179 def __init__(self, title=None):
180 super(_PayloadReport.SectionNode, self).__init__()
181 self.title = title
182 self.max_field_name_len = 0
183
184 def GenerateLines(self, base_indent, sub_indent, curr_section):
185 """Dump a title line, return self as the (new) current section."""
186 report_lines = []
187 if self.title:
188 report_lines.append(self._Indent(base_indent,
189 '=== %s ===\n' % self.title))
190 return report_lines, self
191
192 def __init__(self):
193 self.report = []
194 self.last_section = self.global_section = self.SectionNode()
195 self.is_finalized = False
196
197 def GenerateLines(self, base_indent, sub_indent):
198 """Generates the lines in the report, properly indented.
199
200 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700201 base_indent: The indentation used for root-level report lines.
202 sub_indent: The indentation offset used for sub-reports.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800203
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800204 Returns:
205 A list of indented report lines.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800206 """
207 report_lines = []
208 curr_section = self.global_section
209 for node in self.report:
210 node_report_lines, curr_section = node.GenerateLines(
211 base_indent, sub_indent, curr_section)
212 report_lines.extend(node_report_lines)
213
214 return report_lines
215
216 def Dump(self, out_file, base_indent=0, sub_indent=2):
217 """Dumps the report to a file.
218
219 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700220 out_file: File object to output the content to.
221 base_indent: Base indentation for report lines.
222 sub_indent: Added indentation for sub-reports.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800223 """
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800224 report_lines = self.GenerateLines(base_indent, sub_indent)
225 if report_lines and not self.is_finalized:
226 report_lines.append('(incomplete report)\n')
227
228 for line in report_lines:
229 out_file.write(line)
230
231 def AddField(self, name, value, linebreak=False, indent=0):
232 """Adds a field/value pair to the payload report.
233
234 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700235 name: The field's name.
236 value: The field's value.
237 linebreak: Whether the value should be printed on a new line.
238 indent: Amount of extra indent for each line of the value.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800239 """
240 assert not self.is_finalized
241 if name and self.last_section.max_field_name_len < len(name):
242 self.last_section.max_field_name_len = len(name)
243 self.report.append(self.FieldNode(name, value, linebreak, indent))
244
245 def AddSubReport(self, title):
246 """Adds and returns a sub-report with a title."""
247 assert not self.is_finalized
248 sub_report = self.SubReportNode(title, type(self)())
249 self.report.append(sub_report)
250 return sub_report.report
251
252 def AddSection(self, title):
253 """Adds a new section title."""
254 assert not self.is_finalized
255 self.last_section = self.SectionNode(title)
256 self.report.append(self.last_section)
257
258 def Finalize(self):
259 """Seals the report, marking it as complete."""
260 self.is_finalized = True
261
262
263#
264# Payload verification.
265#
Gilad Arnoldcb638912013-06-24 04:57:11 -0700266
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800267class PayloadChecker(object):
268 """Checking the integrity of an update payload.
269
270 This is a short-lived object whose purpose is to isolate the logic used for
271 verifying the integrity of an update payload.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800272 """
273
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700274 def __init__(self, payload, assert_type=None, block_size=0,
275 allow_unhashed=False, disabled_tests=()):
Gilad Arnold272a4992013-05-08 13:12:53 -0700276 """Initialize the checker.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700277
278 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700279 payload: The payload object to check.
280 assert_type: Assert that payload is either 'full' or 'delta' (optional).
281 block_size: Expected filesystem / payload block size (optional).
282 allow_unhashed: Allow operations with unhashed data blobs.
283 disabled_tests: Sequence of tests to disable.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700284 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700285 if not payload.is_init:
286 raise ValueError('Uninitialized update payload.')
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700287
288 # Set checker configuration.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800289 self.payload = payload
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700290 self.block_size = block_size if block_size else _DEFAULT_BLOCK_SIZE
291 if not _IsPowerOfTwo(self.block_size):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700292 raise error.PayloadError(
293 'Expected block (%d) size is not a power of two.' % self.block_size)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700294 if assert_type not in (None, _TYPE_FULL, _TYPE_DELTA):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700295 raise error.PayloadError('Invalid assert_type value (%r).' %
296 assert_type)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700297 self.payload_type = assert_type
298 self.allow_unhashed = allow_unhashed
299
300 # Disable specific tests.
301 self.check_dst_pseudo_extents = (
302 _CHECK_DST_PSEUDO_EXTENTS not in disabled_tests)
303 self.check_move_same_src_dst_block = (
304 _CHECK_MOVE_SAME_SRC_DST_BLOCK not in disabled_tests)
305 self.check_payload_sig = _CHECK_PAYLOAD_SIG not in disabled_tests
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800306
307 # Reset state; these will be assigned when the manifest is checked.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800308 self.sigs_offset = 0
309 self.sigs_size = 0
Gilad Arnold382df5c2013-05-03 12:49:28 -0700310 self.old_rootfs_fs_size = 0
311 self.old_kernel_fs_size = 0
312 self.new_rootfs_fs_size = 0
313 self.new_kernel_fs_size = 0
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800314
315 @staticmethod
316 def _CheckElem(msg, name, report, is_mandatory, is_submsg, convert=str,
317 msg_name=None, linebreak=False, indent=0):
318 """Adds an element from a protobuf message to the payload report.
319
320 Checks to see whether a message contains a given element, and if so adds
321 the element value to the provided report. A missing mandatory element
322 causes an exception to be raised.
323
324 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700325 msg: The message containing the element.
326 name: The name of the element.
327 report: A report object to add the element name/value to.
328 is_mandatory: Whether or not this element must be present.
329 is_submsg: Whether this element is itself a message.
330 convert: A function for converting the element value for reporting.
331 msg_name: The name of the message object (for error reporting).
332 linebreak: Whether the value report should induce a line break.
333 indent: Amount of indent used for reporting the value.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800334
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800335 Returns:
336 A pair consisting of the element value and the generated sub-report for
337 it (if the element is a sub-message, None otherwise). If the element is
338 missing, returns (None, None).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800339
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800340 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700341 error.PayloadError if a mandatory element is missing.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800342 """
343 if not msg.HasField(name):
344 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700345 raise error.PayloadError('%smissing mandatory %s %r.' %
346 (msg_name + ' ' if msg_name else '',
347 'sub-message' if is_submsg else 'field',
348 name))
349 return None, None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800350
351 value = getattr(msg, name)
352 if is_submsg:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700353 return value, report and report.AddSubReport(name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800354 else:
355 if report:
356 report.AddField(name, convert(value), linebreak=linebreak,
357 indent=indent)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700358 return value, None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800359
360 @staticmethod
361 def _CheckMandatoryField(msg, field_name, report, msg_name, convert=str,
362 linebreak=False, indent=0):
363 """Adds a mandatory field; returning first component from _CheckElem."""
364 return PayloadChecker._CheckElem(msg, field_name, report, True, False,
365 convert=convert, msg_name=msg_name,
366 linebreak=linebreak, indent=indent)[0]
367
368 @staticmethod
369 def _CheckOptionalField(msg, field_name, report, convert=str,
370 linebreak=False, indent=0):
371 """Adds an optional field; returning first component from _CheckElem."""
372 return PayloadChecker._CheckElem(msg, field_name, report, False, False,
373 convert=convert, linebreak=linebreak,
374 indent=indent)[0]
375
376 @staticmethod
377 def _CheckMandatorySubMsg(msg, submsg_name, report, msg_name):
378 """Adds a mandatory sub-message; wrapper for _CheckElem."""
379 return PayloadChecker._CheckElem(msg, submsg_name, report, True, True,
380 msg_name)
381
382 @staticmethod
383 def _CheckOptionalSubMsg(msg, submsg_name, report):
384 """Adds an optional sub-message; wrapper for _CheckElem."""
385 return PayloadChecker._CheckElem(msg, submsg_name, report, False, True)
386
387 @staticmethod
388 def _CheckPresentIff(val1, val2, name1, name2, obj_name):
389 """Checks that val1 is None iff val2 is None.
390
391 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700392 val1: first value to be compared.
393 val2: second value to be compared.
394 name1: name of object holding the first value.
395 name2: name of object holding the second value.
396 obj_name: Name of the object containing these values.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800397
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800398 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700399 error.PayloadError if assertion does not hold.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800400 """
401 if None in (val1, val2) and val1 is not val2:
402 present, missing = (name1, name2) if val2 is None else (name2, name1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700403 raise error.PayloadError('%r present without %r%s.' %
404 (present, missing,
405 ' in ' + obj_name if obj_name else ''))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800406
407 @staticmethod
408 def _Run(cmd, send_data=None):
409 """Runs a subprocess, returns its output.
410
411 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700412 cmd: Sequence of command-line argument for invoking the subprocess.
413 send_data: Data to feed to the process via its stdin.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800414
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800415 Returns:
416 A tuple containing the stdout and stderr output of the process.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800417 """
418 run_process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
419 stdout=subprocess.PIPE)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700420 try:
421 result = run_process.communicate(input=send_data)
422 finally:
423 exit_code = run_process.wait()
424
425 if exit_code:
426 raise RuntimeError('Subprocess %r failed with code %r.' %
427 (cmd, exit_code))
428
429 return result
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800430
431 @staticmethod
432 def _CheckSha256Signature(sig_data, pubkey_file_name, actual_hash, sig_name):
433 """Verifies an actual hash against a signed one.
434
435 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700436 sig_data: The raw signature data.
437 pubkey_file_name: Public key used for verifying signature.
438 actual_hash: The actual hash digest.
439 sig_name: Signature name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800440
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800441 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700442 error.PayloadError if signature could not be verified.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800443 """
444 if len(sig_data) != 256:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700445 raise error.PayloadError(
446 '%s: signature size (%d) not as expected (256).' %
447 (sig_name, len(sig_data)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800448 signed_data, _ = PayloadChecker._Run(
449 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', pubkey_file_name],
450 send_data=sig_data)
451
Gilad Arnold5502b562013-03-08 13:22:31 -0800452 if len(signed_data) != len(common.SIG_ASN1_HEADER) + 32:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700453 raise error.PayloadError('%s: unexpected signed data length (%d).' %
454 (sig_name, len(signed_data)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800455
Gilad Arnold5502b562013-03-08 13:22:31 -0800456 if not signed_data.startswith(common.SIG_ASN1_HEADER):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700457 raise error.PayloadError('%s: not containing standard ASN.1 prefix.' %
458 sig_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800459
Gilad Arnold5502b562013-03-08 13:22:31 -0800460 signed_hash = signed_data[len(common.SIG_ASN1_HEADER):]
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800461 if signed_hash != actual_hash:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700462 raise error.PayloadError(
463 '%s: signed hash (%s) different from actual (%s).' %
464 (sig_name, common.FormatSha256(signed_hash),
465 common.FormatSha256(actual_hash)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800466
467 @staticmethod
468 def _CheckBlocksFitLength(length, num_blocks, block_size, length_name,
469 block_name=None):
470 """Checks that a given length fits given block space.
471
472 This ensures that the number of blocks allocated is appropriate for the
473 length of the data residing in these blocks.
474
475 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700476 length: The actual length of the data.
477 num_blocks: The number of blocks allocated for it.
478 block_size: The size of each block in bytes.
479 length_name: Name of length (used for error reporting).
480 block_name: Name of block (used for error reporting).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800481
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800482 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700483 error.PayloadError if the aforementioned invariant is not satisfied.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800484 """
485 # Check: length <= num_blocks * block_size.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700486 if length > num_blocks * block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700487 raise error.PayloadError(
488 '%s (%d) > num %sblocks (%d) * block_size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800489 (length_name, length, block_name or '', num_blocks, block_size))
490
491 # Check: length > (num_blocks - 1) * block_size.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700492 if length <= (num_blocks - 1) * block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700493 raise error.PayloadError(
494 '%s (%d) <= (num %sblocks - 1 (%d)) * block_size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800495 (length_name, length, block_name or '', num_blocks - 1, block_size))
496
Gilad Arnold382df5c2013-05-03 12:49:28 -0700497 def _CheckManifest(self, report, rootfs_part_size=0, kernel_part_size=0):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800498 """Checks the payload manifest.
499
500 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700501 report: A report object to add to.
502 rootfs_part_size: Size of the rootfs partition in bytes.
503 kernel_part_size: Size of the kernel partition in bytes.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800504
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800505 Returns:
506 A tuple consisting of the partition block size used during the update
507 (integer), the signatures block offset and size.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800508
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800509 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700510 error.PayloadError if any of the checks fail.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800511 """
512 manifest = self.payload.manifest
513 report.AddSection('manifest')
514
515 # Check: block_size must exist and match the expected value.
516 actual_block_size = self._CheckMandatoryField(manifest, 'block_size',
517 report, 'manifest')
518 if actual_block_size != self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700519 raise error.PayloadError('Block_size (%d) not as expected (%d).' %
520 (actual_block_size, self.block_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800521
522 # Check: signatures_offset <==> signatures_size.
523 self.sigs_offset = self._CheckOptionalField(manifest, 'signatures_offset',
524 report)
525 self.sigs_size = self._CheckOptionalField(manifest, 'signatures_size',
526 report)
527 self._CheckPresentIff(self.sigs_offset, self.sigs_size,
528 'signatures_offset', 'signatures_size', 'manifest')
529
530 # Check: old_kernel_info <==> old_rootfs_info.
531 oki_msg, oki_report = self._CheckOptionalSubMsg(manifest,
532 'old_kernel_info', report)
533 ori_msg, ori_report = self._CheckOptionalSubMsg(manifest,
534 'old_rootfs_info', report)
535 self._CheckPresentIff(oki_msg, ori_msg, 'old_kernel_info',
536 'old_rootfs_info', 'manifest')
537 if oki_msg: # equivalently, ori_msg
538 # Assert/mark delta payload.
539 if self.payload_type == _TYPE_FULL:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700540 raise error.PayloadError(
541 'Apparent full payload contains old_{kernel,rootfs}_info.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800542 self.payload_type = _TYPE_DELTA
543
544 # Check: {size, hash} present in old_{kernel,rootfs}_info.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700545 self.old_kernel_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800546 oki_msg, 'size', oki_report, 'old_kernel_info')
547 self._CheckMandatoryField(oki_msg, 'hash', oki_report, 'old_kernel_info',
548 convert=common.FormatSha256)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700549 self.old_rootfs_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800550 ori_msg, 'size', ori_report, 'old_rootfs_info')
551 self._CheckMandatoryField(ori_msg, 'hash', ori_report, 'old_rootfs_info',
552 convert=common.FormatSha256)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700553
554 # Check: old_{kernel,rootfs} size must fit in respective partition.
555 if kernel_part_size and self.old_kernel_fs_size > kernel_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700556 raise error.PayloadError(
557 'Old kernel content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700558 (self.old_kernel_fs_size, kernel_part_size))
559 if rootfs_part_size and self.old_rootfs_fs_size > rootfs_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700560 raise error.PayloadError(
561 'Old rootfs content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700562 (self.old_rootfs_fs_size, rootfs_part_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800563 else:
564 # Assert/mark full payload.
565 if self.payload_type == _TYPE_DELTA:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700566 raise error.PayloadError(
567 'Apparent delta payload missing old_{kernel,rootfs}_info.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800568 self.payload_type = _TYPE_FULL
569
570 # Check: new_kernel_info present; contains {size, hash}.
571 nki_msg, nki_report = self._CheckMandatorySubMsg(
572 manifest, 'new_kernel_info', report, 'manifest')
Gilad Arnold382df5c2013-05-03 12:49:28 -0700573 self.new_kernel_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800574 nki_msg, 'size', nki_report, 'new_kernel_info')
575 self._CheckMandatoryField(nki_msg, 'hash', nki_report, 'new_kernel_info',
576 convert=common.FormatSha256)
577
578 # Check: new_rootfs_info present; contains {size, hash}.
579 nri_msg, nri_report = self._CheckMandatorySubMsg(
580 manifest, 'new_rootfs_info', report, 'manifest')
Gilad Arnold382df5c2013-05-03 12:49:28 -0700581 self.new_rootfs_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800582 nri_msg, 'size', nri_report, 'new_rootfs_info')
583 self._CheckMandatoryField(nri_msg, 'hash', nri_report, 'new_rootfs_info',
584 convert=common.FormatSha256)
585
Gilad Arnold382df5c2013-05-03 12:49:28 -0700586 # Check: new_{kernel,rootfs} size must fit in respective partition.
587 if kernel_part_size and self.new_kernel_fs_size > kernel_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700588 raise error.PayloadError(
589 'New kernel content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700590 (self.new_kernel_fs_size, kernel_part_size))
591 if rootfs_part_size and self.new_rootfs_fs_size > rootfs_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700592 raise error.PayloadError(
593 'New rootfs content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700594 (self.new_rootfs_fs_size, rootfs_part_size))
595
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800596 # Check: minor_version makes sense for the payload type. This check should
597 # run after the payload type has been set.
598 self._CheckMinorVersion(report, manifest.minor_version, self.payload_type)
599
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800600 def _CheckLength(self, length, total_blocks, op_name, length_name):
601 """Checks whether a length matches the space designated in extents.
602
603 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700604 length: The total length of the data.
605 total_blocks: The total number of blocks in extents.
606 op_name: Operation name (for error reporting).
607 length_name: Length name (for error reporting).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800608
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800609 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700610 error.PayloadError is there a problem with the length.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800611 """
612 # Check: length is non-zero.
613 if length == 0:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700614 raise error.PayloadError('%s: %s is zero.' % (op_name, length_name))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800615
616 # Check that length matches number of blocks.
617 self._CheckBlocksFitLength(length, total_blocks, self.block_size,
618 '%s: %s' % (op_name, length_name))
619
Gilad Arnold382df5c2013-05-03 12:49:28 -0700620 def _CheckExtents(self, extents, usable_size, block_counters, name,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800621 allow_pseudo=False, allow_signature=False):
622 """Checks a sequence of extents.
623
624 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700625 extents: The sequence of extents to check.
626 usable_size: The usable size of the partition to which the extents apply.
627 block_counters: Array of counters corresponding to the number of blocks.
628 name: The name of the extent block.
629 allow_pseudo: Whether or not pseudo block numbers are allowed.
630 allow_signature: Whether or not the extents are used for a signature.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800631
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800632 Returns:
633 The total number of blocks in the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800634
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800635 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700636 error.PayloadError if any of the entailed checks fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800637 """
638 total_num_blocks = 0
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800639 for ex, ex_name in common.ExtentIter(extents, name):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700640 # Check: Mandatory fields.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800641 start_block = PayloadChecker._CheckMandatoryField(ex, 'start_block',
642 None, ex_name)
643 num_blocks = PayloadChecker._CheckMandatoryField(ex, 'num_blocks', None,
644 ex_name)
645 end_block = start_block + num_blocks
646
647 # Check: num_blocks > 0.
648 if num_blocks == 0:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700649 raise error.PayloadError('%s: extent length is zero.' % ex_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800650
651 if start_block != common.PSEUDO_EXTENT_MARKER:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700652 # Check: Make sure we're within the partition limit.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700653 if usable_size and end_block * self.block_size > usable_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700654 raise error.PayloadError(
655 '%s: extent (%s) exceeds usable partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700656 (ex_name, common.FormatExtent(ex, self.block_size), usable_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800657
658 # Record block usage.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700659 for i in xrange(start_block, end_block):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800660 block_counters[i] += 1
Gilad Arnold5502b562013-03-08 13:22:31 -0800661 elif not (allow_pseudo or (allow_signature and len(extents) == 1)):
662 # Pseudo-extents must be allowed explicitly, or otherwise be part of a
663 # signature operation (in which case there has to be exactly one).
Gilad Arnoldcb638912013-06-24 04:57:11 -0700664 raise error.PayloadError('%s: unexpected pseudo-extent.' % ex_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800665
666 total_num_blocks += num_blocks
667
668 return total_num_blocks
669
670 def _CheckReplaceOperation(self, op, data_length, total_dst_blocks, op_name):
671 """Specific checks for REPLACE/REPLACE_BZ operations.
672
673 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700674 op: The operation object from the manifest.
675 data_length: The length of the data blob associated with the operation.
676 total_dst_blocks: Total number of blocks in dst_extents.
677 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800678
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800679 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700680 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800681 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700682 # Check: Does not contain src extents.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800683 if op.src_extents:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700684 raise error.PayloadError('%s: contains src_extents.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800685
Gilad Arnoldcb638912013-06-24 04:57:11 -0700686 # Check: Contains data.
Gilad Arnold5502b562013-03-08 13:22:31 -0800687 if data_length is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700688 raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800689
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800690 if op.type == common.OpType.REPLACE:
691 PayloadChecker._CheckBlocksFitLength(data_length, total_dst_blocks,
692 self.block_size,
693 op_name + '.data_length', 'dst')
694 else:
695 # Check: data_length must be smaller than the alotted dst blocks.
696 if data_length >= total_dst_blocks * self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700697 raise error.PayloadError(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800698 '%s: data_length (%d) must be less than allotted dst block '
Gilad Arnoldcb638912013-06-24 04:57:11 -0700699 'space (%d * %d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800700 (op_name, data_length, total_dst_blocks, self.block_size))
701
702 def _CheckMoveOperation(self, op, data_offset, total_src_blocks,
703 total_dst_blocks, op_name):
704 """Specific checks for MOVE operations.
705
706 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700707 op: The operation object from the manifest.
708 data_offset: The offset of a data blob for the operation.
709 total_src_blocks: Total number of blocks in src_extents.
710 total_dst_blocks: Total number of blocks in dst_extents.
711 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800712
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800713 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700714 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800715 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700716 # Check: No data_{offset,length}.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800717 if data_offset is not None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700718 raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800719
Gilad Arnoldcb638912013-06-24 04:57:11 -0700720 # Check: total_src_blocks == total_dst_blocks.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800721 if total_src_blocks != total_dst_blocks:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700722 raise error.PayloadError(
723 '%s: total src blocks (%d) != total dst blocks (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800724 (op_name, total_src_blocks, total_dst_blocks))
725
Gilad Arnoldcb638912013-06-24 04:57:11 -0700726 # Check: For all i, i-th src block index != i-th dst block index.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800727 i = 0
728 src_extent_iter = iter(op.src_extents)
729 dst_extent_iter = iter(op.dst_extents)
730 src_extent = dst_extent = None
731 src_idx = src_num = dst_idx = dst_num = 0
732 while i < total_src_blocks:
733 # Get the next source extent, if needed.
734 if not src_extent:
735 try:
736 src_extent = src_extent_iter.next()
737 except StopIteration:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700738 raise error.PayloadError('%s: ran out of src extents (%d/%d).' %
739 (op_name, i, total_src_blocks))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800740 src_idx = src_extent.start_block
741 src_num = src_extent.num_blocks
742
743 # Get the next dest extent, if needed.
744 if not dst_extent:
745 try:
746 dst_extent = dst_extent_iter.next()
747 except StopIteration:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700748 raise error.PayloadError('%s: ran out of dst extents (%d/%d).' %
749 (op_name, i, total_dst_blocks))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800750 dst_idx = dst_extent.start_block
751 dst_num = dst_extent.num_blocks
752
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700753 if self.check_move_same_src_dst_block and src_idx == dst_idx:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700754 raise error.PayloadError(
755 '%s: src/dst block number %d is the same (%d).' %
756 (op_name, i, src_idx))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800757
758 advance = min(src_num, dst_num)
759 i += advance
760
761 src_idx += advance
762 src_num -= advance
763 if src_num == 0:
764 src_extent = None
765
766 dst_idx += advance
767 dst_num -= advance
768 if dst_num == 0:
769 dst_extent = None
770
Gilad Arnold5502b562013-03-08 13:22:31 -0800771 # Make sure we've exhausted all src/dst extents.
772 if src_extent:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700773 raise error.PayloadError('%s: excess src blocks.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800774 if dst_extent:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700775 raise error.PayloadError('%s: excess dst blocks.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800776
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800777 def _CheckBsdiffOperation(self, data_length, total_dst_blocks, op_name):
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800778 """Specific checks for BSDIFF and SOURCE_BSDIFF operations.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800779
780 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700781 data_length: The length of the data blob associated with the operation.
782 total_dst_blocks: Total number of blocks in dst_extents.
783 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800784
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800785 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700786 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800787 """
Gilad Arnold5502b562013-03-08 13:22:31 -0800788 # Check: data_{offset,length} present.
789 if data_length is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700790 raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800791
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800792 # Check: data_length is strictly smaller than the alotted dst blocks.
793 if data_length >= total_dst_blocks * self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700794 raise error.PayloadError(
Gilad Arnold5502b562013-03-08 13:22:31 -0800795 '%s: data_length (%d) must be smaller than allotted dst space '
Gilad Arnoldcb638912013-06-24 04:57:11 -0700796 '(%d * %d = %d).' %
Gilad Arnold5502b562013-03-08 13:22:31 -0800797 (op_name, data_length, total_dst_blocks, self.block_size,
798 total_dst_blocks * self.block_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800799
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800800 def _CheckSourceCopyOperation(self, data_offset, total_src_blocks,
801 total_dst_blocks, op_name):
802 """Specific checks for SOURCE_COPY.
803
804 Args:
805 op: The operation object from the manifest.
806 data_offset: The offset of a data blob for the operation.
807 total_src_blocks: Total number of blocks in src_extents.
808 total_dst_blocks: Total number of blocks in dst_extents.
809 op_name: Operation name for error reporting.
810
811 Raises:
812 error.PayloadError if any check fails.
813 """
814 # Check: No data_{offset,length}.
815 if data_offset is not None:
816 raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
817
818 # Check: total_src_blocks == total_dst_blocks.
819 if total_src_blocks != total_dst_blocks:
820 raise error.PayloadError(
821 '%s: total src blocks (%d) != total dst blocks (%d).' %
822 (op_name, total_src_blocks, total_dst_blocks))
823
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800824 def _CheckOperation(self, op, op_name, is_last, old_block_counters,
Gilad Arnold4f50b412013-05-14 09:19:17 -0700825 new_block_counters, old_usable_size, new_usable_size,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700826 prev_data_offset, allow_signature, blob_hash_counts):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800827 """Checks a single update operation.
828
829 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700830 op: The operation object.
831 op_name: Operation name string for error reporting.
832 is_last: Whether this is the last operation in the sequence.
833 old_block_counters: Arrays of block read counters.
834 new_block_counters: Arrays of block write counters.
835 old_usable_size: The overall usable size for src data in bytes.
836 new_usable_size: The overall usable size for dst data in bytes.
837 prev_data_offset: Offset of last used data bytes.
838 allow_signature: Whether this may be a signature operation.
839 blob_hash_counts: Counters for hashed/unhashed blobs.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800840
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800841 Returns:
842 The amount of data blob associated with the operation.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800843
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800844 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700845 error.PayloadError if any check has failed.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800846 """
847 # Check extents.
848 total_src_blocks = self._CheckExtents(
Gilad Arnold4f50b412013-05-14 09:19:17 -0700849 op.src_extents, old_usable_size, old_block_counters,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800850 op_name + '.src_extents', allow_pseudo=True)
851 allow_signature_in_extents = (allow_signature and is_last and
852 op.type == common.OpType.REPLACE)
853 total_dst_blocks = self._CheckExtents(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700854 op.dst_extents, new_usable_size, new_block_counters,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700855 op_name + '.dst_extents',
856 allow_pseudo=(not self.check_dst_pseudo_extents),
857 allow_signature=allow_signature_in_extents)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800858
859 # Check: data_offset present <==> data_length present.
860 data_offset = self._CheckOptionalField(op, 'data_offset', None)
861 data_length = self._CheckOptionalField(op, 'data_length', None)
862 self._CheckPresentIff(data_offset, data_length, 'data_offset',
863 'data_length', op_name)
864
Gilad Arnoldcb638912013-06-24 04:57:11 -0700865 # Check: At least one dst_extent.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800866 if not op.dst_extents:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700867 raise error.PayloadError('%s: dst_extents is empty.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800868
869 # Check {src,dst}_length, if present.
870 if op.HasField('src_length'):
871 self._CheckLength(op.src_length, total_src_blocks, op_name, 'src_length')
872 if op.HasField('dst_length'):
873 self._CheckLength(op.dst_length, total_dst_blocks, op_name, 'dst_length')
874
875 if op.HasField('data_sha256_hash'):
876 blob_hash_counts['hashed'] += 1
877
Gilad Arnoldcb638912013-06-24 04:57:11 -0700878 # Check: Operation carries data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800879 if data_offset is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700880 raise error.PayloadError(
881 '%s: data_sha256_hash present but no data_{offset,length}.' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800882 op_name)
883
Gilad Arnoldcb638912013-06-24 04:57:11 -0700884 # Check: Hash verifies correctly.
885 # pylint cannot find the method in hashlib, for some reason.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800886 # pylint: disable=E1101
887 actual_hash = hashlib.sha256(self.payload.ReadDataBlob(data_offset,
888 data_length))
889 if op.data_sha256_hash != actual_hash.digest():
Gilad Arnoldcb638912013-06-24 04:57:11 -0700890 raise error.PayloadError(
891 '%s: data_sha256_hash (%s) does not match actual hash (%s).' %
Gilad Arnold96405372013-05-04 00:24:58 -0700892 (op_name, common.FormatSha256(op.data_sha256_hash),
893 common.FormatSha256(actual_hash.digest())))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800894 elif data_offset is not None:
895 if allow_signature_in_extents:
896 blob_hash_counts['signature'] += 1
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700897 elif self.allow_unhashed:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800898 blob_hash_counts['unhashed'] += 1
899 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700900 raise error.PayloadError('%s: unhashed operation not allowed.' %
901 op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800902
903 if data_offset is not None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700904 # Check: Contiguous use of data section.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800905 if data_offset != prev_data_offset:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700906 raise error.PayloadError(
907 '%s: data offset (%d) not matching amount used so far (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800908 (op_name, data_offset, prev_data_offset))
909
910 # Type-specific checks.
911 if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
912 self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
913 elif self.payload_type == _TYPE_FULL:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700914 raise error.PayloadError('%s: non-REPLACE operation in a full payload.' %
915 op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800916 elif op.type == common.OpType.MOVE:
917 self._CheckMoveOperation(op, data_offset, total_src_blocks,
918 total_dst_blocks, op_name)
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800919 elif op.type in (common.OpType.BSDIFF, common.OpType.SOURCE_BSDIFF):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800920 self._CheckBsdiffOperation(data_length, total_dst_blocks, op_name)
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800921 elif op.type == common.OpType.SOURCE_COPY:
922 self._CheckSourceCopyOperation(data_offset, total_src_blocks,
923 total_dst_blocks, op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800924 else:
925 assert False, 'cannot get here'
926
927 return data_length if data_length is not None else 0
928
Gilad Arnold382df5c2013-05-03 12:49:28 -0700929 def _SizeToNumBlocks(self, size):
930 """Returns the number of blocks needed to contain a given byte size."""
931 return (size + self.block_size - 1) / self.block_size
932
933 def _AllocBlockCounters(self, total_size):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800934 """Returns a freshly initialized array of block counters.
935
Gilad Arnoldcb638912013-06-24 04:57:11 -0700936 Note that the generated array is not portable as is due to byte-ordering
937 issues, hence it should not be serialized.
938
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800939 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700940 total_size: The total block size in bytes.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800941
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800942 Returns:
Gilad Arnold9753f3d2013-07-23 08:34:45 -0700943 An array of unsigned short elements initialized to zero, one for each of
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800944 the blocks necessary for containing the partition.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800945 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700946 return array.array('H',
947 itertools.repeat(0, self._SizeToNumBlocks(total_size)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800948
Gilad Arnold382df5c2013-05-03 12:49:28 -0700949 def _CheckOperations(self, operations, report, base_name, old_fs_size,
950 new_fs_size, new_usable_size, prev_data_offset,
951 allow_signature):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800952 """Checks a sequence of update operations.
953
954 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700955 operations: The sequence of operations to check.
956 report: The report object to add to.
957 base_name: The name of the operation block.
958 old_fs_size: The old filesystem size in bytes.
959 new_fs_size: The new filesystem size in bytes.
960 new_usable_size: The overall usable size of the new partition in bytes.
961 prev_data_offset: Offset of last used data bytes.
962 allow_signature: Whether this sequence may contain signature operations.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800963
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800964 Returns:
Gilad Arnold5502b562013-03-08 13:22:31 -0800965 The total data blob size used.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800966
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800967 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700968 error.PayloadError if any of the checks fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800969 """
970 # The total size of data blobs used by operations scanned thus far.
971 total_data_used = 0
972 # Counts of specific operation types.
973 op_counts = {
974 common.OpType.REPLACE: 0,
975 common.OpType.REPLACE_BZ: 0,
976 common.OpType.MOVE: 0,
977 common.OpType.BSDIFF: 0,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800978 common.OpType.SOURCE_COPY: 0,
979 common.OpType.SOURCE_BSDIFF: 0,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800980 }
981 # Total blob sizes for each operation type.
982 op_blob_totals = {
983 common.OpType.REPLACE: 0,
984 common.OpType.REPLACE_BZ: 0,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700985 # MOVE operations don't have blobs.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800986 common.OpType.BSDIFF: 0,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800987 # SOURCE_COPY operations don't have blobs.
988 common.OpType.SOURCE_BSDIFF: 0,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800989 }
990 # Counts of hashed vs unhashed operations.
991 blob_hash_counts = {
992 'hashed': 0,
993 'unhashed': 0,
994 }
995 if allow_signature:
996 blob_hash_counts['signature'] = 0
997
998 # Allocate old and new block counters.
Gilad Arnold4f50b412013-05-14 09:19:17 -0700999 old_block_counters = (self._AllocBlockCounters(new_usable_size)
Gilad Arnold382df5c2013-05-03 12:49:28 -07001000 if old_fs_size else None)
1001 new_block_counters = self._AllocBlockCounters(new_usable_size)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001002
1003 # Process and verify each operation.
1004 op_num = 0
1005 for op, op_name in common.OperationIter(operations, base_name):
1006 op_num += 1
1007
Gilad Arnoldcb638912013-06-24 04:57:11 -07001008 # Check: Type is valid.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001009 if op.type not in op_counts.keys():
Gilad Arnoldcb638912013-06-24 04:57:11 -07001010 raise error.PayloadError('%s: invalid type (%d).' % (op_name, op.type))
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001011 op_counts[op.type] += 1
1012
1013 is_last = op_num == len(operations)
1014 curr_data_used = self._CheckOperation(
1015 op, op_name, is_last, old_block_counters, new_block_counters,
Gilad Arnold4f50b412013-05-14 09:19:17 -07001016 new_usable_size if old_fs_size else 0, new_usable_size,
1017 prev_data_offset + total_data_used, allow_signature,
1018 blob_hash_counts)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001019 if curr_data_used:
1020 op_blob_totals[op.type] += curr_data_used
1021 total_data_used += curr_data_used
1022
1023 # Report totals and breakdown statistics.
1024 report.AddField('total operations', op_num)
1025 report.AddField(
1026 None,
1027 histogram.Histogram.FromCountDict(op_counts,
1028 key_names=common.OpType.NAMES),
1029 indent=1)
1030 report.AddField('total blobs', sum(blob_hash_counts.values()))
1031 report.AddField(None,
1032 histogram.Histogram.FromCountDict(blob_hash_counts),
1033 indent=1)
1034 report.AddField('total blob size', _AddHumanReadableSize(total_data_used))
1035 report.AddField(
1036 None,
1037 histogram.Histogram.FromCountDict(op_blob_totals,
1038 formatter=_AddHumanReadableSize,
1039 key_names=common.OpType.NAMES),
1040 indent=1)
1041
1042 # Report read/write histograms.
1043 if old_block_counters:
1044 report.AddField('block read hist',
1045 histogram.Histogram.FromKeyList(old_block_counters),
1046 linebreak=True, indent=1)
1047
Gilad Arnold382df5c2013-05-03 12:49:28 -07001048 new_write_hist = histogram.Histogram.FromKeyList(
1049 new_block_counters[:self._SizeToNumBlocks(new_fs_size)])
1050 report.AddField('block write hist', new_write_hist, linebreak=True,
1051 indent=1)
1052
Gilad Arnoldcb638912013-06-24 04:57:11 -07001053 # Check: Full update must write each dst block once.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001054 if self.payload_type == _TYPE_FULL and new_write_hist.GetKeys() != [1]:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001055 raise error.PayloadError(
1056 '%s: not all blocks written exactly once during full update.' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001057 base_name)
1058
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001059 # Check: SOURCE_COPY and SOURCE_BSDIFF ops shouldn't be in minor version 1.
1060 if (self.payload.manifest.minor_version == 1 and
1061 (op_counts[common.OpType.SOURCE_COPY] or
1062 op_counts[common.OpType.SOURCE_BSDIFF])):
1063 raise error.PayloadError(
1064 'SOURCE_COPY/SOURCE_BSDIFF not allowed with minor version 1.')
1065
1066 # Check: MOVE and BSDIFF ops shouldn't be in minor version 2.
1067 if (self.payload.manifest.minor_version == 2 and
1068 (op_counts[common.OpType.MOVE] or op_counts[common.OpType.BSDIFF])):
1069 raise error.PayloadError(
1070 'MOVE/BSDIFF not allowed with minor version 2.')
1071
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001072 return total_data_used
1073
1074 def _CheckSignatures(self, report, pubkey_file_name):
1075 """Checks a payload's signature block."""
1076 sigs_raw = self.payload.ReadDataBlob(self.sigs_offset, self.sigs_size)
1077 sigs = update_metadata_pb2.Signatures()
1078 sigs.ParseFromString(sigs_raw)
1079 report.AddSection('signatures')
1080
Gilad Arnoldcb638912013-06-24 04:57:11 -07001081 # Check: At least one signature present.
1082 # pylint cannot see through the protobuf object, it seems.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001083 # pylint: disable=E1101
1084 if not sigs.signatures:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001085 raise error.PayloadError('Signature block is empty.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001086
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001087 last_ops_section = (self.payload.manifest.kernel_install_operations or
1088 self.payload.manifest.install_operations)
1089 fake_sig_op = last_ops_section[-1]
Gilad Arnold5502b562013-03-08 13:22:31 -08001090 # Check: signatures_{offset,size} must match the last (fake) operation.
1091 if not (fake_sig_op.type == common.OpType.REPLACE and
1092 self.sigs_offset == fake_sig_op.data_offset and
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001093 self.sigs_size == fake_sig_op.data_length):
Gilad Arnoldcb638912013-06-24 04:57:11 -07001094 raise error.PayloadError(
1095 'Signatures_{offset,size} (%d+%d) does not match last operation '
1096 '(%d+%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001097 (self.sigs_offset, self.sigs_size, fake_sig_op.data_offset,
1098 fake_sig_op.data_length))
1099
1100 # Compute the checksum of all data up to signature blob.
1101 # TODO(garnold) we're re-reading the whole data section into a string
1102 # just to compute the checksum; instead, we could do it incrementally as
1103 # we read the blobs one-by-one, under the assumption that we're reading
1104 # them in order (which currently holds). This should be reconsidered.
1105 payload_hasher = self.payload.manifest_hasher.copy()
1106 common.Read(self.payload.payload_file, self.sigs_offset,
1107 offset=self.payload.data_offset, hasher=payload_hasher)
1108
1109 for sig, sig_name in common.SignatureIter(sigs.signatures, 'signatures'):
1110 sig_report = report.AddSubReport(sig_name)
1111
Gilad Arnoldcb638912013-06-24 04:57:11 -07001112 # Check: Signature contains mandatory fields.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001113 self._CheckMandatoryField(sig, 'version', sig_report, sig_name)
1114 self._CheckMandatoryField(sig, 'data', None, sig_name)
1115 sig_report.AddField('data len', len(sig.data))
1116
Gilad Arnoldcb638912013-06-24 04:57:11 -07001117 # Check: Signatures pertains to actual payload hash.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001118 if sig.version == 1:
1119 self._CheckSha256Signature(sig.data, pubkey_file_name,
1120 payload_hasher.digest(), sig_name)
1121 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001122 raise error.PayloadError('Unknown signature version (%d).' %
1123 sig.version)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001124
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001125 def _CheckMinorVersion(self, report, minor_version, payload_type):
1126 """Checks that the minor version matches the payload type.
1127
1128 Args:
1129 report: The report object to add to.
1130 minor_version: The minor version of the payload.
1131 payload_type: The type of payload (full or delta).
1132
1133 Raises:
1134 error.PayloadError if any of the checks fails.
1135 """
1136 report.AddField('minor version', minor_version)
1137
1138 # Minor version 0 implies a full payload.
1139 if minor_version == 0:
1140 if payload_type != _TYPE_FULL:
1141 raise error.PayloadError(
1142 'Minor version 0 not compatible with payload type: %s.'
1143 % payload_type)
1144
1145 # Minor version 1 or 2 implies a delta payload.
1146 elif minor_version == 1 or minor_version == 2:
1147 if payload_type != _TYPE_DELTA:
1148 raise error.PayloadError(
1149 'Minor version %d not compatible with payload type: %s.'
1150 % (minor_version, payload_type))
1151
1152 else:
1153 raise error.PayloadError('Unsupported minor version: %d' % minor_version)
1154
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001155 def Run(self, pubkey_file_name=None, metadata_sig_file=None,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001156 rootfs_part_size=0, kernel_part_size=0, report_out_file=None):
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001157 """Checker entry point, invoking all checks.
1158
1159 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001160 pubkey_file_name: Public key used for signature verification.
1161 metadata_sig_file: Metadata signature, if verification is desired.
1162 rootfs_part_size: The size of rootfs partitions in bytes (default: use
1163 reported filesystem size).
1164 kernel_part_size: The size of kernel partitions in bytes (default: use
1165 reported filesystem size).
1166 report_out_file: File object to dump the report to.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001167
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001168 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001169 error.PayloadError if payload verification failed.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001170 """
Gilad Arnold9b90c932013-05-22 17:12:56 -07001171 if not pubkey_file_name:
1172 pubkey_file_name = _DEFAULT_PUBKEY_FILE_NAME
1173
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001174 report = _PayloadReport()
1175
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001176 # Get payload file size.
1177 self.payload.payload_file.seek(0, 2)
1178 payload_file_size = self.payload.payload_file.tell()
1179 self.payload.ResetFile()
1180
1181 try:
1182 # Check metadata signature (if provided).
1183 if metadata_sig_file:
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001184 metadata_sig = base64.b64decode(metadata_sig_file.read())
1185 self._CheckSha256Signature(metadata_sig, pubkey_file_name,
1186 self.payload.manifest_hasher.digest(),
1187 'metadata signature')
1188
Gilad Arnoldcb638912013-06-24 04:57:11 -07001189 # Part 1: Check the file header.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001190 report.AddSection('header')
Gilad Arnoldcb638912013-06-24 04:57:11 -07001191 # Check: Payload version is valid.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001192 if self.payload.header.version != 1:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001193 raise error.PayloadError('Unknown payload version (%d).' %
1194 self.payload.header.version)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001195 report.AddField('version', self.payload.header.version)
1196 report.AddField('manifest len', self.payload.header.manifest_len)
1197
Gilad Arnoldcb638912013-06-24 04:57:11 -07001198 # Part 2: Check the manifest.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001199 self._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001200 assert self.payload_type, 'payload type should be known by now'
1201
Gilad Arnoldcb638912013-06-24 04:57:11 -07001202 # Part 3: Examine rootfs operations.
Gilad Arnold0990f512013-05-30 17:09:31 -07001203 # TODO(garnold)(chromium:243559) only default to the filesystem size if
1204 # no explicit size provided *and* the partition size is not embedded in
1205 # the payload; see issue for more details.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001206 report.AddSection('rootfs operations')
1207 total_blob_size = self._CheckOperations(
1208 self.payload.manifest.install_operations, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001209 'install_operations', self.old_rootfs_fs_size,
1210 self.new_rootfs_fs_size,
1211 rootfs_part_size if rootfs_part_size else self.new_rootfs_fs_size,
1212 0, False)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001213
Gilad Arnoldcb638912013-06-24 04:57:11 -07001214 # Part 4: Examine kernel operations.
Gilad Arnold0990f512013-05-30 17:09:31 -07001215 # TODO(garnold)(chromium:243559) as above.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001216 report.AddSection('kernel operations')
1217 total_blob_size += self._CheckOperations(
1218 self.payload.manifest.kernel_install_operations, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001219 'kernel_install_operations', self.old_kernel_fs_size,
1220 self.new_kernel_fs_size,
1221 kernel_part_size if kernel_part_size else self.new_kernel_fs_size,
1222 total_blob_size, True)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001223
Gilad Arnoldcb638912013-06-24 04:57:11 -07001224 # Check: Operations data reach the end of the payload file.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001225 used_payload_size = self.payload.data_offset + total_blob_size
1226 if used_payload_size != payload_file_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001227 raise error.PayloadError(
1228 'Used payload size (%d) different from actual file size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001229 (used_payload_size, payload_file_size))
1230
Gilad Arnoldcb638912013-06-24 04:57:11 -07001231 # Part 5: Handle payload signatures message.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001232 if self.check_payload_sig and self.sigs_size:
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001233 self._CheckSignatures(report, pubkey_file_name)
1234
Gilad Arnoldcb638912013-06-24 04:57:11 -07001235 # Part 6: Summary.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001236 report.AddSection('summary')
1237 report.AddField('update type', self.payload_type)
1238
1239 report.Finalize()
1240 finally:
1241 if report_out_file:
1242 report.Dump(report_out_file)