diff --git a/Android.bp b/Android.bp index 676c958..c2f8936 100644 --- a/Android.bp +++ b/Android.bp @@ -145,6 +145,7 @@ cc_library_static { "model/PublicVolume.cpp", "model/StubVolume.cpp", "model/VolumeBase.cpp", + "model/VolumeEncryption.cpp", ], product_variables: { arc: { diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 141f4c9..f20d9eb 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -65,7 +65,7 @@ using android::vold::BuildDataPath; using android::vold::IsFilesystemSupported; using android::vold::kEmptyAuthentication; using android::vold::KeyBuffer; -using android::vold::makeGen; +using android::vold::KeyGeneration; using android::vold::retrieveKey; using android::vold::retrieveOrGenerateKey; using android::vold::SetQuotaInherit; @@ -97,6 +97,11 @@ std::map s_ce_policies; } // namespace +// Returns KeyGeneration suitable for key as described in EncryptionOptions +static KeyGeneration makeGen(const EncryptionOptions& options) { + return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key}; +} + static bool fscrypt_is_emulated() { return property_get_bool("persist.sys.emulate_fbe", false); } diff --git a/KeyUtil.cpp b/KeyUtil.cpp index 2e810ff..6200c42 100644 --- a/KeyUtil.cpp +++ b/KeyUtil.cpp @@ -36,14 +36,6 @@ namespace android { namespace vold { -const KeyGeneration makeGen(const EncryptionOptions& options) { - return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key}; -} - -const KeyGeneration makeGen(const CryptoType& crypto) { - return KeyGeneration{crypto.get_keysize(), true, false}; -} - const KeyGeneration neverGen() { return KeyGeneration{0, false, false}; } diff --git a/KeyUtil.h b/KeyUtil.h index 16aaf99..dcb1dc7 100644 --- a/KeyUtil.h +++ b/KeyUtil.h @@ -17,7 +17,6 @@ #ifndef ANDROID_VOLD_KEYUTIL_H #define ANDROID_VOLD_KEYUTIL_H -#include "CryptoType.h" #include "KeyBuffer.h" #include "KeyStorage.h" @@ -41,12 +40,6 @@ struct KeyGeneration { // Generate a key as specified in KeyGeneration bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key); -// Returns KeyGeneration suitable for key as described in EncryptionOptions -const KeyGeneration makeGen(const EncryptionOptions& options); - -// Returns KeyGeneration suitable for key as described in CryptoType -const KeyGeneration makeGen(const CryptoType& crypto); - // Returns a key with allow_gen false so generateStorageKey returns false; // this is used to indicate to retrieveOrGenerateKey that a key should not // be generated. diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp index 938ba34..7891bee 100644 --- a/MetadataCrypt.cpp +++ b/MetadataCrypt.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -54,11 +55,20 @@ using android::fs_mgr::GetEntryForMountPoint; using android::vold::KeyBuffer; using namespace android::dm; +// Parsed from metadata options +struct CryptoOptions { + struct CryptoType cipher = invalid_crypto_type; + bool is_legacy = false; + bool set_dun = true; // Non-legacy driver always sets DUN + bool use_hw_wrapped_key = false; +}; + 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"; +// The first entry in this table is the default crypto type. constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum}; static_assert(validateSupportedCryptoTypes(64, supported_crypto_types, @@ -68,12 +78,14 @@ static_assert(validateSupportedCryptoTypes(64, supported_crypto_types, 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)), +static_assert(isValidCryptoType(64, legacy_aes_256_xts), "We have a CryptoType which was incompletely constructed."); +// Returns KeyGeneration suitable for key as described in CryptoOptions +const KeyGeneration makeGen(const CryptoOptions& options) { + return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key}; +} + 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. @@ -173,21 +185,38 @@ static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_s } static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device, - bool is_legacy, const std::string& cipher, bool set_dun, - const KeyBuffer& key, std::string* crypto_blkdev) { - uint64_t nr_sec; - if (!get_number_of_sectors(blk_device, &nr_sec)) return false; + const KeyBuffer& key, const CryptoOptions& options, + std::string* crypto_blkdev, uint64_t* nr_sec) { + if (!get_number_of_sectors(blk_device, nr_sec)) return false; + // TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte + // sectors + *nr_sec &= ~7; + + KeyBuffer module_key; + if (options.use_hw_wrapped_key) { + if (!exportWrappedStorageKey(key, &module_key)) { + LOG(ERROR) << "Failed to get ephemeral wrapped key"; + return false; + } + } else { + module_key = key; + } KeyBuffer hex_key_buffer; - if (android::vold::StrToHex(key, hex_key_buffer) != android::OK) { + if (android::vold::StrToHex(module_key, hex_key_buffer) != android::OK) { LOG(ERROR) << "Failed to turn key to hex"; return false; } std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size()); + auto target = std::make_unique(0, *nr_sec, options.cipher.get_kernel_name(), + hex_key, blk_device, 0); + if (options.is_legacy) target->SetIsLegacy(); + if (options.set_dun) target->SetSetDun(); + if (options.use_hw_wrapped_key) target->SetWrappedKeyV0(); + DmTable table; - table.Emplace(0, nr_sec, cipher, hex_key, blk_device, 0, is_legacy, - set_dun); + table.AddTarget(std::move(target)); auto& dm = DeviceMapper::Instance(); for (int i = 0;; i++) { @@ -209,25 +238,38 @@ static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& return true; } -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]; +static const CryptoType& lookup_cipher(const std::string& cipher_name) { + if (cipher_name.empty()) return supported_crypto_types[0]; + for (size_t i = 0; i < array_length(supported_crypto_types); i++) { + if (cipher_name == supported_crypto_types[i].get_config_name()) { + return supported_crypto_types[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 { - return lookup_cipher_in_table(supported_crypto_types, array_length(supported_crypto_types), - cipher_name); +static bool parse_options(const std::string& options_string, CryptoOptions* options) { + auto parts = android::base::Split(options_string, ":"); + if (parts.size() < 1 || parts.size() > 2) { + LOG(ERROR) << "Invalid metadata encryption option: " << options_string; + return false; + } + std::string cipher_name = parts[0]; + options->cipher = lookup_cipher(cipher_name); + if (options->cipher.get_kernel_name() == nullptr) { + LOG(ERROR) << "No metadata cipher named " << cipher_name << " found"; + return false; } + + if (parts.size() == 2) { + if (parts[1] == "wrappedkey_v0") { + options->use_hw_wrapped_key = true; + } else { + LOG(ERROR) << "Invalid metadata encryption flag: " << parts[1]; + return false; + } + } + return true; } bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point, @@ -253,33 +295,36 @@ bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std:: bool is_legacy; if (!DmTargetDefaultKey::IsLegacy(&is_legacy)) return false; - // Non-legacy driver always sets DUN - bool set_dun = !is_legacy || android::base::GetBoolProperty("ro.crypto.set_dun", false); - if (!set_dun && data_rec->fs_mgr_flags.checkpoint_blk) { - LOG(ERROR) << "Block checkpoints and metadata encryption require ro.crypto.set_dun option"; - return false; - } - - auto cipher = lookup_cipher(data_rec->metadata_cipher, is_legacy); - if (cipher.get_kernel_name() == nullptr) { - LOG(ERROR) << "No metadata cipher named " << data_rec->metadata_cipher - << " found, is_legacy=" << is_legacy; - return false; + CryptoOptions options; + if (is_legacy) { + if (!data_rec->metadata_encryption.empty()) { + LOG(ERROR) << "metadata_encryption options cannot be set in legacy mode"; + return false; + } + options.cipher = legacy_aes_256_xts; + options.is_legacy = true; + options.set_dun = android::base::GetBoolProperty("ro.crypto.set_dun", false); + if (!options.set_dun && data_rec->fs_mgr_flags.checkpoint_blk) { + LOG(ERROR) + << "Block checkpoints and metadata encryption require ro.crypto.set_dun option"; + return false; + } + } else { + if (!parse_options(data_rec->metadata_encryption, &options)) return false; } - auto gen = needs_encrypt ? makeGen(cipher) : neverGen(); + auto gen = needs_encrypt ? makeGen(options) : neverGen(); KeyBuffer key; if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false; std::string crypto_blkdev; - if (!create_crypto_blk_dev(kDmNameUserdata, data_rec->blk_device, is_legacy, - cipher.get_kernel_name(), set_dun, key, &crypto_blkdev)) + uint64_t nr_sec; + if (!create_crypto_blk_dev(kDmNameUserdata, data_rec->blk_device, key, options, &crypto_blkdev, + &nr_sec)) return false; // FIXME handle the corrupt case if (needs_encrypt) { - uint64_t nr_sec; - if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false; LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec; off64_t size_already_done = 0; auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec, @@ -300,5 +345,27 @@ bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std:: return true; } +static bool get_volume_options(CryptoOptions* options) { + return parse_options(android::base::GetProperty("ro.crypto.volume.metadata.encryption", ""), + options); +} + +bool defaultkey_volume_keygen(KeyGeneration* gen) { + CryptoOptions options; + if (!get_volume_options(&options)) return false; + *gen = makeGen(options); + return true; +} + +bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device, + const KeyBuffer& key, std::string* out_crypto_blkdev) { + LOG(DEBUG) << "defaultkey_setup_ext_volume: " << label << " " << blk_device; + + CryptoOptions options; + if (!get_volume_options(&options)) return false; + uint64_t nr_sec; + return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec); +} + } // namespace vold } // namespace android diff --git a/MetadataCrypt.h b/MetadataCrypt.h index a1ce7d8..dc68e7c 100644 --- a/MetadataCrypt.h +++ b/MetadataCrypt.h @@ -19,12 +19,21 @@ #include +#include "KeyBuffer.h" +#include "KeyUtil.h" + namespace android { namespace vold { bool fscrypt_mount_metadata_encrypted(const std::string& block_device, const std::string& mount_point, bool needs_encrypt); +bool defaultkey_volume_keygen(KeyGeneration* gen); + +bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device, + const android::vold::KeyBuffer& key, + std::string* out_crypto_blkdev); + } // namespace vold } // namespace android #endif diff --git a/cryptfs.cpp b/cryptfs.cpp index 4487d3b..1431459 100644 --- a/cryptfs.cpp +++ b/cryptfs.cpp @@ -325,7 +325,7 @@ static const CryptoType& get_crypto_type() { } const KeyGeneration cryptfs_get_keygen() { - return makeGen(get_crypto_type()); + return KeyGeneration{get_crypto_type().get_keysize(), true, false}; } /* Should we use keymaster? */ diff --git a/model/Disk.cpp b/model/Disk.cpp index b2ecc50..a4324db 100644 --- a/model/Disk.cpp +++ b/model/Disk.cpp @@ -16,11 +16,11 @@ #include "Disk.h" #include "FsCrypt.h" -#include "KeyUtil.h" #include "PrivateVolume.h" #include "PublicVolume.h" #include "Utils.h" #include "VolumeBase.h" +#include "VolumeEncryption.h" #include "VolumeManager.h" #include @@ -31,8 +31,6 @@ #include #include -#include "cryptfs.h" - #include #include #include @@ -537,7 +535,7 @@ status_t Disk::partitionMixed(int8_t ratio) { } KeyBuffer key; - if (!generateStorageKey(cryptfs_get_keygen(), &key)) { + if (!generate_volume_key(&key)) { LOG(ERROR) << "Failed to generate key"; return -EIO; } diff --git a/model/PrivateVolume.cpp b/model/PrivateVolume.cpp index 75aa938..a54b05e 100644 --- a/model/PrivateVolume.cpp +++ b/model/PrivateVolume.cpp @@ -17,8 +17,8 @@ #include "PrivateVolume.h" #include "EmulatedVolume.h" #include "Utils.h" +#include "VolumeEncryption.h" #include "VolumeManager.h" -#include "cryptfs.h" #include "fs/Ext4.h" #include "fs/F2fs.h" @@ -87,9 +87,8 @@ status_t PrivateVolume::doCreate() { // TODO: figure out better SELinux labels for private volumes - int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(), mKeyRaw, &mDmDevPath); - if (res != 0) { - PLOG(ERROR) << getId() << " failed to setup cryptfs"; + if (!setup_ext_volume(getId(), mRawDevPath, mKeyRaw, &mDmDevPath)) { + LOG(ERROR) << getId() << " failed to setup metadata encryption"; return -EIO; } diff --git a/model/VolumeEncryption.cpp b/model/VolumeEncryption.cpp new file mode 100644 index 0000000..5b0e73d --- /dev/null +++ b/model/VolumeEncryption.cpp @@ -0,0 +1,94 @@ +/* + * 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 "VolumeEncryption.h" + +#include + +#include +#include + +#include "KeyBuffer.h" +#include "KeyUtil.h" +#include "MetadataCrypt.h" +#include "cryptfs.h" + +namespace android { +namespace vold { + +enum class VolumeMethod { kFailed, kCrypt, kDefaultKey }; + +static VolumeMethod lookup_volume_method() { + constexpr uint64_t pre_gki_level = 29; + auto first_api_level = + android::base::GetUintProperty("ro.product.first_api_level", 0); + auto method = android::base::GetProperty("ro.crypto.volume.metadata.method", "default"); + if (method == "default") { + return first_api_level > pre_gki_level ? VolumeMethod::kDefaultKey : VolumeMethod::kCrypt; + } else if (method == "dm-default-key") { + return VolumeMethod::kDefaultKey; + } else if (method == "dm-crypt") { + if (first_api_level > pre_gki_level) { + LOG(ERROR) << "volume encryption method dm-crypt cannot be used, " + "ro.product.first_api_level = " + << first_api_level; + return VolumeMethod::kFailed; + } + return VolumeMethod::kCrypt; + } else { + LOG(ERROR) << "Unknown volume encryption method: " << method; + return VolumeMethod::kFailed; + } +} + +static VolumeMethod volume_method() { + static VolumeMethod method = lookup_volume_method(); + return method; +} + +bool generate_volume_key(android::vold::KeyBuffer* key) { + KeyGeneration gen; + switch (volume_method()) { + case VolumeMethod::kFailed: + LOG(ERROR) << "Volume encryption setup failed"; + return false; + case VolumeMethod::kCrypt: + gen = cryptfs_get_keygen(); + break; + case VolumeMethod::kDefaultKey: + if (!defaultkey_volume_keygen(&gen)) return false; + break; + } + if (!generateStorageKey(gen, key)) return false; + return true; +} + +bool setup_ext_volume(const std::string& label, const std::string& blk_device, + const android::vold::KeyBuffer& key, std::string* out_crypto_blkdev) { + switch (volume_method()) { + case VolumeMethod::kFailed: + LOG(ERROR) << "Volume encryption setup failed"; + return false; + case VolumeMethod::kCrypt: + return cryptfs_setup_ext_volume(label.c_str(), blk_device.c_str(), key, + out_crypto_blkdev) == 0; + case VolumeMethod::kDefaultKey: + return defaultkey_setup_ext_volume(label, blk_device, key, out_crypto_blkdev); + } +} + +} // namespace vold +} // namespace android diff --git a/model/VolumeEncryption.h b/model/VolumeEncryption.h new file mode 100644 index 0000000..d06c12b --- /dev/null +++ b/model/VolumeEncryption.h @@ -0,0 +1,32 @@ +/* + * 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 + +#include "KeyBuffer.h" + +namespace android { +namespace vold { + +bool generate_volume_key(android::vold::KeyBuffer* key); + +bool setup_ext_volume(const std::string& label, const std::string& blk_device, + const android::vold::KeyBuffer& key, std::string* out_crypto_blkdev); + +} // namespace vold +} // namespace android