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