1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // Utility functions for VtsKernelEncryptionTest.
18
19 #include <LzmaLib.h>
20 #include <android-base/properties.h>
21 #include <android-base/unique_fd.h>
22 #include <errno.h>
23 #include <ext4_utils/ext4.h>
24 #include <ext4_utils/ext4_sb.h>
25 #include <ext4_utils/ext4_utils.h>
26 #include <gtest/gtest.h>
27 #include <libdm/dm.h>
28 #include <linux/magic.h>
29 #include <mntent.h>
30 #include <openssl/cmac.h>
31 #include <unistd.h>
32
33 #include "Keymaster.h"
34 #include "vts_kernel_encryption.h"
35
36 using namespace android::dm;
37
38 namespace android {
39 namespace kernel {
40 // Context in fixed input string comprises of software provided context,
41 // padding to eight bytes (if required) and the key policy.
42 static const std::vector<std::vector<uint8_t>> HwWrappedEncryptionKeyContexts =
43 {
44 {'i', 'n', 'l', 'i', 'n', 'e', ' ', 'e', 'n', 'c', 'r', 'y',
45 'p', 't', 'i', 'o', 'n', ' ', 'k', 'e', 'y', 0x0, 0x0, 0x0,
46 0x00, 0x00, 0x00, 0x02, 0x43, 0x00, 0x82, 0x50, 0x0, 0x0, 0x0, 0x0},
47 // Below for "legacy && kdf tied to Trusted Execution
48 // Environment(TEE)".
49 // Where as above caters ( "all latest targets" || ("legacy && kdf
50 // not tied to TEE)).
51 {'i', 'n', 'l', 'i', 'n', 'e', ' ', 'e', 'n', 'c', 'r', 'y',
52 'p', 't', 'i', 'o', 'n', ' ', 'k', 'e', 'y', 0x0, 0x0, 0x0,
53 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x82, 0x18, 0x0, 0x0, 0x0, 0x0},
54 };
55
GetKdfContext(std::vector<uint8_t> * ctx)56 static bool GetKdfContext(std::vector<uint8_t> *ctx) {
57 std::string kdf =
58 android::base::GetProperty("ro.crypto.hw_wrapped_keys.kdf", "v1");
59 if (kdf == "v1") {
60 *ctx = HwWrappedEncryptionKeyContexts[0];
61 return true;
62 }
63 if (kdf == "legacykdf") {
64 *ctx = HwWrappedEncryptionKeyContexts[1];
65 return true;
66 }
67 ADD_FAILURE() << "Unknown KDF: " << kdf;
68 return false;
69 }
70
71 // Offset in bytes to the filesystem superblock, relative to the beginning of
72 // the block device
73 constexpr int kExt4SuperBlockOffset = 1024;
74 constexpr int kF2fsSuperBlockOffset = 1024;
75
76 // For F2FS: the offsets in bytes to the filesystem magic number and filesystem
77 // UUID, relative to the beginning of the block device
78 constexpr int kF2fsMagicOffset = kF2fsSuperBlockOffset;
79 constexpr int kF2fsUuidOffset = kF2fsSuperBlockOffset + 108;
80
81 // hw-wrapped key size in bytes
82 constexpr int kHwWrappedKeySize = 32;
83
Errno()84 std::string Errno() { return std::string(": ") + strerror(errno); }
85
86 // Recursively deletes the file or directory at |path|, if it exists.
DeleteRecursively(const std::string & path)87 void DeleteRecursively(const std::string &path) {
88 if (unlink(path.c_str()) == 0 || errno == ENOENT) return;
89 ASSERT_EQ(EISDIR, errno) << "Failed to unlink " << path << Errno();
90
91 std::unique_ptr<DIR, int (*)(DIR *)> dirp(opendir(path.c_str()), closedir);
92 // If the directory was assigned an encryption policy that the kernel lacks
93 // crypto API support for, then opening it will fail, and it will be empty.
94 // So, we have to allow opening the directory to fail.
95 if (dirp != nullptr) {
96 struct dirent *entry;
97 while ((entry = readdir(dirp.get())) != nullptr) {
98 std::string filename(entry->d_name);
99 if (filename != "." && filename != "..")
100 DeleteRecursively(path + "/" + filename);
101 }
102 }
103 ASSERT_EQ(0, rmdir(path.c_str()))
104 << "Failed to remove directory " << path << Errno();
105 }
106
107 // Generates some "random" bytes. Not secure; this is for testing only.
RandomBytesForTesting(std::vector<uint8_t> & bytes)108 void RandomBytesForTesting(std::vector<uint8_t> &bytes) {
109 for (size_t i = 0; i < bytes.size(); i++) {
110 bytes[i] = rand();
111 }
112 }
113
114 // Generates a "random" key. Not secure; this is for testing only.
GenerateTestKey(size_t size)115 std::vector<uint8_t> GenerateTestKey(size_t size) {
116 std::vector<uint8_t> key(size);
117 RandomBytesForTesting(key);
118 return key;
119 }
120
BytesToHex(const std::vector<uint8_t> & bytes)121 std::string BytesToHex(const std::vector<uint8_t> &bytes) {
122 std::ostringstream o;
123 for (uint8_t b : bytes) {
124 o << std::hex << std::setw(2) << std::setfill('0') << (int)b;
125 }
126 return o.str();
127 }
128
GetFirstApiLevel(int * first_api_level)129 bool GetFirstApiLevel(int *first_api_level) {
130 *first_api_level =
131 android::base::GetIntProperty("ro.product.first_api_level", 0);
132 if (*first_api_level == 0) {
133 ADD_FAILURE() << "ro.product.first_api_level is unset";
134 return false;
135 }
136 GTEST_LOG_(INFO) << "ro.product.first_api_level = " << *first_api_level;
137 return true;
138 }
139
140 // Gets the block device and type of the filesystem mounted on |mountpoint|.
141 // This block device is the one on which the filesystem is directly located. In
142 // the case of device-mapper that means something like /dev/mapper/dm-5, not the
143 // underlying device like /dev/block/by-name/userdata.
GetFsBlockDeviceAndType(const std::string & mountpoint,std::string * fs_blk_device,std::string * fs_type)144 static bool GetFsBlockDeviceAndType(const std::string &mountpoint,
145 std::string *fs_blk_device,
146 std::string *fs_type) {
147 std::unique_ptr<FILE, int (*)(FILE *)> mnts(setmntent("/proc/mounts", "re"),
148 endmntent);
149 if (!mnts) {
150 ADD_FAILURE() << "Failed to open /proc/mounts" << Errno();
151 return false;
152 }
153 struct mntent *mnt;
154 while ((mnt = getmntent(mnts.get())) != nullptr) {
155 if (mnt->mnt_dir == mountpoint) {
156 *fs_blk_device = mnt->mnt_fsname;
157 *fs_type = mnt->mnt_type;
158 return true;
159 }
160 }
161 ADD_FAILURE() << "No /proc/mounts entry found for " << mountpoint;
162 return false;
163 }
164
165 // Gets the UUID of the filesystem of type |fs_type| that's located on
166 // |fs_blk_device|.
167 //
168 // Unfortunately there's no kernel API to get the UUID; instead we have to read
169 // it from the filesystem superblock.
GetFilesystemUuid(const std::string & fs_blk_device,const std::string & fs_type,FilesystemUuid * fs_uuid)170 static bool GetFilesystemUuid(const std::string &fs_blk_device,
171 const std::string &fs_type,
172 FilesystemUuid *fs_uuid) {
173 android::base::unique_fd fd(
174 open(fs_blk_device.c_str(), O_RDONLY | O_CLOEXEC));
175 if (fd < 0) {
176 ADD_FAILURE() << "Failed to open fs block device " << fs_blk_device
177 << Errno();
178 return false;
179 }
180
181 if (fs_type == "ext4") {
182 struct ext4_super_block sb;
183
184 if (pread(fd, &sb, sizeof(sb), kExt4SuperBlockOffset) != sizeof(sb)) {
185 ADD_FAILURE() << "Error reading ext4 superblock from " << fs_blk_device
186 << Errno();
187 return false;
188 }
189 if (sb.s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {
190 ADD_FAILURE() << "Failed to find ext4 superblock on " << fs_blk_device;
191 return false;
192 }
193 static_assert(sizeof(sb.s_uuid) == kFilesystemUuidSize);
194 memcpy(fs_uuid->bytes, sb.s_uuid, kFilesystemUuidSize);
195 } else if (fs_type == "f2fs") {
196 // Android doesn't have an f2fs equivalent of libext4_utils, so we have to
197 // hard-code the offset to the magic number and UUID.
198
199 __le32 magic;
200 if (pread(fd, &magic, sizeof(magic), kF2fsMagicOffset) != sizeof(magic)) {
201 ADD_FAILURE() << "Error reading f2fs superblock from " << fs_blk_device
202 << Errno();
203 return false;
204 }
205 if (magic != cpu_to_le32(F2FS_SUPER_MAGIC)) {
206 ADD_FAILURE() << "Failed to find f2fs superblock on " << fs_blk_device;
207 return false;
208 }
209 if (pread(fd, fs_uuid->bytes, kFilesystemUuidSize, kF2fsUuidOffset) !=
210 kFilesystemUuidSize) {
211 ADD_FAILURE() << "Failed to read f2fs filesystem UUID from "
212 << fs_blk_device << Errno();
213 return false;
214 }
215 } else {
216 ADD_FAILURE() << "Unknown filesystem type " << fs_type;
217 return false;
218 }
219 return true;
220 }
221
222 // Gets the raw block device of the filesystem that is mounted from
223 // |fs_blk_device|. By "raw block device" we mean a block device from which we
224 // can read the encrypted file contents and filesystem metadata. When metadata
225 // encryption is disabled, this is simply |fs_blk_device|. When metadata
226 // encryption is enabled, then |fs_blk_device| is a dm-default-key device and
227 // the "raw block device" is the parent of this dm-default-key device.
228 //
229 // We don't just use the block device listed in the fstab, because (a) it can be
230 // a logical partition name which needs extra code to map to a block device, and
231 // (b) due to block-level checkpointing, there can be a dm-bow device between
232 // the fstab partition and dm-default-key. dm-bow can remap sectors, but for
233 // encryption testing we don't want any sector remapping. So the correct block
234 // device to read ciphertext from is the one directly underneath dm-default-key.
GetRawBlockDevice(const std::string & fs_blk_device,std::string * raw_blk_device)235 static bool GetRawBlockDevice(const std::string &fs_blk_device,
236 std::string *raw_blk_device) {
237 DeviceMapper &dm = DeviceMapper::Instance();
238
239 if (!dm.IsDmBlockDevice(fs_blk_device)) {
240 GTEST_LOG_(INFO)
241 << fs_blk_device
242 << " is not a device-mapper device; metadata encryption is disabled";
243 *raw_blk_device = fs_blk_device;
244 return true;
245 }
246 const std::optional<std::string> name =
247 dm.GetDmDeviceNameByPath(fs_blk_device);
248 if (!name) {
249 ADD_FAILURE() << "Failed to get name of device-mapper device "
250 << fs_blk_device;
251 return false;
252 }
253
254 std::vector<DeviceMapper::TargetInfo> table;
255 if (!dm.GetTableInfo(*name, &table)) {
256 ADD_FAILURE() << "Failed to get table of device-mapper device " << *name;
257 return false;
258 }
259 if (table.size() != 1) {
260 GTEST_LOG_(INFO) << fs_blk_device
261 << " has multiple device-mapper targets; assuming "
262 "metadata encryption is disabled";
263 *raw_blk_device = fs_blk_device;
264 return true;
265 }
266 const std::string target_type = dm.GetTargetType(table[0].spec);
267 if (target_type != "default-key") {
268 GTEST_LOG_(INFO) << fs_blk_device << " is a dm-" << target_type
269 << " device, not dm-default-key; assuming metadata "
270 "encryption is disabled";
271 *raw_blk_device = fs_blk_device;
272 return true;
273 }
274 std::optional<std::string> parent =
275 dm.GetParentBlockDeviceByPath(fs_blk_device);
276 if (!parent) {
277 ADD_FAILURE() << "Failed to get parent of dm-default-key device " << *name;
278 return false;
279 }
280 *raw_blk_device = *parent;
281 return true;
282 }
283
284 // Gets information about the filesystem mounted on |mountpoint|.
GetFilesystemInfo(const std::string & mountpoint,FilesystemInfo * info)285 bool GetFilesystemInfo(const std::string &mountpoint, FilesystemInfo *info) {
286 if (!GetFsBlockDeviceAndType(mountpoint, &info->fs_blk_device, &info->type))
287 return false;
288
289 if (!GetFilesystemUuid(info->fs_blk_device, info->type, &info->uuid))
290 return false;
291
292 if (!GetRawBlockDevice(info->fs_blk_device, &info->raw_blk_device))
293 return false;
294
295 GTEST_LOG_(INFO) << info->fs_blk_device << " is mounted on " << mountpoint
296 << " with type " << info->type << "; UUID is "
297 << BytesToHex(info->uuid.bytes) << ", raw block device is "
298 << info->raw_blk_device;
299 return true;
300 }
301
302 // Returns true if the given data seems to be random.
303 //
304 // Check compressibility rather than byte frequencies. Compressibility is a
305 // stronger test since it also detects repetitions.
306 //
307 // To check compressibility, use LZMA rather than DEFLATE/zlib/gzip because LZMA
308 // compression is stronger and supports a much larger dictionary. DEFLATE is
309 // limited to a 32 KiB dictionary. So, data repeating after 32 KiB (or more)
310 // would not be detected with DEFLATE. But LZMA can detect it.
VerifyDataRandomness(const std::vector<uint8_t> & bytes)311 bool VerifyDataRandomness(const std::vector<uint8_t> &bytes) {
312 // To avoid flakiness, allow the data to be compressed a tiny bit by chance.
313 // There is at most a 2^-32 chance that random data can be compressed to be 4
314 // bytes shorter. In practice it's even lower due to compression overhead.
315 size_t destLen = bytes.size() - std::min<size_t>(4, bytes.size());
316 std::vector<uint8_t> dest(destLen);
317 uint8_t outProps[LZMA_PROPS_SIZE];
318 size_t outPropsSize = LZMA_PROPS_SIZE;
319 int ret;
320
321 ret = LzmaCompress(dest.data(), &destLen, bytes.data(), bytes.size(),
322 outProps, &outPropsSize,
323 6, // compression level (0 <= level <= 9)
324 bytes.size(), // dictionary size
325 -1, -1, -1, -1, // lc, lp, bp, fb (-1 selects the default)
326 1); // number of threads
327
328 if (ret == SZ_ERROR_OUTPUT_EOF) return true; // incompressible
329
330 if (ret == SZ_OK) {
331 ADD_FAILURE() << "Data is not random! Compressed " << bytes.size()
332 << " to " << destLen << " bytes";
333 } else {
334 ADD_FAILURE() << "LZMA compression error: ret=" << ret;
335 }
336 return false;
337 }
338
TryPrepareHwWrappedKey(Keymaster & keymaster,const std::string & master_key_string,std::string * exported_key_string,bool rollback_resistance)339 static bool TryPrepareHwWrappedKey(Keymaster &keymaster,
340 const std::string &master_key_string,
341 std::string *exported_key_string,
342 bool rollback_resistance) {
343 // This key is used to drive a CMAC-based KDF
344 auto paramBuilder =
345 km::AuthorizationSetBuilder().AesEncryptionKey(kHwWrappedKeySize * 8);
346 if (rollback_resistance) {
347 paramBuilder.Authorization(km::TAG_ROLLBACK_RESISTANCE);
348 }
349 paramBuilder.Authorization(km::TAG_STORAGE_KEY);
350
351 std::string wrapped_key_blob;
352 if (keymaster.importKey(paramBuilder, master_key_string, &wrapped_key_blob) &&
353 keymaster.exportKey(wrapped_key_blob, exported_key_string)) {
354 return true;
355 }
356 // It's fine for Keymaster not to support hardware-wrapped keys, but
357 // if generateKey works, importKey must too.
358 if (keymaster.generateKey(paramBuilder, &wrapped_key_blob) &&
359 keymaster.exportKey(wrapped_key_blob, exported_key_string)) {
360 ADD_FAILURE() << "generateKey succeeded but importKey failed";
361 }
362 return false;
363 }
364
CreateHwWrappedKey(std::vector<uint8_t> * master_key,std::vector<uint8_t> * exported_key)365 bool CreateHwWrappedKey(std::vector<uint8_t> *master_key,
366 std::vector<uint8_t> *exported_key) {
367 *master_key = GenerateTestKey(kHwWrappedKeySize);
368
369 Keymaster keymaster;
370 if (!keymaster) {
371 ADD_FAILURE() << "Unable to find keymaster";
372 return false;
373 }
374 std::string master_key_string(master_key->begin(), master_key->end());
375 std::string exported_key_string;
376 // Make two attempts to create a key, first with and then without
377 // rollback resistance.
378 if (TryPrepareHwWrappedKey(keymaster, master_key_string, &exported_key_string,
379 true) ||
380 TryPrepareHwWrappedKey(keymaster, master_key_string, &exported_key_string,
381 false)) {
382 exported_key->assign(exported_key_string.begin(),
383 exported_key_string.end());
384 return true;
385 }
386 GTEST_LOG_(INFO) << "Skipping test because device doesn't support "
387 "hardware-wrapped keys";
388 return false;
389 }
390
PushBigEndian32(uint32_t val,std::vector<uint8_t> * vec)391 static void PushBigEndian32(uint32_t val, std::vector<uint8_t> *vec) {
392 for (int i = 24; i >= 0; i -= 8) {
393 vec->push_back((val >> i) & 0xFF);
394 }
395 }
396
GetFixedInputString(uint32_t counter,const std::vector<uint8_t> & label,const std::vector<uint8_t> & context,uint32_t derived_key_len,std::vector<uint8_t> * fixed_input_string)397 static void GetFixedInputString(uint32_t counter,
398 const std::vector<uint8_t> &label,
399 const std::vector<uint8_t> &context,
400 uint32_t derived_key_len,
401 std::vector<uint8_t> *fixed_input_string) {
402 PushBigEndian32(counter, fixed_input_string);
403 fixed_input_string->insert(fixed_input_string->end(), label.begin(),
404 label.end());
405 fixed_input_string->push_back(0);
406 fixed_input_string->insert(fixed_input_string->end(), context.begin(),
407 context.end());
408 PushBigEndian32(derived_key_len, fixed_input_string);
409 }
410
AesCmacKdfHelper(const std::vector<uint8_t> & key,const std::vector<uint8_t> & label,const std::vector<uint8_t> & context,uint32_t output_key_size,std::vector<uint8_t> * output_data)411 static bool AesCmacKdfHelper(const std::vector<uint8_t> &key,
412 const std::vector<uint8_t> &label,
413 const std::vector<uint8_t> &context,
414 uint32_t output_key_size,
415 std::vector<uint8_t> *output_data) {
416 output_data->resize(output_key_size);
417 for (size_t count = 0; count < (output_key_size / kAesBlockSize); count++) {
418 std::vector<uint8_t> fixed_input_string;
419 GetFixedInputString(count + 1, label, context, (output_key_size * 8),
420 &fixed_input_string);
421 if (!AES_CMAC(output_data->data() + (kAesBlockSize * count), key.data(),
422 key.size(), fixed_input_string.data(),
423 fixed_input_string.size())) {
424 ADD_FAILURE()
425 << "AES_CMAC failed while deriving subkey from HW wrapped key";
426 return false;
427 }
428 }
429 return true;
430 }
431
DeriveHwWrappedEncryptionKey(const std::vector<uint8_t> & master_key,std::vector<uint8_t> * enc_key)432 bool DeriveHwWrappedEncryptionKey(const std::vector<uint8_t> &master_key,
433 std::vector<uint8_t> *enc_key) {
434 std::vector<uint8_t> label{0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
435 0x00, 0x00, 0x00, 0x00, 0x20};
436
437 std::vector<uint8_t> ctx;
438
439 if (!GetKdfContext(&ctx)) return false;
440
441 return AesCmacKdfHelper(master_key, label, ctx, kAes256XtsKeySize, enc_key);
442 }
443
DeriveHwWrappedRawSecret(const std::vector<uint8_t> & master_key,std::vector<uint8_t> * secret)444 bool DeriveHwWrappedRawSecret(const std::vector<uint8_t> &master_key,
445 std::vector<uint8_t> *secret) {
446 std::vector<uint8_t> label{0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
447 0x00, 0x00, 0x00, 0x00, 0x20};
448 // Context in fixed input string comprises of software provided context,
449 // padding to eight bytes (if required) and the key policy.
450 std::vector<uint8_t> context = {'r', 'a', 'w', ' ', 's', 'e', 'c',
451 'r', 'e', 't', 0x0, 0x0, 0x0, 0x0,
452 0x0, 0x0, 0x00, 0x00, 0x00, 0x02, 0x17,
453 0x00, 0x80, 0x50, 0x0, 0x0, 0x0, 0x0};
454
455 return AesCmacKdfHelper(master_key, label, context, kAes256KeySize, secret);
456 }
457
458 } // namespace kernel
459 } // namespace android
460