blob: 394ed0bd7b159349bd31fa2fd180d48178df42e3 [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)
Gilad Arnold5502b562013-03-08 13:22:31 -0800488 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800489 self.assertRaises(PayloadError, payload_checker._CheckManifest, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -0700490 rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800491 else:
Gilad Arnold382df5c2013-05-03 12:49:28 -0700492 self.assertIsNone(payload_checker._CheckManifest(report,
493 rootfs_part_size,
494 kernel_part_size))
Gilad Arnold5502b562013-03-08 13:22:31 -0800495
496 def testCheckLength(self):
497 """Tests _CheckLength()."""
498 payload_checker = checker.PayloadChecker(self.MockPayload())
499 block_size = payload_checker.block_size
500
501 # Passes.
502 self.assertIsNone(payload_checker._CheckLength(
503 int(3.5 * block_size), 4, 'foo', 'bar'))
504 # Fails, too few blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800505 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800506 int(3.5 * block_size), 3, 'foo', 'bar')
507 # Fails, too many 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), 5, 'foo', 'bar')
510
511 def testCheckExtents(self):
512 """Tests _CheckExtents()."""
513 payload_checker = checker.PayloadChecker(self.MockPayload())
514 block_size = payload_checker.block_size
515
516 # Passes w/ all real extents.
517 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
518 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700519 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800520 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700521 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800522
523 # Passes w/ pseudo-extents (aka sparse holes).
524 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
525 (8, 3))
526 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700527 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800528 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
529 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700530 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800531
532 # Passes w/ pseudo-extent due to a signature.
533 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
534 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700535 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800536 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
537 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700538 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800539
540 # Fails, extent missing a start block.
541 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
542 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800543 PayloadError, payload_checker._CheckExtents, extents,
544 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800545
546 # Fails, extent missing block count.
547 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
548 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800549 PayloadError, payload_checker._CheckExtents, extents,
550 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800551
552 # Fails, extent has zero blocks.
553 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
554 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800555 PayloadError, payload_checker._CheckExtents, extents,
556 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800557
558 # Fails, extent exceeds partition boundaries.
559 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
560 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800561 PayloadError, payload_checker._CheckExtents, extents,
562 (1024 + 15) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800563
564 def testCheckReplaceOperation(self):
565 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
566 payload_checker = checker.PayloadChecker(self.MockPayload())
567 block_size = payload_checker.block_size
568 data_length = 10000
569
570 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700571 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800572 op.type = common.OpType.REPLACE
573
574 # Pass.
575 op.src_extents = []
576 self.assertIsNone(
577 payload_checker._CheckReplaceOperation(
578 op, data_length, (data_length + block_size - 1) / block_size,
579 'foo'))
580
581 # Fail, src extents founds.
582 op.src_extents = ['bar']
583 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800584 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800585 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
586
587 # Fail, missing data.
588 op.src_extents = []
589 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800590 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800591 op, None, (data_length + block_size - 1) / block_size, 'foo')
592
593 # Fail, length / block number mismatch.
594 op.src_extents = ['bar']
595 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800596 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800597 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
598
599 def testCheckReplaceBzOperation(self):
600 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
601 payload_checker = checker.PayloadChecker(self.MockPayload())
602 block_size = payload_checker.block_size
603 data_length = block_size * 3
604
605 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700606 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800607 op.type = common.OpType.REPLACE_BZ
608
609 # Pass.
610 op.src_extents = []
611 self.assertIsNone(
612 payload_checker._CheckReplaceOperation(
613 op, data_length, (data_length + block_size - 1) / block_size + 5,
614 'foo'))
615
616 # Fail, src extents founds.
617 op.src_extents = ['bar']
618 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800619 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800620 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
621
622 # Fail, missing data.
623 op.src_extents = []
624 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800625 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800626 op, None, (data_length + block_size - 1) / block_size, 'foo')
627
628 # Fail, too few blocks to justify BZ.
629 op.src_extents = []
630 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800631 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800632 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
633
Amin Hassani0de7f782017-12-07 12:13:03 -0800634 def testCheckReplaceXzOperation(self):
635 """Tests _CheckReplaceOperation() where op.type == REPLACE_XZ."""
636 payload_checker = checker.PayloadChecker(self.MockPayload())
637 block_size = payload_checker.block_size
638 data_length = block_size * 3
639
640 op = self.mox.CreateMock(
641 update_metadata_pb2.InstallOperation)
642 op.type = common.OpType.REPLACE_XZ
643
644 # Pass.
645 op.src_extents = []
646 self.assertIsNone(
647 payload_checker._CheckReplaceOperation(
648 op, data_length, (data_length + block_size - 1) / block_size + 5,
649 'foo'))
650
651 # Fail, src extents founds.
652 op.src_extents = ['bar']
653 self.assertRaises(
654 PayloadError, payload_checker._CheckReplaceOperation,
655 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
656
657 # Fail, missing data.
658 op.src_extents = []
659 self.assertRaises(
660 PayloadError, payload_checker._CheckReplaceOperation,
661 op, None, (data_length + block_size - 1) / block_size, 'foo')
662
663 # Fail, too few blocks to justify XZ.
664 op.src_extents = []
665 self.assertRaises(
666 PayloadError, payload_checker._CheckReplaceOperation,
667 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
668
Gilad Arnold5502b562013-03-08 13:22:31 -0800669 def testCheckMoveOperation_Pass(self):
670 """Tests _CheckMoveOperation(); pass case."""
671 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700672 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800673 op.type = common.OpType.MOVE
674
675 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700676 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800677 self.AddToMessage(op.dst_extents,
678 self.NewExtentList((16, 128), (512, 6)))
679 self.assertIsNone(
680 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
681
682 def testCheckMoveOperation_FailContainsData(self):
683 """Tests _CheckMoveOperation(); fails, message contains data."""
684 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700685 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800686 op.type = common.OpType.MOVE
687
688 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700689 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800690 self.AddToMessage(op.dst_extents,
691 self.NewExtentList((16, 128), (512, 6)))
692 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800693 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800694 op, 1024, 134, 134, 'foo')
695
696 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
697 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
698 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700699 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800700 op.type = common.OpType.MOVE
701
702 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700703 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800704 self.AddToMessage(op.dst_extents,
705 self.NewExtentList((16, 128), (512, 6)))
706 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800707 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800708 op, None, 134, 134, 'foo')
709
710 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
711 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
712 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700713 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800714 op.type = common.OpType.MOVE
715
716 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700717 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800718 self.AddToMessage(op.dst_extents,
719 self.NewExtentList((16, 128), (512, 5)))
720 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800721 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800722 op, None, 134, 134, 'foo')
723
724 def testCheckMoveOperation_FailExcessSrcBlocks(self):
725 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
726 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700727 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800728 op.type = common.OpType.MOVE
729
730 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700731 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800732 self.AddToMessage(op.dst_extents,
733 self.NewExtentList((16, 128), (512, 5)))
734 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800735 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800736 op, None, 134, 134, 'foo')
737 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700738 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800739 self.AddToMessage(op.dst_extents,
740 self.NewExtentList((16, 128), (512, 6)))
741 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800742 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800743 op, None, 134, 134, 'foo')
744
745 def testCheckMoveOperation_FailExcessDstBlocks(self):
746 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
747 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700748 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800749 op.type = common.OpType.MOVE
750
751 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700752 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800753 self.AddToMessage(op.dst_extents,
754 self.NewExtentList((16, 128), (512, 7)))
755 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800756 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800757 op, None, 134, 134, 'foo')
758
759 def testCheckMoveOperation_FailStagnantBlocks(self):
760 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
761 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700762 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800763 op.type = common.OpType.MOVE
764
765 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700766 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
767 self.AddToMessage(op.dst_extents,
768 self.NewExtentList((8, 128), (512, 6)))
769 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800770 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700771 op, None, 134, 134, 'foo')
772
773 def testCheckMoveOperation_FailZeroStartBlock(self):
774 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
775 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700776 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700777 op.type = common.OpType.MOVE
778
779 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800780 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
781 self.AddToMessage(op.dst_extents,
782 self.NewExtentList((8, 128), (512, 6)))
783 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800784 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800785 op, None, 134, 134, 'foo')
786
Allie Woodb065e132015-04-24 10:20:27 -0700787 self.AddToMessage(op.src_extents,
788 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
789 self.AddToMessage(op.dst_extents,
790 self.NewExtentList((0, 128), (512, 6)))
791 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800792 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700793 op, None, 134, 134, 'foo')
794
Sen Jiang92161a72016-06-28 16:09:38 -0700795 def testCheckAnyDiff(self):
796 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800797 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800798 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800799
800 # Pass.
801 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800802 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800803
804 # Fail, missing data blob.
805 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800806 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800807 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800808
809 # Fail, too big of a diff blob (unjustified).
810 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800811 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800812 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800813
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800814 def testCheckSourceCopyOperation_Pass(self):
815 """Tests _CheckSourceCopyOperation(); pass case."""
816 payload_checker = checker.PayloadChecker(self.MockPayload())
817 self.assertIsNone(
818 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
819
820 def testCheckSourceCopyOperation_FailContainsData(self):
821 """Tests _CheckSourceCopyOperation(); message contains data."""
822 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800823 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800824 134, 0, 0, 'foo')
825
826 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
827 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
828 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800829 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800830 None, 0, 1, 'foo')
831
Gilad Arnold5502b562013-03-08 13:22:31 -0800832 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
833 allow_unhashed, fail_src_extents, fail_dst_extents,
834 fail_mismatched_data_offset_length,
835 fail_missing_dst_extents, fail_src_length,
836 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800837 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800838 """Parametric testing of _CheckOperation().
839
840 Args:
Amin Hassani0de7f782017-12-07 12:13:03 -0800841 op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ', 'MOVE', 'BSDIFF',
842 'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700843 is_last: Whether we're testing the last operation in a sequence.
844 allow_signature: Whether we're testing a signature-capable operation.
845 allow_unhashed: Whether we're allowing to not hash the data.
846 fail_src_extents: Tamper with src extents.
847 fail_dst_extents: Tamper with dst extents.
848 fail_mismatched_data_offset_length: Make data_{offset,length}
849 inconsistent.
850 fail_missing_dst_extents: Do not include dst extents.
851 fail_src_length: Make src length inconsistent.
852 fail_dst_length: Make dst length inconsistent.
853 fail_data_hash: Tamper with the data blob hash.
854 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800855 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800856 """
857 op_type = _OpTypeByName(op_type_name)
858
859 # Create the test object.
860 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700861 payload_checker = checker.PayloadChecker(payload,
862 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800863 block_size = payload_checker.block_size
864
865 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700866 old_part_size = test_utils.MiB(4)
867 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800868 old_block_counters = array.array(
869 'B', [0] * ((old_part_size + block_size - 1) / block_size))
870 new_block_counters = array.array(
871 'B', [0] * ((new_part_size + block_size - 1) / block_size))
872 prev_data_offset = 1876
873 blob_hash_counts = collections.defaultdict(int)
874
875 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700876 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800877 op.type = op_type
878
879 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800880 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700881 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800882 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800883 if fail_src_extents:
884 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700885 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800886 else:
887 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700888 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800889 total_src_blocks = 16
890
Allie Wood7cf9f132015-02-26 14:28:19 -0800891 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700892 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800893 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700894 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800895 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700896 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassani0de7f782017-12-07 12:13:03 -0800897 if op_type == common.OpType.REPLACE_XZ:
898 payload_checker.minor_version = 2 if fail_bad_minor_version else 3
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700899 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800900 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700901 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800902
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800903 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800904 if not fail_mismatched_data_offset_length:
905 op.data_length = 16 * block_size - 8
906 if fail_prev_data_offset:
907 op.data_offset = prev_data_offset + 16
908 else:
909 op.data_offset = prev_data_offset
910
911 fake_data = 'fake-data'.ljust(op.data_length)
912 if not (allow_unhashed or (is_last and allow_signature and
913 op_type == common.OpType.REPLACE)):
914 if not fail_data_hash:
915 # Create a valid data blob hash.
916 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
917 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
918 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800919
Gilad Arnold5502b562013-03-08 13:22:31 -0800920 elif fail_data_hash:
921 # Create an invalid data blob hash.
922 op.data_sha256_hash = hashlib.sha256(
923 fake_data.replace(' ', '-')).digest()
924 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
925 fake_data)
926
927 total_dst_blocks = 0
928 if not fail_missing_dst_extents:
929 total_dst_blocks = 16
930 if fail_dst_extents:
931 self.AddToMessage(op.dst_extents,
932 self.NewExtentList((4, 16), (32, 0)))
933 else:
934 self.AddToMessage(op.dst_extents,
935 self.NewExtentList((4, 8), (64, 8)))
936
937 if total_src_blocks:
938 if fail_src_length:
939 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800940 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
941 common.OpType.SOURCE_BSDIFF) and
942 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800943 op.src_length = total_src_blocks * block_size
944 elif fail_src_length:
945 # Add an orphaned src_length.
946 op.src_length = 16
947
948 if total_dst_blocks:
949 if fail_dst_length:
950 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800951 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
952 common.OpType.SOURCE_BSDIFF) and
953 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800954 op.dst_length = total_dst_blocks * block_size
955
956 self.mox.ReplayAll()
957 should_fail = (fail_src_extents or fail_dst_extents or
958 fail_mismatched_data_offset_length or
959 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800960 fail_dst_length or fail_data_hash or fail_prev_data_offset or
961 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700962 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
963 old_part_size, new_part_size, prev_data_offset, allow_signature,
964 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800965 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800966 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800967 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700968 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
969 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800970
971 def testAllocBlockCounters(self):
972 """Tests _CheckMoveOperation()."""
973 payload_checker = checker.PayloadChecker(self.MockPayload())
974 block_size = payload_checker.block_size
975
976 # Check allocation for block-aligned partition size, ensure it's integers.
977 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700978 self.assertEqual(16, len(result))
979 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800980
981 # Check allocation of unaligned partition sizes.
982 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700983 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800984 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700985 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800986
Allie Woodfb04d302015-04-03 14:25:48 -0700987 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800988 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800989 # Generate a test payload. For this test, we only care about one
990 # (arbitrary) set of operations, so we'll only be generating kernel and
991 # test with them.
992 payload_gen = test_utils.PayloadGenerator()
993
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700994 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800995 payload_gen.SetBlockSize(block_size)
996
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700997 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800998
999 # Fake rootfs operations in a full update, tampered with as required.
1000 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -08001001 rootfs_data_length = rootfs_part_size
1002 if fail_nonexhaustive_full_update:
1003 rootfs_data_length -= block_size
1004
1005 payload_gen.AddOperation(False, rootfs_op_type,
1006 dst_extents=[(0, rootfs_data_length / block_size)],
1007 data_offset=0,
1008 data_length=rootfs_data_length)
1009
1010 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001011 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
1012 checker_init_dargs={
1013 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -08001014 payload_checker.payload_type = checker._TYPE_FULL
1015 report = checker._PayloadReport()
1016
Amin Hassaniae853742017-10-11 10:27:27 -07001017 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
1018 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -07001019 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001020 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001021 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001022 self.assertEqual(rootfs_data_length,
1023 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001024
1025 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1026 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1027 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001028 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001029 # Generate a test payload. For this test, we only care about the signature
1030 # block and how it relates to the payload hash. Therefore, we're generating
1031 # a random (otherwise useless) payload for this purpose.
1032 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001033 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001034 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001035 rootfs_part_size = test_utils.MiB(2)
1036 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001037 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1038 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001039 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001040 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001041 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001042 payload_gen.AddOperationWithData(
1043 False, common.OpType.REPLACE,
1044 dst_extents=[(0, rootfs_part_size / block_size)],
1045 data_blob=os.urandom(rootfs_part_size))
1046
1047 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1048 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1049 fail_sig_missing_fields or fail_unknown_sig_version
1050 or fail_incorrect_sig)
1051
1052 sigs_data = None
1053 if do_forge_sigs_data:
1054 sigs_gen = test_utils.SignaturesGenerator()
1055 if not fail_empty_sigs_blob:
1056 if fail_sig_missing_fields:
1057 sig_data = None
1058 else:
1059 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001060 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001061 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1062
1063 sigs_data = sigs_gen.ToBinary()
1064 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1065
1066 if do_forge_pseudo_op:
1067 assert sigs_data is not None, 'should have forged signatures blob by now'
1068 sigs_len = len(sigs_data)
1069 payload_gen.AddOperation(
1070 False, common.OpType.REPLACE,
1071 data_offset=payload_gen.curr_offset / 2,
1072 data_length=sigs_len / 2,
1073 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1074
1075 # Generate payload (complete w/ signature) and create the test object.
1076 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001077 payload_gen.WriteToFileWithData,
1078 payload_gen_dargs={
1079 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001080 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001081 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001082 payload_checker.payload_type = checker._TYPE_FULL
1083 report = checker._PayloadReport()
1084
1085 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001086 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001087
1088 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1089 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1090 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001091 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001092 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001093 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001094 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001095 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001096
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001097 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1098 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001099
1100 Args:
1101 minor_version: The payload minor version to test with.
1102 payload_type: The type of the payload we're testing, delta or full.
1103 """
1104 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001105 payload = self.MockPayload()
1106 payload.manifest.minor_version = minor_version
1107 payload_checker = checker.PayloadChecker(payload)
1108 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001109 report = checker._PayloadReport()
1110
1111 should_succeed = (
1112 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1113 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001114 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001115 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1116 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001117 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001118
1119 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001120 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001121 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001122 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001123 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001124
Gilad Arnold06eea332015-07-13 18:06:33 -07001125 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1126 fail_wrong_payload_type, fail_invalid_block_size,
1127 fail_mismatched_block_size, fail_excess_data,
1128 fail_rootfs_part_size_exceeded,
1129 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001130 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001131 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001132 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001133 # internal PayloadChecker methods that are tested elsewhere, here we only
1134 # tamper with what's actually being manipulated and/or tested in the Run()
1135 # method itself. Note that the checker doesn't verify partition hashes, so
1136 # they're safe to fake.
1137 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001138 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001139 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001140 kernel_filesystem_size = test_utils.KiB(16)
1141 rootfs_filesystem_size = test_utils.MiB(2)
1142 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001143 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001144 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001145 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001146 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001147
1148 rootfs_part_size = 0
1149 if rootfs_part_size_provided:
1150 rootfs_part_size = rootfs_filesystem_size + block_size
1151 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1152 if fail_rootfs_part_size_exceeded:
1153 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001154 payload_gen.AddOperationWithData(
1155 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001156 dst_extents=[(0, rootfs_op_size / block_size)],
1157 data_blob=os.urandom(rootfs_op_size))
1158
1159 kernel_part_size = 0
1160 if kernel_part_size_provided:
1161 kernel_part_size = kernel_filesystem_size + block_size
1162 kernel_op_size = kernel_part_size or kernel_filesystem_size
1163 if fail_kernel_part_size_exceeded:
1164 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001165 payload_gen.AddOperationWithData(
1166 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001167 dst_extents=[(0, kernel_op_size / block_size)],
1168 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001169
1170 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001171 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001172 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001173 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001174 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001175 else:
1176 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001177
Gilad Arnoldcb638912013-06-24 04:57:11 -07001178 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001179 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001180 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001181 'do_add_pseudo_operation': True,
1182 'is_pseudo_in_kernel': True,
1183 'padding': os.urandom(1024) if fail_excess_data else None},
1184 'checker_init_dargs': {
1185 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1186 'block_size': use_block_size}}
1187 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001188 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001189 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001190 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001191 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001192 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001193
1194 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1195 'rootfs_part_size': rootfs_part_size,
1196 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001197 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001198 fail_excess_data or
1199 fail_rootfs_part_size_exceeded or
1200 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001201 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001202 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001203 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001204 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001205
Gilad Arnold5502b562013-03-08 13:22:31 -08001206# This implements a generic API, hence the occasional unused args.
1207# pylint: disable=W0613
1208def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1209 allow_unhashed, fail_src_extents,
1210 fail_dst_extents,
1211 fail_mismatched_data_offset_length,
1212 fail_missing_dst_extents, fail_src_length,
1213 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001214 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001215 """Returns True iff the combination of arguments represents a valid test."""
1216 op_type = _OpTypeByName(op_type_name)
1217
Amin Hassani0de7f782017-12-07 12:13:03 -08001218 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1219 # partition. They are compatible with all valid minor versions, so we don't
1220 # need to check that.
1221 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1222 common.OpType.REPLACE_XZ) and (fail_src_extents or
1223 fail_src_length or
1224 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001225 return False
1226
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001227 # MOVE and SOURCE_COPY operations don't carry data.
1228 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001229 fail_mismatched_data_offset_length or fail_data_hash or
1230 fail_prev_data_offset)):
1231 return False
1232
1233 return True
1234
1235
1236def TestMethodBody(run_method_name, run_dargs):
1237 """Returns a function that invokes a named method with named arguments."""
1238 return lambda self: getattr(self, run_method_name)(**run_dargs)
1239
1240
1241def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1242 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1243
1244 This function enumerates a space of test parameters (defined by arg_space),
1245 then binds a new, unique method name in PayloadCheckerTest to a test function
1246 that gets handed the said parameters. This is a preferable approach to doing
1247 the enumeration and invocation during the tests because this way each test is
1248 treated as a complete run by the unittest framework, and so benefits from the
1249 usual setUp/tearDown mechanics.
1250
1251 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001252 tested_method_name: Name of the tested PayloadChecker method.
1253 arg_space: A dictionary containing variables (keys) and lists of values
1254 (values) associated with them.
1255 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001256 """
1257 for value_tuple in itertools.product(*arg_space.itervalues()):
1258 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1259 if validate_func and not validate_func(**run_dargs):
1260 continue
1261 run_method_name = 'Do%sTest' % tested_method_name
1262 test_method_name = 'test%s' % tested_method_name
1263 for arg_key, arg_val in run_dargs.iteritems():
1264 if arg_val or type(arg_val) is int:
1265 test_method_name += '__%s=%s' % (arg_key, arg_val)
1266 setattr(PayloadCheckerTest, test_method_name,
1267 TestMethodBody(run_method_name, run_dargs))
1268
1269
1270def AddAllParametricTests():
1271 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1272 # Add all _CheckElem() test cases.
1273 AddParametricTests('AddElem',
1274 {'linebreak': (True, False),
1275 'indent': (0, 1, 2),
1276 'convert': (str, lambda s: s[::-1]),
1277 'is_present': (True, False),
1278 'is_mandatory': (True, False),
1279 'is_submsg': (True, False)})
1280
1281 # Add all _Add{Mandatory,Optional}Field tests.
1282 AddParametricTests('AddField',
1283 {'is_mandatory': (True, False),
1284 'linebreak': (True, False),
1285 'indent': (0, 1, 2),
1286 'convert': (str, lambda s: s[::-1]),
1287 'is_present': (True, False)})
1288
1289 # Add all _Add{Mandatory,Optional}SubMsg tests.
1290 AddParametricTests('AddSubMsg',
1291 {'is_mandatory': (True, False),
1292 'is_present': (True, False)})
1293
1294 # Add all _CheckManifest() test cases.
1295 AddParametricTests('CheckManifest',
1296 {'fail_mismatched_block_size': (True, False),
1297 'fail_bad_sigs': (True, False),
1298 'fail_mismatched_oki_ori': (True, False),
1299 'fail_bad_oki': (True, False),
1300 'fail_bad_ori': (True, False),
1301 'fail_bad_nki': (True, False),
1302 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001303 'fail_old_kernel_fs_size': (True, False),
1304 'fail_old_rootfs_fs_size': (True, False),
1305 'fail_new_kernel_fs_size': (True, False),
1306 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001307
1308 # Add all _CheckOperation() test cases.
1309 AddParametricTests('CheckOperation',
Amin Hassani0de7f782017-12-07 12:13:03 -08001310 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
1311 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001312 'SOURCE_BSDIFF', 'PUFFDIFF',
1313 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001314 'is_last': (True, False),
1315 'allow_signature': (True, False),
1316 'allow_unhashed': (True, False),
1317 'fail_src_extents': (True, False),
1318 'fail_dst_extents': (True, False),
1319 'fail_mismatched_data_offset_length': (True, False),
1320 'fail_missing_dst_extents': (True, False),
1321 'fail_src_length': (True, False),
1322 'fail_dst_length': (True, False),
1323 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001324 'fail_prev_data_offset': (True, False),
1325 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001326 validate_func=ValidateCheckOperationTest)
1327
1328 # Add all _CheckOperations() test cases.
1329 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001330 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001331
1332 # Add all _CheckOperations() test cases.
1333 AddParametricTests('CheckSignatures',
1334 {'fail_empty_sigs_blob': (True, False),
1335 'fail_missing_pseudo_op': (True, False),
1336 'fail_mismatched_pseudo_op': (True, False),
1337 'fail_sig_missing_fields': (True, False),
1338 'fail_unknown_sig_version': (True, False),
1339 'fail_incorrect_sig': (True, False)})
1340
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001341 # Add all _CheckManifestMinorVersion() test cases.
1342 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001343 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001344 'payload_type': (checker._TYPE_FULL,
1345 checker._TYPE_DELTA)})
1346
Gilad Arnold5502b562013-03-08 13:22:31 -08001347 # Add all Run() test cases.
1348 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001349 {'rootfs_part_size_provided': (True, False),
1350 'kernel_part_size_provided': (True, False),
1351 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001352 'fail_invalid_block_size': (True, False),
1353 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001354 'fail_excess_data': (True, False),
1355 'fail_rootfs_part_size_exceeded': (True, False),
1356 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001357
1358
1359if __name__ == '__main__':
1360 AddAllParametricTests()
1361 unittest.main()