summaryrefslogtreecommitdiff
path: root/libfscrypt
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2023-11-17 23:23:08 +0000
committerEric Biggers <ebiggers@google.com>2024-01-04 22:39:17 +0000
commit871d51b61d87892ec0d3a5b8b4b8cdd281d6c35b (patch)
tree7d294ebe533bcc39c756f672407a1fe5ed2b6132 /libfscrypt
parentc88f7159b5c5af8765256767d00389216856a52f (diff)
downloadextras-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.cpp39
-rw-r--r--libfscrypt/include/fscrypt/fscrypt.h3
-rw-r--r--libfscrypt/tests/fscrypt_test.cpp13
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);
}