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