blob: ed5ee80d2f5d31188e9a5e70d6f77d47cae902db [file] [log] [blame]
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001#!/usr/bin/python2
Gilad Arnold5502b562013-03-08 13:22:31 -08002#
Amin Hassanif94b6432018-01-26 17:39:47 -08003# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Gilad Arnold5502b562013-03-08 13:22:31 -080017
18"""Unit testing checker.py."""
19
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080020from __future__ import print_function
21
Gilad Arnold5502b562013-03-08 13:22:31 -080022import array
23import collections
24import cStringIO
25import hashlib
26import itertools
27import os
28import unittest
29
Gilad Arnoldcb638912013-06-24 04:57:11 -070030# pylint cannot find mox.
Gilad Arnold5502b562013-03-08 13:22:31 -080031# pylint: disable=F0401
32import mox
33
Amin Hassanib05a65a2017-12-18 15:15:32 -080034from update_payload import checker
35from update_payload import common
36from update_payload import test_utils
37from update_payload import update_metadata_pb2
38from update_payload.error import PayloadError
39from update_payload.payload import Payload # Avoid name conflicts later.
Gilad Arnold5502b562013-03-08 13:22:31 -080040
41
Gilad Arnold5502b562013-03-08 13:22:31 -080042def _OpTypeByName(op_name):
Amin Hassanib05a65a2017-12-18 15:15:32 -080043 """Returns the type of an operation from itsname."""
Gilad Arnold5502b562013-03-08 13:22:31 -080044 op_name_to_type = {
45 'REPLACE': common.OpType.REPLACE,
46 'REPLACE_BZ': common.OpType.REPLACE_BZ,
47 'MOVE': common.OpType.MOVE,
48 'BSDIFF': common.OpType.BSDIFF,
Allie Woodf5c4f3e2015-02-20 16:57:46 -080049 'SOURCE_COPY': common.OpType.SOURCE_COPY,
50 'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
Alex Deymo28466772015-09-11 17:16:44 -070051 'ZERO': common.OpType.ZERO,
52 'DISCARD': common.OpType.DISCARD,
53 'REPLACE_XZ': common.OpType.REPLACE_XZ,
Amin Hassani5ef5d452017-08-04 13:10:59 -070054 'PUFFDIFF': common.OpType.PUFFDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -080055 'BROTLI_BSDIFF': common.OpType.BROTLI_BSDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080056 }
57 return op_name_to_type[op_name]
58
59
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070060def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
61 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080062 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070063 if payload_gen_dargs is None:
64 payload_gen_dargs = {}
65 if checker_init_dargs is None:
66 checker_init_dargs = {}
67
Gilad Arnold5502b562013-03-08 13:22:31 -080068 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070069 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080070 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080071 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080072 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070073 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080074
75
76def _GetPayloadCheckerWithData(payload_gen):
77 """Returns a payload checker from a given payload generator."""
78 payload_file = cStringIO.StringIO()
79 payload_gen.WriteToFile(payload_file)
80 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080081 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080082 payload.Init()
83 return checker.PayloadChecker(payload)
84
85
Gilad Arnoldcb638912013-06-24 04:57:11 -070086# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080087# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070088# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080089# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070090# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080091# pylint: disable=E1101
92class PayloadCheckerTest(mox.MoxTestBase):
93 """Tests the PayloadChecker class.
94
95 In addition to ordinary testFoo() methods, which are automatically invoked by
96 the unittest framework, in this class we make use of DoBarTest() calls that
97 implement parametric tests of certain features. In order to invoke each test,
98 which embodies a unique combination of parameter values, as a complete unit
99 test, we perform explicit enumeration of the parameter space and create
100 individual invocation contexts for each, which are then bound as
101 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
102 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -0800103 """
104
105 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800106 """Create a mock payload object, complete with a mock manifest."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800107 payload = self.mox.CreateMock(Payload)
Gilad Arnold5502b562013-03-08 13:22:31 -0800108 payload.is_init = True
109 payload.manifest = self.mox.CreateMock(
110 update_metadata_pb2.DeltaArchiveManifest)
111 return payload
112
113 @staticmethod
114 def NewExtent(start_block, num_blocks):
115 """Returns an Extent message.
116
117 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
118 its default state.
119
120 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700121 start_block: The starting block of the extent.
122 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800123
Gilad Arnold5502b562013-03-08 13:22:31 -0800124 Returns:
125 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800126 """
127 ex = update_metadata_pb2.Extent()
128 if start_block >= 0:
129 ex.start_block = start_block
130 if num_blocks >= 0:
131 ex.num_blocks = num_blocks
132 return ex
133
134 @staticmethod
135 def NewExtentList(*args):
136 """Returns an list of extents.
137
138 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700139 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800140
Gilad Arnold5502b562013-03-08 13:22:31 -0800141 Returns:
142 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800143 """
144 ex_list = []
145 for start_block, num_blocks in args:
146 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
147 return ex_list
148
149 @staticmethod
150 def AddToMessage(repeated_field, field_vals):
151 for field_val in field_vals:
152 new_field = repeated_field.add()
153 new_field.CopyFrom(field_val)
154
Gilad Arnold5502b562013-03-08 13:22:31 -0800155 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
156 linebreak=False, indent=0):
157 """Setup for testing of _CheckElem() and its derivatives.
158
159 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700160 is_present: Whether or not the element is found in the message.
161 is_submsg: Whether the element is a sub-message itself.
162 convert: A representation conversion function.
163 linebreak: Whether or not a linebreak is to be used in the report.
164 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800165
Gilad Arnold5502b562013-03-08 13:22:31 -0800166 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700167 msg: A mock message object.
168 report: A mock report object.
169 subreport: A mock sub-report object.
170 name: An element name to check.
171 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800172 """
173 name = 'foo'
174 val = 'fake submsg' if is_submsg else 'fake field'
175 subreport = 'fake subreport'
176
177 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700178 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800179 msg.HasField(name).AndReturn(is_present)
180 setattr(msg, name, val)
181
182 # Create a mock report.
183 report = self.mox.CreateMock(checker._PayloadReport)
184 if is_present:
185 if is_submsg:
186 report.AddSubReport(name).AndReturn(subreport)
187 else:
188 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
189
190 self.mox.ReplayAll()
191 return (msg, report, subreport, name, val)
192
193 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
194 linebreak, indent):
195 """Parametric testing of _CheckElem().
196
197 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700198 is_present: Whether or not the element is found in the message.
199 is_mandatory: Whether or not it's a mandatory element.
200 is_submsg: Whether the element is a sub-message itself.
201 convert: A representation conversion function.
202 linebreak: Whether or not a linebreak is to be used in the report.
203 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800204 """
205 msg, report, subreport, name, val = self.SetupAddElemTest(
206 is_present, is_submsg, convert, linebreak, indent)
207
Gilad Arnoldcb638912013-06-24 04:57:11 -0700208 args = (msg, name, report, is_mandatory, is_submsg)
209 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800210 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800211 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700212 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800213 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700214 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
215 **kwargs)
216 self.assertEquals(val if is_present else None, ret_val)
217 self.assertEquals(subreport if is_present and is_submsg else None,
218 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800219
220 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
221 indent):
222 """Parametric testing of _Check{Mandatory,Optional}Field().
223
224 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700225 is_mandatory: Whether we're testing a mandatory call.
226 is_present: Whether or not the element is found in the message.
227 convert: A representation conversion function.
228 linebreak: Whether or not a linebreak is to be used in the report.
229 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800230 """
231 msg, report, _, name, val = self.SetupAddElemTest(
232 is_present, False, convert, linebreak, indent)
233
234 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700235 args = [msg, name, report]
236 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800237 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700238 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800239 tested_func = checker.PayloadChecker._CheckMandatoryField
240 else:
241 tested_func = checker.PayloadChecker._CheckOptionalField
242
243 # Test the method call.
244 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800245 self.assertRaises(PayloadError, tested_func, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800246 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700247 ret_val = tested_func(*args, **kwargs)
248 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800249
250 def DoAddSubMsgTest(self, is_mandatory, is_present):
251 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
252
253 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700254 is_mandatory: Whether we're testing a mandatory call.
255 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800256 """
257 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
258
259 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700260 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800261 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700262 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800263 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
264 else:
265 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
266
267 # Test the method call.
268 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800269 self.assertRaises(PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800270 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700271 ret_val, ret_subreport = tested_func(*args)
272 self.assertEquals(val if is_present else None, ret_val)
273 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800274
275 def testCheckPresentIff(self):
276 """Tests _CheckPresentIff()."""
277 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
278 None, None, 'foo', 'bar', 'baz'))
279 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
280 'a', 'b', 'foo', 'bar', 'baz'))
Amin Hassanib05a65a2017-12-18 15:15:32 -0800281 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800282 'a', None, 'foo', 'bar', 'baz')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800283 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800284 None, 'b', 'foo', 'bar', 'baz')
285
286 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
287 sig_data, sig_asn1_header,
288 returned_signed_hash, expected_signed_hash):
289 """Parametric testing of _CheckSha256SignatureTest().
290
291 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700292 expect_pass: Whether or not it should pass.
293 expect_subprocess_call: Whether to expect the openssl call to happen.
294 sig_data: The signature raw data.
295 sig_asn1_header: The ASN1 header.
296 returned_signed_hash: The signed hash data retuned by openssl.
297 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800298 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700299 try:
300 # Stub out the subprocess invocation.
301 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
302 if expect_subprocess_call:
303 checker.PayloadChecker._Run(
304 mox.IsA(list), send_data=sig_data).AndReturn(
305 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800306
Gilad Arnoldcb638912013-06-24 04:57:11 -0700307 self.mox.ReplayAll()
308 if expect_pass:
309 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
310 sig_data, 'foo', expected_signed_hash, 'bar'))
311 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800312 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700313 checker.PayloadChecker._CheckSha256Signature,
314 sig_data, 'foo', expected_signed_hash, 'bar')
315 finally:
316 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800317
318 def testCheckSha256Signature_Pass(self):
319 """Tests _CheckSha256Signature(); pass case."""
320 sig_data = 'fake-signature'.ljust(256)
321 signed_hash = hashlib.sha256('fake-data').digest()
322 self.DoCheckSha256SignatureTest(True, True, sig_data,
323 common.SIG_ASN1_HEADER, signed_hash,
324 signed_hash)
325
326 def testCheckSha256Signature_FailBadSignature(self):
327 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700328 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800329 signed_hash = hashlib.sha256('fake-data').digest()
330 self.DoCheckSha256SignatureTest(False, False, sig_data,
331 common.SIG_ASN1_HEADER, signed_hash,
332 signed_hash)
333
334 def testCheckSha256Signature_FailBadOutputLength(self):
335 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
336 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700337 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800338 self.DoCheckSha256SignatureTest(False, True, sig_data,
339 common.SIG_ASN1_HEADER, signed_hash,
340 signed_hash)
341
342 def testCheckSha256Signature_FailBadAsnHeader(self):
343 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
344 sig_data = 'fake-signature'.ljust(256)
345 signed_hash = hashlib.sha256('fake-data').digest()
346 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
347 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
348 signed_hash, signed_hash)
349
350 def testCheckSha256Signature_FailBadHash(self):
351 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
352 sig_data = 'fake-signature'.ljust(256)
353 expected_signed_hash = hashlib.sha256('fake-data').digest()
354 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
355 self.DoCheckSha256SignatureTest(False, True, sig_data,
356 common.SIG_ASN1_HEADER,
357 expected_signed_hash, returned_signed_hash)
358
359 def testCheckBlocksFitLength_Pass(self):
360 """Tests _CheckBlocksFitLength(); pass case."""
361 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
362 64, 4, 16, 'foo'))
363 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
364 60, 4, 16, 'foo'))
365 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
366 49, 4, 16, 'foo'))
367 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
368 48, 3, 16, 'foo'))
369
370 def testCheckBlocksFitLength_TooManyBlocks(self):
371 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800372 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800373 checker.PayloadChecker._CheckBlocksFitLength,
374 64, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800375 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800376 checker.PayloadChecker._CheckBlocksFitLength,
377 60, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800378 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800379 checker.PayloadChecker._CheckBlocksFitLength,
380 49, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800381 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800382 checker.PayloadChecker._CheckBlocksFitLength,
383 48, 4, 16, 'foo')
384
385 def testCheckBlocksFitLength_TooFewBlocks(self):
386 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800387 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800388 checker.PayloadChecker._CheckBlocksFitLength,
389 64, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800390 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800391 checker.PayloadChecker._CheckBlocksFitLength,
392 60, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800393 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800394 checker.PayloadChecker._CheckBlocksFitLength,
395 49, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800396 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800397 checker.PayloadChecker._CheckBlocksFitLength,
398 48, 2, 16, 'foo')
399
400 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
401 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800402 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
403 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
404 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800405 """Parametric testing of _CheckManifest().
406
407 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700408 fail_mismatched_block_size: Simulate a missing block_size field.
409 fail_bad_sigs: Make signatures descriptor inconsistent.
410 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
411 fail_bad_oki: Tamper with old kernel info.
412 fail_bad_ori: Tamper with old rootfs info.
413 fail_bad_nki: Tamper with new kernel info.
414 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700415 fail_old_kernel_fs_size: Make old kernel fs size too big.
416 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
417 fail_new_kernel_fs_size: Make new kernel fs size too big.
418 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800419 """
420 # Generate a test payload. For this test, we only care about the manifest
421 # and don't need any data blobs, hence we can use a plain paylaod generator
422 # (which also gives us more control on things that can be screwed up).
423 payload_gen = test_utils.PayloadGenerator()
424
425 # Tamper with block size, if required.
426 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700427 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800428 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700429 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800430
431 # Add some operations.
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800432 payload_gen.AddOperation(False, common.OpType.MOVE,
433 src_extents=[(0, 16), (16, 497)],
434 dst_extents=[(16, 496), (0, 16)])
435 payload_gen.AddOperation(True, common.OpType.MOVE,
436 src_extents=[(0, 8), (8, 8)],
437 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800438
439 # Set an invalid signatures block (offset but no size), if required.
440 if fail_bad_sigs:
441 payload_gen.SetSignatures(32, None)
442
Gilad Arnold382df5c2013-05-03 12:49:28 -0700443 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700444 rootfs_part_size = test_utils.MiB(8)
445 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700446 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
447 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
448 if fail_old_kernel_fs_size:
449 old_kernel_fs_size += 100
450 if fail_old_rootfs_fs_size:
451 old_rootfs_fs_size += 100
452 if fail_new_kernel_fs_size:
453 new_kernel_fs_size += 100
454 if fail_new_rootfs_fs_size:
455 new_rootfs_fs_size += 100
456
Gilad Arnold5502b562013-03-08 13:22:31 -0800457 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700458 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800459 oki_hash = (None if fail_bad_oki
460 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700461 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
462 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
463 fail_bad_ori):
464 ori_hash = (None if fail_bad_ori
465 else hashlib.sha256('fake-ori-content').digest())
466 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800467
468 # Add new kernel/rootfs partition info.
469 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700470 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800471 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
472 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700473 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800474 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
475
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700476 # Set the minor version.
477 payload_gen.SetMinorVersion(0)
478
Gilad Arnold5502b562013-03-08 13:22:31 -0800479 # Create the test object.
480 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
481 report = checker._PayloadReport()
482
483 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
484 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800485 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
486 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
487 fail_new_rootfs_fs_size)
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700488 part_sizes = {
489 common.ROOTFS: rootfs_part_size,
490 common.KERNEL: kernel_part_size
491 }
492
Gilad Arnold5502b562013-03-08 13:22:31 -0800493 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800494 self.assertRaises(PayloadError, payload_checker._CheckManifest, report,
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700495 part_sizes)
Gilad Arnold5502b562013-03-08 13:22:31 -0800496 else:
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700497 self.assertIsNone(payload_checker._CheckManifest(report, part_sizes))
Gilad Arnold5502b562013-03-08 13:22:31 -0800498
499 def testCheckLength(self):
500 """Tests _CheckLength()."""
501 payload_checker = checker.PayloadChecker(self.MockPayload())
502 block_size = payload_checker.block_size
503
504 # Passes.
505 self.assertIsNone(payload_checker._CheckLength(
506 int(3.5 * block_size), 4, 'foo', 'bar'))
507 # Fails, too few blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800508 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800509 int(3.5 * block_size), 3, 'foo', 'bar')
510 # Fails, too many blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800511 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800512 int(3.5 * block_size), 5, 'foo', 'bar')
513
514 def testCheckExtents(self):
515 """Tests _CheckExtents()."""
516 payload_checker = checker.PayloadChecker(self.MockPayload())
517 block_size = payload_checker.block_size
518
519 # Passes w/ all real extents.
520 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
521 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700522 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800523 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700524 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800525
526 # Passes w/ pseudo-extents (aka sparse holes).
527 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
528 (8, 3))
529 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700530 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800531 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
532 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700533 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800534
535 # Passes w/ pseudo-extent due to a signature.
536 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
537 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700538 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800539 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
540 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700541 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800542
543 # Fails, extent missing a start block.
544 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
545 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800546 PayloadError, payload_checker._CheckExtents, extents,
547 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800548
549 # Fails, extent missing block count.
550 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
551 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800552 PayloadError, payload_checker._CheckExtents, extents,
553 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800554
555 # Fails, extent has zero blocks.
556 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
557 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800558 PayloadError, payload_checker._CheckExtents, extents,
559 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800560
561 # Fails, extent exceeds partition boundaries.
562 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
563 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800564 PayloadError, payload_checker._CheckExtents, extents,
565 (1024 + 15) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800566
567 def testCheckReplaceOperation(self):
568 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
569 payload_checker = checker.PayloadChecker(self.MockPayload())
570 block_size = payload_checker.block_size
571 data_length = 10000
572
573 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700574 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800575 op.type = common.OpType.REPLACE
576
577 # Pass.
578 op.src_extents = []
579 self.assertIsNone(
580 payload_checker._CheckReplaceOperation(
581 op, data_length, (data_length + block_size - 1) / block_size,
582 'foo'))
583
584 # Fail, src extents founds.
585 op.src_extents = ['bar']
586 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800587 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800588 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
589
590 # Fail, missing data.
591 op.src_extents = []
592 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800593 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800594 op, None, (data_length + block_size - 1) / block_size, 'foo')
595
596 # Fail, length / block number mismatch.
597 op.src_extents = ['bar']
598 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800599 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800600 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
601
602 def testCheckReplaceBzOperation(self):
603 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
604 payload_checker = checker.PayloadChecker(self.MockPayload())
605 block_size = payload_checker.block_size
606 data_length = block_size * 3
607
608 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700609 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800610 op.type = common.OpType.REPLACE_BZ
611
612 # Pass.
613 op.src_extents = []
614 self.assertIsNone(
615 payload_checker._CheckReplaceOperation(
616 op, data_length, (data_length + block_size - 1) / block_size + 5,
617 'foo'))
618
619 # Fail, src extents founds.
620 op.src_extents = ['bar']
621 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800622 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800623 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
624
625 # Fail, missing data.
626 op.src_extents = []
627 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800628 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800629 op, None, (data_length + block_size - 1) / block_size, 'foo')
630
631 # Fail, too few blocks to justify BZ.
632 op.src_extents = []
633 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800634 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800635 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
636
Amin Hassani0de7f782017-12-07 12:13:03 -0800637 def testCheckReplaceXzOperation(self):
638 """Tests _CheckReplaceOperation() where op.type == REPLACE_XZ."""
639 payload_checker = checker.PayloadChecker(self.MockPayload())
640 block_size = payload_checker.block_size
641 data_length = block_size * 3
642
643 op = self.mox.CreateMock(
644 update_metadata_pb2.InstallOperation)
645 op.type = common.OpType.REPLACE_XZ
646
647 # Pass.
648 op.src_extents = []
649 self.assertIsNone(
650 payload_checker._CheckReplaceOperation(
651 op, data_length, (data_length + block_size - 1) / block_size + 5,
652 'foo'))
653
654 # Fail, src extents founds.
655 op.src_extents = ['bar']
656 self.assertRaises(
657 PayloadError, payload_checker._CheckReplaceOperation,
658 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
659
660 # Fail, missing data.
661 op.src_extents = []
662 self.assertRaises(
663 PayloadError, payload_checker._CheckReplaceOperation,
664 op, None, (data_length + block_size - 1) / block_size, 'foo')
665
666 # Fail, too few blocks to justify XZ.
667 op.src_extents = []
668 self.assertRaises(
669 PayloadError, payload_checker._CheckReplaceOperation,
670 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
671
Gilad Arnold5502b562013-03-08 13:22:31 -0800672 def testCheckMoveOperation_Pass(self):
673 """Tests _CheckMoveOperation(); pass case."""
674 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700675 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800676 op.type = common.OpType.MOVE
677
678 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700679 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800680 self.AddToMessage(op.dst_extents,
681 self.NewExtentList((16, 128), (512, 6)))
682 self.assertIsNone(
683 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
684
685 def testCheckMoveOperation_FailContainsData(self):
686 """Tests _CheckMoveOperation(); fails, message contains data."""
687 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700688 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800689 op.type = common.OpType.MOVE
690
691 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700692 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800693 self.AddToMessage(op.dst_extents,
694 self.NewExtentList((16, 128), (512, 6)))
695 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800696 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800697 op, 1024, 134, 134, 'foo')
698
699 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
700 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
701 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700702 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800703 op.type = common.OpType.MOVE
704
705 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700706 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800707 self.AddToMessage(op.dst_extents,
708 self.NewExtentList((16, 128), (512, 6)))
709 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800710 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800711 op, None, 134, 134, 'foo')
712
713 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
714 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
715 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700716 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800717 op.type = common.OpType.MOVE
718
719 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700720 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800721 self.AddToMessage(op.dst_extents,
722 self.NewExtentList((16, 128), (512, 5)))
723 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800724 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800725 op, None, 134, 134, 'foo')
726
727 def testCheckMoveOperation_FailExcessSrcBlocks(self):
728 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
729 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700730 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800731 op.type = common.OpType.MOVE
732
733 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700734 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800735 self.AddToMessage(op.dst_extents,
736 self.NewExtentList((16, 128), (512, 5)))
737 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800738 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800739 op, None, 134, 134, 'foo')
740 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700741 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800742 self.AddToMessage(op.dst_extents,
743 self.NewExtentList((16, 128), (512, 6)))
744 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800745 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800746 op, None, 134, 134, 'foo')
747
748 def testCheckMoveOperation_FailExcessDstBlocks(self):
749 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
750 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700751 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800752 op.type = common.OpType.MOVE
753
754 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700755 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800756 self.AddToMessage(op.dst_extents,
757 self.NewExtentList((16, 128), (512, 7)))
758 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800759 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800760 op, None, 134, 134, 'foo')
761
762 def testCheckMoveOperation_FailStagnantBlocks(self):
763 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
764 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700765 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800766 op.type = common.OpType.MOVE
767
768 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700769 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
770 self.AddToMessage(op.dst_extents,
771 self.NewExtentList((8, 128), (512, 6)))
772 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800773 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700774 op, None, 134, 134, 'foo')
775
776 def testCheckMoveOperation_FailZeroStartBlock(self):
777 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
778 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700779 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700780 op.type = common.OpType.MOVE
781
782 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800783 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
784 self.AddToMessage(op.dst_extents,
785 self.NewExtentList((8, 128), (512, 6)))
786 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800787 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800788 op, None, 134, 134, 'foo')
789
Allie Woodb065e132015-04-24 10:20:27 -0700790 self.AddToMessage(op.src_extents,
791 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
792 self.AddToMessage(op.dst_extents,
793 self.NewExtentList((0, 128), (512, 6)))
794 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800795 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700796 op, None, 134, 134, 'foo')
797
Sen Jiang92161a72016-06-28 16:09:38 -0700798 def testCheckAnyDiff(self):
799 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800800 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800801 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800802
803 # Pass.
804 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800805 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800806
807 # Fail, missing data blob.
808 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800809 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800810 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800811
812 # Fail, too big of a diff blob (unjustified).
813 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800814 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800815 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800816
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800817 def testCheckSourceCopyOperation_Pass(self):
818 """Tests _CheckSourceCopyOperation(); pass case."""
819 payload_checker = checker.PayloadChecker(self.MockPayload())
820 self.assertIsNone(
821 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
822
823 def testCheckSourceCopyOperation_FailContainsData(self):
824 """Tests _CheckSourceCopyOperation(); message contains data."""
825 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800826 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800827 134, 0, 0, 'foo')
828
829 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
830 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
831 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800832 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800833 None, 0, 1, 'foo')
834
Gilad Arnold5502b562013-03-08 13:22:31 -0800835 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
836 allow_unhashed, fail_src_extents, fail_dst_extents,
837 fail_mismatched_data_offset_length,
838 fail_missing_dst_extents, fail_src_length,
839 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800840 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800841 """Parametric testing of _CheckOperation().
842
843 Args:
Amin Hassani0de7f782017-12-07 12:13:03 -0800844 op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ', 'MOVE', 'BSDIFF',
845 'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700846 is_last: Whether we're testing the last operation in a sequence.
847 allow_signature: Whether we're testing a signature-capable operation.
848 allow_unhashed: Whether we're allowing to not hash the data.
849 fail_src_extents: Tamper with src extents.
850 fail_dst_extents: Tamper with dst extents.
851 fail_mismatched_data_offset_length: Make data_{offset,length}
852 inconsistent.
853 fail_missing_dst_extents: Do not include dst extents.
854 fail_src_length: Make src length inconsistent.
855 fail_dst_length: Make dst length inconsistent.
856 fail_data_hash: Tamper with the data blob hash.
857 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800858 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800859 """
860 op_type = _OpTypeByName(op_type_name)
861
862 # Create the test object.
863 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700864 payload_checker = checker.PayloadChecker(payload,
865 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800866 block_size = payload_checker.block_size
867
868 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700869 old_part_size = test_utils.MiB(4)
870 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800871 old_block_counters = array.array(
872 'B', [0] * ((old_part_size + block_size - 1) / block_size))
873 new_block_counters = array.array(
874 'B', [0] * ((new_part_size + block_size - 1) / block_size))
875 prev_data_offset = 1876
876 blob_hash_counts = collections.defaultdict(int)
877
878 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700879 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800880 op.type = op_type
881
882 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800883 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700884 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800885 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800886 if fail_src_extents:
887 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700888 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800889 else:
890 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700891 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800892 total_src_blocks = 16
893
Allie Wood7cf9f132015-02-26 14:28:19 -0800894 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700895 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800896 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700897 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800898 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700899 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassani0de7f782017-12-07 12:13:03 -0800900 if op_type == common.OpType.REPLACE_XZ:
901 payload_checker.minor_version = 2 if fail_bad_minor_version else 3
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700902 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800903 common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700904 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800905 elif op_type == common.OpType.PUFFDIFF:
906 payload_checker.minor_version = 4 if fail_bad_minor_version else 5
Allie Wood7cf9f132015-02-26 14:28:19 -0800907
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800908 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800909 if not fail_mismatched_data_offset_length:
910 op.data_length = 16 * block_size - 8
911 if fail_prev_data_offset:
912 op.data_offset = prev_data_offset + 16
913 else:
914 op.data_offset = prev_data_offset
915
916 fake_data = 'fake-data'.ljust(op.data_length)
917 if not (allow_unhashed or (is_last and allow_signature and
918 op_type == common.OpType.REPLACE)):
919 if not fail_data_hash:
920 # Create a valid data blob hash.
921 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
922 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
923 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800924
Gilad Arnold5502b562013-03-08 13:22:31 -0800925 elif fail_data_hash:
926 # Create an invalid data blob hash.
927 op.data_sha256_hash = hashlib.sha256(
928 fake_data.replace(' ', '-')).digest()
929 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
930 fake_data)
931
932 total_dst_blocks = 0
933 if not fail_missing_dst_extents:
934 total_dst_blocks = 16
935 if fail_dst_extents:
936 self.AddToMessage(op.dst_extents,
937 self.NewExtentList((4, 16), (32, 0)))
938 else:
939 self.AddToMessage(op.dst_extents,
940 self.NewExtentList((4, 8), (64, 8)))
941
942 if total_src_blocks:
943 if fail_src_length:
944 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800945 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
946 common.OpType.SOURCE_BSDIFF) and
947 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800948 op.src_length = total_src_blocks * block_size
949 elif fail_src_length:
950 # Add an orphaned src_length.
951 op.src_length = 16
952
953 if total_dst_blocks:
954 if fail_dst_length:
955 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800956 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
957 common.OpType.SOURCE_BSDIFF) and
958 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800959 op.dst_length = total_dst_blocks * block_size
960
961 self.mox.ReplayAll()
962 should_fail = (fail_src_extents or fail_dst_extents or
963 fail_mismatched_data_offset_length or
964 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800965 fail_dst_length or fail_data_hash or fail_prev_data_offset or
966 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700967 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
968 old_part_size, new_part_size, prev_data_offset, allow_signature,
969 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800970 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800971 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800972 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700973 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
974 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800975
976 def testAllocBlockCounters(self):
977 """Tests _CheckMoveOperation()."""
978 payload_checker = checker.PayloadChecker(self.MockPayload())
979 block_size = payload_checker.block_size
980
981 # Check allocation for block-aligned partition size, ensure it's integers.
982 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700983 self.assertEqual(16, len(result))
984 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800985
986 # Check allocation of unaligned partition sizes.
987 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700988 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800989 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700990 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800991
Allie Woodfb04d302015-04-03 14:25:48 -0700992 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800993 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800994 # Generate a test payload. For this test, we only care about one
995 # (arbitrary) set of operations, so we'll only be generating kernel and
996 # test with them.
997 payload_gen = test_utils.PayloadGenerator()
998
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700999 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001000 payload_gen.SetBlockSize(block_size)
1001
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001002 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -08001003
1004 # Fake rootfs operations in a full update, tampered with as required.
1005 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -08001006 rootfs_data_length = rootfs_part_size
1007 if fail_nonexhaustive_full_update:
1008 rootfs_data_length -= block_size
1009
1010 payload_gen.AddOperation(False, rootfs_op_type,
1011 dst_extents=[(0, rootfs_data_length / block_size)],
1012 data_offset=0,
1013 data_length=rootfs_data_length)
1014
1015 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001016 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
1017 checker_init_dargs={
1018 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -08001019 payload_checker.payload_type = checker._TYPE_FULL
1020 report = checker._PayloadReport()
1021
Amin Hassaniae853742017-10-11 10:27:27 -07001022 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
1023 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -07001024 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001025 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001026 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001027 self.assertEqual(rootfs_data_length,
1028 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001029
1030 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1031 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1032 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001033 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001034 # Generate a test payload. For this test, we only care about the signature
1035 # block and how it relates to the payload hash. Therefore, we're generating
1036 # a random (otherwise useless) payload for this purpose.
1037 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001038 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001039 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001040 rootfs_part_size = test_utils.MiB(2)
1041 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001042 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1043 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001044 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001045 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001046 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001047 payload_gen.AddOperationWithData(
1048 False, common.OpType.REPLACE,
1049 dst_extents=[(0, rootfs_part_size / block_size)],
1050 data_blob=os.urandom(rootfs_part_size))
1051
1052 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1053 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1054 fail_sig_missing_fields or fail_unknown_sig_version
1055 or fail_incorrect_sig)
1056
1057 sigs_data = None
1058 if do_forge_sigs_data:
1059 sigs_gen = test_utils.SignaturesGenerator()
1060 if not fail_empty_sigs_blob:
1061 if fail_sig_missing_fields:
1062 sig_data = None
1063 else:
1064 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001065 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001066 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1067
1068 sigs_data = sigs_gen.ToBinary()
1069 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1070
1071 if do_forge_pseudo_op:
1072 assert sigs_data is not None, 'should have forged signatures blob by now'
1073 sigs_len = len(sigs_data)
1074 payload_gen.AddOperation(
1075 False, common.OpType.REPLACE,
1076 data_offset=payload_gen.curr_offset / 2,
1077 data_length=sigs_len / 2,
1078 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1079
1080 # Generate payload (complete w/ signature) and create the test object.
1081 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001082 payload_gen.WriteToFileWithData,
1083 payload_gen_dargs={
1084 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001085 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001086 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001087 payload_checker.payload_type = checker._TYPE_FULL
1088 report = checker._PayloadReport()
1089
1090 # We have to check the manifest first in order to set signature attributes.
Tudor Brindus8d05a7e2018-06-14 11:18:18 -07001091 payload_checker._CheckManifest(report, {
1092 common.ROOTFS: rootfs_part_size,
1093 common.KERNEL: kernel_part_size
1094 })
Gilad Arnold5502b562013-03-08 13:22:31 -08001095
1096 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1097 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1098 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001099 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001100 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001101 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001102 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001103 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001104
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001105 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1106 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001107
1108 Args:
1109 minor_version: The payload minor version to test with.
1110 payload_type: The type of the payload we're testing, delta or full.
1111 """
1112 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001113 payload = self.MockPayload()
1114 payload.manifest.minor_version = minor_version
1115 payload_checker = checker.PayloadChecker(payload)
1116 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001117 report = checker._PayloadReport()
1118
1119 should_succeed = (
1120 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1121 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001122 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001123 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
Amin Hassani77d7cbc2018-02-07 16:21:33 -08001124 (minor_version == 4 and payload_type == checker._TYPE_DELTA) or
1125 (minor_version == 5 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001126 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001127
1128 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001129 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001130 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001131 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001132 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001133
Gilad Arnold06eea332015-07-13 18:06:33 -07001134 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1135 fail_wrong_payload_type, fail_invalid_block_size,
Amin Hassania86b1082018-03-08 15:48:59 -08001136 fail_mismatched_metadata_size, fail_mismatched_block_size,
1137 fail_excess_data, fail_rootfs_part_size_exceeded,
Gilad Arnold06eea332015-07-13 18:06:33 -07001138 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001139 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001140 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001141 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001142 # internal PayloadChecker methods that are tested elsewhere, here we only
1143 # tamper with what's actually being manipulated and/or tested in the Run()
1144 # method itself. Note that the checker doesn't verify partition hashes, so
1145 # they're safe to fake.
1146 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001147 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001148 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001149 kernel_filesystem_size = test_utils.KiB(16)
1150 rootfs_filesystem_size = test_utils.MiB(2)
1151 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001152 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001153 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001154 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001155 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001156
1157 rootfs_part_size = 0
1158 if rootfs_part_size_provided:
1159 rootfs_part_size = rootfs_filesystem_size + block_size
1160 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1161 if fail_rootfs_part_size_exceeded:
1162 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001163 payload_gen.AddOperationWithData(
1164 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001165 dst_extents=[(0, rootfs_op_size / block_size)],
1166 data_blob=os.urandom(rootfs_op_size))
1167
1168 kernel_part_size = 0
1169 if kernel_part_size_provided:
1170 kernel_part_size = kernel_filesystem_size + block_size
1171 kernel_op_size = kernel_part_size or kernel_filesystem_size
1172 if fail_kernel_part_size_exceeded:
1173 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001174 payload_gen.AddOperationWithData(
1175 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001176 dst_extents=[(0, kernel_op_size / block_size)],
1177 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001178
1179 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001180 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001181 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001182 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001183 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001184 else:
1185 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001186
Amin Hassania86b1082018-03-08 15:48:59 -08001187 # For the unittests 246 is the value that generated for the payload.
1188 metadata_size = 246
1189 if fail_mismatched_metadata_size:
1190 metadata_size += 1
1191
Gilad Arnoldcb638912013-06-24 04:57:11 -07001192 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001193 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001194 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001195 'do_add_pseudo_operation': True,
1196 'is_pseudo_in_kernel': True,
1197 'padding': os.urandom(1024) if fail_excess_data else None},
1198 'checker_init_dargs': {
1199 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1200 'block_size': use_block_size}}
1201 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001202 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001203 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001204 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001205 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001206 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001207
Tudor Brindus2d22c1a2018-06-15 13:07:13 -07001208 kwargs = {
1209 'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1210 'metadata_size': metadata_size,
1211 'part_sizes': {
1212 common.KERNEL: kernel_part_size,
1213 common.ROOTFS: rootfs_part_size}}
1214
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001215 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Amin Hassania86b1082018-03-08 15:48:59 -08001216 fail_mismatched_metadata_size or fail_excess_data or
Gilad Arnold06eea332015-07-13 18:06:33 -07001217 fail_rootfs_part_size_exceeded or
1218 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001219 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001220 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001221 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001222 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001223
Gilad Arnold5502b562013-03-08 13:22:31 -08001224# This implements a generic API, hence the occasional unused args.
1225# pylint: disable=W0613
1226def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1227 allow_unhashed, fail_src_extents,
1228 fail_dst_extents,
1229 fail_mismatched_data_offset_length,
1230 fail_missing_dst_extents, fail_src_length,
1231 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001232 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001233 """Returns True iff the combination of arguments represents a valid test."""
1234 op_type = _OpTypeByName(op_type_name)
1235
Amin Hassani0de7f782017-12-07 12:13:03 -08001236 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1237 # partition. They are compatible with all valid minor versions, so we don't
1238 # need to check that.
1239 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1240 common.OpType.REPLACE_XZ) and (fail_src_extents or
1241 fail_src_length or
1242 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001243 return False
1244
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001245 # MOVE and SOURCE_COPY operations don't carry data.
1246 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001247 fail_mismatched_data_offset_length or fail_data_hash or
1248 fail_prev_data_offset)):
1249 return False
1250
1251 return True
1252
1253
1254def TestMethodBody(run_method_name, run_dargs):
1255 """Returns a function that invokes a named method with named arguments."""
1256 return lambda self: getattr(self, run_method_name)(**run_dargs)
1257
1258
1259def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1260 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1261
1262 This function enumerates a space of test parameters (defined by arg_space),
1263 then binds a new, unique method name in PayloadCheckerTest to a test function
1264 that gets handed the said parameters. This is a preferable approach to doing
1265 the enumeration and invocation during the tests because this way each test is
1266 treated as a complete run by the unittest framework, and so benefits from the
1267 usual setUp/tearDown mechanics.
1268
1269 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001270 tested_method_name: Name of the tested PayloadChecker method.
1271 arg_space: A dictionary containing variables (keys) and lists of values
1272 (values) associated with them.
1273 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001274 """
1275 for value_tuple in itertools.product(*arg_space.itervalues()):
1276 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1277 if validate_func and not validate_func(**run_dargs):
1278 continue
1279 run_method_name = 'Do%sTest' % tested_method_name
1280 test_method_name = 'test%s' % tested_method_name
1281 for arg_key, arg_val in run_dargs.iteritems():
1282 if arg_val or type(arg_val) is int:
1283 test_method_name += '__%s=%s' % (arg_key, arg_val)
1284 setattr(PayloadCheckerTest, test_method_name,
1285 TestMethodBody(run_method_name, run_dargs))
1286
1287
1288def AddAllParametricTests():
1289 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1290 # Add all _CheckElem() test cases.
1291 AddParametricTests('AddElem',
1292 {'linebreak': (True, False),
1293 'indent': (0, 1, 2),
1294 'convert': (str, lambda s: s[::-1]),
1295 'is_present': (True, False),
1296 'is_mandatory': (True, False),
1297 'is_submsg': (True, False)})
1298
1299 # Add all _Add{Mandatory,Optional}Field tests.
1300 AddParametricTests('AddField',
1301 {'is_mandatory': (True, False),
1302 'linebreak': (True, False),
1303 'indent': (0, 1, 2),
1304 'convert': (str, lambda s: s[::-1]),
1305 'is_present': (True, False)})
1306
1307 # Add all _Add{Mandatory,Optional}SubMsg tests.
1308 AddParametricTests('AddSubMsg',
1309 {'is_mandatory': (True, False),
1310 'is_present': (True, False)})
1311
1312 # Add all _CheckManifest() test cases.
1313 AddParametricTests('CheckManifest',
1314 {'fail_mismatched_block_size': (True, False),
1315 'fail_bad_sigs': (True, False),
1316 'fail_mismatched_oki_ori': (True, False),
1317 'fail_bad_oki': (True, False),
1318 'fail_bad_ori': (True, False),
1319 'fail_bad_nki': (True, False),
1320 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001321 'fail_old_kernel_fs_size': (True, False),
1322 'fail_old_rootfs_fs_size': (True, False),
1323 'fail_new_kernel_fs_size': (True, False),
1324 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001325
1326 # Add all _CheckOperation() test cases.
1327 AddParametricTests('CheckOperation',
Amin Hassani0de7f782017-12-07 12:13:03 -08001328 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
1329 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001330 'SOURCE_BSDIFF', 'PUFFDIFF',
1331 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001332 'is_last': (True, False),
1333 'allow_signature': (True, False),
1334 'allow_unhashed': (True, False),
1335 'fail_src_extents': (True, False),
1336 'fail_dst_extents': (True, False),
1337 'fail_mismatched_data_offset_length': (True, False),
1338 'fail_missing_dst_extents': (True, False),
1339 'fail_src_length': (True, False),
1340 'fail_dst_length': (True, False),
1341 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001342 'fail_prev_data_offset': (True, False),
1343 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001344 validate_func=ValidateCheckOperationTest)
1345
1346 # Add all _CheckOperations() test cases.
1347 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001348 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001349
1350 # Add all _CheckOperations() test cases.
1351 AddParametricTests('CheckSignatures',
1352 {'fail_empty_sigs_blob': (True, False),
1353 'fail_missing_pseudo_op': (True, False),
1354 'fail_mismatched_pseudo_op': (True, False),
1355 'fail_sig_missing_fields': (True, False),
1356 'fail_unknown_sig_version': (True, False),
1357 'fail_incorrect_sig': (True, False)})
1358
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001359 # Add all _CheckManifestMinorVersion() test cases.
1360 AddParametricTests('CheckManifestMinorVersion',
Amin Hassani77d7cbc2018-02-07 16:21:33 -08001361 {'minor_version': (None, 0, 1, 2, 3, 4, 5, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001362 'payload_type': (checker._TYPE_FULL,
1363 checker._TYPE_DELTA)})
1364
Gilad Arnold5502b562013-03-08 13:22:31 -08001365 # Add all Run() test cases.
1366 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001367 {'rootfs_part_size_provided': (True, False),
1368 'kernel_part_size_provided': (True, False),
1369 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001370 'fail_invalid_block_size': (True, False),
Amin Hassania86b1082018-03-08 15:48:59 -08001371 'fail_mismatched_metadata_size': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001372 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001373 'fail_excess_data': (True, False),
1374 'fail_rootfs_part_size_exceeded': (True, False),
1375 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001376
1377
1378if __name__ == '__main__':
1379 AddAllParametricTests()
1380 unittest.main()