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