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