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