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