From 220567c33a19128b0bfa9d0136f57812ea6a13fc Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Fri, 7 Feb 2020 12:45:20 -0800 Subject: [PATCH] Generalize CryptoType infrastructure More consistency between MetadataCrypt and cryptfs, and steps towards supporting Adiantum properly in MetadataCrypt. Test: create private volume on Cuttlefish Bug: 147814592 Change-Id: Ic3993c1fde11b4f5a9e6cc8ee588a7d92241c6ab --- Android.bp | 3 +- CryptoType.cpp | 43 +++++++++++++ CryptoType.h | 103 ++++++++++++++++++++++++++++++ MetadataCrypt.cpp | 65 +++++++++++++------ MetadataCrypt.h | 5 ++ cryptfs.cpp | 155 ++++++++++------------------------------------ cryptfs.h | 1 - 7 files changed, 230 insertions(+), 145 deletions(-) create mode 100644 CryptoType.cpp create mode 100644 CryptoType.h diff --git a/Android.bp b/Android.bp index ab833ad..dae0859 100644 --- a/Android.bp +++ b/Android.bp @@ -111,6 +111,7 @@ cc_library_static { "Benchmark.cpp", "CheckEncryption.cpp", "Checkpoint.cpp", + "CryptoType.cpp", "Devmapper.cpp", "EncryptInplace.cpp", "FileDeviceUtils.cpp", @@ -141,8 +142,8 @@ cc_library_static { "model/ObbVolume.cpp", "model/PrivateVolume.cpp", "model/PublicVolume.cpp", - "model/VolumeBase.cpp", "model/StubVolume.cpp", + "model/VolumeBase.cpp", ], product_variables: { arc: { diff --git a/CryptoType.cpp b/CryptoType.cpp new file mode 100644 index 0000000..155848e --- /dev/null +++ b/CryptoType.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CryptoType.h" + +#include + +#include +#include + +namespace android { +namespace vold { + +const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len, + const CryptoType& default_alg, const char* property) { + char paramstr[PROPERTY_VALUE_MAX]; + + property_get(property, paramstr, default_alg.get_config_name()); + for (int i = 0; i < table_len; i++) { + if (strcmp(paramstr, table[i].get_config_name()) == 0) { + return table[i]; + } + } + LOG(ERROR) << "Invalid name (" << paramstr << ") for " << property << ". Defaulting to " + << default_alg.get_config_name() << "."; + return default_alg; +} + +} // namespace vold +} // namespace android diff --git a/CryptoType.h b/CryptoType.h new file mode 100644 index 0000000..7ec419b --- /dev/null +++ b/CryptoType.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace android { +namespace vold { + +// Struct representing an encryption algorithm supported by vold. +// "config_name" represents the name we give the algorithm in +// read-only properties and fstab files +// "kernel_name" is the name we present to the Linux kernel +// "keysize" is the size of the key in bytes. +struct CryptoType { + // We should only be constructing CryptoTypes as part of + // supported_crypto_types[]. We do it via this pseudo-builder pattern, + // which isn't pure or fully protected as a concession to being able to + // do it all at compile time. Add new CryptoTypes in + // supported_crypto_types[] below. + constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {} + constexpr CryptoType set_keysize(size_t size) const { + return CryptoType(this->config_name, this->kernel_name, size); + } + constexpr CryptoType set_config_name(const char* property) const { + return CryptoType(property, this->kernel_name, this->keysize); + } + constexpr CryptoType set_kernel_name(const char* crypto) const { + return CryptoType(this->config_name, crypto, this->keysize); + } + + constexpr const char* get_config_name() const { return config_name; } + constexpr const char* get_kernel_name() const { return kernel_name; } + constexpr size_t get_keysize() const { return keysize; } + + private: + const char* config_name; + const char* kernel_name; + size_t keysize; + + constexpr CryptoType(const char* property, const char* crypto, size_t ksize) + : config_name(property), kernel_name(crypto), keysize(ksize) {} +}; + +// Use the named android property to look up a type from the table +// If the property is not set or matches no table entry, return the default. +const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len, + const CryptoType& default_alg, const char* property); + +// Some useful types + +constexpr CryptoType invalid_crypto_type = CryptoType(); + +constexpr CryptoType aes_256_xts = CryptoType() + .set_config_name("aes-256-xts") + .set_kernel_name("aes-xts-plain64") + .set_keysize(64); + +constexpr CryptoType adiantum = CryptoType() + .set_config_name("adiantum") + .set_kernel_name("xchacha12,aes-adiantum-plain64") + .set_keysize(32); + +// Support compile-time validation of a crypto type table + +template +constexpr size_t array_length(T (&)[N]) { + return N; +} + +constexpr bool isValidCryptoType(size_t max_keylen, const CryptoType& crypto_type) { + return ((crypto_type.get_config_name() != nullptr) && + (crypto_type.get_kernel_name() != nullptr) && + (crypto_type.get_keysize() <= max_keylen)); +} + +// Confirms that all supported_crypto_types have a small enough keysize and +// had both set_config_name() and set_kernel_name() called. +// Note in C++11 that constexpr functions can only have a single line. +// So our code is a bit convoluted (using recursion instead of a loop), +// but it's asserting at compile time that all of our key lengths are valid. +constexpr bool validateSupportedCryptoTypes(size_t max_keylen, const CryptoType types[], + size_t len) { + return len == 0 || (isValidCryptoType(max_keylen, types[len - 1]) && + validateSupportedCryptoTypes(max_keylen, types, len - 1)); +} + +} // namespace vold +} // namespace android diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp index 76ea9eb..5a1a3c3 100644 --- a/MetadataCrypt.cpp +++ b/MetadataCrypt.cpp @@ -36,6 +36,7 @@ #include #include "Checkpoint.h" +#include "CryptoType.h" #include "EncryptInplace.h" #include "KeyStorage.h" #include "KeyUtil.h" @@ -45,6 +46,9 @@ #define TABLE_LOAD_RETRIES 10 +namespace android { +namespace vold { + using android::fs_mgr::FstabEntry; using android::fs_mgr::GetEntryForMountPoint; using android::vold::KeyBuffer; @@ -55,6 +59,21 @@ static const std::string kDmNameUserdata = "userdata"; static const char* kFn_keymaster_key_blob = "keymaster_key_blob"; static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded"; +constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum}; + +static_assert(validateSupportedCryptoTypes(64, supported_crypto_types, + array_length(supported_crypto_types)), + "We have a CryptoType which was incompletely constructed."); + +constexpr CryptoType legacy_aes_256_xts = + CryptoType().set_config_name("aes-256-xts").set_kernel_name("AES-256-XTS").set_keysize(64); + +constexpr CryptoType legacy_crypto_types[] = {legacy_aes_256_xts}; + +static_assert(validateSupportedCryptoTypes(64, legacy_crypto_types, + array_length(legacy_crypto_types)), + "We have a CryptoType which was incompletely constructed."); + static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) { // We're about to mount data not verified by verified boot. Tell Keymaster that early boot has // ended. @@ -85,9 +104,6 @@ static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) { return true; } -namespace android { -namespace vold { - // Note: It is possible to orphan a key if it is removed before deleting // Update this once keymaster APIs change, and we have a proper commit. static void commit_key(const std::string& dir) { @@ -165,9 +181,6 @@ static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffe return true; } -} // namespace vold -} // namespace android - static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) { if (android::vold::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) { PLOG(ERROR) << "Unable to measure size of " << real_blkdev; @@ -176,20 +189,25 @@ static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_s return true; } -static std::string lookup_cipher(const std::string& cipher_name, bool is_legacy) { - if (is_legacy) { - if (cipher_name.empty() || cipher_name == "aes-256-xts") { - return "AES-256-XTS"; +static const CryptoType& lookup_cipher_in_table(const CryptoType table[], int table_len, + const std::string& cipher_name) { + if (cipher_name.empty()) return table[0]; + for (int i = 0; i < table_len; i++) { + if (cipher_name == table[i].get_config_name()) { + return table[i]; } + } + return invalid_crypto_type; +} + +static const CryptoType& lookup_cipher(const std::string& cipher_name, bool is_legacy) { + if (is_legacy) { + return lookup_cipher_in_table(legacy_crypto_types, array_length(legacy_crypto_types), + cipher_name); } else { - if (cipher_name.empty() || cipher_name == "aes-256-xts") { - return "aes-xts-plain64"; - } else if (cipher_name == "adiantum") { - return "xchacha12,aes-adiantum-plain64"; - } + return lookup_cipher_in_table(supported_crypto_types, array_length(supported_crypto_types), + cipher_name); } - LOG(ERROR) << "No metadata cipher named " << cipher_name << " found, is_legacy=" << is_legacy; - return ""; } static bool create_crypto_blk_dev(const std::string& dm_name, const FstabEntry* data_rec, @@ -201,7 +219,11 @@ static bool create_crypto_blk_dev(const std::string& dm_name, const FstabEntry* if (!DmTargetDefaultKey::IsLegacy(&is_legacy)) return false; auto cipher = lookup_cipher(data_rec->metadata_cipher, is_legacy); - if (cipher.empty()) return false; + if (cipher.get_kernel_name() == nullptr) { + LOG(ERROR) << "No metadata cipher named " << data_rec->metadata_cipher + << " found, is_legacy=" << is_legacy; + return false; + } KeyBuffer hex_key_buffer; if (android::vold::StrToHex(key, hex_key_buffer) != android::OK) { @@ -218,8 +240,8 @@ static bool create_crypto_blk_dev(const std::string& dm_name, const FstabEntry* } DmTable table; - table.Emplace(0, nr_sec, cipher, hex_key, data_rec->blk_device, 0, - is_legacy, set_dun); + table.Emplace(0, nr_sec, cipher.get_kernel_name(), hex_key, + data_rec->blk_device, 0, is_legacy, set_dun); auto& dm = DeviceMapper::Instance(); for (int i = 0;; i++) { @@ -289,3 +311,6 @@ bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std:: mount_via_fs_mgr(data_rec->mount_point.c_str(), crypto_blkdev.c_str()); return true; } + +} // namespace vold +} // namespace android diff --git a/MetadataCrypt.h b/MetadataCrypt.h index cd0f5e5..a1ce7d8 100644 --- a/MetadataCrypt.h +++ b/MetadataCrypt.h @@ -19,7 +19,12 @@ #include +namespace android { +namespace vold { + bool fscrypt_mount_metadata_encrypted(const std::string& block_device, const std::string& mount_point, bool needs_encrypt); +} // namespace vold +} // namespace android #endif diff --git a/cryptfs.cpp b/cryptfs.cpp index c3d5f78..38661f3 100644 --- a/cryptfs.cpp +++ b/cryptfs.cpp @@ -19,6 +19,7 @@ #include "cryptfs.h" #include "Checkpoint.h" +#include "CryptoType.h" #include "EncryptInplace.h" #include "FsCrypt.h" #include "Keymaster.h" @@ -71,6 +72,7 @@ extern "C" { using android::base::ParseUint; using android::base::StringPrintf; using android::fs_mgr::GetEntryForMountPoint; +using android::vold::CryptoType; using android::vold::KeyBuffer; using namespace android::dm; using namespace std::chrono_literals; @@ -296,6 +298,28 @@ static char* saved_mount_point; static int master_key_saved = 0; static struct crypt_persist_data* persist_data = NULL; +constexpr CryptoType aes_128_cbc = CryptoType() + .set_config_name("AES-128-CBC") + .set_kernel_name("aes-cbc-essiv:sha256") + .set_keysize(16); + +constexpr CryptoType supported_crypto_types[] = {aes_128_cbc, android::vold::adiantum}; + +static_assert(validateSupportedCryptoTypes(MAX_KEY_LEN, supported_crypto_types, + array_length(supported_crypto_types)), + "We have a CryptoType with keysize > MAX_KEY_LEN or which was " + "incompletely constructed."); + +static const CryptoType& get_crypto_type() { + // We only want to parse this read-only property once. But we need to wait + // until the system is initialized before we can read it. So we use a static + // scoped within this function to get it only once. + static CryptoType crypto_type = + lookup_crypto_algorithm(supported_crypto_types, array_length(supported_crypto_types), + aes_128_cbc, "ro.crypto.fde_algorithm"); + return crypto_type; +} + /* Should we use keymaster? */ static int keymaster_check_compatibility() { return keymaster_compatibility_cryptfs_scrypt(); @@ -426,118 +450,6 @@ static void cryptfs_reboot(RebootType rt) { return; } -namespace { - -struct CryptoType; - -// Use to get the CryptoType in use on this device. -const CryptoType& get_crypto_type(); - -struct CryptoType { - // We should only be constructing CryptoTypes as part of - // supported_crypto_types[]. We do it via this pseudo-builder pattern, - // which isn't pure or fully protected as a concession to being able to - // do it all at compile time. Add new CryptoTypes in - // supported_crypto_types[] below. - constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {} - constexpr CryptoType set_keysize(size_t keysize) const { - return CryptoType(this->config_name, this->kernel_name, keysize); - } - constexpr CryptoType set_config_name(const char* config_name) const { - return CryptoType(config_name, this->kernel_name, this->keysize); - } - constexpr CryptoType set_kernel_name(const char* kernel_name) const { - return CryptoType(this->config_name, kernel_name, this->keysize); - } - - constexpr const char* get_config_name() const { return config_name; } - constexpr const char* get_kernel_name() const { return kernel_name; } - constexpr size_t get_keysize() const { return keysize; } - - private: - const char* config_name; - const char* kernel_name; - size_t keysize; - - constexpr CryptoType(const char* config, const char* crypto, size_t ksize) - : config_name(config), kernel_name(crypto), keysize(ksize) {} - friend const CryptoType& get_crypto_type(); - static const CryptoType& get_device_crypto_algorithm(); -}; - -// We only want to parse this read-only property once. But we need to wait -// until the system is initialized before we can read it. So we use a static -// scoped within this function to get it only once. -const CryptoType& get_crypto_type() { - static CryptoType crypto_type = CryptoType::get_device_crypto_algorithm(); - return crypto_type; -} - -constexpr CryptoType default_crypto_type = CryptoType() - .set_config_name("AES-128-CBC") - .set_kernel_name("aes-cbc-essiv:sha256") - .set_keysize(16); - -constexpr CryptoType supported_crypto_types[] = { - default_crypto_type, - CryptoType() - .set_config_name("adiantum") - .set_kernel_name("xchacha12,aes-adiantum-plain64") - .set_keysize(32), - // Add new CryptoTypes here. Order is not important. -}; - -// ---------- START COMPILE-TIME SANITY CHECK BLOCK ------------------------- -// We confirm all supported_crypto_types have a small enough keysize and -// had both set_config_name() and set_kernel_name() called. - -template -constexpr size_t array_length(T (&)[N]) { - return N; -} - -constexpr bool indexOutOfBoundsForCryptoTypes(size_t index) { - return (index >= array_length(supported_crypto_types)); -} - -constexpr bool isValidCryptoType(const CryptoType& crypto_type) { - return ((crypto_type.get_config_name() != nullptr) && - (crypto_type.get_kernel_name() != nullptr) && - (crypto_type.get_keysize() <= MAX_KEY_LEN)); -} - -// Note in C++11 that constexpr functions can only have a single line. -// So our code is a bit convoluted (using recursion instead of a loop), -// but it's asserting at compile time that all of our key lengths are valid. -constexpr bool validateSupportedCryptoTypes(size_t index) { - return indexOutOfBoundsForCryptoTypes(index) || - (isValidCryptoType(supported_crypto_types[index]) && - validateSupportedCryptoTypes(index + 1)); -} - -static_assert(validateSupportedCryptoTypes(0), - "We have a CryptoType with keysize > MAX_KEY_LEN or which was " - "incompletely constructed."); -// ---------- END COMPILE-TIME SANITY CHECK BLOCK ------------------------- - -// Don't call this directly, use get_crypto_type(), which caches this result. -const CryptoType& CryptoType::get_device_crypto_algorithm() { - constexpr char CRYPT_ALGO_PROP[] = "ro.crypto.fde_algorithm"; - char paramstr[PROPERTY_VALUE_MAX]; - - property_get(CRYPT_ALGO_PROP, paramstr, default_crypto_type.get_config_name()); - for (auto const& ctype : supported_crypto_types) { - if (strcmp(paramstr, ctype.get_config_name()) == 0) { - return ctype; - } - } - ALOGE("Invalid name (%s) for %s. Defaulting to %s\n", paramstr, CRYPT_ALGO_PROP, - default_crypto_type.get_config_name()); - return default_crypto_type; -} - -} // namespace - /** * Gets the default device scrypt parameters for key derivation time tuning. * The parameters should lead to about one second derivation time for the @@ -561,10 +473,6 @@ size_t cryptfs_get_keysize() { return get_crypto_type().get_keysize(); } -const char* cryptfs_get_kernel_name() { - return get_crypto_type().get_kernel_name(); -} - static uint64_t get_fs_size(const char* dev) { int fd, block_size; struct ext4_super_block sb; @@ -1913,9 +1821,10 @@ errout: */ int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const KeyBuffer& key, std::string* out_crypto_blkdev) { - if (key.size() != cryptfs_get_keysize()) { + auto crypto_type = get_crypto_type(); + if (key.size() != crypto_type.get_keysize()) { SLOGE("Raw keysize %zu does not match crypt keysize %zu", key.size(), - cryptfs_get_keysize()); + crypto_type.get_keysize()); return -1; } uint64_t nr_sec = 0; @@ -1927,8 +1836,8 @@ int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const K struct crypt_mnt_ftr ext_crypt_ftr; memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr)); ext_crypt_ftr.fs_size = nr_sec; - ext_crypt_ftr.keysize = cryptfs_get_keysize(); - strlcpy((char*)ext_crypt_ftr.crypto_type_name, cryptfs_get_kernel_name(), + ext_crypt_ftr.keysize = crypto_type.get_keysize(); + strlcpy((char*)ext_crypt_ftr.crypto_type_name, crypto_type.get_kernel_name(), MAX_CRYPTO_TYPE_NAME_LEN); uint32_t flags = 0; if (fscrypt_is_native() && @@ -2062,7 +1971,7 @@ int cryptfs_verify_passwd(const char* passwd) { } /* Initialize a crypt_mnt_ftr structure. The keysize is - * defaulted to cryptfs_get_keysize() bytes, and the filesystem size to 0. + * defaulted to get_crypto_type().get_keysize() bytes, and the filesystem size to 0. * Presumably, at a minimum, the caller will update the * filesystem size and crypto_type_name after calling this function. */ @@ -2074,7 +1983,7 @@ static int cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr* ftr) { ftr->major_version = CURRENT_MAJOR_VERSION; ftr->minor_version = CURRENT_MINOR_VERSION; ftr->ftr_size = sizeof(struct crypt_mnt_ftr); - ftr->keysize = cryptfs_get_keysize(); + ftr->keysize = get_crypto_type().get_keysize(); switch (keymaster_check_compatibility()) { case 1: @@ -2318,7 +2227,7 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) { crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE; } crypt_ftr.crypt_type = crypt_type; - strlcpy((char*)crypt_ftr.crypto_type_name, cryptfs_get_kernel_name(), + strlcpy((char*)crypt_ftr.crypto_type_name, get_crypto_type().get_kernel_name(), MAX_CRYPTO_TYPE_NAME_LEN); /* Make an encrypted master key */ diff --git a/cryptfs.h b/cryptfs.h index d805df7..b34a8d9 100644 --- a/cryptfs.h +++ b/cryptfs.h @@ -75,6 +75,5 @@ void cryptfs_clear_password(void); int cryptfs_isConvertibleToFBE(void); size_t cryptfs_get_keysize(); -const char* cryptfs_get_crypto_name(); #endif /* ANDROID_VOLD_CRYPTFS_H */