blob: cc080b49facbf6ec0ddacbcbb8506f070f9cac17 [file] [log] [blame]
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001#!/usr/bin/python2
Gilad Arnold5502b562013-03-08 13:22:31 -08002#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit testing checker.py."""
8
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08009from __future__ import print_function
10
Gilad Arnold5502b562013-03-08 13:22:31 -080011import array
12import collections
13import cStringIO
14import hashlib
15import itertools
16import os
17import unittest
18
Gilad Arnoldcb638912013-06-24 04:57:11 -070019# pylint cannot find mox.
Gilad Arnold5502b562013-03-08 13:22:31 -080020# pylint: disable=F0401
21import mox
22
Amin Hassanib05a65a2017-12-18 15:15:32 -080023from update_payload import checker
24from update_payload import common
25from update_payload import test_utils
26from update_payload import update_metadata_pb2
27from update_payload.error import PayloadError
28from update_payload.payload import Payload # Avoid name conflicts later.
Gilad Arnold5502b562013-03-08 13:22:31 -080029
30
Gilad Arnold5502b562013-03-08 13:22:31 -080031def _OpTypeByName(op_name):
Amin Hassanib05a65a2017-12-18 15:15:32 -080032 """Returns the type of an operation from itsname."""
Gilad Arnold5502b562013-03-08 13:22:31 -080033 op_name_to_type = {
34 'REPLACE': common.OpType.REPLACE,
35 'REPLACE_BZ': common.OpType.REPLACE_BZ,
36 'MOVE': common.OpType.MOVE,
37 'BSDIFF': common.OpType.BSDIFF,
Allie Woodf5c4f3e2015-02-20 16:57:46 -080038 'SOURCE_COPY': common.OpType.SOURCE_COPY,
39 'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
Alex Deymo28466772015-09-11 17:16:44 -070040 'ZERO': common.OpType.ZERO,
41 'DISCARD': common.OpType.DISCARD,
42 'REPLACE_XZ': common.OpType.REPLACE_XZ,
Amin Hassani5ef5d452017-08-04 13:10:59 -070043 'PUFFDIFF': common.OpType.PUFFDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -080044 'BROTLI_BSDIFF': common.OpType.BROTLI_BSDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080045 }
46 return op_name_to_type[op_name]
47
48
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070049def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
50 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080051 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070052 if payload_gen_dargs is None:
53 payload_gen_dargs = {}
54 if checker_init_dargs is None:
55 checker_init_dargs = {}
56
Gilad Arnold5502b562013-03-08 13:22:31 -080057 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070058 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080059 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080060 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080061 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070062 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080063
64
65def _GetPayloadCheckerWithData(payload_gen):
66 """Returns a payload checker from a given payload generator."""
67 payload_file = cStringIO.StringIO()
68 payload_gen.WriteToFile(payload_file)
69 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080070 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080071 payload.Init()
72 return checker.PayloadChecker(payload)
73
74
Gilad Arnoldcb638912013-06-24 04:57:11 -070075# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080076# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070077# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080078# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070079# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080080# pylint: disable=E1101
81class PayloadCheckerTest(mox.MoxTestBase):
82 """Tests the PayloadChecker class.
83
84 In addition to ordinary testFoo() methods, which are automatically invoked by
85 the unittest framework, in this class we make use of DoBarTest() calls that
86 implement parametric tests of certain features. In order to invoke each test,
87 which embodies a unique combination of parameter values, as a complete unit
88 test, we perform explicit enumeration of the parameter space and create
89 individual invocation contexts for each, which are then bound as
90 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
91 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -080092 """
93
94 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -080095 """Create a mock payload object, complete with a mock manifest."""
Amin Hassanib05a65a2017-12-18 15:15:32 -080096 payload = self.mox.CreateMock(Payload)
Gilad Arnold5502b562013-03-08 13:22:31 -080097 payload.is_init = True
98 payload.manifest = self.mox.CreateMock(
99 update_metadata_pb2.DeltaArchiveManifest)
100 return payload
101
102 @staticmethod
103 def NewExtent(start_block, num_blocks):
104 """Returns an Extent message.
105
106 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
107 its default state.
108
109 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700110 start_block: The starting block of the extent.
111 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800112
Gilad Arnold5502b562013-03-08 13:22:31 -0800113 Returns:
114 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800115 """
116 ex = update_metadata_pb2.Extent()
117 if start_block >= 0:
118 ex.start_block = start_block
119 if num_blocks >= 0:
120 ex.num_blocks = num_blocks
121 return ex
122
123 @staticmethod
124 def NewExtentList(*args):
125 """Returns an list of extents.
126
127 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700128 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800129
Gilad Arnold5502b562013-03-08 13:22:31 -0800130 Returns:
131 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800132 """
133 ex_list = []
134 for start_block, num_blocks in args:
135 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
136 return ex_list
137
138 @staticmethod
139 def AddToMessage(repeated_field, field_vals):
140 for field_val in field_vals:
141 new_field = repeated_field.add()
142 new_field.CopyFrom(field_val)
143
Gilad Arnold5502b562013-03-08 13:22:31 -0800144 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
145 linebreak=False, indent=0):
146 """Setup for testing of _CheckElem() and its derivatives.
147
148 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700149 is_present: Whether or not the element is found in the message.
150 is_submsg: Whether the element is a sub-message itself.
151 convert: A representation conversion function.
152 linebreak: Whether or not a linebreak is to be used in the report.
153 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800154
Gilad Arnold5502b562013-03-08 13:22:31 -0800155 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700156 msg: A mock message object.
157 report: A mock report object.
158 subreport: A mock sub-report object.
159 name: An element name to check.
160 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800161 """
162 name = 'foo'
163 val = 'fake submsg' if is_submsg else 'fake field'
164 subreport = 'fake subreport'
165
166 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700167 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800168 msg.HasField(name).AndReturn(is_present)
169 setattr(msg, name, val)
170
171 # Create a mock report.
172 report = self.mox.CreateMock(checker._PayloadReport)
173 if is_present:
174 if is_submsg:
175 report.AddSubReport(name).AndReturn(subreport)
176 else:
177 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
178
179 self.mox.ReplayAll()
180 return (msg, report, subreport, name, val)
181
182 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
183 linebreak, indent):
184 """Parametric testing of _CheckElem().
185
186 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700187 is_present: Whether or not the element is found in the message.
188 is_mandatory: Whether or not it's a mandatory element.
189 is_submsg: Whether the element is a sub-message itself.
190 convert: A representation conversion function.
191 linebreak: Whether or not a linebreak is to be used in the report.
192 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800193 """
194 msg, report, subreport, name, val = self.SetupAddElemTest(
195 is_present, is_submsg, convert, linebreak, indent)
196
Gilad Arnoldcb638912013-06-24 04:57:11 -0700197 args = (msg, name, report, is_mandatory, is_submsg)
198 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800199 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800200 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700201 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800202 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700203 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
204 **kwargs)
205 self.assertEquals(val if is_present else None, ret_val)
206 self.assertEquals(subreport if is_present and is_submsg else None,
207 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800208
209 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
210 indent):
211 """Parametric testing of _Check{Mandatory,Optional}Field().
212
213 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700214 is_mandatory: Whether we're testing a mandatory call.
215 is_present: Whether or not the element is found in the message.
216 convert: A representation conversion function.
217 linebreak: Whether or not a linebreak is to be used in the report.
218 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800219 """
220 msg, report, _, name, val = self.SetupAddElemTest(
221 is_present, False, convert, linebreak, indent)
222
223 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700224 args = [msg, name, report]
225 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800226 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700227 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800228 tested_func = checker.PayloadChecker._CheckMandatoryField
229 else:
230 tested_func = checker.PayloadChecker._CheckOptionalField
231
232 # Test the method call.
233 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800234 self.assertRaises(PayloadError, tested_func, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800235 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700236 ret_val = tested_func(*args, **kwargs)
237 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800238
239 def DoAddSubMsgTest(self, is_mandatory, is_present):
240 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
241
242 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700243 is_mandatory: Whether we're testing a mandatory call.
244 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800245 """
246 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
247
248 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700249 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800250 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700251 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800252 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
253 else:
254 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
255
256 # Test the method call.
257 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800258 self.assertRaises(PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800259 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700260 ret_val, ret_subreport = tested_func(*args)
261 self.assertEquals(val if is_present else None, ret_val)
262 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800263
264 def testCheckPresentIff(self):
265 """Tests _CheckPresentIff()."""
266 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
267 None, None, 'foo', 'bar', 'baz'))
268 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
269 'a', 'b', 'foo', 'bar', 'baz'))
Amin Hassanib05a65a2017-12-18 15:15:32 -0800270 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800271 'a', None, 'foo', 'bar', 'baz')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800272 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800273 None, 'b', 'foo', 'bar', 'baz')
274
275 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
276 sig_data, sig_asn1_header,
277 returned_signed_hash, expected_signed_hash):
278 """Parametric testing of _CheckSha256SignatureTest().
279
280 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700281 expect_pass: Whether or not it should pass.
282 expect_subprocess_call: Whether to expect the openssl call to happen.
283 sig_data: The signature raw data.
284 sig_asn1_header: The ASN1 header.
285 returned_signed_hash: The signed hash data retuned by openssl.
286 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800287 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700288 try:
289 # Stub out the subprocess invocation.
290 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
291 if expect_subprocess_call:
292 checker.PayloadChecker._Run(
293 mox.IsA(list), send_data=sig_data).AndReturn(
294 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800295
Gilad Arnoldcb638912013-06-24 04:57:11 -0700296 self.mox.ReplayAll()
297 if expect_pass:
298 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
299 sig_data, 'foo', expected_signed_hash, 'bar'))
300 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800301 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700302 checker.PayloadChecker._CheckSha256Signature,
303 sig_data, 'foo', expected_signed_hash, 'bar')
304 finally:
305 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800306
307 def testCheckSha256Signature_Pass(self):
308 """Tests _CheckSha256Signature(); pass case."""
309 sig_data = 'fake-signature'.ljust(256)
310 signed_hash = hashlib.sha256('fake-data').digest()
311 self.DoCheckSha256SignatureTest(True, True, sig_data,
312 common.SIG_ASN1_HEADER, signed_hash,
313 signed_hash)
314
315 def testCheckSha256Signature_FailBadSignature(self):
316 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700317 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800318 signed_hash = hashlib.sha256('fake-data').digest()
319 self.DoCheckSha256SignatureTest(False, False, sig_data,
320 common.SIG_ASN1_HEADER, signed_hash,
321 signed_hash)
322
323 def testCheckSha256Signature_FailBadOutputLength(self):
324 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
325 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700326 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800327 self.DoCheckSha256SignatureTest(False, True, sig_data,
328 common.SIG_ASN1_HEADER, signed_hash,
329 signed_hash)
330
331 def testCheckSha256Signature_FailBadAsnHeader(self):
332 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
333 sig_data = 'fake-signature'.ljust(256)
334 signed_hash = hashlib.sha256('fake-data').digest()
335 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
336 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
337 signed_hash, signed_hash)
338
339 def testCheckSha256Signature_FailBadHash(self):
340 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
341 sig_data = 'fake-signature'.ljust(256)
342 expected_signed_hash = hashlib.sha256('fake-data').digest()
343 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
344 self.DoCheckSha256SignatureTest(False, True, sig_data,
345 common.SIG_ASN1_HEADER,
346 expected_signed_hash, returned_signed_hash)
347
348 def testCheckBlocksFitLength_Pass(self):
349 """Tests _CheckBlocksFitLength(); pass case."""
350 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
351 64, 4, 16, 'foo'))
352 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
353 60, 4, 16, 'foo'))
354 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
355 49, 4, 16, 'foo'))
356 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
357 48, 3, 16, 'foo'))
358
359 def testCheckBlocksFitLength_TooManyBlocks(self):
360 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800361 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800362 checker.PayloadChecker._CheckBlocksFitLength,
363 64, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800364 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800365 checker.PayloadChecker._CheckBlocksFitLength,
366 60, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800367 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800368 checker.PayloadChecker._CheckBlocksFitLength,
369 49, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800370 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800371 checker.PayloadChecker._CheckBlocksFitLength,
372 48, 4, 16, 'foo')
373
374 def testCheckBlocksFitLength_TooFewBlocks(self):
375 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800376 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800377 checker.PayloadChecker._CheckBlocksFitLength,
378 64, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800379 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800380 checker.PayloadChecker._CheckBlocksFitLength,
381 60, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800382 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800383 checker.PayloadChecker._CheckBlocksFitLength,
384 49, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800385 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800386 checker.PayloadChecker._CheckBlocksFitLength,
387 48, 2, 16, 'foo')
388
389 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
390 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800391 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
392 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
393 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800394 """Parametric testing of _CheckManifest().
395
396 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700397 fail_mismatched_block_size: Simulate a missing block_size field.
398 fail_bad_sigs: Make signatures descriptor inconsistent.
399 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
400 fail_bad_oki: Tamper with old kernel info.
401 fail_bad_ori: Tamper with old rootfs info.
402 fail_bad_nki: Tamper with new kernel info.
403 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700404 fail_old_kernel_fs_size: Make old kernel fs size too big.
405 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
406 fail_new_kernel_fs_size: Make new kernel fs size too big.
407 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800408 """
409 # Generate a test payload. For this test, we only care about the manifest
410 # and don't need any data blobs, hence we can use a plain paylaod generator
411 # (which also gives us more control on things that can be screwed up).
412 payload_gen = test_utils.PayloadGenerator()
413
414 # Tamper with block size, if required.
415 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700416 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800417 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700418 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800419
420 # Add some operations.
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800421 payload_gen.AddOperation(False, common.OpType.MOVE,
422 src_extents=[(0, 16), (16, 497)],
423 dst_extents=[(16, 496), (0, 16)])
424 payload_gen.AddOperation(True, common.OpType.MOVE,
425 src_extents=[(0, 8), (8, 8)],
426 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800427
428 # Set an invalid signatures block (offset but no size), if required.
429 if fail_bad_sigs:
430 payload_gen.SetSignatures(32, None)
431
Gilad Arnold382df5c2013-05-03 12:49:28 -0700432 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700433 rootfs_part_size = test_utils.MiB(8)
434 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700435 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
436 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
437 if fail_old_kernel_fs_size:
438 old_kernel_fs_size += 100
439 if fail_old_rootfs_fs_size:
440 old_rootfs_fs_size += 100
441 if fail_new_kernel_fs_size:
442 new_kernel_fs_size += 100
443 if fail_new_rootfs_fs_size:
444 new_rootfs_fs_size += 100
445
Gilad Arnold5502b562013-03-08 13:22:31 -0800446 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700447 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800448 oki_hash = (None if fail_bad_oki
449 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700450 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
451 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
452 fail_bad_ori):
453 ori_hash = (None if fail_bad_ori
454 else hashlib.sha256('fake-ori-content').digest())
455 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800456
457 # Add new kernel/rootfs partition info.
458 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700459 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800460 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
461 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700462 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800463 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
464
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700465 # Set the minor version.
466 payload_gen.SetMinorVersion(0)
467
Gilad Arnold5502b562013-03-08 13:22:31 -0800468 # Create the test object.
469 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
470 report = checker._PayloadReport()
471
472 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
473 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800474 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
475 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
476 fail_new_rootfs_fs_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800477 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800478 self.assertRaises(PayloadError, payload_checker._CheckManifest, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -0700479 rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800480 else:
Gilad Arnold382df5c2013-05-03 12:49:28 -0700481 self.assertIsNone(payload_checker._CheckManifest(report,
482 rootfs_part_size,
483 kernel_part_size))
Gilad Arnold5502b562013-03-08 13:22:31 -0800484
485 def testCheckLength(self):
486 """Tests _CheckLength()."""
487 payload_checker = checker.PayloadChecker(self.MockPayload())
488 block_size = payload_checker.block_size
489
490 # Passes.
491 self.assertIsNone(payload_checker._CheckLength(
492 int(3.5 * block_size), 4, 'foo', 'bar'))
493 # Fails, too few blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800494 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800495 int(3.5 * block_size), 3, 'foo', 'bar')
496 # Fails, too many blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800497 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800498 int(3.5 * block_size), 5, 'foo', 'bar')
499
500 def testCheckExtents(self):
501 """Tests _CheckExtents()."""
502 payload_checker = checker.PayloadChecker(self.MockPayload())
503 block_size = payload_checker.block_size
504
505 # Passes w/ all real extents.
506 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
507 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700508 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800509 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700510 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800511
512 # Passes w/ pseudo-extents (aka sparse holes).
513 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
514 (8, 3))
515 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700516 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800517 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
518 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700519 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800520
521 # Passes w/ pseudo-extent due to a signature.
522 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
523 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700524 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800525 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
526 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700527 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800528
529 # Fails, extent missing a start block.
530 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
531 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800532 PayloadError, payload_checker._CheckExtents, extents,
533 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800534
535 # Fails, extent missing block count.
536 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
537 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800538 PayloadError, payload_checker._CheckExtents, extents,
539 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800540
541 # Fails, extent has zero blocks.
542 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
543 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800544 PayloadError, payload_checker._CheckExtents, extents,
545 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800546
547 # Fails, extent exceeds partition boundaries.
548 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
549 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800550 PayloadError, payload_checker._CheckExtents, extents,
551 (1024 + 15) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800552
553 def testCheckReplaceOperation(self):
554 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
555 payload_checker = checker.PayloadChecker(self.MockPayload())
556 block_size = payload_checker.block_size
557 data_length = 10000
558
559 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700560 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800561 op.type = common.OpType.REPLACE
562
563 # Pass.
564 op.src_extents = []
565 self.assertIsNone(
566 payload_checker._CheckReplaceOperation(
567 op, data_length, (data_length + block_size - 1) / block_size,
568 'foo'))
569
570 # Fail, src extents founds.
571 op.src_extents = ['bar']
572 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800573 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800574 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
575
576 # Fail, missing data.
577 op.src_extents = []
578 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800579 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800580 op, None, (data_length + block_size - 1) / block_size, 'foo')
581
582 # Fail, length / block number mismatch.
583 op.src_extents = ['bar']
584 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800585 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800586 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
587
588 def testCheckReplaceBzOperation(self):
589 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
590 payload_checker = checker.PayloadChecker(self.MockPayload())
591 block_size = payload_checker.block_size
592 data_length = block_size * 3
593
594 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700595 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800596 op.type = common.OpType.REPLACE_BZ
597
598 # Pass.
599 op.src_extents = []
600 self.assertIsNone(
601 payload_checker._CheckReplaceOperation(
602 op, data_length, (data_length + block_size - 1) / block_size + 5,
603 'foo'))
604
605 # Fail, src extents founds.
606 op.src_extents = ['bar']
607 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800608 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800609 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
610
611 # Fail, missing data.
612 op.src_extents = []
613 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800614 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800615 op, None, (data_length + block_size - 1) / block_size, 'foo')
616
617 # Fail, too few blocks to justify BZ.
618 op.src_extents = []
619 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800620 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800621 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
622
Amin Hassani0de7f782017-12-07 12:13:03 -0800623 def testCheckReplaceXzOperation(self):
624 """Tests _CheckReplaceOperation() where op.type == REPLACE_XZ."""
625 payload_checker = checker.PayloadChecker(self.MockPayload())
626 block_size = payload_checker.block_size
627 data_length = block_size * 3
628
629 op = self.mox.CreateMock(
630 update_metadata_pb2.InstallOperation)
631 op.type = common.OpType.REPLACE_XZ
632
633 # Pass.
634 op.src_extents = []
635 self.assertIsNone(
636 payload_checker._CheckReplaceOperation(
637 op, data_length, (data_length + block_size - 1) / block_size + 5,
638 'foo'))
639
640 # Fail, src extents founds.
641 op.src_extents = ['bar']
642 self.assertRaises(
643 PayloadError, payload_checker._CheckReplaceOperation,
644 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
645
646 # Fail, missing data.
647 op.src_extents = []
648 self.assertRaises(
649 PayloadError, payload_checker._CheckReplaceOperation,
650 op, None, (data_length + block_size - 1) / block_size, 'foo')
651
652 # Fail, too few blocks to justify XZ.
653 op.src_extents = []
654 self.assertRaises(
655 PayloadError, payload_checker._CheckReplaceOperation,
656 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
657
Gilad Arnold5502b562013-03-08 13:22:31 -0800658 def testCheckMoveOperation_Pass(self):
659 """Tests _CheckMoveOperation(); pass case."""
660 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700661 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800662 op.type = common.OpType.MOVE
663
664 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700665 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800666 self.AddToMessage(op.dst_extents,
667 self.NewExtentList((16, 128), (512, 6)))
668 self.assertIsNone(
669 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
670
671 def testCheckMoveOperation_FailContainsData(self):
672 """Tests _CheckMoveOperation(); fails, message contains data."""
673 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700674 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800675 op.type = common.OpType.MOVE
676
677 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700678 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800679 self.AddToMessage(op.dst_extents,
680 self.NewExtentList((16, 128), (512, 6)))
681 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800682 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800683 op, 1024, 134, 134, 'foo')
684
685 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
686 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
687 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700688 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800689 op.type = common.OpType.MOVE
690
691 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700692 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800693 self.AddToMessage(op.dst_extents,
694 self.NewExtentList((16, 128), (512, 6)))
695 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800696 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800697 op, None, 134, 134, 'foo')
698
699 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
700 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
701 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700702 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800703 op.type = common.OpType.MOVE
704
705 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700706 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800707 self.AddToMessage(op.dst_extents,
708 self.NewExtentList((16, 128), (512, 5)))
709 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800710 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800711 op, None, 134, 134, 'foo')
712
713 def testCheckMoveOperation_FailExcessSrcBlocks(self):
714 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
715 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700716 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800717 op.type = common.OpType.MOVE
718
719 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700720 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800721 self.AddToMessage(op.dst_extents,
722 self.NewExtentList((16, 128), (512, 5)))
723 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800724 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800725 op, None, 134, 134, 'foo')
726 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700727 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800728 self.AddToMessage(op.dst_extents,
729 self.NewExtentList((16, 128), (512, 6)))
730 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800731 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800732 op, None, 134, 134, 'foo')
733
734 def testCheckMoveOperation_FailExcessDstBlocks(self):
735 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
736 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700737 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800738 op.type = common.OpType.MOVE
739
740 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700741 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800742 self.AddToMessage(op.dst_extents,
743 self.NewExtentList((16, 128), (512, 7)))
744 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800745 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800746 op, None, 134, 134, 'foo')
747
748 def testCheckMoveOperation_FailStagnantBlocks(self):
749 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
750 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700751 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800752 op.type = common.OpType.MOVE
753
754 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700755 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
756 self.AddToMessage(op.dst_extents,
757 self.NewExtentList((8, 128), (512, 6)))
758 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800759 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700760 op, None, 134, 134, 'foo')
761
762 def testCheckMoveOperation_FailZeroStartBlock(self):
763 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
764 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700765 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700766 op.type = common.OpType.MOVE
767
768 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800769 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
770 self.AddToMessage(op.dst_extents,
771 self.NewExtentList((8, 128), (512, 6)))
772 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800773 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800774 op, None, 134, 134, 'foo')
775
Allie Woodb065e132015-04-24 10:20:27 -0700776 self.AddToMessage(op.src_extents,
777 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
778 self.AddToMessage(op.dst_extents,
779 self.NewExtentList((0, 128), (512, 6)))
780 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800781 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700782 op, None, 134, 134, 'foo')
783
Sen Jiang92161a72016-06-28 16:09:38 -0700784 def testCheckAnyDiff(self):
785 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800786 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800787 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800788
789 # Pass.
790 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800791 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800792
793 # Fail, missing data blob.
794 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800795 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800796 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800797
798 # Fail, too big of a diff blob (unjustified).
799 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800800 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800801 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800802
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800803 def testCheckSourceCopyOperation_Pass(self):
804 """Tests _CheckSourceCopyOperation(); pass case."""
805 payload_checker = checker.PayloadChecker(self.MockPayload())
806 self.assertIsNone(
807 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
808
809 def testCheckSourceCopyOperation_FailContainsData(self):
810 """Tests _CheckSourceCopyOperation(); message contains data."""
811 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800812 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800813 134, 0, 0, 'foo')
814
815 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
816 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
817 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800818 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800819 None, 0, 1, 'foo')
820
Gilad Arnold5502b562013-03-08 13:22:31 -0800821 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
822 allow_unhashed, fail_src_extents, fail_dst_extents,
823 fail_mismatched_data_offset_length,
824 fail_missing_dst_extents, fail_src_length,
825 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800826 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800827 """Parametric testing of _CheckOperation().
828
829 Args:
Amin Hassani0de7f782017-12-07 12:13:03 -0800830 op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ', 'MOVE', 'BSDIFF',
831 'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700832 is_last: Whether we're testing the last operation in a sequence.
833 allow_signature: Whether we're testing a signature-capable operation.
834 allow_unhashed: Whether we're allowing to not hash the data.
835 fail_src_extents: Tamper with src extents.
836 fail_dst_extents: Tamper with dst extents.
837 fail_mismatched_data_offset_length: Make data_{offset,length}
838 inconsistent.
839 fail_missing_dst_extents: Do not include dst extents.
840 fail_src_length: Make src length inconsistent.
841 fail_dst_length: Make dst length inconsistent.
842 fail_data_hash: Tamper with the data blob hash.
843 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800844 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800845 """
846 op_type = _OpTypeByName(op_type_name)
847
848 # Create the test object.
849 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700850 payload_checker = checker.PayloadChecker(payload,
851 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800852 block_size = payload_checker.block_size
853
854 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700855 old_part_size = test_utils.MiB(4)
856 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800857 old_block_counters = array.array(
858 'B', [0] * ((old_part_size + block_size - 1) / block_size))
859 new_block_counters = array.array(
860 'B', [0] * ((new_part_size + block_size - 1) / block_size))
861 prev_data_offset = 1876
862 blob_hash_counts = collections.defaultdict(int)
863
864 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700865 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800866 op.type = op_type
867
868 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800869 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700870 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800871 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800872 if fail_src_extents:
873 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700874 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800875 else:
876 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700877 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800878 total_src_blocks = 16
879
Allie Wood7cf9f132015-02-26 14:28:19 -0800880 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700881 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800882 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700883 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800884 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700885 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassani0de7f782017-12-07 12:13:03 -0800886 if op_type == common.OpType.REPLACE_XZ:
887 payload_checker.minor_version = 2 if fail_bad_minor_version else 3
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700888 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800889 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700890 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800891
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800892 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800893 if not fail_mismatched_data_offset_length:
894 op.data_length = 16 * block_size - 8
895 if fail_prev_data_offset:
896 op.data_offset = prev_data_offset + 16
897 else:
898 op.data_offset = prev_data_offset
899
900 fake_data = 'fake-data'.ljust(op.data_length)
901 if not (allow_unhashed or (is_last and allow_signature and
902 op_type == common.OpType.REPLACE)):
903 if not fail_data_hash:
904 # Create a valid data blob hash.
905 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
906 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
907 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800908
Gilad Arnold5502b562013-03-08 13:22:31 -0800909 elif fail_data_hash:
910 # Create an invalid data blob hash.
911 op.data_sha256_hash = hashlib.sha256(
912 fake_data.replace(' ', '-')).digest()
913 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
914 fake_data)
915
916 total_dst_blocks = 0
917 if not fail_missing_dst_extents:
918 total_dst_blocks = 16
919 if fail_dst_extents:
920 self.AddToMessage(op.dst_extents,
921 self.NewExtentList((4, 16), (32, 0)))
922 else:
923 self.AddToMessage(op.dst_extents,
924 self.NewExtentList((4, 8), (64, 8)))
925
926 if total_src_blocks:
927 if fail_src_length:
928 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800929 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
930 common.OpType.SOURCE_BSDIFF) and
931 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800932 op.src_length = total_src_blocks * block_size
933 elif fail_src_length:
934 # Add an orphaned src_length.
935 op.src_length = 16
936
937 if total_dst_blocks:
938 if fail_dst_length:
939 op.dst_length = total_dst_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.dst_length = total_dst_blocks * block_size
944
945 self.mox.ReplayAll()
946 should_fail = (fail_src_extents or fail_dst_extents or
947 fail_mismatched_data_offset_length or
948 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800949 fail_dst_length or fail_data_hash or fail_prev_data_offset or
950 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700951 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
952 old_part_size, new_part_size, prev_data_offset, allow_signature,
953 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800954 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800955 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800956 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700957 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
958 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800959
960 def testAllocBlockCounters(self):
961 """Tests _CheckMoveOperation()."""
962 payload_checker = checker.PayloadChecker(self.MockPayload())
963 block_size = payload_checker.block_size
964
965 # Check allocation for block-aligned partition size, ensure it's integers.
966 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700967 self.assertEqual(16, len(result))
968 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800969
970 # Check allocation of unaligned partition sizes.
971 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700972 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800973 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700974 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800975
Allie Woodfb04d302015-04-03 14:25:48 -0700976 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800977 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800978 # Generate a test payload. For this test, we only care about one
979 # (arbitrary) set of operations, so we'll only be generating kernel and
980 # test with them.
981 payload_gen = test_utils.PayloadGenerator()
982
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700983 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800984 payload_gen.SetBlockSize(block_size)
985
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700986 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800987
988 # Fake rootfs operations in a full update, tampered with as required.
989 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -0800990 rootfs_data_length = rootfs_part_size
991 if fail_nonexhaustive_full_update:
992 rootfs_data_length -= block_size
993
994 payload_gen.AddOperation(False, rootfs_op_type,
995 dst_extents=[(0, rootfs_data_length / block_size)],
996 data_offset=0,
997 data_length=rootfs_data_length)
998
999 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001000 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
1001 checker_init_dargs={
1002 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -08001003 payload_checker.payload_type = checker._TYPE_FULL
1004 report = checker._PayloadReport()
1005
Amin Hassaniae853742017-10-11 10:27:27 -07001006 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
1007 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -07001008 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001009 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001010 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001011 self.assertEqual(rootfs_data_length,
1012 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001013
1014 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1015 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1016 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001017 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001018 # Generate a test payload. For this test, we only care about the signature
1019 # block and how it relates to the payload hash. Therefore, we're generating
1020 # a random (otherwise useless) payload for this purpose.
1021 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001022 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001023 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001024 rootfs_part_size = test_utils.MiB(2)
1025 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001026 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1027 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001028 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001029 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001030 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001031 payload_gen.AddOperationWithData(
1032 False, common.OpType.REPLACE,
1033 dst_extents=[(0, rootfs_part_size / block_size)],
1034 data_blob=os.urandom(rootfs_part_size))
1035
1036 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1037 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1038 fail_sig_missing_fields or fail_unknown_sig_version
1039 or fail_incorrect_sig)
1040
1041 sigs_data = None
1042 if do_forge_sigs_data:
1043 sigs_gen = test_utils.SignaturesGenerator()
1044 if not fail_empty_sigs_blob:
1045 if fail_sig_missing_fields:
1046 sig_data = None
1047 else:
1048 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001049 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001050 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1051
1052 sigs_data = sigs_gen.ToBinary()
1053 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1054
1055 if do_forge_pseudo_op:
1056 assert sigs_data is not None, 'should have forged signatures blob by now'
1057 sigs_len = len(sigs_data)
1058 payload_gen.AddOperation(
1059 False, common.OpType.REPLACE,
1060 data_offset=payload_gen.curr_offset / 2,
1061 data_length=sigs_len / 2,
1062 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1063
1064 # Generate payload (complete w/ signature) and create the test object.
1065 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001066 payload_gen.WriteToFileWithData,
1067 payload_gen_dargs={
1068 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001069 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001070 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001071 payload_checker.payload_type = checker._TYPE_FULL
1072 report = checker._PayloadReport()
1073
1074 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001075 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001076
1077 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1078 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1079 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001080 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001081 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001082 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001083 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001084 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001085
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001086 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1087 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001088
1089 Args:
1090 minor_version: The payload minor version to test with.
1091 payload_type: The type of the payload we're testing, delta or full.
1092 """
1093 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001094 payload = self.MockPayload()
1095 payload.manifest.minor_version = minor_version
1096 payload_checker = checker.PayloadChecker(payload)
1097 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001098 report = checker._PayloadReport()
1099
1100 should_succeed = (
1101 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1102 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001103 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001104 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1105 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001106 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001107
1108 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001109 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001110 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001111 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001112 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001113
Gilad Arnold06eea332015-07-13 18:06:33 -07001114 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1115 fail_wrong_payload_type, fail_invalid_block_size,
1116 fail_mismatched_block_size, fail_excess_data,
1117 fail_rootfs_part_size_exceeded,
1118 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001119 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001120 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001121 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001122 # internal PayloadChecker methods that are tested elsewhere, here we only
1123 # tamper with what's actually being manipulated and/or tested in the Run()
1124 # method itself. Note that the checker doesn't verify partition hashes, so
1125 # they're safe to fake.
1126 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001127 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001128 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001129 kernel_filesystem_size = test_utils.KiB(16)
1130 rootfs_filesystem_size = test_utils.MiB(2)
1131 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001132 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001133 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001134 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001135 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001136
1137 rootfs_part_size = 0
1138 if rootfs_part_size_provided:
1139 rootfs_part_size = rootfs_filesystem_size + block_size
1140 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1141 if fail_rootfs_part_size_exceeded:
1142 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001143 payload_gen.AddOperationWithData(
1144 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001145 dst_extents=[(0, rootfs_op_size / block_size)],
1146 data_blob=os.urandom(rootfs_op_size))
1147
1148 kernel_part_size = 0
1149 if kernel_part_size_provided:
1150 kernel_part_size = kernel_filesystem_size + block_size
1151 kernel_op_size = kernel_part_size or kernel_filesystem_size
1152 if fail_kernel_part_size_exceeded:
1153 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001154 payload_gen.AddOperationWithData(
1155 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001156 dst_extents=[(0, kernel_op_size / block_size)],
1157 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001158
1159 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001160 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001161 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001162 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001163 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001164 else:
1165 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001166
Gilad Arnoldcb638912013-06-24 04:57:11 -07001167 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001168 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001169 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001170 'do_add_pseudo_operation': True,
1171 'is_pseudo_in_kernel': True,
1172 'padding': os.urandom(1024) if fail_excess_data else None},
1173 'checker_init_dargs': {
1174 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1175 'block_size': use_block_size}}
1176 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001177 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001178 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001179 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001180 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001181 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001182
1183 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1184 'rootfs_part_size': rootfs_part_size,
1185 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001186 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001187 fail_excess_data or
1188 fail_rootfs_part_size_exceeded or
1189 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001190 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001191 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001192 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001193 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001194
Gilad Arnold5502b562013-03-08 13:22:31 -08001195# This implements a generic API, hence the occasional unused args.
1196# pylint: disable=W0613
1197def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1198 allow_unhashed, fail_src_extents,
1199 fail_dst_extents,
1200 fail_mismatched_data_offset_length,
1201 fail_missing_dst_extents, fail_src_length,
1202 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001203 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001204 """Returns True iff the combination of arguments represents a valid test."""
1205 op_type = _OpTypeByName(op_type_name)
1206
Amin Hassani0de7f782017-12-07 12:13:03 -08001207 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1208 # partition. They are compatible with all valid minor versions, so we don't
1209 # need to check that.
1210 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1211 common.OpType.REPLACE_XZ) and (fail_src_extents or
1212 fail_src_length or
1213 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001214 return False
1215
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001216 # MOVE and SOURCE_COPY operations don't carry data.
1217 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001218 fail_mismatched_data_offset_length or fail_data_hash or
1219 fail_prev_data_offset)):
1220 return False
1221
1222 return True
1223
1224
1225def TestMethodBody(run_method_name, run_dargs):
1226 """Returns a function that invokes a named method with named arguments."""
1227 return lambda self: getattr(self, run_method_name)(**run_dargs)
1228
1229
1230def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1231 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1232
1233 This function enumerates a space of test parameters (defined by arg_space),
1234 then binds a new, unique method name in PayloadCheckerTest to a test function
1235 that gets handed the said parameters. This is a preferable approach to doing
1236 the enumeration and invocation during the tests because this way each test is
1237 treated as a complete run by the unittest framework, and so benefits from the
1238 usual setUp/tearDown mechanics.
1239
1240 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001241 tested_method_name: Name of the tested PayloadChecker method.
1242 arg_space: A dictionary containing variables (keys) and lists of values
1243 (values) associated with them.
1244 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001245 """
1246 for value_tuple in itertools.product(*arg_space.itervalues()):
1247 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1248 if validate_func and not validate_func(**run_dargs):
1249 continue
1250 run_method_name = 'Do%sTest' % tested_method_name
1251 test_method_name = 'test%s' % tested_method_name
1252 for arg_key, arg_val in run_dargs.iteritems():
1253 if arg_val or type(arg_val) is int:
1254 test_method_name += '__%s=%s' % (arg_key, arg_val)
1255 setattr(PayloadCheckerTest, test_method_name,
1256 TestMethodBody(run_method_name, run_dargs))
1257
1258
1259def AddAllParametricTests():
1260 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1261 # Add all _CheckElem() test cases.
1262 AddParametricTests('AddElem',
1263 {'linebreak': (True, False),
1264 'indent': (0, 1, 2),
1265 'convert': (str, lambda s: s[::-1]),
1266 'is_present': (True, False),
1267 'is_mandatory': (True, False),
1268 'is_submsg': (True, False)})
1269
1270 # Add all _Add{Mandatory,Optional}Field tests.
1271 AddParametricTests('AddField',
1272 {'is_mandatory': (True, False),
1273 'linebreak': (True, False),
1274 'indent': (0, 1, 2),
1275 'convert': (str, lambda s: s[::-1]),
1276 'is_present': (True, False)})
1277
1278 # Add all _Add{Mandatory,Optional}SubMsg tests.
1279 AddParametricTests('AddSubMsg',
1280 {'is_mandatory': (True, False),
1281 'is_present': (True, False)})
1282
1283 # Add all _CheckManifest() test cases.
1284 AddParametricTests('CheckManifest',
1285 {'fail_mismatched_block_size': (True, False),
1286 'fail_bad_sigs': (True, False),
1287 'fail_mismatched_oki_ori': (True, False),
1288 'fail_bad_oki': (True, False),
1289 'fail_bad_ori': (True, False),
1290 'fail_bad_nki': (True, False),
1291 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001292 'fail_old_kernel_fs_size': (True, False),
1293 'fail_old_rootfs_fs_size': (True, False),
1294 'fail_new_kernel_fs_size': (True, False),
1295 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001296
1297 # Add all _CheckOperation() test cases.
1298 AddParametricTests('CheckOperation',
Amin Hassani0de7f782017-12-07 12:13:03 -08001299 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
1300 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001301 'SOURCE_BSDIFF', 'PUFFDIFF',
1302 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001303 'is_last': (True, False),
1304 'allow_signature': (True, False),
1305 'allow_unhashed': (True, False),
1306 'fail_src_extents': (True, False),
1307 'fail_dst_extents': (True, False),
1308 'fail_mismatched_data_offset_length': (True, False),
1309 'fail_missing_dst_extents': (True, False),
1310 'fail_src_length': (True, False),
1311 'fail_dst_length': (True, False),
1312 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001313 'fail_prev_data_offset': (True, False),
1314 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001315 validate_func=ValidateCheckOperationTest)
1316
1317 # Add all _CheckOperations() test cases.
1318 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001319 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001320
1321 # Add all _CheckOperations() test cases.
1322 AddParametricTests('CheckSignatures',
1323 {'fail_empty_sigs_blob': (True, False),
1324 'fail_missing_pseudo_op': (True, False),
1325 'fail_mismatched_pseudo_op': (True, False),
1326 'fail_sig_missing_fields': (True, False),
1327 'fail_unknown_sig_version': (True, False),
1328 'fail_incorrect_sig': (True, False)})
1329
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001330 # Add all _CheckManifestMinorVersion() test cases.
1331 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001332 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001333 'payload_type': (checker._TYPE_FULL,
1334 checker._TYPE_DELTA)})
1335
Gilad Arnold5502b562013-03-08 13:22:31 -08001336 # Add all Run() test cases.
1337 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001338 {'rootfs_part_size_provided': (True, False),
1339 'kernel_part_size_provided': (True, False),
1340 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001341 'fail_invalid_block_size': (True, False),
1342 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001343 'fail_excess_data': (True, False),
1344 'fail_rootfs_part_size_exceeded': (True, False),
1345 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001346
1347
1348if __name__ == '__main__':
1349 AddAllParametricTests()
1350 unittest.main()