xref: /aosp_15_r20/system/update_engine/scripts/update_payload/checker.py (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
1*5a923131SAndroid Build Coastguard Worker#
2*5a923131SAndroid Build Coastguard Worker# Copyright (C) 2013 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker#
4*5a923131SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker#
8*5a923131SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker#
10*5a923131SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker# limitations under the License.
15*5a923131SAndroid Build Coastguard Worker#
16*5a923131SAndroid Build Coastguard Worker
17*5a923131SAndroid Build Coastguard Worker"""Verifying the integrity of a Chrome OS update payload.
18*5a923131SAndroid Build Coastguard Worker
19*5a923131SAndroid Build Coastguard WorkerThis module is used internally by the main Payload class for verifying the
20*5a923131SAndroid Build Coastguard Workerintegrity of an update payload. The interface for invoking the checks is as
21*5a923131SAndroid Build Coastguard Workerfollows:
22*5a923131SAndroid Build Coastguard Worker
23*5a923131SAndroid Build Coastguard Worker  checker = PayloadChecker(payload)
24*5a923131SAndroid Build Coastguard Worker  checker.Run(...)
25*5a923131SAndroid Build Coastguard Worker"""
26*5a923131SAndroid Build Coastguard Worker
27*5a923131SAndroid Build Coastguard Workerfrom __future__ import absolute_import
28*5a923131SAndroid Build Coastguard Workerfrom __future__ import print_function
29*5a923131SAndroid Build Coastguard Worker
30*5a923131SAndroid Build Coastguard Workerimport array
31*5a923131SAndroid Build Coastguard Workerimport base64
32*5a923131SAndroid Build Coastguard Workerimport collections
33*5a923131SAndroid Build Coastguard Workerimport hashlib
34*5a923131SAndroid Build Coastguard Workerimport itertools
35*5a923131SAndroid Build Coastguard Workerimport os
36*5a923131SAndroid Build Coastguard Workerimport subprocess
37*5a923131SAndroid Build Coastguard Worker
38*5a923131SAndroid Build Coastguard Worker# pylint: disable=redefined-builtin
39*5a923131SAndroid Build Coastguard Workerfrom six.moves import range
40*5a923131SAndroid Build Coastguard Worker
41*5a923131SAndroid Build Coastguard Workerfrom update_payload import common
42*5a923131SAndroid Build Coastguard Workerfrom update_payload import error
43*5a923131SAndroid Build Coastguard Workerfrom update_payload import format_utils
44*5a923131SAndroid Build Coastguard Workerfrom update_payload import histogram
45*5a923131SAndroid Build Coastguard Workerimport update_metadata_pb2
46*5a923131SAndroid Build Coastguard Worker
47*5a923131SAndroid Build Coastguard Worker#
48*5a923131SAndroid Build Coastguard Worker# Constants.
49*5a923131SAndroid Build Coastguard Worker#
50*5a923131SAndroid Build Coastguard Worker
51*5a923131SAndroid Build Coastguard Worker_CHECK_MOVE_SAME_SRC_DST_BLOCK = 'move-same-src-dst-block'
52*5a923131SAndroid Build Coastguard Worker_CHECK_PAYLOAD_SIG = 'payload-sig'
53*5a923131SAndroid Build Coastguard WorkerCHECKS_TO_DISABLE = (
54*5a923131SAndroid Build Coastguard Worker    _CHECK_MOVE_SAME_SRC_DST_BLOCK,
55*5a923131SAndroid Build Coastguard Worker    _CHECK_PAYLOAD_SIG,
56*5a923131SAndroid Build Coastguard Worker)
57*5a923131SAndroid Build Coastguard Worker
58*5a923131SAndroid Build Coastguard Worker_TYPE_FULL = 'full'
59*5a923131SAndroid Build Coastguard Worker_TYPE_DELTA = 'delta'
60*5a923131SAndroid Build Coastguard Worker
61*5a923131SAndroid Build Coastguard Worker_DEFAULT_BLOCK_SIZE = 4096
62*5a923131SAndroid Build Coastguard Worker
63*5a923131SAndroid Build Coastguard Worker_DEFAULT_PUBKEY_BASE_NAME = 'update-payload-key.pub.pem'
64*5a923131SAndroid Build Coastguard Worker_DEFAULT_PUBKEY_FILE_NAME = os.path.join(os.path.dirname(__file__),
65*5a923131SAndroid Build Coastguard Worker                                         _DEFAULT_PUBKEY_BASE_NAME)
66*5a923131SAndroid Build Coastguard Worker
67*5a923131SAndroid Build Coastguard Worker# Supported minor version map to payload types allowed to be using them.
68*5a923131SAndroid Build Coastguard Worker_SUPPORTED_MINOR_VERSIONS = {
69*5a923131SAndroid Build Coastguard Worker    0: (_TYPE_FULL,),
70*5a923131SAndroid Build Coastguard Worker    2: (_TYPE_DELTA,),
71*5a923131SAndroid Build Coastguard Worker    3: (_TYPE_DELTA,),
72*5a923131SAndroid Build Coastguard Worker    4: (_TYPE_DELTA,),
73*5a923131SAndroid Build Coastguard Worker    5: (_TYPE_DELTA,),
74*5a923131SAndroid Build Coastguard Worker    6: (_TYPE_DELTA,),
75*5a923131SAndroid Build Coastguard Worker    7: (_TYPE_DELTA,),
76*5a923131SAndroid Build Coastguard Worker}
77*5a923131SAndroid Build Coastguard Worker
78*5a923131SAndroid Build Coastguard Worker
79*5a923131SAndroid Build Coastguard Worker#
80*5a923131SAndroid Build Coastguard Worker# Helper functions.
81*5a923131SAndroid Build Coastguard Worker#
82*5a923131SAndroid Build Coastguard Worker
83*5a923131SAndroid Build Coastguard Workerdef _IsPowerOfTwo(val):
84*5a923131SAndroid Build Coastguard Worker  """Returns True iff val is a power of two."""
85*5a923131SAndroid Build Coastguard Worker  return val > 0 and (val & (val - 1)) == 0
86*5a923131SAndroid Build Coastguard Worker
87*5a923131SAndroid Build Coastguard Worker
88*5a923131SAndroid Build Coastguard Workerdef _AddFormat(format_func, value):
89*5a923131SAndroid Build Coastguard Worker  """Adds a custom formatted representation to ordinary string representation.
90*5a923131SAndroid Build Coastguard Worker
91*5a923131SAndroid Build Coastguard Worker  Args:
92*5a923131SAndroid Build Coastguard Worker    format_func: A value formatter.
93*5a923131SAndroid Build Coastguard Worker    value: Value to be formatted and returned.
94*5a923131SAndroid Build Coastguard Worker
95*5a923131SAndroid Build Coastguard Worker  Returns:
96*5a923131SAndroid Build Coastguard Worker    A string 'x (y)' where x = str(value) and y = format_func(value).
97*5a923131SAndroid Build Coastguard Worker  """
98*5a923131SAndroid Build Coastguard Worker  ret = str(value)
99*5a923131SAndroid Build Coastguard Worker  formatted_str = format_func(value)
100*5a923131SAndroid Build Coastguard Worker  if formatted_str:
101*5a923131SAndroid Build Coastguard Worker    ret += ' (%s)' % formatted_str
102*5a923131SAndroid Build Coastguard Worker  return ret
103*5a923131SAndroid Build Coastguard Worker
104*5a923131SAndroid Build Coastguard Worker
105*5a923131SAndroid Build Coastguard Workerdef _AddHumanReadableSize(size):
106*5a923131SAndroid Build Coastguard Worker  """Adds a human readable representation to a byte size value."""
107*5a923131SAndroid Build Coastguard Worker  return _AddFormat(format_utils.BytesToHumanReadable, size)
108*5a923131SAndroid Build Coastguard Worker
109*5a923131SAndroid Build Coastguard Worker
110*5a923131SAndroid Build Coastguard Worker#
111*5a923131SAndroid Build Coastguard Worker# Payload report generator.
112*5a923131SAndroid Build Coastguard Worker#
113*5a923131SAndroid Build Coastguard Worker
114*5a923131SAndroid Build Coastguard Workerclass _PayloadReport(object):
115*5a923131SAndroid Build Coastguard Worker  """A payload report generator.
116*5a923131SAndroid Build Coastguard Worker
117*5a923131SAndroid Build Coastguard Worker  A report is essentially a sequence of nodes, which represent data points. It
118*5a923131SAndroid Build Coastguard Worker  is initialized to have a "global", untitled section. A node may be a
119*5a923131SAndroid Build Coastguard Worker  sub-report itself.
120*5a923131SAndroid Build Coastguard Worker  """
121*5a923131SAndroid Build Coastguard Worker
122*5a923131SAndroid Build Coastguard Worker  # Report nodes: Field, sub-report, section.
123*5a923131SAndroid Build Coastguard Worker  class Node(object):
124*5a923131SAndroid Build Coastguard Worker    """A report node interface."""
125*5a923131SAndroid Build Coastguard Worker
126*5a923131SAndroid Build Coastguard Worker    @staticmethod
127*5a923131SAndroid Build Coastguard Worker    def _Indent(indent, line):
128*5a923131SAndroid Build Coastguard Worker      """Indents a line by a given indentation amount.
129*5a923131SAndroid Build Coastguard Worker
130*5a923131SAndroid Build Coastguard Worker      Args:
131*5a923131SAndroid Build Coastguard Worker        indent: The indentation amount.
132*5a923131SAndroid Build Coastguard Worker        line: The line content (string).
133*5a923131SAndroid Build Coastguard Worker
134*5a923131SAndroid Build Coastguard Worker      Returns:
135*5a923131SAndroid Build Coastguard Worker        The properly indented line (string).
136*5a923131SAndroid Build Coastguard Worker      """
137*5a923131SAndroid Build Coastguard Worker      return '%*s%s' % (indent, '', line)
138*5a923131SAndroid Build Coastguard Worker
139*5a923131SAndroid Build Coastguard Worker    def GenerateLines(self, base_indent, sub_indent, curr_section):
140*5a923131SAndroid Build Coastguard Worker      """Generates the report lines for this node.
141*5a923131SAndroid Build Coastguard Worker
142*5a923131SAndroid Build Coastguard Worker      Args:
143*5a923131SAndroid Build Coastguard Worker        base_indent: Base indentation for each line.
144*5a923131SAndroid Build Coastguard Worker        sub_indent: Additional indentation for sub-nodes.
145*5a923131SAndroid Build Coastguard Worker        curr_section: The current report section object.
146*5a923131SAndroid Build Coastguard Worker
147*5a923131SAndroid Build Coastguard Worker      Returns:
148*5a923131SAndroid Build Coastguard Worker        A pair consisting of a list of properly indented report lines and a new
149*5a923131SAndroid Build Coastguard Worker        current section object.
150*5a923131SAndroid Build Coastguard Worker      """
151*5a923131SAndroid Build Coastguard Worker      raise NotImplementedError
152*5a923131SAndroid Build Coastguard Worker
153*5a923131SAndroid Build Coastguard Worker  class FieldNode(Node):
154*5a923131SAndroid Build Coastguard Worker    """A field report node, representing a (name, value) pair."""
155*5a923131SAndroid Build Coastguard Worker
156*5a923131SAndroid Build Coastguard Worker    def __init__(self, name, value, linebreak, indent):
157*5a923131SAndroid Build Coastguard Worker      super(_PayloadReport.FieldNode, self).__init__()
158*5a923131SAndroid Build Coastguard Worker      self.name = name
159*5a923131SAndroid Build Coastguard Worker      self.value = value
160*5a923131SAndroid Build Coastguard Worker      self.linebreak = linebreak
161*5a923131SAndroid Build Coastguard Worker      self.indent = indent
162*5a923131SAndroid Build Coastguard Worker
163*5a923131SAndroid Build Coastguard Worker    def GenerateLines(self, base_indent, sub_indent, curr_section):
164*5a923131SAndroid Build Coastguard Worker      """Generates a properly formatted 'name : value' entry."""
165*5a923131SAndroid Build Coastguard Worker      report_output = ''
166*5a923131SAndroid Build Coastguard Worker      if self.name:
167*5a923131SAndroid Build Coastguard Worker        report_output += self.name.ljust(curr_section.max_field_name_len) + ' :'
168*5a923131SAndroid Build Coastguard Worker      value_lines = str(self.value).splitlines()
169*5a923131SAndroid Build Coastguard Worker      if self.linebreak and self.name:
170*5a923131SAndroid Build Coastguard Worker        report_output += '\n' + '\n'.join(
171*5a923131SAndroid Build Coastguard Worker            ['%*s%s' % (self.indent, '', line) for line in value_lines])
172*5a923131SAndroid Build Coastguard Worker      else:
173*5a923131SAndroid Build Coastguard Worker        if self.name:
174*5a923131SAndroid Build Coastguard Worker          report_output += ' '
175*5a923131SAndroid Build Coastguard Worker        report_output += '%*s' % (self.indent, '')
176*5a923131SAndroid Build Coastguard Worker        cont_line_indent = len(report_output)
177*5a923131SAndroid Build Coastguard Worker        indented_value_lines = [value_lines[0]]
178*5a923131SAndroid Build Coastguard Worker        indented_value_lines.extend(['%*s%s' % (cont_line_indent, '', line)
179*5a923131SAndroid Build Coastguard Worker                                     for line in value_lines[1:]])
180*5a923131SAndroid Build Coastguard Worker        report_output += '\n'.join(indented_value_lines)
181*5a923131SAndroid Build Coastguard Worker
182*5a923131SAndroid Build Coastguard Worker      report_lines = [self._Indent(base_indent, line + '\n')
183*5a923131SAndroid Build Coastguard Worker                      for line in report_output.split('\n')]
184*5a923131SAndroid Build Coastguard Worker      return report_lines, curr_section
185*5a923131SAndroid Build Coastguard Worker
186*5a923131SAndroid Build Coastguard Worker  class SubReportNode(Node):
187*5a923131SAndroid Build Coastguard Worker    """A sub-report node, representing a nested report."""
188*5a923131SAndroid Build Coastguard Worker
189*5a923131SAndroid Build Coastguard Worker    def __init__(self, title, report):
190*5a923131SAndroid Build Coastguard Worker      super(_PayloadReport.SubReportNode, self).__init__()
191*5a923131SAndroid Build Coastguard Worker      self.title = title
192*5a923131SAndroid Build Coastguard Worker      self.report = report
193*5a923131SAndroid Build Coastguard Worker
194*5a923131SAndroid Build Coastguard Worker    def GenerateLines(self, base_indent, sub_indent, curr_section):
195*5a923131SAndroid Build Coastguard Worker      """Recurse with indentation."""
196*5a923131SAndroid Build Coastguard Worker      report_lines = [self._Indent(base_indent, self.title + ' =>\n')]
197*5a923131SAndroid Build Coastguard Worker      report_lines.extend(self.report.GenerateLines(base_indent + sub_indent,
198*5a923131SAndroid Build Coastguard Worker                                                    sub_indent))
199*5a923131SAndroid Build Coastguard Worker      return report_lines, curr_section
200*5a923131SAndroid Build Coastguard Worker
201*5a923131SAndroid Build Coastguard Worker  class SectionNode(Node):
202*5a923131SAndroid Build Coastguard Worker    """A section header node."""
203*5a923131SAndroid Build Coastguard Worker
204*5a923131SAndroid Build Coastguard Worker    def __init__(self, title=None):
205*5a923131SAndroid Build Coastguard Worker      super(_PayloadReport.SectionNode, self).__init__()
206*5a923131SAndroid Build Coastguard Worker      self.title = title
207*5a923131SAndroid Build Coastguard Worker      self.max_field_name_len = 0
208*5a923131SAndroid Build Coastguard Worker
209*5a923131SAndroid Build Coastguard Worker    def GenerateLines(self, base_indent, sub_indent, curr_section):
210*5a923131SAndroid Build Coastguard Worker      """Dump a title line, return self as the (new) current section."""
211*5a923131SAndroid Build Coastguard Worker      report_lines = []
212*5a923131SAndroid Build Coastguard Worker      if self.title:
213*5a923131SAndroid Build Coastguard Worker        report_lines.append(self._Indent(base_indent,
214*5a923131SAndroid Build Coastguard Worker                                         '=== %s ===\n' % self.title))
215*5a923131SAndroid Build Coastguard Worker      return report_lines, self
216*5a923131SAndroid Build Coastguard Worker
217*5a923131SAndroid Build Coastguard Worker  def __init__(self):
218*5a923131SAndroid Build Coastguard Worker    self.report = []
219*5a923131SAndroid Build Coastguard Worker    self.last_section = self.global_section = self.SectionNode()
220*5a923131SAndroid Build Coastguard Worker    self.is_finalized = False
221*5a923131SAndroid Build Coastguard Worker
222*5a923131SAndroid Build Coastguard Worker  def GenerateLines(self, base_indent, sub_indent):
223*5a923131SAndroid Build Coastguard Worker    """Generates the lines in the report, properly indented.
224*5a923131SAndroid Build Coastguard Worker
225*5a923131SAndroid Build Coastguard Worker    Args:
226*5a923131SAndroid Build Coastguard Worker      base_indent: The indentation used for root-level report lines.
227*5a923131SAndroid Build Coastguard Worker      sub_indent: The indentation offset used for sub-reports.
228*5a923131SAndroid Build Coastguard Worker
229*5a923131SAndroid Build Coastguard Worker    Returns:
230*5a923131SAndroid Build Coastguard Worker      A list of indented report lines.
231*5a923131SAndroid Build Coastguard Worker    """
232*5a923131SAndroid Build Coastguard Worker    report_lines = []
233*5a923131SAndroid Build Coastguard Worker    curr_section = self.global_section
234*5a923131SAndroid Build Coastguard Worker    for node in self.report:
235*5a923131SAndroid Build Coastguard Worker      node_report_lines, curr_section = node.GenerateLines(
236*5a923131SAndroid Build Coastguard Worker          base_indent, sub_indent, curr_section)
237*5a923131SAndroid Build Coastguard Worker      report_lines.extend(node_report_lines)
238*5a923131SAndroid Build Coastguard Worker
239*5a923131SAndroid Build Coastguard Worker    return report_lines
240*5a923131SAndroid Build Coastguard Worker
241*5a923131SAndroid Build Coastguard Worker  def Dump(self, out_file, base_indent=0, sub_indent=2):
242*5a923131SAndroid Build Coastguard Worker    """Dumps the report to a file.
243*5a923131SAndroid Build Coastguard Worker
244*5a923131SAndroid Build Coastguard Worker    Args:
245*5a923131SAndroid Build Coastguard Worker      out_file: File object to output the content to.
246*5a923131SAndroid Build Coastguard Worker      base_indent: Base indentation for report lines.
247*5a923131SAndroid Build Coastguard Worker      sub_indent: Added indentation for sub-reports.
248*5a923131SAndroid Build Coastguard Worker    """
249*5a923131SAndroid Build Coastguard Worker    report_lines = self.GenerateLines(base_indent, sub_indent)
250*5a923131SAndroid Build Coastguard Worker    if report_lines and not self.is_finalized:
251*5a923131SAndroid Build Coastguard Worker      report_lines.append('(incomplete report)\n')
252*5a923131SAndroid Build Coastguard Worker
253*5a923131SAndroid Build Coastguard Worker    for line in report_lines:
254*5a923131SAndroid Build Coastguard Worker      out_file.write(line)
255*5a923131SAndroid Build Coastguard Worker
256*5a923131SAndroid Build Coastguard Worker  def AddField(self, name, value, linebreak=False, indent=0):
257*5a923131SAndroid Build Coastguard Worker    """Adds a field/value pair to the payload report.
258*5a923131SAndroid Build Coastguard Worker
259*5a923131SAndroid Build Coastguard Worker    Args:
260*5a923131SAndroid Build Coastguard Worker      name: The field's name.
261*5a923131SAndroid Build Coastguard Worker      value: The field's value.
262*5a923131SAndroid Build Coastguard Worker      linebreak: Whether the value should be printed on a new line.
263*5a923131SAndroid Build Coastguard Worker      indent: Amount of extra indent for each line of the value.
264*5a923131SAndroid Build Coastguard Worker    """
265*5a923131SAndroid Build Coastguard Worker    assert not self.is_finalized
266*5a923131SAndroid Build Coastguard Worker    if name and self.last_section.max_field_name_len < len(name):
267*5a923131SAndroid Build Coastguard Worker      self.last_section.max_field_name_len = len(name)
268*5a923131SAndroid Build Coastguard Worker    self.report.append(self.FieldNode(name, value, linebreak, indent))
269*5a923131SAndroid Build Coastguard Worker
270*5a923131SAndroid Build Coastguard Worker  def AddSubReport(self, title):
271*5a923131SAndroid Build Coastguard Worker    """Adds and returns a sub-report with a title."""
272*5a923131SAndroid Build Coastguard Worker    assert not self.is_finalized
273*5a923131SAndroid Build Coastguard Worker    sub_report = self.SubReportNode(title, type(self)())
274*5a923131SAndroid Build Coastguard Worker    self.report.append(sub_report)
275*5a923131SAndroid Build Coastguard Worker    return sub_report.report
276*5a923131SAndroid Build Coastguard Worker
277*5a923131SAndroid Build Coastguard Worker  def AddSection(self, title):
278*5a923131SAndroid Build Coastguard Worker    """Adds a new section title."""
279*5a923131SAndroid Build Coastguard Worker    assert not self.is_finalized
280*5a923131SAndroid Build Coastguard Worker    self.last_section = self.SectionNode(title)
281*5a923131SAndroid Build Coastguard Worker    self.report.append(self.last_section)
282*5a923131SAndroid Build Coastguard Worker
283*5a923131SAndroid Build Coastguard Worker  def Finalize(self):
284*5a923131SAndroid Build Coastguard Worker    """Seals the report, marking it as complete."""
285*5a923131SAndroid Build Coastguard Worker    self.is_finalized = True
286*5a923131SAndroid Build Coastguard Worker
287*5a923131SAndroid Build Coastguard Worker
288*5a923131SAndroid Build Coastguard Worker#
289*5a923131SAndroid Build Coastguard Worker# Payload verification.
290*5a923131SAndroid Build Coastguard Worker#
291*5a923131SAndroid Build Coastguard Worker
292*5a923131SAndroid Build Coastguard Workerclass PayloadChecker(object):
293*5a923131SAndroid Build Coastguard Worker  """Checking the integrity of an update payload.
294*5a923131SAndroid Build Coastguard Worker
295*5a923131SAndroid Build Coastguard Worker  This is a short-lived object whose purpose is to isolate the logic used for
296*5a923131SAndroid Build Coastguard Worker  verifying the integrity of an update payload.
297*5a923131SAndroid Build Coastguard Worker  """
298*5a923131SAndroid Build Coastguard Worker
299*5a923131SAndroid Build Coastguard Worker  def __init__(self, payload, assert_type=None, block_size=0,
300*5a923131SAndroid Build Coastguard Worker               allow_unhashed=False, disabled_tests=()):
301*5a923131SAndroid Build Coastguard Worker    """Initialize the checker.
302*5a923131SAndroid Build Coastguard Worker
303*5a923131SAndroid Build Coastguard Worker    Args:
304*5a923131SAndroid Build Coastguard Worker      payload: The payload object to check.
305*5a923131SAndroid Build Coastguard Worker      assert_type: Assert that payload is either 'full' or 'delta' (optional).
306*5a923131SAndroid Build Coastguard Worker      block_size: Expected filesystem / payload block size (optional).
307*5a923131SAndroid Build Coastguard Worker      allow_unhashed: Allow operations with unhashed data blobs.
308*5a923131SAndroid Build Coastguard Worker      disabled_tests: Sequence of tests to disable.
309*5a923131SAndroid Build Coastguard Worker    """
310*5a923131SAndroid Build Coastguard Worker    if not payload.is_init:
311*5a923131SAndroid Build Coastguard Worker      raise ValueError('Uninitialized update payload.')
312*5a923131SAndroid Build Coastguard Worker
313*5a923131SAndroid Build Coastguard Worker    # Set checker configuration.
314*5a923131SAndroid Build Coastguard Worker    self.payload = payload
315*5a923131SAndroid Build Coastguard Worker    self.block_size = block_size if block_size else _DEFAULT_BLOCK_SIZE
316*5a923131SAndroid Build Coastguard Worker    if not _IsPowerOfTwo(self.block_size):
317*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
318*5a923131SAndroid Build Coastguard Worker          'Expected block (%d) size is not a power of two.' % self.block_size)
319*5a923131SAndroid Build Coastguard Worker    if assert_type not in (None, _TYPE_FULL, _TYPE_DELTA):
320*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('Invalid assert_type value (%r).' %
321*5a923131SAndroid Build Coastguard Worker                               assert_type)
322*5a923131SAndroid Build Coastguard Worker    self.payload_type = assert_type
323*5a923131SAndroid Build Coastguard Worker    self.allow_unhashed = allow_unhashed
324*5a923131SAndroid Build Coastguard Worker
325*5a923131SAndroid Build Coastguard Worker    # Disable specific tests.
326*5a923131SAndroid Build Coastguard Worker    self.check_move_same_src_dst_block = (
327*5a923131SAndroid Build Coastguard Worker        _CHECK_MOVE_SAME_SRC_DST_BLOCK not in disabled_tests)
328*5a923131SAndroid Build Coastguard Worker    self.check_payload_sig = _CHECK_PAYLOAD_SIG not in disabled_tests
329*5a923131SAndroid Build Coastguard Worker
330*5a923131SAndroid Build Coastguard Worker    # Reset state; these will be assigned when the manifest is checked.
331*5a923131SAndroid Build Coastguard Worker    self.sigs_offset = 0
332*5a923131SAndroid Build Coastguard Worker    self.sigs_size = 0
333*5a923131SAndroid Build Coastguard Worker    self.old_part_info = {}
334*5a923131SAndroid Build Coastguard Worker    self.new_part_info = {}
335*5a923131SAndroid Build Coastguard Worker    self.new_fs_sizes = collections.defaultdict(int)
336*5a923131SAndroid Build Coastguard Worker    self.old_fs_sizes = collections.defaultdict(int)
337*5a923131SAndroid Build Coastguard Worker    self.minor_version = None
338*5a923131SAndroid Build Coastguard Worker    self.major_version = None
339*5a923131SAndroid Build Coastguard Worker
340*5a923131SAndroid Build Coastguard Worker  @staticmethod
341*5a923131SAndroid Build Coastguard Worker  def _CheckElem(msg, name, report, is_mandatory, is_submsg, convert=str,
342*5a923131SAndroid Build Coastguard Worker                 msg_name=None, linebreak=False, indent=0):
343*5a923131SAndroid Build Coastguard Worker    """Adds an element from a protobuf message to the payload report.
344*5a923131SAndroid Build Coastguard Worker
345*5a923131SAndroid Build Coastguard Worker    Checks to see whether a message contains a given element, and if so adds
346*5a923131SAndroid Build Coastguard Worker    the element value to the provided report. A missing mandatory element
347*5a923131SAndroid Build Coastguard Worker    causes an exception to be raised.
348*5a923131SAndroid Build Coastguard Worker
349*5a923131SAndroid Build Coastguard Worker    Args:
350*5a923131SAndroid Build Coastguard Worker      msg: The message containing the element.
351*5a923131SAndroid Build Coastguard Worker      name: The name of the element.
352*5a923131SAndroid Build Coastguard Worker      report: A report object to add the element name/value to.
353*5a923131SAndroid Build Coastguard Worker      is_mandatory: Whether or not this element must be present.
354*5a923131SAndroid Build Coastguard Worker      is_submsg: Whether this element is itself a message.
355*5a923131SAndroid Build Coastguard Worker      convert: A function for converting the element value for reporting.
356*5a923131SAndroid Build Coastguard Worker      msg_name: The name of the message object (for error reporting).
357*5a923131SAndroid Build Coastguard Worker      linebreak: Whether the value report should induce a line break.
358*5a923131SAndroid Build Coastguard Worker      indent: Amount of indent used for reporting the value.
359*5a923131SAndroid Build Coastguard Worker
360*5a923131SAndroid Build Coastguard Worker    Returns:
361*5a923131SAndroid Build Coastguard Worker      A pair consisting of the element value and the generated sub-report for
362*5a923131SAndroid Build Coastguard Worker      it (if the element is a sub-message, None otherwise). If the element is
363*5a923131SAndroid Build Coastguard Worker      missing, returns (None, None).
364*5a923131SAndroid Build Coastguard Worker
365*5a923131SAndroid Build Coastguard Worker    Raises:
366*5a923131SAndroid Build Coastguard Worker      error.PayloadError if a mandatory element is missing.
367*5a923131SAndroid Build Coastguard Worker    """
368*5a923131SAndroid Build Coastguard Worker    element_result = collections.namedtuple('element_result', ['msg', 'report'])
369*5a923131SAndroid Build Coastguard Worker
370*5a923131SAndroid Build Coastguard Worker    if not msg.HasField(name):
371*5a923131SAndroid Build Coastguard Worker      if is_mandatory:
372*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%smissing mandatory %s %r.' %
373*5a923131SAndroid Build Coastguard Worker                                 (msg_name + ' ' if msg_name else '',
374*5a923131SAndroid Build Coastguard Worker                                  'sub-message' if is_submsg else 'field',
375*5a923131SAndroid Build Coastguard Worker                                  name))
376*5a923131SAndroid Build Coastguard Worker      return element_result(None, None)
377*5a923131SAndroid Build Coastguard Worker
378*5a923131SAndroid Build Coastguard Worker    value = getattr(msg, name)
379*5a923131SAndroid Build Coastguard Worker    if is_submsg:
380*5a923131SAndroid Build Coastguard Worker      return element_result(value, report and report.AddSubReport(name))
381*5a923131SAndroid Build Coastguard Worker    else:
382*5a923131SAndroid Build Coastguard Worker      if report:
383*5a923131SAndroid Build Coastguard Worker        report.AddField(name, convert(value), linebreak=linebreak,
384*5a923131SAndroid Build Coastguard Worker                        indent=indent)
385*5a923131SAndroid Build Coastguard Worker      return element_result(value, None)
386*5a923131SAndroid Build Coastguard Worker
387*5a923131SAndroid Build Coastguard Worker  @staticmethod
388*5a923131SAndroid Build Coastguard Worker  def _CheckRepeatedElemNotPresent(msg, field_name, msg_name):
389*5a923131SAndroid Build Coastguard Worker    """Checks that a repeated element is not specified in the message.
390*5a923131SAndroid Build Coastguard Worker
391*5a923131SAndroid Build Coastguard Worker    Args:
392*5a923131SAndroid Build Coastguard Worker      msg: The message containing the element.
393*5a923131SAndroid Build Coastguard Worker      field_name: The name of the element.
394*5a923131SAndroid Build Coastguard Worker      msg_name: The name of the message object (for error reporting).
395*5a923131SAndroid Build Coastguard Worker
396*5a923131SAndroid Build Coastguard Worker    Raises:
397*5a923131SAndroid Build Coastguard Worker      error.PayloadError if the repeated element is present or non-empty.
398*5a923131SAndroid Build Coastguard Worker    """
399*5a923131SAndroid Build Coastguard Worker    if getattr(msg, field_name, None):
400*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%sfield %r not empty.' %
401*5a923131SAndroid Build Coastguard Worker                               (msg_name + ' ' if msg_name else '', field_name))
402*5a923131SAndroid Build Coastguard Worker
403*5a923131SAndroid Build Coastguard Worker  @staticmethod
404*5a923131SAndroid Build Coastguard Worker  def _CheckElemNotPresent(msg, field_name, msg_name):
405*5a923131SAndroid Build Coastguard Worker    """Checks that an element is not specified in the message.
406*5a923131SAndroid Build Coastguard Worker
407*5a923131SAndroid Build Coastguard Worker    Args:
408*5a923131SAndroid Build Coastguard Worker      msg: The message containing the element.
409*5a923131SAndroid Build Coastguard Worker      field_name: The name of the element.
410*5a923131SAndroid Build Coastguard Worker      msg_name: The name of the message object (for error reporting).
411*5a923131SAndroid Build Coastguard Worker
412*5a923131SAndroid Build Coastguard Worker    Raises:
413*5a923131SAndroid Build Coastguard Worker      error.PayloadError if the repeated element is present.
414*5a923131SAndroid Build Coastguard Worker    """
415*5a923131SAndroid Build Coastguard Worker    if msg.HasField(field_name):
416*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%sfield %r exists.' %
417*5a923131SAndroid Build Coastguard Worker                               (msg_name + ' ' if msg_name else '', field_name))
418*5a923131SAndroid Build Coastguard Worker
419*5a923131SAndroid Build Coastguard Worker  @staticmethod
420*5a923131SAndroid Build Coastguard Worker  def _CheckMandatoryField(msg, field_name, report, msg_name, convert=str,
421*5a923131SAndroid Build Coastguard Worker                           linebreak=False, indent=0):
422*5a923131SAndroid Build Coastguard Worker    """Adds a mandatory field; returning first component from _CheckElem."""
423*5a923131SAndroid Build Coastguard Worker    return PayloadChecker._CheckElem(msg, field_name, report, True, False,
424*5a923131SAndroid Build Coastguard Worker                                     convert=convert, msg_name=msg_name,
425*5a923131SAndroid Build Coastguard Worker                                     linebreak=linebreak, indent=indent)[0]
426*5a923131SAndroid Build Coastguard Worker
427*5a923131SAndroid Build Coastguard Worker  @staticmethod
428*5a923131SAndroid Build Coastguard Worker  def _CheckOptionalField(msg, field_name, report, convert=str,
429*5a923131SAndroid Build Coastguard Worker                          linebreak=False, indent=0):
430*5a923131SAndroid Build Coastguard Worker    """Adds an optional field; returning first component from _CheckElem."""
431*5a923131SAndroid Build Coastguard Worker    return PayloadChecker._CheckElem(msg, field_name, report, False, False,
432*5a923131SAndroid Build Coastguard Worker                                     convert=convert, linebreak=linebreak,
433*5a923131SAndroid Build Coastguard Worker                                     indent=indent)[0]
434*5a923131SAndroid Build Coastguard Worker
435*5a923131SAndroid Build Coastguard Worker  @staticmethod
436*5a923131SAndroid Build Coastguard Worker  def _CheckMandatorySubMsg(msg, submsg_name, report, msg_name):
437*5a923131SAndroid Build Coastguard Worker    """Adds a mandatory sub-message; wrapper for _CheckElem."""
438*5a923131SAndroid Build Coastguard Worker    return PayloadChecker._CheckElem(msg, submsg_name, report, True, True,
439*5a923131SAndroid Build Coastguard Worker                                     msg_name)
440*5a923131SAndroid Build Coastguard Worker
441*5a923131SAndroid Build Coastguard Worker  @staticmethod
442*5a923131SAndroid Build Coastguard Worker  def _CheckOptionalSubMsg(msg, submsg_name, report):
443*5a923131SAndroid Build Coastguard Worker    """Adds an optional sub-message; wrapper for _CheckElem."""
444*5a923131SAndroid Build Coastguard Worker    return PayloadChecker._CheckElem(msg, submsg_name, report, False, True)
445*5a923131SAndroid Build Coastguard Worker
446*5a923131SAndroid Build Coastguard Worker  @staticmethod
447*5a923131SAndroid Build Coastguard Worker  def _CheckPresentIff(val1, val2, name1, name2, obj_name):
448*5a923131SAndroid Build Coastguard Worker    """Checks that val1 is None iff val2 is None.
449*5a923131SAndroid Build Coastguard Worker
450*5a923131SAndroid Build Coastguard Worker    Args:
451*5a923131SAndroid Build Coastguard Worker      val1: first value to be compared.
452*5a923131SAndroid Build Coastguard Worker      val2: second value to be compared.
453*5a923131SAndroid Build Coastguard Worker      name1: name of object holding the first value.
454*5a923131SAndroid Build Coastguard Worker      name2: name of object holding the second value.
455*5a923131SAndroid Build Coastguard Worker      obj_name: Name of the object containing these values.
456*5a923131SAndroid Build Coastguard Worker
457*5a923131SAndroid Build Coastguard Worker    Raises:
458*5a923131SAndroid Build Coastguard Worker      error.PayloadError if assertion does not hold.
459*5a923131SAndroid Build Coastguard Worker    """
460*5a923131SAndroid Build Coastguard Worker    if None in (val1, val2) and val1 is not val2:
461*5a923131SAndroid Build Coastguard Worker      present, missing = (name1, name2) if val2 is None else (name2, name1)
462*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%r present without %r%s.' %
463*5a923131SAndroid Build Coastguard Worker                               (present, missing,
464*5a923131SAndroid Build Coastguard Worker                                ' in ' + obj_name if obj_name else ''))
465*5a923131SAndroid Build Coastguard Worker
466*5a923131SAndroid Build Coastguard Worker  @staticmethod
467*5a923131SAndroid Build Coastguard Worker  def _CheckPresentIffMany(vals, name, obj_name):
468*5a923131SAndroid Build Coastguard Worker    """Checks that a set of vals and names imply every other element.
469*5a923131SAndroid Build Coastguard Worker
470*5a923131SAndroid Build Coastguard Worker    Args:
471*5a923131SAndroid Build Coastguard Worker      vals: The set of values to be compared.
472*5a923131SAndroid Build Coastguard Worker      name: The name of the objects holding the corresponding value.
473*5a923131SAndroid Build Coastguard Worker      obj_name: Name of the object containing these values.
474*5a923131SAndroid Build Coastguard Worker
475*5a923131SAndroid Build Coastguard Worker    Raises:
476*5a923131SAndroid Build Coastguard Worker      error.PayloadError if assertion does not hold.
477*5a923131SAndroid Build Coastguard Worker    """
478*5a923131SAndroid Build Coastguard Worker    if any(vals) and not all(vals):
479*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%r is not present in all values%s.' %
480*5a923131SAndroid Build Coastguard Worker                               (name, ' in ' + obj_name if obj_name else ''))
481*5a923131SAndroid Build Coastguard Worker
482*5a923131SAndroid Build Coastguard Worker  @staticmethod
483*5a923131SAndroid Build Coastguard Worker  def _Run(cmd, send_data=None):
484*5a923131SAndroid Build Coastguard Worker    """Runs a subprocess, returns its output.
485*5a923131SAndroid Build Coastguard Worker
486*5a923131SAndroid Build Coastguard Worker    Args:
487*5a923131SAndroid Build Coastguard Worker      cmd: Sequence of command-line argument for invoking the subprocess.
488*5a923131SAndroid Build Coastguard Worker      send_data: Data to feed to the process via its stdin.
489*5a923131SAndroid Build Coastguard Worker
490*5a923131SAndroid Build Coastguard Worker    Returns:
491*5a923131SAndroid Build Coastguard Worker      A tuple containing the stdout and stderr output of the process.
492*5a923131SAndroid Build Coastguard Worker    """
493*5a923131SAndroid Build Coastguard Worker    run_process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
494*5a923131SAndroid Build Coastguard Worker                                   stdout=subprocess.PIPE)
495*5a923131SAndroid Build Coastguard Worker    try:
496*5a923131SAndroid Build Coastguard Worker      result = run_process.communicate(input=send_data)
497*5a923131SAndroid Build Coastguard Worker    finally:
498*5a923131SAndroid Build Coastguard Worker      exit_code = run_process.wait()
499*5a923131SAndroid Build Coastguard Worker
500*5a923131SAndroid Build Coastguard Worker    if exit_code:
501*5a923131SAndroid Build Coastguard Worker      raise RuntimeError('Subprocess %r failed with code %r.' %
502*5a923131SAndroid Build Coastguard Worker                         (cmd, exit_code))
503*5a923131SAndroid Build Coastguard Worker
504*5a923131SAndroid Build Coastguard Worker    return result
505*5a923131SAndroid Build Coastguard Worker
506*5a923131SAndroid Build Coastguard Worker  @staticmethod
507*5a923131SAndroid Build Coastguard Worker  def _CheckSha256Signature(sig_data, pubkey_file_name, actual_hash, sig_name):
508*5a923131SAndroid Build Coastguard Worker    """Verifies an actual hash against a signed one.
509*5a923131SAndroid Build Coastguard Worker
510*5a923131SAndroid Build Coastguard Worker    Args:
511*5a923131SAndroid Build Coastguard Worker      sig_data: The raw signature data.
512*5a923131SAndroid Build Coastguard Worker      pubkey_file_name: Public key used for verifying signature.
513*5a923131SAndroid Build Coastguard Worker      actual_hash: The actual hash digest.
514*5a923131SAndroid Build Coastguard Worker      sig_name: Signature name for error reporting.
515*5a923131SAndroid Build Coastguard Worker
516*5a923131SAndroid Build Coastguard Worker    Raises:
517*5a923131SAndroid Build Coastguard Worker      error.PayloadError if signature could not be verified.
518*5a923131SAndroid Build Coastguard Worker    """
519*5a923131SAndroid Build Coastguard Worker    if len(sig_data) != 256:
520*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
521*5a923131SAndroid Build Coastguard Worker          '%s: signature size (%d) not as expected (256).' %
522*5a923131SAndroid Build Coastguard Worker          (sig_name, len(sig_data)))
523*5a923131SAndroid Build Coastguard Worker    signed_data, _ = PayloadChecker._Run(
524*5a923131SAndroid Build Coastguard Worker        ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', pubkey_file_name],
525*5a923131SAndroid Build Coastguard Worker        send_data=sig_data)
526*5a923131SAndroid Build Coastguard Worker
527*5a923131SAndroid Build Coastguard Worker    if len(signed_data) != len(common.SIG_ASN1_HEADER) + 32:
528*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: unexpected signed data length (%d).' %
529*5a923131SAndroid Build Coastguard Worker                               (sig_name, len(signed_data)))
530*5a923131SAndroid Build Coastguard Worker
531*5a923131SAndroid Build Coastguard Worker    if not signed_data.startswith(common.SIG_ASN1_HEADER):
532*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: not containing standard ASN.1 prefix.' %
533*5a923131SAndroid Build Coastguard Worker                               sig_name)
534*5a923131SAndroid Build Coastguard Worker
535*5a923131SAndroid Build Coastguard Worker    signed_hash = signed_data[len(common.SIG_ASN1_HEADER):]
536*5a923131SAndroid Build Coastguard Worker    if signed_hash != actual_hash:
537*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
538*5a923131SAndroid Build Coastguard Worker          '%s: signed hash (%s) different from actual (%s).' %
539*5a923131SAndroid Build Coastguard Worker          (sig_name, common.FormatSha256(signed_hash),
540*5a923131SAndroid Build Coastguard Worker           common.FormatSha256(actual_hash)))
541*5a923131SAndroid Build Coastguard Worker
542*5a923131SAndroid Build Coastguard Worker  @staticmethod
543*5a923131SAndroid Build Coastguard Worker  def _CheckBlocksFitLength(length, num_blocks, block_size, length_name,
544*5a923131SAndroid Build Coastguard Worker                            block_name=None):
545*5a923131SAndroid Build Coastguard Worker    """Checks that a given length fits given block space.
546*5a923131SAndroid Build Coastguard Worker
547*5a923131SAndroid Build Coastguard Worker    This ensures that the number of blocks allocated is appropriate for the
548*5a923131SAndroid Build Coastguard Worker    length of the data residing in these blocks.
549*5a923131SAndroid Build Coastguard Worker
550*5a923131SAndroid Build Coastguard Worker    Args:
551*5a923131SAndroid Build Coastguard Worker      length: The actual length of the data.
552*5a923131SAndroid Build Coastguard Worker      num_blocks: The number of blocks allocated for it.
553*5a923131SAndroid Build Coastguard Worker      block_size: The size of each block in bytes.
554*5a923131SAndroid Build Coastguard Worker      length_name: Name of length (used for error reporting).
555*5a923131SAndroid Build Coastguard Worker      block_name: Name of block (used for error reporting).
556*5a923131SAndroid Build Coastguard Worker
557*5a923131SAndroid Build Coastguard Worker    Raises:
558*5a923131SAndroid Build Coastguard Worker      error.PayloadError if the aforementioned invariant is not satisfied.
559*5a923131SAndroid Build Coastguard Worker    """
560*5a923131SAndroid Build Coastguard Worker    # Check: length <= num_blocks * block_size.
561*5a923131SAndroid Build Coastguard Worker    if length > num_blocks * block_size:
562*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
563*5a923131SAndroid Build Coastguard Worker          '%s (%d) > num %sblocks (%d) * block_size (%d).' %
564*5a923131SAndroid Build Coastguard Worker          (length_name, length, block_name or '', num_blocks, block_size))
565*5a923131SAndroid Build Coastguard Worker
566*5a923131SAndroid Build Coastguard Worker    # Check: length > (num_blocks - 1) * block_size.
567*5a923131SAndroid Build Coastguard Worker    if length <= (num_blocks - 1) * block_size:
568*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
569*5a923131SAndroid Build Coastguard Worker          '%s (%d) <= (num %sblocks - 1 (%d)) * block_size (%d).' %
570*5a923131SAndroid Build Coastguard Worker          (length_name, length, block_name or '', num_blocks - 1, block_size))
571*5a923131SAndroid Build Coastguard Worker
572*5a923131SAndroid Build Coastguard Worker  def _CheckManifestMinorVersion(self, report):
573*5a923131SAndroid Build Coastguard Worker    """Checks the payload manifest minor_version field.
574*5a923131SAndroid Build Coastguard Worker
575*5a923131SAndroid Build Coastguard Worker    Args:
576*5a923131SAndroid Build Coastguard Worker      report: The report object to add to.
577*5a923131SAndroid Build Coastguard Worker
578*5a923131SAndroid Build Coastguard Worker    Raises:
579*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any of the checks fail.
580*5a923131SAndroid Build Coastguard Worker    """
581*5a923131SAndroid Build Coastguard Worker    self.minor_version = self._CheckOptionalField(self.payload.manifest,
582*5a923131SAndroid Build Coastguard Worker                                                  'minor_version', report)
583*5a923131SAndroid Build Coastguard Worker    if self.minor_version in _SUPPORTED_MINOR_VERSIONS:
584*5a923131SAndroid Build Coastguard Worker      if self.payload_type not in _SUPPORTED_MINOR_VERSIONS[self.minor_version]:
585*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
586*5a923131SAndroid Build Coastguard Worker            'Minor version %d not compatible with payload type %s.' %
587*5a923131SAndroid Build Coastguard Worker            (self.minor_version, self.payload_type))
588*5a923131SAndroid Build Coastguard Worker    elif self.minor_version is None:
589*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('Minor version is not set.')
590*5a923131SAndroid Build Coastguard Worker    else:
591*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('Unsupported minor version: %d' %
592*5a923131SAndroid Build Coastguard Worker                               self.minor_version)
593*5a923131SAndroid Build Coastguard Worker
594*5a923131SAndroid Build Coastguard Worker  def _CheckManifest(self, report, part_sizes=None):
595*5a923131SAndroid Build Coastguard Worker    """Checks the payload manifest.
596*5a923131SAndroid Build Coastguard Worker
597*5a923131SAndroid Build Coastguard Worker    Args:
598*5a923131SAndroid Build Coastguard Worker      report: A report object to add to.
599*5a923131SAndroid Build Coastguard Worker      part_sizes: Map of partition label to partition size in bytes.
600*5a923131SAndroid Build Coastguard Worker
601*5a923131SAndroid Build Coastguard Worker    Returns:
602*5a923131SAndroid Build Coastguard Worker      A tuple consisting of the partition block size used during the update
603*5a923131SAndroid Build Coastguard Worker      (integer), the signatures block offset and size.
604*5a923131SAndroid Build Coastguard Worker
605*5a923131SAndroid Build Coastguard Worker    Raises:
606*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any of the checks fail.
607*5a923131SAndroid Build Coastguard Worker    """
608*5a923131SAndroid Build Coastguard Worker    self.major_version = self.payload.header.version
609*5a923131SAndroid Build Coastguard Worker
610*5a923131SAndroid Build Coastguard Worker    part_sizes = part_sizes or collections.defaultdict(int)
611*5a923131SAndroid Build Coastguard Worker    manifest = self.payload.manifest
612*5a923131SAndroid Build Coastguard Worker    report.AddSection('manifest')
613*5a923131SAndroid Build Coastguard Worker
614*5a923131SAndroid Build Coastguard Worker    # Check: block_size must exist and match the expected value.
615*5a923131SAndroid Build Coastguard Worker    actual_block_size = self._CheckMandatoryField(manifest, 'block_size',
616*5a923131SAndroid Build Coastguard Worker                                                  report, 'manifest')
617*5a923131SAndroid Build Coastguard Worker    if actual_block_size != self.block_size:
618*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('Block_size (%d) not as expected (%d).' %
619*5a923131SAndroid Build Coastguard Worker                               (actual_block_size, self.block_size))
620*5a923131SAndroid Build Coastguard Worker
621*5a923131SAndroid Build Coastguard Worker    # Check: signatures_offset <==> signatures_size.
622*5a923131SAndroid Build Coastguard Worker    self.sigs_offset = self._CheckOptionalField(manifest, 'signatures_offset',
623*5a923131SAndroid Build Coastguard Worker                                                report)
624*5a923131SAndroid Build Coastguard Worker    self.sigs_size = self._CheckOptionalField(manifest, 'signatures_size',
625*5a923131SAndroid Build Coastguard Worker                                              report)
626*5a923131SAndroid Build Coastguard Worker    self._CheckPresentIff(self.sigs_offset, self.sigs_size,
627*5a923131SAndroid Build Coastguard Worker                          'signatures_offset', 'signatures_size', 'manifest')
628*5a923131SAndroid Build Coastguard Worker
629*5a923131SAndroid Build Coastguard Worker    for part in manifest.partitions:
630*5a923131SAndroid Build Coastguard Worker      name = part.partition_name
631*5a923131SAndroid Build Coastguard Worker      self.old_part_info[name] = self._CheckOptionalSubMsg(
632*5a923131SAndroid Build Coastguard Worker          part, 'old_partition_info', report)
633*5a923131SAndroid Build Coastguard Worker      self.new_part_info[name] = self._CheckMandatorySubMsg(
634*5a923131SAndroid Build Coastguard Worker          part, 'new_partition_info', report, 'manifest.partitions')
635*5a923131SAndroid Build Coastguard Worker
636*5a923131SAndroid Build Coastguard Worker    # Check: Old-style partition infos should not be specified.
637*5a923131SAndroid Build Coastguard Worker    for _, part in common.CROS_PARTITIONS:
638*5a923131SAndroid Build Coastguard Worker      self._CheckElemNotPresent(manifest, 'old_%s_info' % part, 'manifest')
639*5a923131SAndroid Build Coastguard Worker      self._CheckElemNotPresent(manifest, 'new_%s_info' % part, 'manifest')
640*5a923131SAndroid Build Coastguard Worker
641*5a923131SAndroid Build Coastguard Worker    # Check: If old_partition_info is specified anywhere, it must be
642*5a923131SAndroid Build Coastguard Worker    # specified everywhere.
643*5a923131SAndroid Build Coastguard Worker    old_part_msgs = [part.msg for part in self.old_part_info.values() if part]
644*5a923131SAndroid Build Coastguard Worker    self._CheckPresentIffMany(old_part_msgs, 'old_partition_info',
645*5a923131SAndroid Build Coastguard Worker                              'manifest.partitions')
646*5a923131SAndroid Build Coastguard Worker
647*5a923131SAndroid Build Coastguard Worker    is_delta = any(part and part.msg for part in self.old_part_info.values())
648*5a923131SAndroid Build Coastguard Worker    if is_delta:
649*5a923131SAndroid Build Coastguard Worker      # Assert/mark delta payload.
650*5a923131SAndroid Build Coastguard Worker      if self.payload_type == _TYPE_FULL:
651*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
652*5a923131SAndroid Build Coastguard Worker            'Apparent full payload contains old_{kernel,rootfs}_info.')
653*5a923131SAndroid Build Coastguard Worker      self.payload_type = _TYPE_DELTA
654*5a923131SAndroid Build Coastguard Worker
655*5a923131SAndroid Build Coastguard Worker      for part, (msg, part_report) in self.old_part_info.items():
656*5a923131SAndroid Build Coastguard Worker        # Check: {size, hash} present in old_{kernel,rootfs}_info.
657*5a923131SAndroid Build Coastguard Worker        field = 'old_%s_info' % part
658*5a923131SAndroid Build Coastguard Worker        self.old_fs_sizes[part] = self._CheckMandatoryField(msg, 'size',
659*5a923131SAndroid Build Coastguard Worker                                                            part_report, field)
660*5a923131SAndroid Build Coastguard Worker        self._CheckMandatoryField(msg, 'hash', part_report, field,
661*5a923131SAndroid Build Coastguard Worker                                  convert=common.FormatSha256)
662*5a923131SAndroid Build Coastguard Worker
663*5a923131SAndroid Build Coastguard Worker        # Check: old_{kernel,rootfs} size must fit in respective partition.
664*5a923131SAndroid Build Coastguard Worker        if self.old_fs_sizes[part] > part_sizes[part] > 0:
665*5a923131SAndroid Build Coastguard Worker          raise error.PayloadError(
666*5a923131SAndroid Build Coastguard Worker              'Old %s content (%d) exceed partition size (%d).' %
667*5a923131SAndroid Build Coastguard Worker              (part, self.old_fs_sizes[part], part_sizes[part]))
668*5a923131SAndroid Build Coastguard Worker    else:
669*5a923131SAndroid Build Coastguard Worker      # Assert/mark full payload.
670*5a923131SAndroid Build Coastguard Worker      if self.payload_type == _TYPE_DELTA:
671*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
672*5a923131SAndroid Build Coastguard Worker            'Apparent delta payload missing old_{kernel,rootfs}_info.')
673*5a923131SAndroid Build Coastguard Worker      self.payload_type = _TYPE_FULL
674*5a923131SAndroid Build Coastguard Worker
675*5a923131SAndroid Build Coastguard Worker    # Check: new_{kernel,rootfs}_info present; contains {size, hash}.
676*5a923131SAndroid Build Coastguard Worker    for part, (msg, part_report) in self.new_part_info.items():
677*5a923131SAndroid Build Coastguard Worker      field = 'new_%s_info' % part
678*5a923131SAndroid Build Coastguard Worker      self.new_fs_sizes[part] = self._CheckMandatoryField(msg, 'size',
679*5a923131SAndroid Build Coastguard Worker                                                          part_report, field)
680*5a923131SAndroid Build Coastguard Worker      self._CheckMandatoryField(msg, 'hash', part_report, field,
681*5a923131SAndroid Build Coastguard Worker                                convert=common.FormatSha256)
682*5a923131SAndroid Build Coastguard Worker
683*5a923131SAndroid Build Coastguard Worker      # Check: new_{kernel,rootfs} size must fit in respective partition.
684*5a923131SAndroid Build Coastguard Worker      if self.new_fs_sizes[part] > part_sizes[part] > 0:
685*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
686*5a923131SAndroid Build Coastguard Worker            'New %s content (%d) exceed partition size (%d).' %
687*5a923131SAndroid Build Coastguard Worker            (part, self.new_fs_sizes[part], part_sizes[part]))
688*5a923131SAndroid Build Coastguard Worker
689*5a923131SAndroid Build Coastguard Worker    # Check: minor_version makes sense for the payload type. This check should
690*5a923131SAndroid Build Coastguard Worker    # run after the payload type has been set.
691*5a923131SAndroid Build Coastguard Worker    self._CheckManifestMinorVersion(report)
692*5a923131SAndroid Build Coastguard Worker
693*5a923131SAndroid Build Coastguard Worker  def _CheckLength(self, length, total_blocks, op_name, length_name):
694*5a923131SAndroid Build Coastguard Worker    """Checks whether a length matches the space designated in extents.
695*5a923131SAndroid Build Coastguard Worker
696*5a923131SAndroid Build Coastguard Worker    Args:
697*5a923131SAndroid Build Coastguard Worker      length: The total length of the data.
698*5a923131SAndroid Build Coastguard Worker      total_blocks: The total number of blocks in extents.
699*5a923131SAndroid Build Coastguard Worker      op_name: Operation name (for error reporting).
700*5a923131SAndroid Build Coastguard Worker      length_name: Length name (for error reporting).
701*5a923131SAndroid Build Coastguard Worker
702*5a923131SAndroid Build Coastguard Worker    Raises:
703*5a923131SAndroid Build Coastguard Worker      error.PayloadError is there a problem with the length.
704*5a923131SAndroid Build Coastguard Worker    """
705*5a923131SAndroid Build Coastguard Worker    # Check: length is non-zero.
706*5a923131SAndroid Build Coastguard Worker    if length == 0:
707*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: %s is zero.' % (op_name, length_name))
708*5a923131SAndroid Build Coastguard Worker
709*5a923131SAndroid Build Coastguard Worker    # Check that length matches number of blocks.
710*5a923131SAndroid Build Coastguard Worker    self._CheckBlocksFitLength(length, total_blocks, self.block_size,
711*5a923131SAndroid Build Coastguard Worker                               '%s: %s' % (op_name, length_name))
712*5a923131SAndroid Build Coastguard Worker
713*5a923131SAndroid Build Coastguard Worker  def _CheckExtents(self, extents, usable_size, block_counters, name):
714*5a923131SAndroid Build Coastguard Worker    """Checks a sequence of extents.
715*5a923131SAndroid Build Coastguard Worker
716*5a923131SAndroid Build Coastguard Worker    Args:
717*5a923131SAndroid Build Coastguard Worker      extents: The sequence of extents to check.
718*5a923131SAndroid Build Coastguard Worker      usable_size: The usable size of the partition to which the extents apply.
719*5a923131SAndroid Build Coastguard Worker      block_counters: Array of counters corresponding to the number of blocks.
720*5a923131SAndroid Build Coastguard Worker      name: The name of the extent block.
721*5a923131SAndroid Build Coastguard Worker
722*5a923131SAndroid Build Coastguard Worker    Returns:
723*5a923131SAndroid Build Coastguard Worker      The total number of blocks in the extents.
724*5a923131SAndroid Build Coastguard Worker
725*5a923131SAndroid Build Coastguard Worker    Raises:
726*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any of the entailed checks fails.
727*5a923131SAndroid Build Coastguard Worker    """
728*5a923131SAndroid Build Coastguard Worker    total_num_blocks = 0
729*5a923131SAndroid Build Coastguard Worker    for ex, ex_name in common.ExtentIter(extents, name):
730*5a923131SAndroid Build Coastguard Worker      # Check: Mandatory fields.
731*5a923131SAndroid Build Coastguard Worker      start_block = PayloadChecker._CheckMandatoryField(ex, 'start_block',
732*5a923131SAndroid Build Coastguard Worker                                                        None, ex_name)
733*5a923131SAndroid Build Coastguard Worker      num_blocks = PayloadChecker._CheckMandatoryField(ex, 'num_blocks', None,
734*5a923131SAndroid Build Coastguard Worker                                                       ex_name)
735*5a923131SAndroid Build Coastguard Worker      end_block = start_block + num_blocks
736*5a923131SAndroid Build Coastguard Worker
737*5a923131SAndroid Build Coastguard Worker      # Check: num_blocks > 0.
738*5a923131SAndroid Build Coastguard Worker      if num_blocks == 0:
739*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%s: extent length is zero.' % ex_name)
740*5a923131SAndroid Build Coastguard Worker
741*5a923131SAndroid Build Coastguard Worker      # Check: Make sure we're within the partition limit.
742*5a923131SAndroid Build Coastguard Worker      if usable_size and end_block * self.block_size > usable_size:
743*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
744*5a923131SAndroid Build Coastguard Worker            '%s: extent (%s) exceeds usable partition size (%d).' %
745*5a923131SAndroid Build Coastguard Worker            (ex_name, common.FormatExtent(ex, self.block_size), usable_size))
746*5a923131SAndroid Build Coastguard Worker
747*5a923131SAndroid Build Coastguard Worker      # Record block usage.
748*5a923131SAndroid Build Coastguard Worker      for i in range(start_block, end_block):
749*5a923131SAndroid Build Coastguard Worker        block_counters[i] += 1
750*5a923131SAndroid Build Coastguard Worker
751*5a923131SAndroid Build Coastguard Worker      total_num_blocks += num_blocks
752*5a923131SAndroid Build Coastguard Worker
753*5a923131SAndroid Build Coastguard Worker    return total_num_blocks
754*5a923131SAndroid Build Coastguard Worker
755*5a923131SAndroid Build Coastguard Worker  def _CheckReplaceOperation(self, op, data_length, total_dst_blocks, op_name):
756*5a923131SAndroid Build Coastguard Worker    """Specific checks for REPLACE/REPLACE_BZ/REPLACE_XZ operations.
757*5a923131SAndroid Build Coastguard Worker
758*5a923131SAndroid Build Coastguard Worker    Args:
759*5a923131SAndroid Build Coastguard Worker      op: The operation object from the manifest.
760*5a923131SAndroid Build Coastguard Worker      data_length: The length of the data blob associated with the operation.
761*5a923131SAndroid Build Coastguard Worker      total_dst_blocks: Total number of blocks in dst_extents.
762*5a923131SAndroid Build Coastguard Worker      op_name: Operation name for error reporting.
763*5a923131SAndroid Build Coastguard Worker
764*5a923131SAndroid Build Coastguard Worker    Raises:
765*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check fails.
766*5a923131SAndroid Build Coastguard Worker    """
767*5a923131SAndroid Build Coastguard Worker    # Check: total_dst_blocks is not a floating point.
768*5a923131SAndroid Build Coastguard Worker    if isinstance(total_dst_blocks, float):
769*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains invalid data type of '
770*5a923131SAndroid Build Coastguard Worker                               'total_dst_blocks.' % op_name)
771*5a923131SAndroid Build Coastguard Worker
772*5a923131SAndroid Build Coastguard Worker    # Check: Does not contain src extents.
773*5a923131SAndroid Build Coastguard Worker    if op.src_extents:
774*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains src_extents.' % op_name)
775*5a923131SAndroid Build Coastguard Worker
776*5a923131SAndroid Build Coastguard Worker    # Check: Contains data.
777*5a923131SAndroid Build Coastguard Worker    if data_length is None:
778*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
779*5a923131SAndroid Build Coastguard Worker
780*5a923131SAndroid Build Coastguard Worker    if op.type == common.OpType.REPLACE:
781*5a923131SAndroid Build Coastguard Worker      PayloadChecker._CheckBlocksFitLength(data_length, total_dst_blocks,
782*5a923131SAndroid Build Coastguard Worker                                           self.block_size,
783*5a923131SAndroid Build Coastguard Worker                                           op_name + '.data_length', 'dst')
784*5a923131SAndroid Build Coastguard Worker    else:
785*5a923131SAndroid Build Coastguard Worker      # Check: data_length must be smaller than the allotted dst blocks.
786*5a923131SAndroid Build Coastguard Worker      if data_length >= total_dst_blocks * self.block_size:
787*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
788*5a923131SAndroid Build Coastguard Worker            '%s: data_length (%d) must be less than allotted dst block '
789*5a923131SAndroid Build Coastguard Worker            'space (%d * %d).' %
790*5a923131SAndroid Build Coastguard Worker            (op_name, data_length, total_dst_blocks, self.block_size))
791*5a923131SAndroid Build Coastguard Worker
792*5a923131SAndroid Build Coastguard Worker  def _CheckZeroOperation(self, op, op_name):
793*5a923131SAndroid Build Coastguard Worker    """Specific checks for ZERO operations.
794*5a923131SAndroid Build Coastguard Worker
795*5a923131SAndroid Build Coastguard Worker    Args:
796*5a923131SAndroid Build Coastguard Worker      op: The operation object from the manifest.
797*5a923131SAndroid Build Coastguard Worker      op_name: Operation name for error reporting.
798*5a923131SAndroid Build Coastguard Worker
799*5a923131SAndroid Build Coastguard Worker    Raises:
800*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check fails.
801*5a923131SAndroid Build Coastguard Worker    """
802*5a923131SAndroid Build Coastguard Worker    # Check: Does not contain src extents, data_length and data_offset.
803*5a923131SAndroid Build Coastguard Worker    if op.src_extents:
804*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains src_extents.' % op_name)
805*5a923131SAndroid Build Coastguard Worker    if op.data_length:
806*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains data_length.' % op_name)
807*5a923131SAndroid Build Coastguard Worker    if op.data_offset:
808*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains data_offset.' % op_name)
809*5a923131SAndroid Build Coastguard Worker
810*5a923131SAndroid Build Coastguard Worker  def _CheckAnyDiffOperation(self, op, data_length, total_dst_blocks, op_name):
811*5a923131SAndroid Build Coastguard Worker    """Specific checks for SOURCE_BSDIFF, PUFFDIFF and BROTLI_BSDIFF
812*5a923131SAndroid Build Coastguard Worker       operations.
813*5a923131SAndroid Build Coastguard Worker
814*5a923131SAndroid Build Coastguard Worker    Args:
815*5a923131SAndroid Build Coastguard Worker      op: The operation.
816*5a923131SAndroid Build Coastguard Worker      data_length: The length of the data blob associated with the operation.
817*5a923131SAndroid Build Coastguard Worker      total_dst_blocks: Total number of blocks in dst_extents.
818*5a923131SAndroid Build Coastguard Worker      op_name: Operation name for error reporting.
819*5a923131SAndroid Build Coastguard Worker
820*5a923131SAndroid Build Coastguard Worker    Raises:
821*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check fails.
822*5a923131SAndroid Build Coastguard Worker    """
823*5a923131SAndroid Build Coastguard Worker    # Check: data_{offset,length} present.
824*5a923131SAndroid Build Coastguard Worker    if data_length is None:
825*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
826*5a923131SAndroid Build Coastguard Worker
827*5a923131SAndroid Build Coastguard Worker    # Check: data_length is strictly smaller than the allotted dst blocks.
828*5a923131SAndroid Build Coastguard Worker    if data_length >= total_dst_blocks * self.block_size:
829*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
830*5a923131SAndroid Build Coastguard Worker          '%s: data_length (%d) must be smaller than allotted dst space '
831*5a923131SAndroid Build Coastguard Worker          '(%d * %d = %d).' %
832*5a923131SAndroid Build Coastguard Worker          (op_name, data_length, total_dst_blocks, self.block_size,
833*5a923131SAndroid Build Coastguard Worker           total_dst_blocks * self.block_size))
834*5a923131SAndroid Build Coastguard Worker
835*5a923131SAndroid Build Coastguard Worker    # Check the existence of src_length and dst_length for legacy bsdiffs.
836*5a923131SAndroid Build Coastguard Worker    if op.type == common.OpType.SOURCE_BSDIFF and self.minor_version <= 3:
837*5a923131SAndroid Build Coastguard Worker      if not op.HasField('src_length') or not op.HasField('dst_length'):
838*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%s: require {src,dst}_length.' % op_name)
839*5a923131SAndroid Build Coastguard Worker    else:
840*5a923131SAndroid Build Coastguard Worker      if op.HasField('src_length') or op.HasField('dst_length'):
841*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%s: unneeded {src,dst}_length.' % op_name)
842*5a923131SAndroid Build Coastguard Worker
843*5a923131SAndroid Build Coastguard Worker  def _CheckSourceCopyOperation(self, data_offset, total_src_blocks,
844*5a923131SAndroid Build Coastguard Worker                                total_dst_blocks, op_name):
845*5a923131SAndroid Build Coastguard Worker    """Specific checks for SOURCE_COPY.
846*5a923131SAndroid Build Coastguard Worker
847*5a923131SAndroid Build Coastguard Worker    Args:
848*5a923131SAndroid Build Coastguard Worker      data_offset: The offset of a data blob for the operation.
849*5a923131SAndroid Build Coastguard Worker      total_src_blocks: Total number of blocks in src_extents.
850*5a923131SAndroid Build Coastguard Worker      total_dst_blocks: Total number of blocks in dst_extents.
851*5a923131SAndroid Build Coastguard Worker      op_name: Operation name for error reporting.
852*5a923131SAndroid Build Coastguard Worker
853*5a923131SAndroid Build Coastguard Worker    Raises:
854*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check fails.
855*5a923131SAndroid Build Coastguard Worker    """
856*5a923131SAndroid Build Coastguard Worker    # Check: No data_{offset,length}.
857*5a923131SAndroid Build Coastguard Worker    if data_offset is not None:
858*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
859*5a923131SAndroid Build Coastguard Worker
860*5a923131SAndroid Build Coastguard Worker    # Check: total_src_blocks == total_dst_blocks.
861*5a923131SAndroid Build Coastguard Worker    if total_src_blocks != total_dst_blocks:
862*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
863*5a923131SAndroid Build Coastguard Worker          '%s: total src blocks (%d) != total dst blocks (%d).' %
864*5a923131SAndroid Build Coastguard Worker          (op_name, total_src_blocks, total_dst_blocks))
865*5a923131SAndroid Build Coastguard Worker
866*5a923131SAndroid Build Coastguard Worker  def _CheckAnySourceOperation(self, op, total_src_blocks, op_name):
867*5a923131SAndroid Build Coastguard Worker    """Specific checks for SOURCE_* operations.
868*5a923131SAndroid Build Coastguard Worker
869*5a923131SAndroid Build Coastguard Worker    Args:
870*5a923131SAndroid Build Coastguard Worker      op: The operation object from the manifest.
871*5a923131SAndroid Build Coastguard Worker      total_src_blocks: Total number of blocks in src_extents.
872*5a923131SAndroid Build Coastguard Worker      op_name: Operation name for error reporting.
873*5a923131SAndroid Build Coastguard Worker
874*5a923131SAndroid Build Coastguard Worker    Raises:
875*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check fails.
876*5a923131SAndroid Build Coastguard Worker    """
877*5a923131SAndroid Build Coastguard Worker    # Check: total_src_blocks != 0.
878*5a923131SAndroid Build Coastguard Worker    if total_src_blocks == 0:
879*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: no src blocks in a source op.' % op_name)
880*5a923131SAndroid Build Coastguard Worker
881*5a923131SAndroid Build Coastguard Worker    # Check: src_sha256_hash present in minor version >= 3.
882*5a923131SAndroid Build Coastguard Worker    if self.minor_version >= 3 and op.src_sha256_hash is None:
883*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: source hash missing.' % op_name)
884*5a923131SAndroid Build Coastguard Worker
885*5a923131SAndroid Build Coastguard Worker  def _CheckOperation(self, op, op_name, old_block_counters, new_block_counters,
886*5a923131SAndroid Build Coastguard Worker                      old_usable_size, new_usable_size, prev_data_offset,
887*5a923131SAndroid Build Coastguard Worker                      blob_hash_counts):
888*5a923131SAndroid Build Coastguard Worker    """Checks a single update operation.
889*5a923131SAndroid Build Coastguard Worker
890*5a923131SAndroid Build Coastguard Worker    Args:
891*5a923131SAndroid Build Coastguard Worker      op: The operation object.
892*5a923131SAndroid Build Coastguard Worker      op_name: Operation name string for error reporting.
893*5a923131SAndroid Build Coastguard Worker      old_block_counters: Arrays of block read counters.
894*5a923131SAndroid Build Coastguard Worker      new_block_counters: Arrays of block write counters.
895*5a923131SAndroid Build Coastguard Worker      old_usable_size: The overall usable size for src data in bytes.
896*5a923131SAndroid Build Coastguard Worker      new_usable_size: The overall usable size for dst data in bytes.
897*5a923131SAndroid Build Coastguard Worker      prev_data_offset: Offset of last used data bytes.
898*5a923131SAndroid Build Coastguard Worker      blob_hash_counts: Counters for hashed/unhashed blobs.
899*5a923131SAndroid Build Coastguard Worker
900*5a923131SAndroid Build Coastguard Worker    Returns:
901*5a923131SAndroid Build Coastguard Worker      The amount of data blob associated with the operation.
902*5a923131SAndroid Build Coastguard Worker
903*5a923131SAndroid Build Coastguard Worker    Raises:
904*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any check has failed.
905*5a923131SAndroid Build Coastguard Worker    """
906*5a923131SAndroid Build Coastguard Worker    # Check extents.
907*5a923131SAndroid Build Coastguard Worker    total_src_blocks = self._CheckExtents(
908*5a923131SAndroid Build Coastguard Worker        op.src_extents, old_usable_size, old_block_counters,
909*5a923131SAndroid Build Coastguard Worker        op_name + '.src_extents')
910*5a923131SAndroid Build Coastguard Worker    total_dst_blocks = self._CheckExtents(
911*5a923131SAndroid Build Coastguard Worker        op.dst_extents, new_usable_size, new_block_counters,
912*5a923131SAndroid Build Coastguard Worker        op_name + '.dst_extents')
913*5a923131SAndroid Build Coastguard Worker
914*5a923131SAndroid Build Coastguard Worker    # Check: data_offset present <==> data_length present.
915*5a923131SAndroid Build Coastguard Worker    data_offset = self._CheckOptionalField(op, 'data_offset', None)
916*5a923131SAndroid Build Coastguard Worker    data_length = self._CheckOptionalField(op, 'data_length', None)
917*5a923131SAndroid Build Coastguard Worker    self._CheckPresentIff(data_offset, data_length, 'data_offset',
918*5a923131SAndroid Build Coastguard Worker                          'data_length', op_name)
919*5a923131SAndroid Build Coastguard Worker
920*5a923131SAndroid Build Coastguard Worker    # Check: At least one dst_extent.
921*5a923131SAndroid Build Coastguard Worker    if not op.dst_extents:
922*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('%s: dst_extents is empty.' % op_name)
923*5a923131SAndroid Build Coastguard Worker
924*5a923131SAndroid Build Coastguard Worker    # Check {src,dst}_length, if present.
925*5a923131SAndroid Build Coastguard Worker    if op.HasField('src_length'):
926*5a923131SAndroid Build Coastguard Worker      self._CheckLength(op.src_length, total_src_blocks, op_name, 'src_length')
927*5a923131SAndroid Build Coastguard Worker    if op.HasField('dst_length'):
928*5a923131SAndroid Build Coastguard Worker      self._CheckLength(op.dst_length, total_dst_blocks, op_name, 'dst_length')
929*5a923131SAndroid Build Coastguard Worker
930*5a923131SAndroid Build Coastguard Worker    if op.HasField('data_sha256_hash'):
931*5a923131SAndroid Build Coastguard Worker      blob_hash_counts['hashed'] += 1
932*5a923131SAndroid Build Coastguard Worker
933*5a923131SAndroid Build Coastguard Worker      # Check: Operation carries data.
934*5a923131SAndroid Build Coastguard Worker      if data_offset is None:
935*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
936*5a923131SAndroid Build Coastguard Worker            '%s: data_sha256_hash present but no data_{offset,length}.' %
937*5a923131SAndroid Build Coastguard Worker            op_name)
938*5a923131SAndroid Build Coastguard Worker
939*5a923131SAndroid Build Coastguard Worker      # Check: Hash verifies correctly.
940*5a923131SAndroid Build Coastguard Worker      actual_hash = hashlib.sha256(self.payload.ReadDataBlob(data_offset,
941*5a923131SAndroid Build Coastguard Worker                                                             data_length))
942*5a923131SAndroid Build Coastguard Worker      if op.data_sha256_hash != actual_hash.digest():
943*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
944*5a923131SAndroid Build Coastguard Worker            '%s: data_sha256_hash (%s) does not match actual hash (%s).' %
945*5a923131SAndroid Build Coastguard Worker            (op_name, common.FormatSha256(op.data_sha256_hash),
946*5a923131SAndroid Build Coastguard Worker             common.FormatSha256(actual_hash.digest())))
947*5a923131SAndroid Build Coastguard Worker    elif data_offset is not None:
948*5a923131SAndroid Build Coastguard Worker      if self.allow_unhashed:
949*5a923131SAndroid Build Coastguard Worker        blob_hash_counts['unhashed'] += 1
950*5a923131SAndroid Build Coastguard Worker      else:
951*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%s: unhashed operation not allowed.' %
952*5a923131SAndroid Build Coastguard Worker                                 op_name)
953*5a923131SAndroid Build Coastguard Worker
954*5a923131SAndroid Build Coastguard Worker    if data_offset is not None:
955*5a923131SAndroid Build Coastguard Worker      # Check: Contiguous use of data section.
956*5a923131SAndroid Build Coastguard Worker      if data_offset != prev_data_offset:
957*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
958*5a923131SAndroid Build Coastguard Worker            '%s: data offset (%d) not matching amount used so far (%d).' %
959*5a923131SAndroid Build Coastguard Worker            (op_name, data_offset, prev_data_offset))
960*5a923131SAndroid Build Coastguard Worker
961*5a923131SAndroid Build Coastguard Worker    # Type-specific checks.
962*5a923131SAndroid Build Coastguard Worker    if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
963*5a923131SAndroid Build Coastguard Worker                   common.OpType.REPLACE_XZ):
964*5a923131SAndroid Build Coastguard Worker      self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
965*5a923131SAndroid Build Coastguard Worker    elif op.type == common.OpType.ZERO and self.minor_version >= 4:
966*5a923131SAndroid Build Coastguard Worker      self._CheckZeroOperation(op, op_name)
967*5a923131SAndroid Build Coastguard Worker    elif op.type == common.OpType.SOURCE_COPY and self.minor_version >= 2:
968*5a923131SAndroid Build Coastguard Worker      self._CheckSourceCopyOperation(data_offset, total_src_blocks,
969*5a923131SAndroid Build Coastguard Worker                                     total_dst_blocks, op_name)
970*5a923131SAndroid Build Coastguard Worker      self._CheckAnySourceOperation(op, total_src_blocks, op_name)
971*5a923131SAndroid Build Coastguard Worker    elif op.type == common.OpType.SOURCE_BSDIFF and self.minor_version >= 2:
972*5a923131SAndroid Build Coastguard Worker      self._CheckAnyDiffOperation(op, data_length, total_dst_blocks, op_name)
973*5a923131SAndroid Build Coastguard Worker      self._CheckAnySourceOperation(op, total_src_blocks, op_name)
974*5a923131SAndroid Build Coastguard Worker    elif op.type == common.OpType.BROTLI_BSDIFF and self.minor_version >= 4:
975*5a923131SAndroid Build Coastguard Worker      self._CheckAnyDiffOperation(op, data_length, total_dst_blocks, op_name)
976*5a923131SAndroid Build Coastguard Worker      self._CheckAnySourceOperation(op, total_src_blocks, op_name)
977*5a923131SAndroid Build Coastguard Worker    elif op.type == common.OpType.PUFFDIFF and self.minor_version >= 5:
978*5a923131SAndroid Build Coastguard Worker      self._CheckAnyDiffOperation(op, data_length, total_dst_blocks, op_name)
979*5a923131SAndroid Build Coastguard Worker      self._CheckAnySourceOperation(op, total_src_blocks, op_name)
980*5a923131SAndroid Build Coastguard Worker    else:
981*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
982*5a923131SAndroid Build Coastguard Worker          'Operation %s (type %d) not allowed in minor version %d' %
983*5a923131SAndroid Build Coastguard Worker          (op_name, op.type, self.minor_version))
984*5a923131SAndroid Build Coastguard Worker    return data_length if data_length is not None else 0
985*5a923131SAndroid Build Coastguard Worker
986*5a923131SAndroid Build Coastguard Worker  def _SizeToNumBlocks(self, size):
987*5a923131SAndroid Build Coastguard Worker    """Returns the number of blocks needed to contain a given byte size."""
988*5a923131SAndroid Build Coastguard Worker    return (size + self.block_size - 1) // self.block_size
989*5a923131SAndroid Build Coastguard Worker
990*5a923131SAndroid Build Coastguard Worker  def _AllocBlockCounters(self, total_size):
991*5a923131SAndroid Build Coastguard Worker    """Returns a freshly initialized array of block counters.
992*5a923131SAndroid Build Coastguard Worker
993*5a923131SAndroid Build Coastguard Worker    Note that the generated array is not portable as is due to byte-ordering
994*5a923131SAndroid Build Coastguard Worker    issues, hence it should not be serialized.
995*5a923131SAndroid Build Coastguard Worker
996*5a923131SAndroid Build Coastguard Worker    Args:
997*5a923131SAndroid Build Coastguard Worker      total_size: The total block size in bytes.
998*5a923131SAndroid Build Coastguard Worker
999*5a923131SAndroid Build Coastguard Worker    Returns:
1000*5a923131SAndroid Build Coastguard Worker      An array of unsigned short elements initialized to zero, one for each of
1001*5a923131SAndroid Build Coastguard Worker      the blocks necessary for containing the partition.
1002*5a923131SAndroid Build Coastguard Worker    """
1003*5a923131SAndroid Build Coastguard Worker    return array.array('H',
1004*5a923131SAndroid Build Coastguard Worker                       itertools.repeat(0, self._SizeToNumBlocks(total_size)))
1005*5a923131SAndroid Build Coastguard Worker
1006*5a923131SAndroid Build Coastguard Worker  def _CheckOperations(self, operations, report, base_name, old_fs_size,
1007*5a923131SAndroid Build Coastguard Worker                       new_fs_size, old_usable_size, new_usable_size,
1008*5a923131SAndroid Build Coastguard Worker                       prev_data_offset):
1009*5a923131SAndroid Build Coastguard Worker    """Checks a sequence of update operations.
1010*5a923131SAndroid Build Coastguard Worker
1011*5a923131SAndroid Build Coastguard Worker    Args:
1012*5a923131SAndroid Build Coastguard Worker      operations: The sequence of operations to check.
1013*5a923131SAndroid Build Coastguard Worker      report: The report object to add to.
1014*5a923131SAndroid Build Coastguard Worker      base_name: The name of the operation block.
1015*5a923131SAndroid Build Coastguard Worker      old_fs_size: The old filesystem size in bytes.
1016*5a923131SAndroid Build Coastguard Worker      new_fs_size: The new filesystem size in bytes.
1017*5a923131SAndroid Build Coastguard Worker      old_usable_size: The overall usable size of the old partition in bytes.
1018*5a923131SAndroid Build Coastguard Worker      new_usable_size: The overall usable size of the new partition in bytes.
1019*5a923131SAndroid Build Coastguard Worker      prev_data_offset: Offset of last used data bytes.
1020*5a923131SAndroid Build Coastguard Worker
1021*5a923131SAndroid Build Coastguard Worker    Returns:
1022*5a923131SAndroid Build Coastguard Worker      The total data blob size used.
1023*5a923131SAndroid Build Coastguard Worker
1024*5a923131SAndroid Build Coastguard Worker    Raises:
1025*5a923131SAndroid Build Coastguard Worker      error.PayloadError if any of the checks fails.
1026*5a923131SAndroid Build Coastguard Worker    """
1027*5a923131SAndroid Build Coastguard Worker    # The total size of data blobs used by operations scanned thus far.
1028*5a923131SAndroid Build Coastguard Worker    total_data_used = 0
1029*5a923131SAndroid Build Coastguard Worker    # Counts of specific operation types.
1030*5a923131SAndroid Build Coastguard Worker    op_counts = {
1031*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE: 0,
1032*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE_BZ: 0,
1033*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE_XZ: 0,
1034*5a923131SAndroid Build Coastguard Worker        common.OpType.ZERO: 0,
1035*5a923131SAndroid Build Coastguard Worker        common.OpType.SOURCE_COPY: 0,
1036*5a923131SAndroid Build Coastguard Worker        common.OpType.SOURCE_BSDIFF: 0,
1037*5a923131SAndroid Build Coastguard Worker        common.OpType.PUFFDIFF: 0,
1038*5a923131SAndroid Build Coastguard Worker        common.OpType.BROTLI_BSDIFF: 0,
1039*5a923131SAndroid Build Coastguard Worker    }
1040*5a923131SAndroid Build Coastguard Worker    # Total blob sizes for each operation type.
1041*5a923131SAndroid Build Coastguard Worker    op_blob_totals = {
1042*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE: 0,
1043*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE_BZ: 0,
1044*5a923131SAndroid Build Coastguard Worker        common.OpType.REPLACE_XZ: 0,
1045*5a923131SAndroid Build Coastguard Worker        # SOURCE_COPY operations don't have blobs.
1046*5a923131SAndroid Build Coastguard Worker        common.OpType.SOURCE_BSDIFF: 0,
1047*5a923131SAndroid Build Coastguard Worker        common.OpType.PUFFDIFF: 0,
1048*5a923131SAndroid Build Coastguard Worker        common.OpType.BROTLI_BSDIFF: 0,
1049*5a923131SAndroid Build Coastguard Worker    }
1050*5a923131SAndroid Build Coastguard Worker    # Counts of hashed vs unhashed operations.
1051*5a923131SAndroid Build Coastguard Worker    blob_hash_counts = {
1052*5a923131SAndroid Build Coastguard Worker        'hashed': 0,
1053*5a923131SAndroid Build Coastguard Worker        'unhashed': 0,
1054*5a923131SAndroid Build Coastguard Worker    }
1055*5a923131SAndroid Build Coastguard Worker
1056*5a923131SAndroid Build Coastguard Worker    # Allocate old and new block counters.
1057*5a923131SAndroid Build Coastguard Worker    old_block_counters = (self._AllocBlockCounters(old_usable_size)
1058*5a923131SAndroid Build Coastguard Worker                          if old_fs_size else None)
1059*5a923131SAndroid Build Coastguard Worker    new_block_counters = self._AllocBlockCounters(new_usable_size)
1060*5a923131SAndroid Build Coastguard Worker
1061*5a923131SAndroid Build Coastguard Worker    # Process and verify each operation.
1062*5a923131SAndroid Build Coastguard Worker    op_num = 0
1063*5a923131SAndroid Build Coastguard Worker    for op, op_name in common.OperationIter(operations, base_name):
1064*5a923131SAndroid Build Coastguard Worker      op_num += 1
1065*5a923131SAndroid Build Coastguard Worker
1066*5a923131SAndroid Build Coastguard Worker      # Check: Type is valid.
1067*5a923131SAndroid Build Coastguard Worker      if op.type not in op_counts:
1068*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('%s: invalid type (%d).' % (op_name, op.type))
1069*5a923131SAndroid Build Coastguard Worker      op_counts[op.type] += 1
1070*5a923131SAndroid Build Coastguard Worker
1071*5a923131SAndroid Build Coastguard Worker      curr_data_used = self._CheckOperation(
1072*5a923131SAndroid Build Coastguard Worker          op, op_name, old_block_counters, new_block_counters,
1073*5a923131SAndroid Build Coastguard Worker          old_usable_size, new_usable_size,
1074*5a923131SAndroid Build Coastguard Worker          prev_data_offset + total_data_used, blob_hash_counts)
1075*5a923131SAndroid Build Coastguard Worker      if curr_data_used:
1076*5a923131SAndroid Build Coastguard Worker        op_blob_totals[op.type] += curr_data_used
1077*5a923131SAndroid Build Coastguard Worker        total_data_used += curr_data_used
1078*5a923131SAndroid Build Coastguard Worker
1079*5a923131SAndroid Build Coastguard Worker    # Report totals and breakdown statistics.
1080*5a923131SAndroid Build Coastguard Worker    report.AddField('total operations', op_num)
1081*5a923131SAndroid Build Coastguard Worker    report.AddField(
1082*5a923131SAndroid Build Coastguard Worker        None,
1083*5a923131SAndroid Build Coastguard Worker        histogram.Histogram.FromCountDict(op_counts,
1084*5a923131SAndroid Build Coastguard Worker                                          key_names=common.OpType.NAMES),
1085*5a923131SAndroid Build Coastguard Worker        indent=1)
1086*5a923131SAndroid Build Coastguard Worker    report.AddField('total blobs', sum(blob_hash_counts.values()))
1087*5a923131SAndroid Build Coastguard Worker    report.AddField(None,
1088*5a923131SAndroid Build Coastguard Worker                    histogram.Histogram.FromCountDict(blob_hash_counts),
1089*5a923131SAndroid Build Coastguard Worker                    indent=1)
1090*5a923131SAndroid Build Coastguard Worker    report.AddField('total blob size', _AddHumanReadableSize(total_data_used))
1091*5a923131SAndroid Build Coastguard Worker    report.AddField(
1092*5a923131SAndroid Build Coastguard Worker        None,
1093*5a923131SAndroid Build Coastguard Worker        histogram.Histogram.FromCountDict(op_blob_totals,
1094*5a923131SAndroid Build Coastguard Worker                                          formatter=_AddHumanReadableSize,
1095*5a923131SAndroid Build Coastguard Worker                                          key_names=common.OpType.NAMES),
1096*5a923131SAndroid Build Coastguard Worker        indent=1)
1097*5a923131SAndroid Build Coastguard Worker
1098*5a923131SAndroid Build Coastguard Worker    # Report read/write histograms.
1099*5a923131SAndroid Build Coastguard Worker    if old_block_counters:
1100*5a923131SAndroid Build Coastguard Worker      report.AddField('block read hist',
1101*5a923131SAndroid Build Coastguard Worker                      histogram.Histogram.FromKeyList(old_block_counters),
1102*5a923131SAndroid Build Coastguard Worker                      linebreak=True, indent=1)
1103*5a923131SAndroid Build Coastguard Worker
1104*5a923131SAndroid Build Coastguard Worker    new_write_hist = histogram.Histogram.FromKeyList(
1105*5a923131SAndroid Build Coastguard Worker        new_block_counters[:self._SizeToNumBlocks(new_fs_size)])
1106*5a923131SAndroid Build Coastguard Worker    report.AddField('block write hist', new_write_hist, linebreak=True,
1107*5a923131SAndroid Build Coastguard Worker                    indent=1)
1108*5a923131SAndroid Build Coastguard Worker
1109*5a923131SAndroid Build Coastguard Worker    # Check: Full update must write each dst block once.
1110*5a923131SAndroid Build Coastguard Worker    if self.payload_type == _TYPE_FULL and new_write_hist.GetKeys() != [1]:
1111*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError(
1112*5a923131SAndroid Build Coastguard Worker          '%s: not all blocks written exactly once during full update.' %
1113*5a923131SAndroid Build Coastguard Worker          base_name)
1114*5a923131SAndroid Build Coastguard Worker
1115*5a923131SAndroid Build Coastguard Worker    return total_data_used
1116*5a923131SAndroid Build Coastguard Worker
1117*5a923131SAndroid Build Coastguard Worker  def _CheckSignatures(self, report, pubkey_file_name):
1118*5a923131SAndroid Build Coastguard Worker    """Checks a payload's signature block."""
1119*5a923131SAndroid Build Coastguard Worker    sigs_raw = self.payload.ReadDataBlob(self.sigs_offset, self.sigs_size)
1120*5a923131SAndroid Build Coastguard Worker    sigs = update_metadata_pb2.Signatures()
1121*5a923131SAndroid Build Coastguard Worker    sigs.ParseFromString(sigs_raw)
1122*5a923131SAndroid Build Coastguard Worker    report.AddSection('signatures')
1123*5a923131SAndroid Build Coastguard Worker
1124*5a923131SAndroid Build Coastguard Worker    # Check: At least one signature present.
1125*5a923131SAndroid Build Coastguard Worker    if not sigs.signatures:
1126*5a923131SAndroid Build Coastguard Worker      raise error.PayloadError('Signature block is empty.')
1127*5a923131SAndroid Build Coastguard Worker
1128*5a923131SAndroid Build Coastguard Worker    # Check that we don't have the signature operation blob at the end (used to
1129*5a923131SAndroid Build Coastguard Worker    # be for major version 1).
1130*5a923131SAndroid Build Coastguard Worker    last_partition = self.payload.manifest.partitions[-1]
1131*5a923131SAndroid Build Coastguard Worker    if last_partition.operations:
1132*5a923131SAndroid Build Coastguard Worker      last_op = last_partition.operations[-1]
1133*5a923131SAndroid Build Coastguard Worker      # Check: signatures_{offset,size} must match the last (fake) operation.
1134*5a923131SAndroid Build Coastguard Worker      if (last_op.type == common.OpType.REPLACE and
1135*5a923131SAndroid Build Coastguard Worker          last_op.data_offset == self.sigs_offset and
1136*5a923131SAndroid Build Coastguard Worker          last_op.data_length == self.sigs_size):
1137*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('It seems like the last operation is the '
1138*5a923131SAndroid Build Coastguard Worker                                 'signature blob. This is an invalid payload.')
1139*5a923131SAndroid Build Coastguard Worker
1140*5a923131SAndroid Build Coastguard Worker    # Compute the checksum of all data up to signature blob.
1141*5a923131SAndroid Build Coastguard Worker    # TODO(garnold) we're re-reading the whole data section into a string
1142*5a923131SAndroid Build Coastguard Worker    # just to compute the checksum; instead, we could do it incrementally as
1143*5a923131SAndroid Build Coastguard Worker    # we read the blobs one-by-one, under the assumption that we're reading
1144*5a923131SAndroid Build Coastguard Worker    # them in order (which currently holds). This should be reconsidered.
1145*5a923131SAndroid Build Coastguard Worker    payload_hasher = self.payload.manifest_hasher.copy()
1146*5a923131SAndroid Build Coastguard Worker    common.Read(self.payload.payload_file, self.sigs_offset,
1147*5a923131SAndroid Build Coastguard Worker                offset=self.payload.data_offset, hasher=payload_hasher)
1148*5a923131SAndroid Build Coastguard Worker
1149*5a923131SAndroid Build Coastguard Worker    for sig, sig_name in common.SignatureIter(sigs.signatures, 'signatures'):
1150*5a923131SAndroid Build Coastguard Worker      sig_report = report.AddSubReport(sig_name)
1151*5a923131SAndroid Build Coastguard Worker
1152*5a923131SAndroid Build Coastguard Worker      # Check: Signature contains mandatory fields.
1153*5a923131SAndroid Build Coastguard Worker      self._CheckMandatoryField(sig, 'data', None, sig_name)
1154*5a923131SAndroid Build Coastguard Worker      sig_report.AddField('data len', len(sig.data))
1155*5a923131SAndroid Build Coastguard Worker
1156*5a923131SAndroid Build Coastguard Worker      # Check: Signatures pertains to actual payload hash.
1157*5a923131SAndroid Build Coastguard Worker      if sig.data:
1158*5a923131SAndroid Build Coastguard Worker        self._CheckSha256Signature(sig.data, pubkey_file_name,
1159*5a923131SAndroid Build Coastguard Worker                                   payload_hasher.digest(), sig_name)
1160*5a923131SAndroid Build Coastguard Worker
1161*5a923131SAndroid Build Coastguard Worker  def Run(self, pubkey_file_name=None, metadata_sig_file=None, metadata_size=0,
1162*5a923131SAndroid Build Coastguard Worker          part_sizes=None, report_out_file=None):
1163*5a923131SAndroid Build Coastguard Worker    """Checker entry point, invoking all checks.
1164*5a923131SAndroid Build Coastguard Worker
1165*5a923131SAndroid Build Coastguard Worker    Args:
1166*5a923131SAndroid Build Coastguard Worker      pubkey_file_name: Public key used for signature verification.
1167*5a923131SAndroid Build Coastguard Worker      metadata_sig_file: Metadata signature, if verification is desired.
1168*5a923131SAndroid Build Coastguard Worker      metadata_size: Metadata size, if verification is desired.
1169*5a923131SAndroid Build Coastguard Worker      part_sizes: Mapping of partition label to size in bytes (default: infer
1170*5a923131SAndroid Build Coastguard Worker        based on payload type and version or filesystem).
1171*5a923131SAndroid Build Coastguard Worker      report_out_file: File object to dump the report to.
1172*5a923131SAndroid Build Coastguard Worker
1173*5a923131SAndroid Build Coastguard Worker    Raises:
1174*5a923131SAndroid Build Coastguard Worker      error.PayloadError if payload verification failed.
1175*5a923131SAndroid Build Coastguard Worker    """
1176*5a923131SAndroid Build Coastguard Worker    if not pubkey_file_name:
1177*5a923131SAndroid Build Coastguard Worker      pubkey_file_name = _DEFAULT_PUBKEY_FILE_NAME
1178*5a923131SAndroid Build Coastguard Worker
1179*5a923131SAndroid Build Coastguard Worker    report = _PayloadReport()
1180*5a923131SAndroid Build Coastguard Worker
1181*5a923131SAndroid Build Coastguard Worker    # Get payload file size.
1182*5a923131SAndroid Build Coastguard Worker    self.payload.payload_file.seek(0, 2)
1183*5a923131SAndroid Build Coastguard Worker    payload_file_size = self.payload.payload_file.tell()
1184*5a923131SAndroid Build Coastguard Worker    self.payload.ResetFile()
1185*5a923131SAndroid Build Coastguard Worker
1186*5a923131SAndroid Build Coastguard Worker    try:
1187*5a923131SAndroid Build Coastguard Worker      # Check metadata_size (if provided).
1188*5a923131SAndroid Build Coastguard Worker      if metadata_size and self.payload.metadata_size != metadata_size:
1189*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('Invalid payload metadata size in payload(%d) '
1190*5a923131SAndroid Build Coastguard Worker                                 'vs given(%d)' % (self.payload.metadata_size,
1191*5a923131SAndroid Build Coastguard Worker                                                   metadata_size))
1192*5a923131SAndroid Build Coastguard Worker
1193*5a923131SAndroid Build Coastguard Worker      # Check metadata signature (if provided).
1194*5a923131SAndroid Build Coastguard Worker      if metadata_sig_file:
1195*5a923131SAndroid Build Coastguard Worker        metadata_sig = base64.b64decode(metadata_sig_file.read())
1196*5a923131SAndroid Build Coastguard Worker        self._CheckSha256Signature(metadata_sig, pubkey_file_name,
1197*5a923131SAndroid Build Coastguard Worker                                   self.payload.manifest_hasher.digest(),
1198*5a923131SAndroid Build Coastguard Worker                                   'metadata signature')
1199*5a923131SAndroid Build Coastguard Worker
1200*5a923131SAndroid Build Coastguard Worker      # Part 1: Check the file header.
1201*5a923131SAndroid Build Coastguard Worker      report.AddSection('header')
1202*5a923131SAndroid Build Coastguard Worker      # Check: Payload version is valid.
1203*5a923131SAndroid Build Coastguard Worker      if self.payload.header.version not in (1, 2):
1204*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError('Unknown payload version (%d).' %
1205*5a923131SAndroid Build Coastguard Worker                                 self.payload.header.version)
1206*5a923131SAndroid Build Coastguard Worker      report.AddField('version', self.payload.header.version)
1207*5a923131SAndroid Build Coastguard Worker      report.AddField('manifest len', self.payload.header.manifest_len)
1208*5a923131SAndroid Build Coastguard Worker
1209*5a923131SAndroid Build Coastguard Worker      # Part 2: Check the manifest.
1210*5a923131SAndroid Build Coastguard Worker      self._CheckManifest(report, part_sizes)
1211*5a923131SAndroid Build Coastguard Worker      assert self.payload_type, 'payload type should be known by now'
1212*5a923131SAndroid Build Coastguard Worker
1213*5a923131SAndroid Build Coastguard Worker      # Make sure deprecated values are not present in the payload.
1214*5a923131SAndroid Build Coastguard Worker      for field in ('install_operations', 'kernel_install_operations'):
1215*5a923131SAndroid Build Coastguard Worker        self._CheckRepeatedElemNotPresent(self.payload.manifest, field,
1216*5a923131SAndroid Build Coastguard Worker                                          'manifest')
1217*5a923131SAndroid Build Coastguard Worker      for field in ('old_kernel_info', 'old_rootfs_info',
1218*5a923131SAndroid Build Coastguard Worker                    'new_kernel_info', 'new_rootfs_info'):
1219*5a923131SAndroid Build Coastguard Worker        self._CheckElemNotPresent(self.payload.manifest, field, 'manifest')
1220*5a923131SAndroid Build Coastguard Worker
1221*5a923131SAndroid Build Coastguard Worker      total_blob_size = 0
1222*5a923131SAndroid Build Coastguard Worker      for part, operations in ((p.partition_name, p.operations)
1223*5a923131SAndroid Build Coastguard Worker                               for p in self.payload.manifest.partitions):
1224*5a923131SAndroid Build Coastguard Worker        report.AddSection('%s operations' % part)
1225*5a923131SAndroid Build Coastguard Worker
1226*5a923131SAndroid Build Coastguard Worker        new_fs_usable_size = self.new_fs_sizes[part]
1227*5a923131SAndroid Build Coastguard Worker        old_fs_usable_size = self.old_fs_sizes[part]
1228*5a923131SAndroid Build Coastguard Worker
1229*5a923131SAndroid Build Coastguard Worker        if part_sizes is not None and part_sizes.get(part, None):
1230*5a923131SAndroid Build Coastguard Worker          new_fs_usable_size = old_fs_usable_size = part_sizes[part]
1231*5a923131SAndroid Build Coastguard Worker
1232*5a923131SAndroid Build Coastguard Worker        # TODO(chromium:243559) only default to the filesystem size if no
1233*5a923131SAndroid Build Coastguard Worker        # explicit size provided *and* the partition size is not embedded in the
1234*5a923131SAndroid Build Coastguard Worker        # payload; see issue for more details.
1235*5a923131SAndroid Build Coastguard Worker        total_blob_size += self._CheckOperations(
1236*5a923131SAndroid Build Coastguard Worker            operations, report, '%s_install_operations' % part,
1237*5a923131SAndroid Build Coastguard Worker            self.old_fs_sizes[part], self.new_fs_sizes[part],
1238*5a923131SAndroid Build Coastguard Worker            old_fs_usable_size, new_fs_usable_size, total_blob_size)
1239*5a923131SAndroid Build Coastguard Worker
1240*5a923131SAndroid Build Coastguard Worker      # Check: Operations data reach the end of the payload file.
1241*5a923131SAndroid Build Coastguard Worker      used_payload_size = self.payload.data_offset + total_blob_size
1242*5a923131SAndroid Build Coastguard Worker      # Major versions 2 and higher have a signature at the end, so it should be
1243*5a923131SAndroid Build Coastguard Worker      # considered in the total size of the image.
1244*5a923131SAndroid Build Coastguard Worker      if self.sigs_size:
1245*5a923131SAndroid Build Coastguard Worker        used_payload_size += self.sigs_size
1246*5a923131SAndroid Build Coastguard Worker
1247*5a923131SAndroid Build Coastguard Worker      if used_payload_size != payload_file_size:
1248*5a923131SAndroid Build Coastguard Worker        raise error.PayloadError(
1249*5a923131SAndroid Build Coastguard Worker            'Used payload size (%d) different from actual file size (%d).' %
1250*5a923131SAndroid Build Coastguard Worker            (used_payload_size, payload_file_size))
1251*5a923131SAndroid Build Coastguard Worker
1252*5a923131SAndroid Build Coastguard Worker      # Part 4: Handle payload signatures message.
1253*5a923131SAndroid Build Coastguard Worker      if self.check_payload_sig and self.sigs_size:
1254*5a923131SAndroid Build Coastguard Worker        self._CheckSignatures(report, pubkey_file_name)
1255*5a923131SAndroid Build Coastguard Worker
1256*5a923131SAndroid Build Coastguard Worker      # Part 5: Summary.
1257*5a923131SAndroid Build Coastguard Worker      report.AddSection('summary')
1258*5a923131SAndroid Build Coastguard Worker      report.AddField('update type', self.payload_type)
1259*5a923131SAndroid Build Coastguard Worker
1260*5a923131SAndroid Build Coastguard Worker      report.Finalize()
1261*5a923131SAndroid Build Coastguard Worker    finally:
1262*5a923131SAndroid Build Coastguard Worker      if report_out_file:
1263*5a923131SAndroid Build Coastguard Worker        report.Dump(report_out_file)
1264