# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from autotest_lib.client.common_lib import utils AUTHOR = "Chromium OS" NAME = "autoupdate_CatchBadSignatures" TIME = "MEDIUM" TEST_CATEGORY = "Functional" TEST_CLASS = "platform" TEST_TYPE = "server" ATTRIBUTES = "suite:bvt-inline" JOB_RETRIES = 2 DOC = """ This is a test to verify that update_engine correctly checks signatures in the metadata hash and the update payload itself. This is achieved by feeding updates to update_engine where the private key used to make the signature, intentionally does not match with the public key used for verification. By its very nature, this test requires an image signed with a well-known key. Since payload-generation is a resource-intensive process, we prepare the image ahead of time. Also, since the image is never successfully applied, we can get away with not caring that the image is built for one board but used on another. If you ever need to replace the test image and payloads, follow these simple (jk!) steps: 1. Build a test image: $ cd ~/trunk/src/scripts $ ./build_packages --board=${BOARD} $ ./build_image --board=${BOARD} --noenable_rootfs_verification test Alternatively, you can use any kind of Chrome OS image you already have or downloaded. 2. Reduce the size of Rootfs and Kernel partitions. This is done so these autotests don't have to download a huge payload as the payload is not going to be applied fully anyway. This can be done many ways. One is: $ sudo losetup -fP chromiumos_test_image.bin At this point take a note of which loopback device was set up for the image. e.g. /dev/loop1 $ sudo mkfs.ext4 -b 4k /dev/loop1p3 100 # For ROOT-A $ sudo mkfs.ext4 -b 4k /dev/loop1p4 100 # For KERN-B $ mkdir rootfs kernel $ sudo mount /dev/loop1p3 rootfs $ sudo mount /dev/loop1p4 kernel Now you need a lsb-release file copied from any Chrome OS. $ mkdir rootfs/etc && touch cp rootfs/etc $ touch kernel/fake-kernel.bin # Optional $ sudo umount rootfs kernel Now, the chromiumos_test_image.bin has very small Rootfs and Kernel partitions. 3. Generate a full payload in which its metadata and payload signatures are signed by two different private keys. An update payload has two signatures embedded in it. The first is the signature of the header (metadata signature) and the second is the signature of the entire payload (payload signature). We do two tests here: One that makes sure the metadata signature verification fails if signed incorrectly (we do this by sending a different public key that doesn't verify the aforementioned signature). The second one to make sure the metadata signature verification passes fine, but the payload signature verification fails. One (not so) simple, but available way of doing this is as follows: Since we can't generate a payload with metadata and payload signatures signed by different keys (simply we haven't designed tools for that), we need to sign a payload two times with different keys and swap the payload signature of one of them with the other. $ mkdir key1 key2 $ cros_generate_update_payload \ --image chromiumos_test_image.bin \ --output key1/full.bin \ --work_dir key1 \ --private_key ~/trunk/src/aosp/system/update_engine/unittest_key.pem $ cros_generate_update_payload \ --image chromiumos_test_image.bin \ --output key2/full.bin \ --work_dir key2 \ --private_key ~/trunk/src/aosp/system/update_engine/unittest_key2.pem Now we should re-sign an unsigned image (key1/delta.bin) with metadata signature from key1 and payload signature from key2 directories. Because we passed the --work_dir flag, the intermediate temporary files (including signature files) are saved in those directories. $ delta_generator \ --in_file=key1/delta.bin \ --metadata_signature_file=key1/signature_metadata_.bin \ --payload_signature_file=key2/signature_payload_.bin \ --out_file=autoupdate_CatchBadSignatures.bin This file is signed and ready to be tested. 4. Generate/modify a payload properties file. For each payload we need a payload properties file in JSON format. This file has already been generated in the previous step when we generated the signed image. We just need to modify it. $ cp key1/delta.bin.json autoupdate_CatchBadSignatures.bin.json However, since we re-signed the payload, we need to calculate its SHA256 hash again. Easy way to do this is to use either delta_generator or cros_generate_update_payload as: $ cros_generate_update_payload \ --payload autoupdate_CatchBadSignatures.bin --output foo.json Open autoupdate_CatchBadSignatures.bin.json and replace the value of sha256_hex with the one from foo.json. Also empty the value of 'appid' (keep its key) to empty string. 5. Replace _IMAGE_PUBLIC_KEY2 in this file with value in key2/full.bin.json. (You don't need to do this step if you used the same keys as mentioned above.) 6. Upload the generated payload and its properties file to the public gsbucket. 7. Now run the test and ensure that it passes. $ cd ~/trunk/src/scripts $ test_that -b ${BOARD} --fast autoupdate_CatchBadSignatures With this in place, you can now run the test: $ test_that autoupdate_CatchBadSignatures -b ${BOARD} """ def run_test(machine): """Execute a test configuration on a given machine.""" host = hosts.create_host(machine) job.run_test("autoupdate_CatchBadSignatures", host=host) # Invoke parallel tests. parallel_simple(run_test, machines)