1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/delta_performer.h"
18
19 #include <inttypes.h>
20 #include <sys/mount.h>
21
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25
26 #include <base/files/file_path.h>
27 #include <base/files/file_util.h>
28 #include <base/strings/string_util.h>
29 #include <base/strings/stringprintf.h>
30 #include <google/protobuf/repeated_field.h>
31 #include <gtest/gtest.h>
32
33 #include "update_engine/common/constants.h"
34 #include "update_engine/common/fake_boot_control.h"
35 #include "update_engine/common/fake_hardware.h"
36 #include "update_engine/common/mock_prefs.h"
37 #include "update_engine/common/test_utils.h"
38 #include "update_engine/common/utils.h"
39 #include "update_engine/payload_consumer/mock_download_action.h"
40 #include "update_engine/payload_consumer/payload_constants.h"
41 #include "update_engine/payload_consumer/payload_verifier.h"
42 #include "update_engine/payload_generator/delta_diff_generator.h"
43 #include "update_engine/payload_generator/payload_signer.h"
44 #include "update_engine/update_metadata.pb.h"
45
46 namespace chromeos_update_engine {
47
48 using std::string;
49 using std::vector;
50 using test_utils::ScopedLoopMounter;
51 using test_utils::System;
52 using test_utils::kRandomString;
53 using testing::Return;
54 using testing::_;
55
56 extern const char* kUnittestPrivateKeyPath;
57 extern const char* kUnittestPublicKeyPath;
58 extern const char* kUnittestPrivateKey2Path;
59 extern const char* kUnittestPublicKey2Path;
60
61 static const uint32_t kDefaultKernelSize = 4096; // Something small for a test
62 static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
63 'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
64
65 namespace {
66 struct DeltaState {
67 string a_img;
68 string b_img;
69 string result_img;
70 size_t image_size;
71
72 string delta_path;
73 uint64_t metadata_size;
74
75 string old_kernel;
76 brillo::Blob old_kernel_data;
77
78 string new_kernel;
79 brillo::Blob new_kernel_data;
80
81 string result_kernel;
82 brillo::Blob result_kernel_data;
83 size_t kernel_size;
84
85 // The InstallPlan referenced by the DeltaPerformer. This needs to outlive
86 // the DeltaPerformer.
87 InstallPlan install_plan;
88
89 // The in-memory copy of delta file.
90 brillo::Blob delta;
91
92 // Mock and fake instances used by the delta performer.
93 FakeBootControl fake_boot_control_;
94 FakeHardware fake_hardware_;
95 MockDownloadActionDelegate mock_delegate_;
96 };
97
98 enum SignatureTest {
99 kSignatureNone, // No payload signing.
100 kSignatureGenerator, // Sign the payload at generation time.
101 kSignatureGenerated, // Sign the payload after it's generated.
102 kSignatureGeneratedPlaceholder, // Insert placeholder signatures, then real.
103 kSignatureGeneratedPlaceholderMismatch, // Insert a wrong sized placeholder.
104 kSignatureGeneratedShell, // Sign the generated payload through shell cmds.
105 kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds.
106 kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1
107 kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2
108 };
109
110 enum OperationHashTest {
111 kInvalidOperationData,
112 kValidOperationData,
113 };
114
115 } // namespace
116
117 class DeltaPerformerIntegrationTest : public ::testing::Test {
118 public:
SetSupportedVersion(DeltaPerformer * performer,uint64_t minor_version)119 static void SetSupportedVersion(DeltaPerformer* performer,
120 uint64_t minor_version) {
121 performer->supported_minor_version_ = minor_version;
122 }
123 };
124
CompareFilesByBlock(const string & a_file,const string & b_file,size_t image_size)125 static void CompareFilesByBlock(const string& a_file, const string& b_file,
126 size_t image_size) {
127 EXPECT_EQ(0U, image_size % kBlockSize);
128
129 brillo::Blob a_data, b_data;
130 EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
131 EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
132
133 EXPECT_GE(a_data.size(), image_size);
134 EXPECT_GE(b_data.size(), image_size);
135 for (size_t i = 0; i < image_size; i += kBlockSize) {
136 EXPECT_EQ(0U, i % kBlockSize);
137 brillo::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
138 brillo::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
139 EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
140 }
141 if (::testing::Test::HasNonfatalFailure()) {
142 LOG(INFO) << "Compared filesystems with size " << image_size
143 << ", partition A " << a_file << " size: " << a_data.size()
144 << ", partition B " << b_file << " size: " << b_data.size();
145 }
146 }
147
WriteSparseFile(const string & path,off_t size)148 static bool WriteSparseFile(const string& path, off_t size) {
149 int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
150 TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
151 ScopedFdCloser fd_closer(&fd);
152 off_t rc = lseek(fd, size + 1, SEEK_SET);
153 TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
154 int return_code = ftruncate(fd, size);
155 TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
156 return true;
157 }
158
GetSignatureSize(const string & private_key_path)159 static size_t GetSignatureSize(const string& private_key_path) {
160 const brillo::Blob data(1, 'x');
161 brillo::Blob hash;
162 EXPECT_TRUE(HashCalculator::RawHashOfData(data, &hash));
163 brillo::Blob signature;
164 EXPECT_TRUE(PayloadSigner::SignHash(hash,
165 private_key_path,
166 &signature));
167 return signature.size();
168 }
169
InsertSignaturePlaceholder(int signature_size,const string & payload_path,uint64_t * out_metadata_size)170 static bool InsertSignaturePlaceholder(int signature_size,
171 const string& payload_path,
172 uint64_t* out_metadata_size) {
173 vector<brillo::Blob> signatures;
174 signatures.push_back(brillo::Blob(signature_size, 0));
175
176 return PayloadSigner::AddSignatureToPayload(
177 payload_path,
178 signatures,
179 {},
180 payload_path,
181 out_metadata_size);
182 }
183
SignGeneratedPayload(const string & payload_path,uint64_t * out_metadata_size)184 static void SignGeneratedPayload(const string& payload_path,
185 uint64_t* out_metadata_size) {
186 int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
187 brillo::Blob hash;
188 ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
189 payload_path,
190 vector<int>(1, signature_size),
191 &hash,
192 nullptr));
193 brillo::Blob signature;
194 ASSERT_TRUE(PayloadSigner::SignHash(hash,
195 kUnittestPrivateKeyPath,
196 &signature));
197 ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
198 payload_path,
199 vector<brillo::Blob>(1, signature),
200 {},
201 payload_path,
202 out_metadata_size));
203 EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
204 payload_path,
205 kUnittestPublicKeyPath));
206 }
207
SignGeneratedShellPayload(SignatureTest signature_test,const string & payload_path)208 static void SignGeneratedShellPayload(SignatureTest signature_test,
209 const string& payload_path) {
210 string private_key_path = kUnittestPrivateKeyPath;
211 if (signature_test == kSignatureGeneratedShellBadKey) {
212 ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
213 &private_key_path,
214 nullptr));
215 } else {
216 ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
217 signature_test == kSignatureGeneratedShellRotateCl1 ||
218 signature_test == kSignatureGeneratedShellRotateCl2);
219 }
220 ScopedPathUnlinker key_unlinker(private_key_path);
221 key_unlinker.set_should_remove(signature_test ==
222 kSignatureGeneratedShellBadKey);
223 // Generates a new private key that will not match the public key.
224 if (signature_test == kSignatureGeneratedShellBadKey) {
225 LOG(INFO) << "Generating a mismatched private key.";
226 ASSERT_EQ(0, System(base::StringPrintf(
227 "openssl genrsa -out %s 2048", private_key_path.c_str())));
228 }
229 int signature_size = GetSignatureSize(private_key_path);
230 string hash_file;
231 ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
232 ScopedPathUnlinker hash_unlinker(hash_file);
233 string signature_size_string;
234 if (signature_test == kSignatureGeneratedShellRotateCl1 ||
235 signature_test == kSignatureGeneratedShellRotateCl2)
236 signature_size_string = base::StringPrintf("%d:%d",
237 signature_size, signature_size);
238 else
239 signature_size_string = base::StringPrintf("%d", signature_size);
240 ASSERT_EQ(0,
241 System(base::StringPrintf(
242 "./delta_generator -in_file=%s -signature_size=%s "
243 "-out_hash_file=%s",
244 payload_path.c_str(),
245 signature_size_string.c_str(),
246 hash_file.c_str())));
247
248 // Pad the hash
249 brillo::Blob hash;
250 ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
251 ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
252 ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
253
254 string sig_file;
255 ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
256 ScopedPathUnlinker sig_unlinker(sig_file);
257 ASSERT_EQ(0,
258 System(base::StringPrintf(
259 "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
260 private_key_path.c_str(),
261 hash_file.c_str(),
262 sig_file.c_str())));
263 string sig_file2;
264 ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
265 ScopedPathUnlinker sig2_unlinker(sig_file2);
266 if (signature_test == kSignatureGeneratedShellRotateCl1 ||
267 signature_test == kSignatureGeneratedShellRotateCl2) {
268 ASSERT_EQ(0,
269 System(base::StringPrintf(
270 "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
271 kUnittestPrivateKey2Path,
272 hash_file.c_str(),
273 sig_file2.c_str())));
274 // Append second sig file to first path
275 sig_file += ":" + sig_file2;
276 }
277
278 ASSERT_EQ(0,
279 System(base::StringPrintf(
280 "./delta_generator -in_file=%s -signature_file=%s "
281 "-out_file=%s",
282 payload_path.c_str(),
283 sig_file.c_str(),
284 payload_path.c_str())));
285 int verify_result =
286 System(base::StringPrintf(
287 "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
288 payload_path.c_str(),
289 signature_test == kSignatureGeneratedShellRotateCl2 ?
290 kUnittestPublicKey2Path : kUnittestPublicKeyPath,
291 signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
292 if (signature_test == kSignatureGeneratedShellBadKey) {
293 ASSERT_NE(0, verify_result);
294 } else {
295 ASSERT_EQ(0, verify_result);
296 }
297 }
298
GenerateDeltaFile(bool full_kernel,bool full_rootfs,bool noop,ssize_t chunk_size,SignatureTest signature_test,DeltaState * state,uint32_t minor_version)299 static void GenerateDeltaFile(bool full_kernel,
300 bool full_rootfs,
301 bool noop,
302 ssize_t chunk_size,
303 SignatureTest signature_test,
304 DeltaState *state,
305 uint32_t minor_version) {
306 EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
307 EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
308
309 // result_img is used in minor version 2. Instead of applying the update
310 // in-place on A, we apply it to a new image, result_img.
311 EXPECT_TRUE(
312 utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
313 test_utils::CreateExtImageAtPath(state->a_img, nullptr);
314
315 state->image_size = utils::FileSize(state->a_img);
316
317 // Create ImageInfo A & B
318 ImageInfo old_image_info;
319 ImageInfo new_image_info;
320
321 if (!full_rootfs) {
322 old_image_info.set_channel("src-channel");
323 old_image_info.set_board("src-board");
324 old_image_info.set_version("src-version");
325 old_image_info.set_key("src-key");
326 old_image_info.set_build_channel("src-build-channel");
327 old_image_info.set_build_version("src-build-version");
328 }
329
330 new_image_info.set_channel("test-channel");
331 new_image_info.set_board("test-board");
332 new_image_info.set_version("test-version");
333 new_image_info.set_key("test-key");
334 new_image_info.set_build_channel("test-build-channel");
335 new_image_info.set_build_version("test-build-version");
336
337 // Make some changes to the A image.
338 {
339 string a_mnt;
340 ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
341
342 brillo::Blob hardtocompress;
343 while (hardtocompress.size() < 3 * kBlockSize) {
344 hardtocompress.insert(hardtocompress.end(),
345 std::begin(kRandomString), std::end(kRandomString));
346 }
347 EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
348 a_mnt.c_str()).c_str(),
349 hardtocompress.data(),
350 hardtocompress.size()));
351
352 brillo::Blob zeros(16 * 1024, 0);
353 EXPECT_EQ(static_cast<int>(zeros.size()),
354 base::WriteFile(base::FilePath(base::StringPrintf(
355 "%s/move-to-sparse", a_mnt.c_str())),
356 reinterpret_cast<const char*>(zeros.data()),
357 zeros.size()));
358
359 EXPECT_TRUE(
360 WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
361 a_mnt.c_str()), 16 * 1024));
362
363 EXPECT_EQ(0,
364 System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
365 " bs=1 seek=4096 count=1 status=none",
366 a_mnt.c_str()).c_str()));
367
368 // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
369 // patch fails to zero out the final block.
370 brillo::Blob ones(1024 * 1024, 0xff);
371 EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
372 a_mnt.c_str()).c_str(),
373 ones.data(),
374 ones.size()));
375 }
376
377 if (noop) {
378 EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
379 base::FilePath(state->b_img)));
380 old_image_info = new_image_info;
381 } else {
382 if (minor_version == kSourceMinorPayloadVersion) {
383 // Create a result image with image_size bytes of garbage.
384 brillo::Blob ones(state->image_size, 0xff);
385 EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(),
386 ones.data(),
387 ones.size()));
388 EXPECT_EQ(utils::FileSize(state->a_img),
389 utils::FileSize(state->result_img));
390 }
391
392 test_utils::CreateExtImageAtPath(state->b_img, nullptr);
393
394 // Make some changes to the B image.
395 string b_mnt;
396 ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
397
398 EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
399 b_mnt.c_str(),
400 b_mnt.c_str()).c_str()));
401 EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
402 b_mnt.c_str()).c_str()));
403 EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
404 b_mnt.c_str(),
405 b_mnt.c_str()).c_str()));
406 EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
407 b_mnt.c_str()).c_str()));
408 EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
409 b_mnt.c_str()).c_str()));
410 EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
411 b_mnt.c_str()),
412 1024 * 1024));
413
414 EXPECT_TRUE(
415 WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
416 16 * 1024));
417
418 brillo::Blob zeros(16 * 1024, 0);
419 EXPECT_EQ(static_cast<int>(zeros.size()),
420 base::WriteFile(base::FilePath(base::StringPrintf(
421 "%s/move-from-sparse", b_mnt.c_str())),
422 reinterpret_cast<const char*>(zeros.data()),
423 zeros.size()));
424
425 EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
426 "of=%s/move-semi-sparse "
427 "bs=1 seek=4096 count=1 status=none",
428 b_mnt.c_str()).c_str()));
429
430 EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
431 "of=%s/partsparse bs=1 "
432 "seek=4096 count=1 status=none",
433 b_mnt.c_str()).c_str()));
434 EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
435 "mv %s/tmp %s/srchardlink1",
436 b_mnt.c_str(),
437 b_mnt.c_str(),
438 b_mnt.c_str(),
439 b_mnt.c_str()).c_str()));
440 EXPECT_EQ(0, System(
441 base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
442 b_mnt.c_str(), b_mnt.c_str()).c_str()));
443
444 brillo::Blob hardtocompress;
445 while (hardtocompress.size() < 3 * kBlockSize) {
446 hardtocompress.insert(hardtocompress.end(),
447 std::begin(kRandomString), std::end(kRandomString));
448 }
449 EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
450 b_mnt.c_str()).c_str(),
451 hardtocompress.data(),
452 hardtocompress.size()));
453 }
454
455 string old_kernel;
456 EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
457 &state->old_kernel,
458 nullptr));
459
460 string new_kernel;
461 EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
462 &state->new_kernel,
463 nullptr));
464
465 string result_kernel;
466 EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
467 &state->result_kernel,
468 nullptr));
469
470 state->kernel_size = kDefaultKernelSize;
471 state->old_kernel_data.resize(kDefaultKernelSize);
472 state->new_kernel_data.resize(state->old_kernel_data.size());
473 state->result_kernel_data.resize(state->old_kernel_data.size());
474 test_utils::FillWithData(&state->old_kernel_data);
475 test_utils::FillWithData(&state->new_kernel_data);
476 test_utils::FillWithData(&state->result_kernel_data);
477
478 // change the new kernel data
479 std::copy(std::begin(kNewData), std::end(kNewData),
480 state->new_kernel_data.begin());
481
482 if (noop) {
483 state->old_kernel_data = state->new_kernel_data;
484 }
485
486 // Write kernels to disk
487 EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
488 state->old_kernel_data.data(),
489 state->old_kernel_data.size()));
490 EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
491 state->new_kernel_data.data(),
492 state->new_kernel_data.size()));
493 EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
494 state->result_kernel_data.data(),
495 state->result_kernel_data.size()));
496
497 EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
498 &state->delta_path,
499 nullptr));
500 LOG(INFO) << "delta path: " << state->delta_path;
501 {
502 const string private_key =
503 signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
504
505 PayloadGenerationConfig payload_config;
506 payload_config.is_delta = !full_rootfs;
507 payload_config.hard_chunk_size = chunk_size;
508 payload_config.rootfs_partition_size = kRootFSPartitionSize;
509 payload_config.version.major = kChromeOSMajorPayloadVersion;
510 payload_config.version.minor = minor_version;
511 if (!full_rootfs) {
512 payload_config.source.partitions.emplace_back(kLegacyPartitionNameRoot);
513 payload_config.source.partitions.emplace_back(kLegacyPartitionNameKernel);
514 payload_config.source.partitions.front().path = state->a_img;
515 if (!full_kernel)
516 payload_config.source.partitions.back().path = state->old_kernel;
517 payload_config.source.image_info = old_image_info;
518 EXPECT_TRUE(payload_config.source.LoadImageSize());
519 for (PartitionConfig& part : payload_config.source.partitions)
520 EXPECT_TRUE(part.OpenFilesystem());
521 } else {
522 if (payload_config.hard_chunk_size == -1)
523 // Use 1 MiB chunk size for the full unittests.
524 payload_config.hard_chunk_size = 1024 * 1024;
525 }
526 payload_config.target.partitions.emplace_back(kLegacyPartitionNameRoot);
527 payload_config.target.partitions.back().path = state->b_img;
528 payload_config.target.partitions.emplace_back(kLegacyPartitionNameKernel);
529 payload_config.target.partitions.back().path = state->new_kernel;
530 payload_config.target.image_info = new_image_info;
531 EXPECT_TRUE(payload_config.target.LoadImageSize());
532 for (PartitionConfig& part : payload_config.target.partitions)
533 EXPECT_TRUE(part.OpenFilesystem());
534
535 EXPECT_TRUE(payload_config.Validate());
536 EXPECT_TRUE(
537 GenerateUpdatePayloadFile(
538 payload_config,
539 state->delta_path,
540 private_key,
541 &state->metadata_size));
542 }
543 // Extend the "partitions" holding the file system a bit.
544 EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
545 state->image_size + 1024 * 1024)));
546 EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
547 utils::FileSize(state->a_img));
548 EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
549 state->image_size + 1024 * 1024)));
550 EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
551 utils::FileSize(state->b_img));
552
553 if (signature_test == kSignatureGeneratedPlaceholder ||
554 signature_test == kSignatureGeneratedPlaceholderMismatch) {
555 int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
556 LOG(INFO) << "Inserting placeholder signature.";
557 ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path,
558 &state->metadata_size));
559
560 if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
561 signature_size -= 1;
562 LOG(INFO) << "Inserting mismatched placeholder signature.";
563 ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path,
564 &state->metadata_size));
565 return;
566 }
567 }
568
569 if (signature_test == kSignatureGenerated ||
570 signature_test == kSignatureGeneratedPlaceholder ||
571 signature_test == kSignatureGeneratedPlaceholderMismatch) {
572 // Generate the signed payload and update the metadata size in state to
573 // reflect the new size after adding the signature operation to the
574 // manifest.
575 LOG(INFO) << "Signing payload.";
576 SignGeneratedPayload(state->delta_path, &state->metadata_size);
577 } else if (signature_test == kSignatureGeneratedShell ||
578 signature_test == kSignatureGeneratedShellBadKey ||
579 signature_test == kSignatureGeneratedShellRotateCl1 ||
580 signature_test == kSignatureGeneratedShellRotateCl2) {
581 SignGeneratedShellPayload(signature_test, state->delta_path);
582 }
583 }
584
ApplyDeltaFile(bool full_kernel,bool full_rootfs,bool noop,SignatureTest signature_test,DeltaState * state,bool hash_checks_mandatory,OperationHashTest op_hash_test,DeltaPerformer ** performer,uint32_t minor_version)585 static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop,
586 SignatureTest signature_test, DeltaState* state,
587 bool hash_checks_mandatory,
588 OperationHashTest op_hash_test,
589 DeltaPerformer** performer,
590 uint32_t minor_version) {
591 // Check the metadata.
592 {
593 DeltaArchiveManifest manifest;
594 EXPECT_TRUE(PayloadSigner::LoadPayloadMetadata(state->delta_path,
595 nullptr,
596 &manifest,
597 nullptr,
598 &state->metadata_size,
599 nullptr));
600 LOG(INFO) << "Metadata size: " << state->metadata_size;
601 EXPECT_TRUE(utils::ReadFile(state->delta_path, &state->delta));
602
603 if (signature_test == kSignatureNone) {
604 EXPECT_FALSE(manifest.has_signatures_offset());
605 EXPECT_FALSE(manifest.has_signatures_size());
606 } else {
607 EXPECT_TRUE(manifest.has_signatures_offset());
608 EXPECT_TRUE(manifest.has_signatures_size());
609 Signatures sigs_message;
610 EXPECT_TRUE(sigs_message.ParseFromArray(
611 &state->delta[state->metadata_size + manifest.signatures_offset()],
612 manifest.signatures_size()));
613 if (signature_test == kSignatureGeneratedShellRotateCl1 ||
614 signature_test == kSignatureGeneratedShellRotateCl2)
615 EXPECT_EQ(2, sigs_message.signatures_size());
616 else
617 EXPECT_EQ(1, sigs_message.signatures_size());
618 const Signatures_Signature& signature = sigs_message.signatures(0);
619 EXPECT_EQ(1U, signature.version());
620
621 uint64_t expected_sig_data_length = 0;
622 vector<string> key_paths{kUnittestPrivateKeyPath};
623 if (signature_test == kSignatureGeneratedShellRotateCl1 ||
624 signature_test == kSignatureGeneratedShellRotateCl2) {
625 key_paths.push_back(kUnittestPrivateKey2Path);
626 }
627 EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
628 key_paths,
629 &expected_sig_data_length));
630 EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
631 EXPECT_FALSE(signature.data().empty());
632 }
633
634 if (noop) {
635 EXPECT_EQ(0, manifest.install_operations_size());
636 EXPECT_EQ(1, manifest.kernel_install_operations_size());
637 }
638
639 if (full_kernel) {
640 EXPECT_FALSE(manifest.has_old_kernel_info());
641 } else {
642 EXPECT_EQ(state->old_kernel_data.size(),
643 manifest.old_kernel_info().size());
644 EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
645 }
646
647 EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
648 EXPECT_EQ(manifest.new_image_info().board(), "test-board");
649 EXPECT_EQ(manifest.new_image_info().version(), "test-version");
650 EXPECT_EQ(manifest.new_image_info().key(), "test-key");
651 EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
652 EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
653
654 if (!full_rootfs) {
655 if (noop) {
656 EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
657 EXPECT_EQ(manifest.old_image_info().board(), "test-board");
658 EXPECT_EQ(manifest.old_image_info().version(), "test-version");
659 EXPECT_EQ(manifest.old_image_info().key(), "test-key");
660 EXPECT_EQ(manifest.old_image_info().build_channel(),
661 "test-build-channel");
662 EXPECT_EQ(manifest.old_image_info().build_version(),
663 "test-build-version");
664 } else {
665 EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
666 EXPECT_EQ(manifest.old_image_info().board(), "src-board");
667 EXPECT_EQ(manifest.old_image_info().version(), "src-version");
668 EXPECT_EQ(manifest.old_image_info().key(), "src-key");
669 EXPECT_EQ(manifest.old_image_info().build_channel(),
670 "src-build-channel");
671 EXPECT_EQ(manifest.old_image_info().build_version(),
672 "src-build-version");
673 }
674 }
675
676
677 if (full_rootfs) {
678 EXPECT_FALSE(manifest.has_old_rootfs_info());
679 EXPECT_FALSE(manifest.has_old_image_info());
680 EXPECT_TRUE(manifest.has_new_image_info());
681 } else {
682 EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
683 EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
684 }
685
686 EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
687 EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
688
689 EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
690 EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
691 }
692
693 MockPrefs prefs;
694 EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
695 state->metadata_size)).WillOnce(Return(true));
696 EXPECT_CALL(prefs, SetInt64(kPrefsManifestSignatureSize, 0))
697 .WillOnce(Return(true));
698 EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
699 .WillRepeatedly(Return(true));
700 EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
701 .WillOnce(Return(false));
702 EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
703 .WillRepeatedly(Return(true));
704 EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
705 .WillRepeatedly(Return(true));
706 EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
707 .WillRepeatedly(Return(true));
708 EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
709 .WillRepeatedly(Return(true));
710 if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
711 EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
712 .WillOnce(Return(true));
713 }
714
715 EXPECT_CALL(state->mock_delegate_, ShouldCancel(_))
716 .WillRepeatedly(Return(false));
717
718 // Update the A image in place.
719 InstallPlan* install_plan = &state->install_plan;
720 install_plan->hash_checks_mandatory = hash_checks_mandatory;
721 install_plan->metadata_size = state->metadata_size;
722 install_plan->payload_type = (full_kernel && full_rootfs)
723 ? InstallPayloadType::kFull
724 : InstallPayloadType::kDelta;
725 install_plan->source_slot = 0;
726 install_plan->target_slot = 1;
727
728 InstallPlan::Partition root_part;
729 root_part.name = kLegacyPartitionNameRoot;
730
731 InstallPlan::Partition kernel_part;
732 kernel_part.name = kLegacyPartitionNameKernel;
733
734 LOG(INFO) << "Setting payload metadata size in Omaha = "
735 << state->metadata_size;
736 ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
737 state->delta.data(),
738 state->metadata_size,
739 kUnittestPrivateKeyPath,
740 &install_plan->metadata_signature));
741 EXPECT_FALSE(install_plan->metadata_signature.empty());
742
743 *performer = new DeltaPerformer(&prefs,
744 &state->fake_boot_control_,
745 &state->fake_hardware_,
746 &state->mock_delegate_,
747 install_plan);
748 EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
749 (*performer)->set_public_key_path(kUnittestPublicKeyPath);
750 DeltaPerformerIntegrationTest::SetSupportedVersion(*performer, minor_version);
751
752 EXPECT_EQ(static_cast<off_t>(state->image_size),
753 HashCalculator::RawHashOfFile(
754 state->a_img,
755 state->image_size,
756 &root_part.source_hash));
757 EXPECT_TRUE(HashCalculator::RawHashOfData(
758 state->old_kernel_data,
759 &kernel_part.source_hash));
760
761 // This partitions are normally filed by the FilesystemVerifierAction with
762 // the source hashes used for deltas.
763 install_plan->partitions = {root_part, kernel_part};
764
765 // With minor version 2, we want the target to be the new image, result_img,
766 // but with version 1, we want to update A in place.
767 string target_root, target_kernel;
768 if (minor_version == kSourceMinorPayloadVersion) {
769 target_root = state->result_img;
770 target_kernel = state->result_kernel;
771 } else {
772 target_root = state->a_img;
773 target_kernel = state->old_kernel;
774 }
775
776 state->fake_boot_control_.SetPartitionDevice(
777 kLegacyPartitionNameRoot, install_plan->source_slot, state->a_img);
778 state->fake_boot_control_.SetPartitionDevice(
779 kLegacyPartitionNameKernel, install_plan->source_slot, state->old_kernel);
780 state->fake_boot_control_.SetPartitionDevice(
781 kLegacyPartitionNameRoot, install_plan->target_slot, target_root);
782 state->fake_boot_control_.SetPartitionDevice(
783 kLegacyPartitionNameKernel, install_plan->target_slot, target_kernel);
784
785 ErrorCode expected_error, actual_error;
786 bool continue_writing;
787 switch (op_hash_test) {
788 case kInvalidOperationData: {
789 // Muck with some random offset post the metadata size so that
790 // some operation hash will result in a mismatch.
791 int some_offset = state->metadata_size + 300;
792 LOG(INFO) << "Tampered value at offset: " << some_offset;
793 state->delta[some_offset]++;
794 expected_error = ErrorCode::kDownloadOperationHashMismatch;
795 continue_writing = false;
796 break;
797 }
798
799 case kValidOperationData:
800 default:
801 // no change.
802 expected_error = ErrorCode::kSuccess;
803 continue_writing = true;
804 break;
805 }
806
807 // Write at some number of bytes per operation. Arbitrarily chose 5.
808 const size_t kBytesPerWrite = 5;
809 for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
810 size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
811 bool write_succeeded = ((*performer)->Write(&state->delta[i],
812 count,
813 &actual_error));
814 // Normally write_succeeded should be true every time and
815 // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
816 // But if we seeded an operation hash error above, then write_succeeded
817 // will be false. The failure may happen at any operation n. So, all
818 // Writes until n-1 should succeed and the nth operation will fail with
819 // actual_error. In this case, we should bail out of the loop because
820 // we cannot proceed applying the delta.
821 if (!write_succeeded) {
822 LOG(INFO) << "Write failed. Checking if it failed with expected error";
823 EXPECT_EQ(expected_error, actual_error);
824 if (!continue_writing) {
825 LOG(INFO) << "Cannot continue writing. Bailing out.";
826 break;
827 }
828 }
829
830 EXPECT_EQ(ErrorCode::kSuccess, actual_error);
831 }
832
833 // If we had continued all the way through, Close should succeed.
834 // Otherwise, it should fail. Check appropriately.
835 bool close_result = (*performer)->Close();
836 if (continue_writing)
837 EXPECT_EQ(0, close_result);
838 else
839 EXPECT_LE(0, close_result);
840 }
841
VerifyPayloadResult(DeltaPerformer * performer,DeltaState * state,ErrorCode expected_result,uint32_t minor_version)842 void VerifyPayloadResult(DeltaPerformer* performer,
843 DeltaState* state,
844 ErrorCode expected_result,
845 uint32_t minor_version) {
846 if (!performer) {
847 EXPECT_TRUE(!"Skipping payload verification since performer is null.");
848 return;
849 }
850
851 int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
852 EXPECT_CALL(state->mock_delegate_, DownloadComplete()).Times(expected_times);
853
854 LOG(INFO) << "Verifying payload for expected result "
855 << expected_result;
856 EXPECT_EQ(expected_result, performer->VerifyPayload(
857 HashCalculator::HashOfData(state->delta),
858 state->delta.size()));
859 LOG(INFO) << "Verified payload.";
860
861 if (expected_result != ErrorCode::kSuccess) {
862 // no need to verify new partition if VerifyPayload failed.
863 return;
864 }
865
866 brillo::Blob updated_kernel_partition;
867 if (minor_version == kSourceMinorPayloadVersion) {
868 CompareFilesByBlock(state->result_kernel, state->new_kernel,
869 state->kernel_size);
870 CompareFilesByBlock(state->result_img, state->b_img,
871 state->image_size);
872 EXPECT_TRUE(utils::ReadFile(state->result_kernel,
873 &updated_kernel_partition));
874 } else {
875 CompareFilesByBlock(state->old_kernel, state->new_kernel,
876 state->kernel_size);
877 CompareFilesByBlock(state->a_img, state->b_img,
878 state->image_size);
879 EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
880 }
881
882 ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
883 EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData),
884 updated_kernel_partition.begin()));
885
886 const auto& partitions = state->install_plan.partitions;
887 EXPECT_EQ(2U, partitions.size());
888 EXPECT_EQ(kLegacyPartitionNameRoot, partitions[0].name);
889 EXPECT_EQ(kLegacyPartitionNameKernel, partitions[1].name);
890
891 EXPECT_EQ(kDefaultKernelSize, partitions[1].target_size);
892 brillo::Blob expected_new_kernel_hash;
893 EXPECT_TRUE(HashCalculator::RawHashOfData(state->new_kernel_data,
894 &expected_new_kernel_hash));
895 EXPECT_EQ(expected_new_kernel_hash, partitions[1].target_hash);
896
897 EXPECT_EQ(state->image_size, partitions[0].target_size);
898 brillo::Blob expected_new_rootfs_hash;
899 EXPECT_EQ(static_cast<off_t>(state->image_size),
900 HashCalculator::RawHashOfFile(state->b_img,
901 state->image_size,
902 &expected_new_rootfs_hash));
903 EXPECT_EQ(expected_new_rootfs_hash, partitions[0].target_hash);
904 }
905
VerifyPayload(DeltaPerformer * performer,DeltaState * state,SignatureTest signature_test,uint32_t minor_version)906 void VerifyPayload(DeltaPerformer* performer,
907 DeltaState* state,
908 SignatureTest signature_test,
909 uint32_t minor_version) {
910 ErrorCode expected_result = ErrorCode::kSuccess;
911 switch (signature_test) {
912 case kSignatureNone:
913 expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
914 break;
915 case kSignatureGeneratedShellBadKey:
916 expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
917 break;
918 default: break; // appease gcc
919 }
920
921 VerifyPayloadResult(performer, state, expected_result, minor_version);
922 }
923
DoSmallImageTest(bool full_kernel,bool full_rootfs,bool noop,ssize_t chunk_size,SignatureTest signature_test,bool hash_checks_mandatory,uint32_t minor_version)924 void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
925 ssize_t chunk_size,
926 SignatureTest signature_test,
927 bool hash_checks_mandatory, uint32_t minor_version) {
928 DeltaState state;
929 DeltaPerformer *performer = nullptr;
930 GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
931 signature_test, &state, minor_version);
932
933 ScopedPathUnlinker a_img_unlinker(state.a_img);
934 ScopedPathUnlinker b_img_unlinker(state.b_img);
935 ScopedPathUnlinker new_img_unlinker(state.result_img);
936 ScopedPathUnlinker delta_unlinker(state.delta_path);
937 ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
938 ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
939 ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
940 ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
941 &state, hash_checks_mandatory, kValidOperationData,
942 &performer, minor_version);
943 VerifyPayload(performer, &state, signature_test, minor_version);
944 delete performer;
945 }
946
DoOperationHashMismatchTest(OperationHashTest op_hash_test,bool hash_checks_mandatory)947 void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
948 bool hash_checks_mandatory) {
949 DeltaState state;
950 uint64_t minor_version = kFullPayloadMinorVersion;
951 GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
952 minor_version);
953 ScopedPathUnlinker a_img_unlinker(state.a_img);
954 ScopedPathUnlinker b_img_unlinker(state.b_img);
955 ScopedPathUnlinker delta_unlinker(state.delta_path);
956 ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
957 ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
958 DeltaPerformer *performer = nullptr;
959 ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
960 hash_checks_mandatory, op_hash_test, &performer,
961 minor_version);
962 delete performer;
963 }
964
965
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageTest)966 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
967 DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
968 false, kInPlaceMinorPayloadVersion);
969 }
970
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignaturePlaceholderTest)971 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderTest) {
972 DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
973 false, kInPlaceMinorPayloadVersion);
974 }
975
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignaturePlaceholderMismatchTest)976 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
977 DeltaState state;
978 GenerateDeltaFile(false, false, false, -1,
979 kSignatureGeneratedPlaceholderMismatch, &state,
980 kInPlaceMinorPayloadVersion);
981 }
982
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageChunksTest)983 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
984 DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
985 false, kInPlaceMinorPayloadVersion);
986 }
987
TEST(DeltaPerformerIntegrationTest,RunAsRootFullKernelSmallImageTest)988 TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
989 DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
990 false, kInPlaceMinorPayloadVersion);
991 }
992
TEST(DeltaPerformerIntegrationTest,RunAsRootFullSmallImageTest)993 TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
994 DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
995 true, kFullPayloadMinorVersion);
996 }
997
TEST(DeltaPerformerIntegrationTest,RunAsRootNoopSmallImageTest)998 TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) {
999 DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
1000 false, kInPlaceMinorPayloadVersion);
1001 }
1002
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignNoneTest)1003 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
1004 DoSmallImageTest(false, false, false, -1, kSignatureNone,
1005 false, kInPlaceMinorPayloadVersion);
1006 }
1007
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignGeneratedTest)1008 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
1009 DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
1010 true, kInPlaceMinorPayloadVersion);
1011 }
1012
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignGeneratedShellTest)1013 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) {
1014 DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
1015 false, kInPlaceMinorPayloadVersion);
1016 }
1017
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignGeneratedShellBadKeyTest)1018 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
1019 DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
1020 false, kInPlaceMinorPayloadVersion);
1021 }
1022
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignGeneratedShellRotateCl1Test)1023 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
1024 DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
1025 false, kInPlaceMinorPayloadVersion);
1026 }
1027
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSignGeneratedShellRotateCl2Test)1028 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
1029 DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
1030 false, kInPlaceMinorPayloadVersion);
1031 }
1032
TEST(DeltaPerformerIntegrationTest,RunAsRootSmallImageSourceOpsTest)1033 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
1034 DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
1035 false, kSourceMinorPayloadVersion);
1036 }
1037
TEST(DeltaPerformerIntegrationTest,RunAsRootMandatoryOperationHashMismatchTest)1038 TEST(DeltaPerformerIntegrationTest, RunAsRootMandatoryOperationHashMismatchTest) {
1039 DoOperationHashMismatchTest(kInvalidOperationData, true);
1040 }
1041
1042 } // namespace chromeos_update_engine
1043