• 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"
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