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