1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from autotest_lib.client.common_lib import utils 6 7AUTHOR = "Chromium OS" 8NAME = "autoupdate_CatchBadSignatures" 9TIME = "MEDIUM" 10TEST_CATEGORY = "Functional" 11TEST_CLASS = "platform" 12TEST_TYPE = "server" 13ATTRIBUTES = "suite:bvt-inline" 14JOB_RETRIES = 2 15 16DOC = """ 17This is a test to verify that update_engine correctly checks signatures in the 18metadata hash and the update payload itself. This is achieved by feeding updates 19to update_engine where the private key used to make the signature, intentionally 20does not match with the public key used for verification. 21 22By its very nature, this test requires an image signed with a well-known 23key. Since payload-generation is a resource-intensive process, we prepare the 24image ahead of time. Also, since the image is never successfully applied, we can 25get away with not caring that the image is built for one board but used on 26another. 27 28If you ever need to replace the test image and payloads, follow these simple 29(jk!) steps: 30 31 1. Build a test image: 32 33 $ cd ~/trunk/src/scripts 34 $ ./build_packages --board=${BOARD} 35 $ ./build_image --board=${BOARD} --noenable_rootfs_verification test 36 37 Alternatively, you can use any kind of Chrome OS image you already have or 38 downloaded. 39 40 2. Reduce the size of Rootfs and Kernel partitions. This is done so these 41 autotests don't have to download a huge payload as the payload is not going 42 to be applied fully anyway. This can be done many ways. One is: 43 44 $ sudo losetup -fP chromiumos_test_image.bin 45 46 At this point take a note of which loopback device was set up for the 47 image. e.g. /dev/loop1 48 49 $ sudo mkfs.ext4 -b 4k /dev/loop1p3 100 # For ROOT-A 50 $ sudo mkfs.ext4 -b 4k /dev/loop1p4 100 # For KERN-B 51 $ mkdir rootfs kernel 52 $ sudo mount /dev/loop1p3 rootfs 53 $ sudo mount /dev/loop1p4 kernel 54 55 Now you need a lsb-release file copied from any Chrome OS. 56 57 $ mkdir rootfs/etc && touch cp <lsb-release> rootfs/etc 58 $ touch kernel/fake-kernel.bin # Optional 59 $ sudo umount rootfs kernel 60 61 Now, the chromiumos_test_image.bin has very small Rootfs and Kernel 62 partitions. 63 64 3. Generate a full payload in which its metadata and payload signatures are 65 signed by two different private keys. An update payload has two signatures 66 embedded in it. The first is the signature of the header (metadata 67 signature) and the second is the signature of the entire payload (payload 68 signature). We do two tests here: One that makes sure the metadata 69 signature verification fails if signed incorrectly (we do this by sending a 70 different public key that doesn't verify the aforementioned signature). The 71 second one to make sure the metadata signature verification passes fine, 72 but the payload signature verification fails. One (not so) simple, but 73 available way of doing this is as follows: 74 75 Since we can't generate a payload with metadata and payload signatures 76 signed by different keys (simply we haven't designed tools for that), we 77 need to sign a payload two times with different keys and swap the payload 78 signature of one of them with the other. 79 80 $ mkdir key1 key2 81 $ cros_generate_update_payload \ 82 --image chromiumos_test_image.bin \ 83 --output key1/full.bin \ 84 --work_dir key1 \ 85 --private_key ~/trunk/src/aosp/system/update_engine/unittest_key.pem 86 $ cros_generate_update_payload \ 87 --image chromiumos_test_image.bin \ 88 --output key2/full.bin \ 89 --work_dir key2 \ 90 --private_key ~/trunk/src/aosp/system/update_engine/unittest_key2.pem 91 92 Now we should re-sign an unsigned image (key1/delta.bin) with metadata 93 signature from key1 and payload signature from key2 directories. Because we 94 passed the --work_dir flag, the intermediate temporary files (including 95 signature files) are saved in those directories. 96 97 $ delta_generator \ 98 --in_file=key1/delta.bin \ 99 --metadata_signature_file=key1/signature_metadata_<hash>.bin \ 100 --payload_signature_file=key2/signature_payload_<hash>.bin \ 101 --out_file=autoupdate_CatchBadSignatures.bin 102 103 This file is signed and ready to be tested. 104 105 4. Generate/modify a payload properties file. For each payload we need a 106 payload properties file in JSON format. This file has already been 107 generated in the previous step when we generated the signed image. We just 108 need to modify it. 109 110 $ cp key1/delta.bin.json autoupdate_CatchBadSignatures.bin.json 111 112 However, since we re-signed the payload, we need to calculate its SHA256 113 hash again. Easy way to do this is to use either delta_generator or 114 cros_generate_update_payload as: 115 116 $ cros_generate_update_payload \ 117 --payload autoupdate_CatchBadSignatures.bin --output foo.json 118 119 Open autoupdate_CatchBadSignatures.bin.json and replace the value of 120 sha256_hex with the one from foo.json. Also empty the value of 'appid' 121 (keep its key) to empty string. 122 123 5. Replace _IMAGE_PUBLIC_KEY2 in this file with value in 124 key2/full.bin.json. (You don't need to do this step if you used the same 125 keys as mentioned above.) 126 127 6. Upload the generated payload and its properties file to the public 128 gsbucket. 129 130 7. Now run the test and ensure that it passes. 131 132 $ cd ~/trunk/src/scripts 133 $ test_that -b ${BOARD} --fast <DUT_IP> autoupdate_CatchBadSignatures 134 135With this in place, you can now run the test: 136 137 $ test_that <DUT_IP> autoupdate_CatchBadSignatures -b ${BOARD} 138 139""" 140 141def run_test(machine): 142 """Execute a test configuration on a given machine.""" 143 host = hosts.create_host(machine) 144 job.run_test("autoupdate_CatchBadSignatures", host=host) 145 146# Invoke parallel tests. 147parallel_simple(run_test, machines) 148