xref: /aosp_15_r20/system/update_engine/scripts/update_payload/payload.py (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
1*5a923131SAndroid Build Coastguard Worker#
2*5a923131SAndroid Build Coastguard Worker# Copyright (C) 2013 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker#
4*5a923131SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker#
8*5a923131SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker#
10*5a923131SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker# limitations under the License.
15*5a923131SAndroid Build Coastguard Worker#
16*5a923131SAndroid Build Coastguard Worker
17*5a923131SAndroid Build Coastguard Worker"""Tools for reading, verifying and applying Chrome OS update payloads."""
18*5a923131SAndroid Build Coastguard Worker
19*5a923131SAndroid Build Coastguard Workerfrom __future__ import absolute_import
20*5a923131SAndroid Build Coastguard Workerfrom __future__ import print_function
21*5a923131SAndroid Build Coastguard Workerimport binascii
22*5a923131SAndroid Build Coastguard Worker
23*5a923131SAndroid Build Coastguard Workerimport hashlib
24*5a923131SAndroid Build Coastguard Workerimport io
25*5a923131SAndroid Build Coastguard Workerimport mmap
26*5a923131SAndroid Build Coastguard Workerimport struct
27*5a923131SAndroid Build Coastguard Workerimport zipfile
28*5a923131SAndroid Build Coastguard Worker
29*5a923131SAndroid Build Coastguard Workerimport update_metadata_pb2
30*5a923131SAndroid Build Coastguard Worker
31*5a923131SAndroid Build Coastguard Workerfrom update_payload import checker
32*5a923131SAndroid Build Coastguard Workerfrom update_payload import common
33*5a923131SAndroid Build Coastguard Workerfrom update_payload.error import PayloadError
34*5a923131SAndroid Build Coastguard Worker
35*5a923131SAndroid Build Coastguard Worker
36*5a923131SAndroid Build Coastguard Worker#
37*5a923131SAndroid Build Coastguard Worker# Helper functions.
38*5a923131SAndroid Build Coastguard Worker#
39*5a923131SAndroid Build Coastguard Workerdef _ReadInt(file_obj, size, is_unsigned, hasher=None):
40*5a923131SAndroid Build Coastguard Worker  """Reads a binary-encoded integer from a file.
41*5a923131SAndroid Build Coastguard Worker
42*5a923131SAndroid Build Coastguard Worker  It will do the correct conversion based on the reported size and whether or
43*5a923131SAndroid Build Coastguard Worker  not a signed number is expected. Assumes a network (big-endian) byte
44*5a923131SAndroid Build Coastguard Worker  ordering.
45*5a923131SAndroid Build Coastguard Worker
46*5a923131SAndroid Build Coastguard Worker  Args:
47*5a923131SAndroid Build Coastguard Worker    file_obj: a file object
48*5a923131SAndroid Build Coastguard Worker    size: the integer size in bytes (2, 4 or 8)
49*5a923131SAndroid Build Coastguard Worker    is_unsigned: whether it is signed or not
50*5a923131SAndroid Build Coastguard Worker    hasher: an optional hasher to pass the value through
51*5a923131SAndroid Build Coastguard Worker
52*5a923131SAndroid Build Coastguard Worker  Returns:
53*5a923131SAndroid Build Coastguard Worker    An "unpacked" (Python) integer value.
54*5a923131SAndroid Build Coastguard Worker
55*5a923131SAndroid Build Coastguard Worker  Raises:
56*5a923131SAndroid Build Coastguard Worker    PayloadError if an read error occurred.
57*5a923131SAndroid Build Coastguard Worker  """
58*5a923131SAndroid Build Coastguard Worker  return struct.unpack(common.IntPackingFmtStr(size, is_unsigned),
59*5a923131SAndroid Build Coastguard Worker                       common.Read(file_obj, size, hasher=hasher))[0]
60*5a923131SAndroid Build Coastguard Worker
61*5a923131SAndroid Build Coastguard Worker
62*5a923131SAndroid Build Coastguard Worker#
63*5a923131SAndroid Build Coastguard Worker# Update payload.
64*5a923131SAndroid Build Coastguard Worker#
65*5a923131SAndroid Build Coastguard Workerclass Payload(object):
66*5a923131SAndroid Build Coastguard Worker  """Chrome OS update payload processor."""
67*5a923131SAndroid Build Coastguard Worker
68*5a923131SAndroid Build Coastguard Worker  class _PayloadHeader(object):
69*5a923131SAndroid Build Coastguard Worker    """Update payload header struct."""
70*5a923131SAndroid Build Coastguard Worker
71*5a923131SAndroid Build Coastguard Worker    # Header constants; sizes are in bytes.
72*5a923131SAndroid Build Coastguard Worker    _MAGIC = b'CrAU'
73*5a923131SAndroid Build Coastguard Worker    _VERSION_SIZE = 8
74*5a923131SAndroid Build Coastguard Worker    _MANIFEST_LEN_SIZE = 8
75*5a923131SAndroid Build Coastguard Worker    _METADATA_SIGNATURE_LEN_SIZE = 4
76*5a923131SAndroid Build Coastguard Worker
77*5a923131SAndroid Build Coastguard Worker    def __init__(self):
78*5a923131SAndroid Build Coastguard Worker      self.version = None
79*5a923131SAndroid Build Coastguard Worker      self.manifest_len = None
80*5a923131SAndroid Build Coastguard Worker      self.metadata_signature_len = None
81*5a923131SAndroid Build Coastguard Worker      self.size = None
82*5a923131SAndroid Build Coastguard Worker
83*5a923131SAndroid Build Coastguard Worker    def ReadFromPayload(self, payload_file, hasher=None):
84*5a923131SAndroid Build Coastguard Worker      """Reads the payload header from a file.
85*5a923131SAndroid Build Coastguard Worker
86*5a923131SAndroid Build Coastguard Worker      Reads the payload header from the |payload_file| and updates the |hasher|
87*5a923131SAndroid Build Coastguard Worker      if one is passed. The parsed header is stored in the _PayloadHeader
88*5a923131SAndroid Build Coastguard Worker      instance attributes.
89*5a923131SAndroid Build Coastguard Worker
90*5a923131SAndroid Build Coastguard Worker      Args:
91*5a923131SAndroid Build Coastguard Worker        payload_file: a file object
92*5a923131SAndroid Build Coastguard Worker        hasher: an optional hasher to pass the value through
93*5a923131SAndroid Build Coastguard Worker
94*5a923131SAndroid Build Coastguard Worker      Returns:
95*5a923131SAndroid Build Coastguard Worker        None.
96*5a923131SAndroid Build Coastguard Worker
97*5a923131SAndroid Build Coastguard Worker      Raises:
98*5a923131SAndroid Build Coastguard Worker        PayloadError if a read error occurred or the header is invalid.
99*5a923131SAndroid Build Coastguard Worker      """
100*5a923131SAndroid Build Coastguard Worker      # Verify magic
101*5a923131SAndroid Build Coastguard Worker      magic = common.Read(payload_file, len(self._MAGIC), hasher=hasher)
102*5a923131SAndroid Build Coastguard Worker      if magic != self._MAGIC:
103*5a923131SAndroid Build Coastguard Worker        raise PayloadError('invalid payload magic: %s' % magic)
104*5a923131SAndroid Build Coastguard Worker
105*5a923131SAndroid Build Coastguard Worker      self.version = _ReadInt(payload_file, self._VERSION_SIZE, True,
106*5a923131SAndroid Build Coastguard Worker                              hasher=hasher)
107*5a923131SAndroid Build Coastguard Worker      self.manifest_len = _ReadInt(payload_file, self._MANIFEST_LEN_SIZE, True,
108*5a923131SAndroid Build Coastguard Worker                                   hasher=hasher)
109*5a923131SAndroid Build Coastguard Worker      self.size = (len(self._MAGIC) + self._VERSION_SIZE +
110*5a923131SAndroid Build Coastguard Worker                   self._MANIFEST_LEN_SIZE)
111*5a923131SAndroid Build Coastguard Worker      self.metadata_signature_len = 0
112*5a923131SAndroid Build Coastguard Worker
113*5a923131SAndroid Build Coastguard Worker      if self.version == common.BRILLO_MAJOR_PAYLOAD_VERSION:
114*5a923131SAndroid Build Coastguard Worker        self.size += self._METADATA_SIGNATURE_LEN_SIZE
115*5a923131SAndroid Build Coastguard Worker        self.metadata_signature_len = _ReadInt(
116*5a923131SAndroid Build Coastguard Worker            payload_file, self._METADATA_SIGNATURE_LEN_SIZE, True,
117*5a923131SAndroid Build Coastguard Worker            hasher=hasher)
118*5a923131SAndroid Build Coastguard Worker
119*5a923131SAndroid Build Coastguard Worker  def __init__(self, payload_file, payload_file_offset=0):
120*5a923131SAndroid Build Coastguard Worker    """Initialize the payload object.
121*5a923131SAndroid Build Coastguard Worker
122*5a923131SAndroid Build Coastguard Worker    Args:
123*5a923131SAndroid Build Coastguard Worker      payload_file: update payload file object open for reading
124*5a923131SAndroid Build Coastguard Worker      payload_file_offset: the offset of the actual payload
125*5a923131SAndroid Build Coastguard Worker    """
126*5a923131SAndroid Build Coastguard Worker    if zipfile.is_zipfile(payload_file):
127*5a923131SAndroid Build Coastguard Worker      self.name = payload_file
128*5a923131SAndroid Build Coastguard Worker      with zipfile.ZipFile(payload_file) as zfp:
129*5a923131SAndroid Build Coastguard Worker        if "payload.bin" not in zfp.namelist():
130*5a923131SAndroid Build Coastguard Worker          raise ValueError(f"payload.bin missing in archive {payload_file}")
131*5a923131SAndroid Build Coastguard Worker        self.payload_file = zfp.open("payload.bin", "r")
132*5a923131SAndroid Build Coastguard Worker    elif isinstance(payload_file, str):
133*5a923131SAndroid Build Coastguard Worker      self.name = payload_file
134*5a923131SAndroid Build Coastguard Worker      payload_fp = open(payload_file, "rb")
135*5a923131SAndroid Build Coastguard Worker      payload_bytes = mmap.mmap(
136*5a923131SAndroid Build Coastguard Worker          payload_fp.fileno(), 0, access=mmap.ACCESS_READ)
137*5a923131SAndroid Build Coastguard Worker      self.payload_file = io.BytesIO(payload_bytes)
138*5a923131SAndroid Build Coastguard Worker    else:
139*5a923131SAndroid Build Coastguard Worker      self.name = payload_file.name
140*5a923131SAndroid Build Coastguard Worker      self.payload_file = payload_file
141*5a923131SAndroid Build Coastguard Worker    self.payload_file_size = self.payload_file.seek(0, io.SEEK_END)
142*5a923131SAndroid Build Coastguard Worker    self.payload_file.seek(0, io.SEEK_SET)
143*5a923131SAndroid Build Coastguard Worker    self.payload_file_offset = payload_file_offset
144*5a923131SAndroid Build Coastguard Worker    self.manifest_hasher = None
145*5a923131SAndroid Build Coastguard Worker    self.is_init = False
146*5a923131SAndroid Build Coastguard Worker    self.header = None
147*5a923131SAndroid Build Coastguard Worker    self.manifest = None
148*5a923131SAndroid Build Coastguard Worker    self.data_offset = None
149*5a923131SAndroid Build Coastguard Worker    self.metadata_signature = None
150*5a923131SAndroid Build Coastguard Worker    self.payload_signature = None
151*5a923131SAndroid Build Coastguard Worker    self.metadata_size = None
152*5a923131SAndroid Build Coastguard Worker    self.Init()
153*5a923131SAndroid Build Coastguard Worker
154*5a923131SAndroid Build Coastguard Worker  @property
155*5a923131SAndroid Build Coastguard Worker  def metadata_hash(self):
156*5a923131SAndroid Build Coastguard Worker    return self.manifest_hasher.digest()
157*5a923131SAndroid Build Coastguard Worker
158*5a923131SAndroid Build Coastguard Worker  @property
159*5a923131SAndroid Build Coastguard Worker  def payload_hash(self):
160*5a923131SAndroid Build Coastguard Worker    hasher = hashlib.sha256()
161*5a923131SAndroid Build Coastguard Worker    self.payload_file.seek(0)
162*5a923131SAndroid Build Coastguard Worker    hasher.update(self.payload_file.read(self.metadata_size))
163*5a923131SAndroid Build Coastguard Worker    self.payload_file.seek(self.header.metadata_signature_len, io.SEEK_CUR)
164*5a923131SAndroid Build Coastguard Worker    hasher.update(self.payload_file.read(self.total_data_length))
165*5a923131SAndroid Build Coastguard Worker    return hasher.digest()
166*5a923131SAndroid Build Coastguard Worker
167*5a923131SAndroid Build Coastguard Worker  @property
168*5a923131SAndroid Build Coastguard Worker  def is_incremental(self):
169*5a923131SAndroid Build Coastguard Worker    return any([part.HasField("old_partition_info") for part in self.manifest.partitions])
170*5a923131SAndroid Build Coastguard Worker
171*5a923131SAndroid Build Coastguard Worker  @property
172*5a923131SAndroid Build Coastguard Worker  def is_partial(self):
173*5a923131SAndroid Build Coastguard Worker    return self.manifest.partial_update
174*5a923131SAndroid Build Coastguard Worker
175*5a923131SAndroid Build Coastguard Worker  @property
176*5a923131SAndroid Build Coastguard Worker  def total_data_length(self):
177*5a923131SAndroid Build Coastguard Worker    """Return the total data length of this payload, excluding payload
178*5a923131SAndroid Build Coastguard Worker    signature at the very end.
179*5a923131SAndroid Build Coastguard Worker    """
180*5a923131SAndroid Build Coastguard Worker    # Operations are sorted in ascending data_offset order, so iterating
181*5a923131SAndroid Build Coastguard Worker    # backwards and find the first one with non zero data_offset will tell
182*5a923131SAndroid Build Coastguard Worker    # us total data length
183*5a923131SAndroid Build Coastguard Worker    for partition in reversed(self.manifest.partitions):
184*5a923131SAndroid Build Coastguard Worker      for op in reversed(partition.operations):
185*5a923131SAndroid Build Coastguard Worker        if op.data_length > 0:
186*5a923131SAndroid Build Coastguard Worker          return op.data_offset + op.data_length
187*5a923131SAndroid Build Coastguard Worker    return 0
188*5a923131SAndroid Build Coastguard Worker
189*5a923131SAndroid Build Coastguard Worker  def _ReadHeader(self):
190*5a923131SAndroid Build Coastguard Worker    """Reads and returns the payload header.
191*5a923131SAndroid Build Coastguard Worker
192*5a923131SAndroid Build Coastguard Worker    Returns:
193*5a923131SAndroid Build Coastguard Worker      A payload header object.
194*5a923131SAndroid Build Coastguard Worker
195*5a923131SAndroid Build Coastguard Worker    Raises:
196*5a923131SAndroid Build Coastguard Worker      PayloadError if a read error occurred.
197*5a923131SAndroid Build Coastguard Worker    """
198*5a923131SAndroid Build Coastguard Worker    header = self._PayloadHeader()
199*5a923131SAndroid Build Coastguard Worker    header.ReadFromPayload(self.payload_file, self.manifest_hasher)
200*5a923131SAndroid Build Coastguard Worker    return header
201*5a923131SAndroid Build Coastguard Worker
202*5a923131SAndroid Build Coastguard Worker  def _ReadManifest(self):
203*5a923131SAndroid Build Coastguard Worker    """Reads and returns the payload manifest.
204*5a923131SAndroid Build Coastguard Worker
205*5a923131SAndroid Build Coastguard Worker    Returns:
206*5a923131SAndroid Build Coastguard Worker      A string containing the payload manifest in binary form.
207*5a923131SAndroid Build Coastguard Worker
208*5a923131SAndroid Build Coastguard Worker    Raises:
209*5a923131SAndroid Build Coastguard Worker      PayloadError if a read error occurred.
210*5a923131SAndroid Build Coastguard Worker    """
211*5a923131SAndroid Build Coastguard Worker    if not self.header:
212*5a923131SAndroid Build Coastguard Worker      raise PayloadError('payload header not present')
213*5a923131SAndroid Build Coastguard Worker
214*5a923131SAndroid Build Coastguard Worker    return common.Read(self.payload_file, self.header.manifest_len,
215*5a923131SAndroid Build Coastguard Worker                       hasher=self.manifest_hasher)
216*5a923131SAndroid Build Coastguard Worker
217*5a923131SAndroid Build Coastguard Worker  def _ReadMetadataSignature(self):
218*5a923131SAndroid Build Coastguard Worker    """Reads and returns the metadata signatures.
219*5a923131SAndroid Build Coastguard Worker
220*5a923131SAndroid Build Coastguard Worker    Returns:
221*5a923131SAndroid Build Coastguard Worker      A string containing the metadata signatures protobuf in binary form or
222*5a923131SAndroid Build Coastguard Worker      an empty string if no metadata signature found in the payload.
223*5a923131SAndroid Build Coastguard Worker
224*5a923131SAndroid Build Coastguard Worker    Raises:
225*5a923131SAndroid Build Coastguard Worker      PayloadError if a read error occurred.
226*5a923131SAndroid Build Coastguard Worker    """
227*5a923131SAndroid Build Coastguard Worker    if not self.header:
228*5a923131SAndroid Build Coastguard Worker      raise PayloadError('payload header not present')
229*5a923131SAndroid Build Coastguard Worker
230*5a923131SAndroid Build Coastguard Worker    return common.Read(
231*5a923131SAndroid Build Coastguard Worker        self.payload_file, self.header.metadata_signature_len,
232*5a923131SAndroid Build Coastguard Worker        offset=self.payload_file_offset + self.header.size +
233*5a923131SAndroid Build Coastguard Worker        self.header.manifest_len)
234*5a923131SAndroid Build Coastguard Worker
235*5a923131SAndroid Build Coastguard Worker  def ReadDataBlob(self, offset, length):
236*5a923131SAndroid Build Coastguard Worker    """Reads and returns a single data blob from the update payload.
237*5a923131SAndroid Build Coastguard Worker
238*5a923131SAndroid Build Coastguard Worker    Args:
239*5a923131SAndroid Build Coastguard Worker      offset: offset to the beginning of the blob from the end of the manifest
240*5a923131SAndroid Build Coastguard Worker      length: the blob's length
241*5a923131SAndroid Build Coastguard Worker
242*5a923131SAndroid Build Coastguard Worker    Returns:
243*5a923131SAndroid Build Coastguard Worker      A string containing the raw blob data.
244*5a923131SAndroid Build Coastguard Worker
245*5a923131SAndroid Build Coastguard Worker    Raises:
246*5a923131SAndroid Build Coastguard Worker      PayloadError if a read error occurred.
247*5a923131SAndroid Build Coastguard Worker    """
248*5a923131SAndroid Build Coastguard Worker    return common.Read(self.payload_file, length,
249*5a923131SAndroid Build Coastguard Worker                       offset=self.payload_file_offset + self.data_offset +
250*5a923131SAndroid Build Coastguard Worker                       offset)
251*5a923131SAndroid Build Coastguard Worker
252*5a923131SAndroid Build Coastguard Worker  def Init(self):
253*5a923131SAndroid Build Coastguard Worker    """Initializes the payload object.
254*5a923131SAndroid Build Coastguard Worker
255*5a923131SAndroid Build Coastguard Worker    This is a prerequisite for any other public API call.
256*5a923131SAndroid Build Coastguard Worker
257*5a923131SAndroid Build Coastguard Worker    Raises:
258*5a923131SAndroid Build Coastguard Worker      PayloadError if object already initialized or fails to initialize
259*5a923131SAndroid Build Coastguard Worker      correctly.
260*5a923131SAndroid Build Coastguard Worker    """
261*5a923131SAndroid Build Coastguard Worker    if self.is_init:
262*5a923131SAndroid Build Coastguard Worker      return
263*5a923131SAndroid Build Coastguard Worker
264*5a923131SAndroid Build Coastguard Worker    self.manifest_hasher = hashlib.sha256()
265*5a923131SAndroid Build Coastguard Worker
266*5a923131SAndroid Build Coastguard Worker    # Read the file header.
267*5a923131SAndroid Build Coastguard Worker    self.payload_file.seek(self.payload_file_offset)
268*5a923131SAndroid Build Coastguard Worker    self.header = self._ReadHeader()
269*5a923131SAndroid Build Coastguard Worker
270*5a923131SAndroid Build Coastguard Worker    # Read the manifest.
271*5a923131SAndroid Build Coastguard Worker    manifest_raw = self._ReadManifest()
272*5a923131SAndroid Build Coastguard Worker    self.manifest = update_metadata_pb2.DeltaArchiveManifest()
273*5a923131SAndroid Build Coastguard Worker    self.manifest.ParseFromString(manifest_raw)
274*5a923131SAndroid Build Coastguard Worker
275*5a923131SAndroid Build Coastguard Worker    # Read the metadata signature (if any).
276*5a923131SAndroid Build Coastguard Worker    metadata_signature_raw = self._ReadMetadataSignature()
277*5a923131SAndroid Build Coastguard Worker    if metadata_signature_raw:
278*5a923131SAndroid Build Coastguard Worker      self.metadata_signature = update_metadata_pb2.Signatures()
279*5a923131SAndroid Build Coastguard Worker      self.metadata_signature.ParseFromString(metadata_signature_raw)
280*5a923131SAndroid Build Coastguard Worker
281*5a923131SAndroid Build Coastguard Worker    self.metadata_size = self.header.size + self.header.manifest_len
282*5a923131SAndroid Build Coastguard Worker    self.data_offset = self.metadata_size + self.header.metadata_signature_len
283*5a923131SAndroid Build Coastguard Worker
284*5a923131SAndroid Build Coastguard Worker    if self.manifest.signatures_offset and self.manifest.signatures_size and self.manifest.signatures_offset + self.manifest.signatures_size <= self.payload_file_size:
285*5a923131SAndroid Build Coastguard Worker      payload_signature_blob = self.ReadDataBlob(
286*5a923131SAndroid Build Coastguard Worker          self.manifest.signatures_offset, self.manifest.signatures_size)
287*5a923131SAndroid Build Coastguard Worker      payload_signature = update_metadata_pb2.Signatures()
288*5a923131SAndroid Build Coastguard Worker      payload_signature.ParseFromString(payload_signature_blob)
289*5a923131SAndroid Build Coastguard Worker      self.payload_signature = payload_signature
290*5a923131SAndroid Build Coastguard Worker
291*5a923131SAndroid Build Coastguard Worker    self.is_init = True
292*5a923131SAndroid Build Coastguard Worker
293*5a923131SAndroid Build Coastguard Worker  def _AssertInit(self):
294*5a923131SAndroid Build Coastguard Worker    """Raises an exception if the object was not initialized."""
295*5a923131SAndroid Build Coastguard Worker    if not self.is_init:
296*5a923131SAndroid Build Coastguard Worker      raise PayloadError('payload object not initialized')
297*5a923131SAndroid Build Coastguard Worker
298*5a923131SAndroid Build Coastguard Worker  def ResetFile(self):
299*5a923131SAndroid Build Coastguard Worker    """Resets the offset of the payload file to right past the manifest."""
300*5a923131SAndroid Build Coastguard Worker    self.payload_file.seek(self.payload_file_offset + self.data_offset)
301*5a923131SAndroid Build Coastguard Worker
302*5a923131SAndroid Build Coastguard Worker  def IsDelta(self):
303*5a923131SAndroid Build Coastguard Worker    """Returns True iff the payload appears to be a delta."""
304*5a923131SAndroid Build Coastguard Worker    self._AssertInit()
305*5a923131SAndroid Build Coastguard Worker    return (any(partition.HasField('old_partition_info')
306*5a923131SAndroid Build Coastguard Worker                for partition in self.manifest.partitions))
307*5a923131SAndroid Build Coastguard Worker
308*5a923131SAndroid Build Coastguard Worker  def IsFull(self):
309*5a923131SAndroid Build Coastguard Worker    """Returns True iff the payload appears to be a full."""
310*5a923131SAndroid Build Coastguard Worker    return not self.IsDelta()
311*5a923131SAndroid Build Coastguard Worker
312*5a923131SAndroid Build Coastguard Worker  def Check(self, pubkey_file_name=None, metadata_sig_file=None,
313*5a923131SAndroid Build Coastguard Worker            metadata_size=0, report_out_file=None, assert_type=None,
314*5a923131SAndroid Build Coastguard Worker            block_size=0, part_sizes=None, allow_unhashed=False,
315*5a923131SAndroid Build Coastguard Worker            disabled_tests=()):
316*5a923131SAndroid Build Coastguard Worker    """Checks the payload integrity.
317*5a923131SAndroid Build Coastguard Worker
318*5a923131SAndroid Build Coastguard Worker    Args:
319*5a923131SAndroid Build Coastguard Worker      pubkey_file_name: public key used for signature verification
320*5a923131SAndroid Build Coastguard Worker      metadata_sig_file: metadata signature, if verification is desired
321*5a923131SAndroid Build Coastguard Worker      metadata_size: metadata size, if verification is desired
322*5a923131SAndroid Build Coastguard Worker      report_out_file: file object to dump the report to
323*5a923131SAndroid Build Coastguard Worker      assert_type: assert that payload is either 'full' or 'delta'
324*5a923131SAndroid Build Coastguard Worker      block_size: expected filesystem / payload block size
325*5a923131SAndroid Build Coastguard Worker      part_sizes: map of partition label to (physical) size in bytes
326*5a923131SAndroid Build Coastguard Worker      allow_unhashed: allow unhashed operation blobs
327*5a923131SAndroid Build Coastguard Worker      disabled_tests: list of tests to disable
328*5a923131SAndroid Build Coastguard Worker
329*5a923131SAndroid Build Coastguard Worker    Raises:
330*5a923131SAndroid Build Coastguard Worker      PayloadError if payload verification failed.
331*5a923131SAndroid Build Coastguard Worker    """
332*5a923131SAndroid Build Coastguard Worker    self._AssertInit()
333*5a923131SAndroid Build Coastguard Worker
334*5a923131SAndroid Build Coastguard Worker    # Create a short-lived payload checker object and run it.
335*5a923131SAndroid Build Coastguard Worker    helper = checker.PayloadChecker(
336*5a923131SAndroid Build Coastguard Worker        self, assert_type=assert_type, block_size=block_size,
337*5a923131SAndroid Build Coastguard Worker        allow_unhashed=allow_unhashed, disabled_tests=disabled_tests)
338*5a923131SAndroid Build Coastguard Worker    helper.Run(pubkey_file_name=pubkey_file_name,
339*5a923131SAndroid Build Coastguard Worker               metadata_sig_file=metadata_sig_file,
340*5a923131SAndroid Build Coastguard Worker               metadata_size=metadata_size,
341*5a923131SAndroid Build Coastguard Worker               part_sizes=part_sizes,
342*5a923131SAndroid Build Coastguard Worker               report_out_file=report_out_file)
343*5a923131SAndroid Build Coastguard Worker
344*5a923131SAndroid Build Coastguard Worker  def CheckDataHash(self):
345*5a923131SAndroid Build Coastguard Worker    for part in self.manifest.partitions:
346*5a923131SAndroid Build Coastguard Worker      for op in part.operations:
347*5a923131SAndroid Build Coastguard Worker        if op.data_length == 0:
348*5a923131SAndroid Build Coastguard Worker          continue
349*5a923131SAndroid Build Coastguard Worker        if not op.data_sha256_hash:
350*5a923131SAndroid Build Coastguard Worker          raise PayloadError(
351*5a923131SAndroid Build Coastguard Worker              f"Operation {op} in partition {part.partition_name} missing data_sha256_hash")
352*5a923131SAndroid Build Coastguard Worker        blob = self.ReadDataBlob(op.data_offset, op.data_length)
353*5a923131SAndroid Build Coastguard Worker        blob_hash = hashlib.sha256(blob)
354*5a923131SAndroid Build Coastguard Worker        if blob_hash.digest() != op.data_sha256_hash:
355*5a923131SAndroid Build Coastguard Worker          raise PayloadError(
356*5a923131SAndroid Build Coastguard Worker              f"Operation {op} in partition {part.partition_name} has unexpected hash, expected: {binascii.hexlify(op.data_sha256_hash)}, actual: {blob_hash.hexdigest()}")
357