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