1 // Copyright 2017 The Fuchsia 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 
5 #include <inttypes.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 
9 #include <crypto/cipher.h>
10 #include <crypto/secret.h>
11 #include <unittest/unittest.h>
12 #include <zircon/errors.h>
13 #include <zircon/types.h>
14 #include <zxcrypt/volume.h>
15 
16 #include <utility>
17 
18 #include "test-device.h"
19 
20 namespace zxcrypt {
21 namespace testing {
22 namespace {
23 
24 // See test-device.h; the following macros allow reusing tests for each of the supported versions.
25 #define EACH_PARAM(OP, Test) OP(Test, Volume, AES256_XTS_SHA256)
26 
27 // ZX-1948: Dump extra information if encountering an unexpected error during volume creation.
VolumeCreate(const fbl::unique_fd & fd,const crypto::Secret & key,bool fvm,zx_status_t expected)28 bool VolumeCreate(const fbl::unique_fd& fd, const crypto::Secret& key, bool fvm,
29                   zx_status_t expected) {
30     BEGIN_HELPER;
31 
32     char err[128];
33     block_info_t bInfo;
34     ASSERT_GE(ioctl_block_get_info(fd.get(), &bInfo), 0);
35     if (fvm) {
36         fvm_info_t fInfo;
37         ASSERT_GE(ioctl_block_fvm_query(fd.get(), &fInfo), 0);
38         snprintf(err, sizeof(err),
39                  "details: block size=%" PRIu32 ", block count=%" PRIu64
40                  ", slice size=%zu, slice count=%zu",
41                  bInfo.block_size, bInfo.block_count, fInfo.slice_size, fInfo.vslice_count);
42     } else {
43         snprintf(err, sizeof(err), "details: block size=%" PRIu32 ", block count=%" PRIu64,
44                  bInfo.block_size, bInfo.block_count);
45     }
46 
47     fbl::unique_fd new_fd(dup(fd.get()));
48     EXPECT_EQ(Volume::Create(std::move(new_fd), key), expected, err);
49 
50     END_HELPER;
51 }
52 
TestInit(Volume::Version version,bool fvm)53 bool TestInit(Volume::Version version, bool fvm) {
54     BEGIN_TEST;
55 
56     TestDevice device;
57     ASSERT_TRUE(device.Create(kDeviceSize, kBlockSize, fvm));
58 
59     // Invalid arguments
60     fbl::unique_fd bad_fd;
61     fbl::unique_ptr<Volume> volume;
62     EXPECT_ZX(Volume::Init(std::move(bad_fd), &volume), ZX_ERR_INVALID_ARGS);
63     EXPECT_ZX(Volume::Init(device.parent(), nullptr), ZX_ERR_INVALID_ARGS);
64 
65     // Valid
66     EXPECT_ZX(Volume::Init(device.parent(), &volume), ZX_OK);
67     ASSERT_TRUE(!!volume);
68     EXPECT_EQ(volume->reserved_blocks(), fvm ? (FVM_BLOCK_SIZE / kBlockSize) : 2u);
69     EXPECT_EQ(volume->reserved_slices(), fvm ? 1u : 0u);
70 
71     END_TEST;
72 }
73 DEFINE_EACH_DEVICE(TestInit);
74 
TestCreate(Volume::Version version,bool fvm)75 bool TestCreate(Volume::Version version, bool fvm) {
76     BEGIN_TEST;
77 
78     TestDevice device;
79     ASSERT_TRUE(device.Create(kDeviceSize, kBlockSize, fvm));
80 
81     // Invalid file descriptor
82     fbl::unique_fd bad_fd;
83     EXPECT_ZX(Volume::Create(std::move(bad_fd), device.key()), ZX_ERR_INVALID_ARGS);
84 
85     // Weak key
86     crypto::Secret short_key;
87     ASSERT_OK(short_key.Generate(device.key().len() - 1));
88     EXPECT_TRUE(VolumeCreate(device.parent(), short_key, fvm, ZX_ERR_INVALID_ARGS));
89 
90     // Valid
91     EXPECT_TRUE(VolumeCreate(device.parent(), device.key(), fvm, ZX_OK));
92 
93     END_TEST;
94 }
95 DEFINE_EACH_DEVICE(TestCreate);
96 
TestUnlock(Volume::Version version,bool fvm)97 bool TestUnlock(Volume::Version version, bool fvm) {
98     BEGIN_TEST;
99 
100     TestDevice device;
101     ASSERT_TRUE(device.Create(kDeviceSize, kBlockSize, fvm));
102 
103     // Invalid device
104     fbl::unique_ptr<Volume> volume;
105     EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), 0, &volume),
106               ZX_ERR_ACCESS_DENIED);
107 
108     // Bad file descriptor
109     fbl::unique_fd bad_fd;
110     EXPECT_ZX(Volume::Unlock(std::move(bad_fd), device.key(), 0, &volume), ZX_ERR_INVALID_ARGS);
111 
112     // Bad key
113     ASSERT_TRUE(VolumeCreate(device.parent(), device.key(), fvm, ZX_OK));
114 
115     crypto::Secret bad_key;
116     ASSERT_OK(bad_key.Generate(device.key().len()));
117     EXPECT_ZX(Volume::Unlock(device.parent(), bad_key, 0, &volume),
118               ZX_ERR_ACCESS_DENIED);
119 
120     // Bad slot
121     EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), -1, &volume),
122               ZX_ERR_ACCESS_DENIED);
123     EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), 1, &volume),
124               ZX_ERR_ACCESS_DENIED);
125 
126     // Valid
127     EXPECT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
128 
129     // Corrupt the key in each block.
130     fbl::unique_fd parent = device.parent();
131     off_t off = 0;
132     uint8_t before[kBlockSize];
133     uint8_t after[sizeof(before)];
134     const size_t num_blocks = volume->reserved_blocks();
135 
136     for (size_t i = 0; i < num_blocks; ++i) {
137         // On FVM, the trailing reserved blocks may just be to pad to a slice, and not have any
138         // metdata.  Start from the end and iterate backward to ensure the last block corrupted has
139         // metadata.
140         ASSERT_TRUE(device.Corrupt(num_blocks - 1 - i, 0));
141         lseek(parent.get(), off, SEEK_SET);
142         read(parent.get(), before, sizeof(before));
143 
144         if (i < num_blocks - 1) {
145             // Volume should still be unlockable as long as one copy of the key exists
146             EXPECT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
147         } else {
148             // Key should fail when last copy is corrupted.
149             EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), 0, &volume),
150                       ZX_ERR_ACCESS_DENIED);
151         }
152 
153         lseek(parent.get(), off, SEEK_SET);
154         read(parent.get(), after, sizeof(after));
155 
156         // Unlock should not modify the parent
157         EXPECT_EQ(memcmp(before, after, sizeof(before)), 0);
158     }
159 
160     END_TEST;
161 }
162 DEFINE_EACH_DEVICE(TestUnlock);
163 
TestEnroll(Volume::Version version,bool fvm)164 bool TestEnroll(Volume::Version version, bool fvm) {
165     BEGIN_TEST;
166     TestDevice device;
167     ASSERT_TRUE(device.Bind(version, fvm));
168 
169     fbl::unique_ptr<Volume> volume;
170     ASSERT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
171 
172     // Bad key
173     crypto::Secret bad_key;
174     EXPECT_ZX(volume->Enroll(bad_key, 1), ZX_ERR_INVALID_ARGS);
175 
176     // Bad slot
177     EXPECT_ZX(volume->Enroll(device.key(), volume->num_slots()), ZX_ERR_INVALID_ARGS);
178 
179     // Valid; new slot
180     EXPECT_OK(volume->Enroll(device.key(), 1));
181     EXPECT_OK(Volume::Unlock(device.parent(), device.key(), 1, &volume));
182 
183     // Valid; existing slot
184     EXPECT_OK(volume->Enroll(device.key(), 0));
185     EXPECT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
186 
187     END_TEST;
188 }
189 DEFINE_EACH_DEVICE(TestEnroll);
190 
TestRevoke(Volume::Version version,bool fvm)191 bool TestRevoke(Volume::Version version, bool fvm) {
192     BEGIN_TEST;
193 
194     TestDevice device;
195     ASSERT_TRUE(device.Bind(version, fvm));
196 
197     fbl::unique_ptr<Volume> volume;
198     ASSERT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
199 
200     // Bad slot
201     EXPECT_ZX(volume->Revoke(volume->num_slots()), ZX_ERR_INVALID_ARGS);
202 
203     // Valid, even if slot isn't enrolled
204     EXPECT_OK(volume->Revoke(volume->num_slots() - 1));
205 
206     // Valid, even if last slot
207     EXPECT_OK(volume->Revoke(0));
208     EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), 0, &volume),
209               ZX_ERR_ACCESS_DENIED);
210 
211     END_TEST;
212 }
213 DEFINE_EACH_DEVICE(TestRevoke);
214 
TestShred(Volume::Version version,bool fvm)215 bool TestShred(Volume::Version version, bool fvm) {
216     BEGIN_TEST;
217 
218     TestDevice device;
219     ASSERT_TRUE(device.Bind(version, fvm));
220 
221     fbl::unique_ptr<Volume> volume;
222     ASSERT_OK(Volume::Unlock(device.parent(), device.key(), 0, &volume));
223 
224     // Valid
225     EXPECT_OK(volume->Shred());
226 
227     // No further methods work
228     EXPECT_ZX(volume->Enroll(device.key(), 0), ZX_ERR_BAD_STATE);
229     EXPECT_ZX(volume->Revoke(0), ZX_ERR_BAD_STATE);
230     EXPECT_ZX(Volume::Unlock(device.parent(), device.key(), 0, &volume),
231               ZX_ERR_ACCESS_DENIED);
232 
233     END_TEST;
234 }
235 DEFINE_EACH_DEVICE(TestShred);
236 
237 BEGIN_TEST_CASE(VolumeTest)
238 RUN_EACH_DEVICE(TestInit)
239 RUN_EACH_DEVICE(TestCreate)
240 RUN_EACH_DEVICE(TestUnlock)
241 RUN_EACH_DEVICE(TestEnroll)
242 RUN_EACH_DEVICE(TestRevoke)
243 RUN_EACH_DEVICE(TestShred)
244 END_TEST_CASE(VolumeTest)
245 
246 } // namespace
247 } // namespace testing
248 } // namespace zxcrypt
249