diff options
author | Eric Biggers <ebiggers@google.com> | 2023-11-17 23:23:08 +0000 |
---|---|---|
committer | Eric Biggers <ebiggers@google.com> | 2024-01-04 22:39:17 +0000 |
commit | 871d51b61d87892ec0d3a5b8b4b8cdd281d6c35b (patch) | |
tree | 7d294ebe533bcc39c756f672407a1fe5ed2b6132 /libfscrypt | |
parent | c88f7159b5c5af8765256767d00389216856a52f (diff) | |
download | extras-871d51b61d87892ec0d3a5b8b4b8cdd281d6c35b.tar.gz |
libfscrypt: add support for specifying 4K data units
Add a new flag "dusize_4k" that can be specified in the fileencryption
option in the fstab and in the ro.crypto.volume.options system property.
This flag causes the data unit size of the fscrypt policy to be set to
4K if that's not already the filesystem's default. This takes advantage
of the ability to specify a sub-block data unit size that was recently
added to the kernel.
The use case for this is to allow inline encryption hardware that only
supports a data unit size of 4K to be used when the userdata filesystem
uses a 16K filesystem block size. A 16K filesystem block size is needed
on f2fs when the system page size is 16K. Note that currently there is
no known use case for data unit sizes other than 4K or the filesystem
block size, so for now we keep things simple and just handle "dusize_4k"
specifically. We could allow other dusize_* values in the future.
This new flag will be added to the documentation at
https://source.android.com/docs/security/features/encryption/file-based#enabling-fbe-on-internal-storage
Bug: 299136786
Test: atest libfscrypt_unit_test
Change-Id: I0a6c889e05b9ded39cfe726e6dea6285ee85f129
Diffstat (limited to 'libfscrypt')
-rw-r--r-- | libfscrypt/fscrypt.cpp | 39 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 3 | ||||
-rw-r--r-- | libfscrypt/tests/fscrypt_test.cpp | 13 |
3 files changed, 53 insertions, 2 deletions
diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp index 174cecad..ff28ce53 100644 --- a/libfscrypt/fscrypt.cpp +++ b/libfscrypt/fscrypt.cpp @@ -29,6 +29,7 @@ #include <logwrap/logwrap.h> #include <string.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/types.h> #include <unistd.h> #include <utils/misc.h> @@ -45,6 +46,17 @@ using namespace std::string_literals; #define HEX_LOOKUP "0123456789abcdef" +// TODO: remove this when <linux/fscrypt.h> is updated to Linux 6.7 +struct fscrypt_policy_v2__with_log2_data_unit_size { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 log2_data_unit_size; + __u8 __reserved[3]; + __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; +}; + struct ModeLookupEntry { std::string name; int id; @@ -161,6 +173,9 @@ bool OptionsToStringForApiLevel(unsigned int first_api_level, const EncryptionOp if (options.use_hw_wrapped_key) { *options_string += "+wrappedkey_v0"; } + if (options.dusize_4k) { + *options_string += "+dusize_4k"; + } EncryptionOptions options_check; if (!ParseOptionsForApiLevel(first_api_level, *options_string, &options_check)) { @@ -207,6 +222,7 @@ bool ParseOptionsForApiLevel(unsigned int first_api_level, const std::string& op // Default to v2 after Q options->version = first_api_level > __ANDROID_API_Q__ ? 2 : 1; options->flags = 0; + options->dusize_4k = false; options->use_hw_wrapped_key = false; if (parts.size() > 2 && !parts[2].empty()) { auto flags = android::base::Split(parts[2], "+"); @@ -221,6 +237,8 @@ bool ParseOptionsForApiLevel(unsigned int first_api_level, const std::string& op options->flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32; } else if (flag == "wrappedkey_v0") { options->use_hw_wrapped_key = true; + } else if (flag == "dusize_4k") { + options->dusize_4k = true; } else { LOG(ERROR) << "Unknown flag: " << flag; return false; @@ -284,10 +302,19 @@ static std::string PolicyDebugString(const EncryptionPolicy& policy) { return ss.str(); } +static int GetFilesystemBlockSize(const std::string& path) { + struct statvfs info; + if (statvfs(path.c_str(), &info) == 0) { + return info.f_bsize; + } + PLOG(ERROR) << "Error retrieving filesystem information from " << path; + return getpagesize(); +} + bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory) { union { fscrypt_policy_v1 v1; - fscrypt_policy_v2 v2; + fscrypt_policy_v2__with_log2_data_unit_size v2; } kern_policy; memset(&kern_policy, 0, sizeof(kern_policy)); @@ -317,6 +344,16 @@ bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory) kern_policy.v2.contents_encryption_mode = policy.options.contents_mode; kern_policy.v2.filenames_encryption_mode = policy.options.filenames_mode; kern_policy.v2.flags = policy.options.flags; + // Configure the data unit size if one was explicitly specified and it doesn't match the + // default data unit size of the filesystem. + // + // We don't configure a data unit size if one wasn't explicitly specified, since the + // kernel might not support it. We also don't configure a data unit size that's already + // the filesystem default, since this allows dusize_4k to be added to the fstab of an + // existing device using 4K filesystem blocks without changing the policy. + if (policy.options.dusize_4k && GetFilesystemBlockSize(directory) != 4096) { + kern_policy.v2.log2_data_unit_size = 12; + } policy.key_raw_ref.copy(reinterpret_cast<char*>(kern_policy.v2.master_key_identifier), FSCRYPT_KEY_IDENTIFIER_SIZE); break; diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h index 11f37119..11c3c04a 100644 --- a/libfscrypt/include/fscrypt/fscrypt.h +++ b/libfscrypt/include/fscrypt/fscrypt.h @@ -35,6 +35,7 @@ struct EncryptionOptions { int filenames_mode; int flags; bool use_hw_wrapped_key; + bool dusize_4k; // Ensure that "version" is not valid on creation and so must be explicitly set EncryptionOptions() : version(0) {} @@ -64,7 +65,7 @@ bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory); inline bool operator==(const EncryptionOptions& lhs, const EncryptionOptions& rhs) { return (lhs.version == rhs.version) && (lhs.contents_mode == rhs.contents_mode) && (lhs.filenames_mode == rhs.filenames_mode) && (lhs.flags == rhs.flags) && - (lhs.use_hw_wrapped_key == rhs.use_hw_wrapped_key); + (lhs.use_hw_wrapped_key == rhs.use_hw_wrapped_key) && (lhs.dusize_4k == rhs.dusize_4k); } inline bool operator!=(const EncryptionOptions& lhs, const EncryptionOptions& rhs) { diff --git a/libfscrypt/tests/fscrypt_test.cpp b/libfscrypt/tests/fscrypt_test.cpp index 0cd79950..90297db4 100644 --- a/libfscrypt/tests/fscrypt_test.cpp +++ b/libfscrypt/tests/fscrypt_test.cpp @@ -63,6 +63,7 @@ TEST(fscrypt, ParseOptions) { EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_FALSE(options.dusize_4k); } for (const auto& d : defaults) { TEST_STRING(30, d, "aes-256-xts:aes-256-cts:v2"); @@ -71,6 +72,7 @@ TEST(fscrypt, ParseOptions) { EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + EXPECT_FALSE(options.dusize_4k); } EXPECT_FALSE(ParseOptionsForApiLevel(29, "blah", &dummy_options)); @@ -184,6 +186,15 @@ TEST(fscrypt, ParseOptions) { EXPECT_EQ(FSCRYPT_MODE_AES_256_HCTR2, options.filenames_mode); EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); } + + { + TEST_STRING(34, "::dusize_4k", "aes-256-xts:aes-256-cts:v2+dusize_4k"); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + EXPECT_TRUE(options.dusize_4k); + } } TEST(fscrypt, ComparePolicies) { @@ -201,6 +212,7 @@ TEST(fscrypt, ComparePolicies) { foo_options.filenames_mode = 1; foo_options.flags = 1; foo_options.use_hw_wrapped_key = true; + foo_options.dusize_4k = true; foo.options = foo_options; EXPECT_EQ(foo, foo); TEST_INEQUALITY(foo, key_raw_ref, "bar"); @@ -209,4 +221,5 @@ TEST(fscrypt, ComparePolicies) { TEST_INEQUALITY(foo, options.filenames_mode, 3); TEST_INEQUALITY(foo, options.flags, 0); TEST_INEQUALITY(foo, options.use_hw_wrapped_key, false); + TEST_INEQUALITY(foo, options.dusize_4k, false); } |