blob: f71823425b89972a01d52dfda61dde46639892c4 [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 Hassani77d7cbc2018-02-07 16:21:33 -0800900 common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700901 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800902 elif op_type == common.OpType.PUFFDIFF:
903 payload_checker.minor_version = 4 if fail_bad_minor_version else 5
Allie Wood7cf9f132015-02-26 14:28:19 -0800904
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800905 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800906 if not fail_mismatched_data_offset_length:
907 op.data_length = 16 * block_size - 8
908 if fail_prev_data_offset:
909 op.data_offset = prev_data_offset + 16
910 else:
911 op.data_offset = prev_data_offset
912
913 fake_data = 'fake-data'.ljust(op.data_length)
914 if not (allow_unhashed or (is_last and allow_signature and
915 op_type == common.OpType.REPLACE)):
916 if not fail_data_hash:
917 # Create a valid data blob hash.
918 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
919 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
920 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800921
Gilad Arnold5502b562013-03-08 13:22:31 -0800922 elif fail_data_hash:
923 # Create an invalid data blob hash.
924 op.data_sha256_hash = hashlib.sha256(
925 fake_data.replace(' ', '-')).digest()
926 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
927 fake_data)
928
929 total_dst_blocks = 0
930 if not fail_missing_dst_extents:
931 total_dst_blocks = 16
932 if fail_dst_extents:
933 self.AddToMessage(op.dst_extents,
934 self.NewExtentList((4, 16), (32, 0)))
935 else:
936 self.AddToMessage(op.dst_extents,
937 self.NewExtentList((4, 8), (64, 8)))
938
939 if total_src_blocks:
940 if fail_src_length:
941 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800942 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
943 common.OpType.SOURCE_BSDIFF) and
944 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800945 op.src_length = total_src_blocks * block_size
946 elif fail_src_length:
947 # Add an orphaned src_length.
948 op.src_length = 16
949
950 if total_dst_blocks:
951 if fail_dst_length:
952 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800953 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
954 common.OpType.SOURCE_BSDIFF) and
955 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800956 op.dst_length = total_dst_blocks * block_size
957
958 self.mox.ReplayAll()
959 should_fail = (fail_src_extents or fail_dst_extents or
960 fail_mismatched_data_offset_length or
961 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800962 fail_dst_length or fail_data_hash or fail_prev_data_offset or
963 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700964 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
965 old_part_size, new_part_size, prev_data_offset, allow_signature,
966 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800967 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800968 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800969 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700970 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
971 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800972
973 def testAllocBlockCounters(self):
974 """Tests _CheckMoveOperation()."""
975 payload_checker = checker.PayloadChecker(self.MockPayload())
976 block_size = payload_checker.block_size
977
978 # Check allocation for block-aligned partition size, ensure it's integers.
979 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700980 self.assertEqual(16, len(result))
981 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800982
983 # Check allocation of unaligned partition sizes.
984 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700985 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800986 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700987 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800988
Allie Woodfb04d302015-04-03 14:25:48 -0700989 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800990 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800991 # Generate a test payload. For this test, we only care about one
992 # (arbitrary) set of operations, so we'll only be generating kernel and
993 # test with them.
994 payload_gen = test_utils.PayloadGenerator()
995
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700996 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800997 payload_gen.SetBlockSize(block_size)
998
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700999 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -08001000
1001 # Fake rootfs operations in a full update, tampered with as required.
1002 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -08001003 rootfs_data_length = rootfs_part_size
1004 if fail_nonexhaustive_full_update:
1005 rootfs_data_length -= block_size
1006
1007 payload_gen.AddOperation(False, rootfs_op_type,
1008 dst_extents=[(0, rootfs_data_length / block_size)],
1009 data_offset=0,
1010 data_length=rootfs_data_length)
1011
1012 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001013 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
1014 checker_init_dargs={
1015 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -08001016 payload_checker.payload_type = checker._TYPE_FULL
1017 report = checker._PayloadReport()
1018
Amin Hassaniae853742017-10-11 10:27:27 -07001019 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
1020 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -07001021 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001022 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001023 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001024 self.assertEqual(rootfs_data_length,
1025 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001026
1027 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1028 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1029 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001030 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001031 # Generate a test payload. For this test, we only care about the signature
1032 # block and how it relates to the payload hash. Therefore, we're generating
1033 # a random (otherwise useless) payload for this purpose.
1034 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001035 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001036 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001037 rootfs_part_size = test_utils.MiB(2)
1038 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001039 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1040 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001041 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001042 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001043 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001044 payload_gen.AddOperationWithData(
1045 False, common.OpType.REPLACE,
1046 dst_extents=[(0, rootfs_part_size / block_size)],
1047 data_blob=os.urandom(rootfs_part_size))
1048
1049 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1050 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1051 fail_sig_missing_fields or fail_unknown_sig_version
1052 or fail_incorrect_sig)
1053
1054 sigs_data = None
1055 if do_forge_sigs_data:
1056 sigs_gen = test_utils.SignaturesGenerator()
1057 if not fail_empty_sigs_blob:
1058 if fail_sig_missing_fields:
1059 sig_data = None
1060 else:
1061 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001062 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001063 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1064
1065 sigs_data = sigs_gen.ToBinary()
1066 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1067
1068 if do_forge_pseudo_op:
1069 assert sigs_data is not None, 'should have forged signatures blob by now'
1070 sigs_len = len(sigs_data)
1071 payload_gen.AddOperation(
1072 False, common.OpType.REPLACE,
1073 data_offset=payload_gen.curr_offset / 2,
1074 data_length=sigs_len / 2,
1075 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1076
1077 # Generate payload (complete w/ signature) and create the test object.
1078 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001079 payload_gen.WriteToFileWithData,
1080 payload_gen_dargs={
1081 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001082 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001083 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001084 payload_checker.payload_type = checker._TYPE_FULL
1085 report = checker._PayloadReport()
1086
1087 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001088 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001089
1090 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1091 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1092 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001093 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001094 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001095 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001096 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001097 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001098
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001099 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1100 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001101
1102 Args:
1103 minor_version: The payload minor version to test with.
1104 payload_type: The type of the payload we're testing, delta or full.
1105 """
1106 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001107 payload = self.MockPayload()
1108 payload.manifest.minor_version = minor_version
1109 payload_checker = checker.PayloadChecker(payload)
1110 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001111 report = checker._PayloadReport()
1112
1113 should_succeed = (
1114 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1115 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001116 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001117 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
Amin Hassani77d7cbc2018-02-07 16:21:33 -08001118 (minor_version == 4 and payload_type == checker._TYPE_DELTA) or
1119 (minor_version == 5 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001120 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001121
1122 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001123 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001124 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001125 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001126 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001127
Gilad Arnold06eea332015-07-13 18:06:33 -07001128 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1129 fail_wrong_payload_type, fail_invalid_block_size,
1130 fail_mismatched_block_size, fail_excess_data,
1131 fail_rootfs_part_size_exceeded,
1132 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001133 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001134 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001135 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001136 # internal PayloadChecker methods that are tested elsewhere, here we only
1137 # tamper with what's actually being manipulated and/or tested in the Run()
1138 # method itself. Note that the checker doesn't verify partition hashes, so
1139 # they're safe to fake.
1140 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001141 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001142 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001143 kernel_filesystem_size = test_utils.KiB(16)
1144 rootfs_filesystem_size = test_utils.MiB(2)
1145 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001146 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001147 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001148 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001149 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001150
1151 rootfs_part_size = 0
1152 if rootfs_part_size_provided:
1153 rootfs_part_size = rootfs_filesystem_size + block_size
1154 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1155 if fail_rootfs_part_size_exceeded:
1156 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001157 payload_gen.AddOperationWithData(
1158 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001159 dst_extents=[(0, rootfs_op_size / block_size)],
1160 data_blob=os.urandom(rootfs_op_size))
1161
1162 kernel_part_size = 0
1163 if kernel_part_size_provided:
1164 kernel_part_size = kernel_filesystem_size + block_size
1165 kernel_op_size = kernel_part_size or kernel_filesystem_size
1166 if fail_kernel_part_size_exceeded:
1167 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001168 payload_gen.AddOperationWithData(
1169 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001170 dst_extents=[(0, kernel_op_size / block_size)],
1171 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001172
1173 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001174 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001175 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001176 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001177 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001178 else:
1179 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001180
Gilad Arnoldcb638912013-06-24 04:57:11 -07001181 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001182 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001183 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001184 'do_add_pseudo_operation': True,
1185 'is_pseudo_in_kernel': True,
1186 'padding': os.urandom(1024) if fail_excess_data else None},
1187 'checker_init_dargs': {
1188 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1189 'block_size': use_block_size}}
1190 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001191 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001192 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001193 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001194 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001195 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001196
1197 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1198 'rootfs_part_size': rootfs_part_size,
1199 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001200 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001201 fail_excess_data or
1202 fail_rootfs_part_size_exceeded or
1203 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001204 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001205 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001206 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001207 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001208
Gilad Arnold5502b562013-03-08 13:22:31 -08001209# This implements a generic API, hence the occasional unused args.
1210# pylint: disable=W0613
1211def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1212 allow_unhashed, fail_src_extents,
1213 fail_dst_extents,
1214 fail_mismatched_data_offset_length,
1215 fail_missing_dst_extents, fail_src_length,
1216 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001217 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001218 """Returns True iff the combination of arguments represents a valid test."""
1219 op_type = _OpTypeByName(op_type_name)
1220
Amin Hassani0de7f782017-12-07 12:13:03 -08001221 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1222 # partition. They are compatible with all valid minor versions, so we don't
1223 # need to check that.
1224 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1225 common.OpType.REPLACE_XZ) and (fail_src_extents or
1226 fail_src_length or
1227 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001228 return False
1229
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001230 # MOVE and SOURCE_COPY operations don't carry data.
1231 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001232 fail_mismatched_data_offset_length or fail_data_hash or
1233 fail_prev_data_offset)):
1234 return False
1235
1236 return True
1237
1238
1239def TestMethodBody(run_method_name, run_dargs):
1240 """Returns a function that invokes a named method with named arguments."""
1241 return lambda self: getattr(self, run_method_name)(**run_dargs)
1242
1243
1244def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1245 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1246
1247 This function enumerates a space of test parameters (defined by arg_space),
1248 then binds a new, unique method name in PayloadCheckerTest to a test function
1249 that gets handed the said parameters. This is a preferable approach to doing
1250 the enumeration and invocation during the tests because this way each test is
1251 treated as a complete run by the unittest framework, and so benefits from the
1252 usual setUp/tearDown mechanics.
1253
1254 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001255 tested_method_name: Name of the tested PayloadChecker method.
1256 arg_space: A dictionary containing variables (keys) and lists of values
1257 (values) associated with them.
1258 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001259 """
1260 for value_tuple in itertools.product(*arg_space.itervalues()):
1261 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1262 if validate_func and not validate_func(**run_dargs):
1263 continue
1264 run_method_name = 'Do%sTest' % tested_method_name
1265 test_method_name = 'test%s' % tested_method_name
1266 for arg_key, arg_val in run_dargs.iteritems():
1267 if arg_val or type(arg_val) is int:
1268 test_method_name += '__%s=%s' % (arg_key, arg_val)
1269 setattr(PayloadCheckerTest, test_method_name,
1270 TestMethodBody(run_method_name, run_dargs))
1271
1272
1273def AddAllParametricTests():
1274 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1275 # Add all _CheckElem() test cases.
1276 AddParametricTests('AddElem',
1277 {'linebreak': (True, False),
1278 'indent': (0, 1, 2),
1279 'convert': (str, lambda s: s[::-1]),
1280 'is_present': (True, False),
1281 'is_mandatory': (True, False),
1282 'is_submsg': (True, False)})
1283
1284 # Add all _Add{Mandatory,Optional}Field tests.
1285 AddParametricTests('AddField',
1286 {'is_mandatory': (True, False),
1287 'linebreak': (True, False),
1288 'indent': (0, 1, 2),
1289 'convert': (str, lambda s: s[::-1]),
1290 'is_present': (True, False)})
1291
1292 # Add all _Add{Mandatory,Optional}SubMsg tests.
1293 AddParametricTests('AddSubMsg',
1294 {'is_mandatory': (True, False),
1295 'is_present': (True, False)})
1296
1297 # Add all _CheckManifest() test cases.
1298 AddParametricTests('CheckManifest',
1299 {'fail_mismatched_block_size': (True, False),
1300 'fail_bad_sigs': (True, False),
1301 'fail_mismatched_oki_ori': (True, False),
1302 'fail_bad_oki': (True, False),
1303 'fail_bad_ori': (True, False),
1304 'fail_bad_nki': (True, False),
1305 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001306 'fail_old_kernel_fs_size': (True, False),
1307 'fail_old_rootfs_fs_size': (True, False),
1308 'fail_new_kernel_fs_size': (True, False),
1309 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001310
1311 # Add all _CheckOperation() test cases.
1312 AddParametricTests('CheckOperation',
Amin Hassani0de7f782017-12-07 12:13:03 -08001313 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
1314 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001315 'SOURCE_BSDIFF', 'PUFFDIFF',
1316 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001317 'is_last': (True, False),
1318 'allow_signature': (True, False),
1319 'allow_unhashed': (True, False),
1320 'fail_src_extents': (True, False),
1321 'fail_dst_extents': (True, False),
1322 'fail_mismatched_data_offset_length': (True, False),
1323 'fail_missing_dst_extents': (True, False),
1324 'fail_src_length': (True, False),
1325 'fail_dst_length': (True, False),
1326 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001327 'fail_prev_data_offset': (True, False),
1328 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001329 validate_func=ValidateCheckOperationTest)
1330
1331 # Add all _CheckOperations() test cases.
1332 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001333 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001334
1335 # Add all _CheckOperations() test cases.
1336 AddParametricTests('CheckSignatures',
1337 {'fail_empty_sigs_blob': (True, False),
1338 'fail_missing_pseudo_op': (True, False),
1339 'fail_mismatched_pseudo_op': (True, False),
1340 'fail_sig_missing_fields': (True, False),
1341 'fail_unknown_sig_version': (True, False),
1342 'fail_incorrect_sig': (True, False)})
1343
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001344 # Add all _CheckManifestMinorVersion() test cases.
1345 AddParametricTests('CheckManifestMinorVersion',
Amin Hassani77d7cbc2018-02-07 16:21:33 -08001346 {'minor_version': (None, 0, 1, 2, 3, 4, 5, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001347 'payload_type': (checker._TYPE_FULL,
1348 checker._TYPE_DELTA)})
1349
Gilad Arnold5502b562013-03-08 13:22:31 -08001350 # Add all Run() test cases.
1351 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001352 {'rootfs_part_size_provided': (True, False),
1353 'kernel_part_size_provided': (True, False),
1354 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001355 'fail_invalid_block_size': (True, False),
1356 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001357 'fail_excess_data': (True, False),
1358 'fail_rootfs_part_size_exceeded': (True, False),
1359 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001360
1361
1362if __name__ == '__main__':
1363 AddAllParametricTests()
1364 unittest.main()