summaryrefslogtreecommitdiff
path: root/libfscrypt
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2019-09-13 11:07:42 -0700
committerEric Biggers <ebiggers@google.com>2019-09-30 10:26:12 -0700
commit17115c7725f56b68f5117d6f4cd6eb68a99ad406 (patch)
treef96fe8d7ff4ba6939b7e0f093763b33dce3b2a55 /libfscrypt
parent0888874bb4a645dc5c6cacd0be17d83ae1cad168 (diff)
downloadextras-17115c7725f56b68f5117d6f4cd6eb68a99ad406.tar.gz
libfscrypt: support setting v2 encryption policies
Update libfscrypt to support setting v2 encryption policies. For this, the ioctl to use is still FS_IOC_SET_ENCRYPTION_POLICY; we just need to pass it a slightly different structure. v2 policies support the same encryption modes and flags as v1 policies, but internally they use a more standard, secure, and flexible KDF. Due to this, some future features will be supported by v2 policies only. Other notes: - Use 16 byte filenames padding for all v2 policies. There's no need to use the legacy 4 bytes padding. - Unlike v1 policies, setting a v2 policy requires CAP_FOWNER if the key hasn't been installed. This isn't an issue for Android, however -- Android always installs the keys first, and even if it didn't, policies are only set by init and vold, which have CAP_FOWNER. Bug: 140500999 Test: tested as series; see If64028d8580584b2c33c614cabd5d6b93657f608 Change-Id: I325f75fd3e59d6f00a5c66938b99b127981183a5
Diffstat (limited to 'libfscrypt')
-rw-r--r--libfscrypt/fscrypt.cpp147
-rw-r--r--libfscrypt/include/fscrypt/fscrypt.h7
2 files changed, 109 insertions, 45 deletions
diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp
index d0950df9..66a43200 100644
--- a/libfscrypt/fscrypt.cpp
+++ b/libfscrypt/fscrypt.cpp
@@ -33,7 +33,33 @@
#include <array>
-#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1)
+// TODO: switch to <linux/fscrypt.h> once it's in Bionic
+#ifndef FSCRYPT_POLICY_V1
+
+// Careful: due to an API quirk this is actually 0, not 1. We use 1 everywhere
+// else, so make sure to only use this constant in the ioctl itself.
+#define FSCRYPT_POLICY_V1 0
+#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
+struct fscrypt_policy_v1 {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+};
+
+#define FSCRYPT_POLICY_V2 2
+#define FSCRYPT_KEY_IDENTIFIER_SIZE 16
+struct fscrypt_policy_v2 {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 __reserved[4];
+ __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
+};
+
+#endif /* FSCRYPT_POLICY_V1 */
/* modes not supported by upstream kernel, so not in <linux/fs.h> */
#define FS_ENCRYPTION_MODE_AES_256_HEH 126
@@ -41,6 +67,8 @@
#define HEX_LOOKUP "0123456789abcdef"
+#define MAX_KEY_REF_SIZE_HEX (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1)
+
bool fscrypt_is_native() {
char value[PROPERTY_VALUE_MAX];
property_get("ro.crypto.type", value, "none");
@@ -68,40 +96,52 @@ static void log_ls(const char* dirname) {
}
}
-static void policy_to_hex(const char* policy, char* hex) {
- for (size_t i = 0, j = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) {
- hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
- hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+static void keyrefstring(const char* key_raw_ref, size_t key_raw_ref_length, char* hex) {
+ size_t j = 0;
+ for (size_t i = 0; i < key_raw_ref_length; i++) {
+ hex[j++] = HEX_LOOKUP[(key_raw_ref[i] & 0xF0) >> 4];
+ hex[j++] = HEX_LOOKUP[key_raw_ref[i] & 0x0F];
}
- hex[FS_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+ hex[j] = '\0';
}
-static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode) {
- if (filenames_encryption_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
- // Use legacy padding with our original filenames encryption mode.
- return FS_POLICY_FLAGS_PAD_4;
- } else if (filenames_encryption_mode == FS_ENCRYPTION_MODE_ADIANTUM) {
- // Use DIRECT_KEY for Adiantum, since it's much more efficient but just
- // as secure since Android doesn't reuse the same master key for
- // multiple encryption modes
- return (FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY);
+static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode, int policy_version) {
+ uint8_t flags = 0;
+
+ // In the original setting of v1 policies and AES-256-CTS we used 4-byte
+ // padding of filenames, so we have to retain that for compatibility.
+ //
+ // For everything else, use 16-byte padding. This is more secure (it helps
+ // hide the length of filenames), and it makes the inputs evenly divisible
+ // into cipher blocks which is more efficient for encryption and decryption.
+ if (policy_version == 1 && filenames_encryption_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
+ flags |= FS_POLICY_FLAGS_PAD_4;
+ } else {
+ flags |= FS_POLICY_FLAGS_PAD_16;
+ }
+
+ // Use DIRECT_KEY for Adiantum, since it's much more efficient but just as
+ // secure since Android doesn't reuse the same master key for multiple
+ // encryption modes.
+ if (filenames_encryption_mode == FS_ENCRYPTION_MODE_ADIANTUM) {
+ flags |= FS_POLICY_FLAG_DIRECT_KEY;
}
- // With a new mode we can use the better padding flag without breaking existing devices: pad
- // filenames with zeroes to the next 16-byte boundary. This is more secure (helps hide the
- // length of filenames) and makes the inputs evenly divisible into blocks which is more
- // efficient for encryption and decryption.
- return FS_POLICY_FLAGS_PAD_16;
+
+ return flags;
}
static bool fscrypt_is_encrypted(int fd) {
- fscrypt_policy fp;
- return ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &fp) == 0;
+ fscrypt_policy_v1 policy;
+
+ // success => encrypted with v1 policy
+ // EINVAL => encrypted with v2 policy
+ // ENODATA => not encrypted
+ return ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) == 0 || errno == EINVAL;
}
-int fscrypt_policy_ensure(const char *directory, const char *policy,
- size_t policy_length,
- const char *contents_encryption_mode,
- const char *filenames_encryption_mode) {
+int fscrypt_policy_ensure(const char* directory, const char* key_raw_ref, size_t key_raw_ref_length,
+ const char* contents_encryption_mode,
+ const char* filenames_encryption_mode, int policy_version) {
int contents_mode = 0;
int filenames_mode = 0;
@@ -130,19 +170,44 @@ int fscrypt_policy_ensure(const char *directory, const char *policy,
return -1;
}
- if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
- LOG(ERROR) << "Policy wrong length: " << policy_length;
- return -1;
+ union {
+ fscrypt_policy_v1 v1;
+ fscrypt_policy_v2 v2;
+ } policy;
+ memset(&policy, 0, sizeof(policy));
+
+ switch (policy_version) {
+ case 1:
+ if (key_raw_ref_length != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Invalid key ref length for v1 policy: " << key_raw_ref_length;
+ return -1;
+ }
+ // Careful: FSCRYPT_POLICY_V1 is actually 0 in the API, so make sure
+ // to use it here instead of a literal 1.
+ policy.v1.version = FSCRYPT_POLICY_V1;
+ policy.v1.contents_encryption_mode = contents_mode;
+ policy.v1.filenames_encryption_mode = filenames_mode;
+ policy.v1.flags = fscrypt_get_policy_flags(filenames_mode, policy_version);
+ memcpy(policy.v1.master_key_descriptor, key_raw_ref, FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ break;
+ case 2:
+ if (key_raw_ref_length != FSCRYPT_KEY_IDENTIFIER_SIZE) {
+ LOG(ERROR) << "Invalid key ref length for v2 policy: " << key_raw_ref_length;
+ return -1;
+ }
+ policy.v2.version = FSCRYPT_POLICY_V2;
+ policy.v2.contents_encryption_mode = contents_mode;
+ policy.v2.filenames_encryption_mode = filenames_mode;
+ policy.v2.flags = fscrypt_get_policy_flags(filenames_mode, policy_version);
+ memcpy(policy.v2.master_key_identifier, key_raw_ref, FSCRYPT_KEY_IDENTIFIER_SIZE);
+ break;
+ default:
+ LOG(ERROR) << "Invalid encryption policy version: " << policy_version;
+ return -1;
}
- char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
- policy_to_hex(policy, policy_hex);
- fscrypt_policy fp;
- fp.version = 0;
- fp.contents_encryption_mode = contents_mode;
- fp.filenames_encryption_mode = filenames_mode;
- fp.flags = fscrypt_get_policy_flags(filenames_mode);
- memcpy(fp.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE);
+ char ref[MAX_KEY_REF_SIZE_HEX];
+ keyrefstring(key_raw_ref, key_raw_ref_length, ref);
android::base::unique_fd fd(open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
if (fd == -1) {
@@ -155,7 +220,7 @@ int fscrypt_policy_ensure(const char *directory, const char *policy,
// FS_IOC_SET_ENCRYPTION_POLICY will set the policy if the directory is
// unencrypted; otherwise it will verify that the existing policy matches.
// Setting the policy will fail if the directory is already nonempty.
- if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fp) != 0) {
+ if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) != 0) {
std::string reason;
switch (errno) {
case EEXIST:
@@ -165,7 +230,7 @@ int fscrypt_policy_ensure(const char *directory, const char *policy,
reason = strerror(errno);
break;
}
- LOG(ERROR) << "Failed to set encryption policy of " << directory << " to " << policy_hex
+ LOG(ERROR) << "Failed to set encryption policy of " << directory << " to " << ref
<< " modes " << contents_mode << "/" << filenames_mode << ": " << reason;
if (errno == ENOTEMPTY) {
log_ls(directory);
@@ -174,10 +239,10 @@ int fscrypt_policy_ensure(const char *directory, const char *policy,
}
if (already_encrypted) {
- LOG(INFO) << "Verified that " << directory << " has the encryption policy " << policy_hex
+ LOG(INFO) << "Verified that " << directory << " has the encryption policy " << ref
<< " modes " << contents_mode << "/" << filenames_mode;
} else {
- LOG(INFO) << "Encryption policy of " << directory << " set to " << policy_hex << " modes "
+ LOG(INFO) << "Encryption policy of " << directory << " set to " << ref << " modes "
<< contents_mode << "/" << filenames_mode;
}
return 0;
diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h
index ff82d47a..13358bb7 100644
--- a/libfscrypt/include/fscrypt/fscrypt.h
+++ b/libfscrypt/include/fscrypt/fscrypt.h
@@ -25,10 +25,9 @@ __BEGIN_DECLS
bool fscrypt_is_native();
-int fscrypt_policy_ensure(const char *directory, const char *policy,
- size_t policy_length,
- const char *contents_encryption_mode,
- const char *filenames_encryption_mode);
+int fscrypt_policy_ensure(const char* directory, const char* key_raw_ref, size_t key_raw_ref_length,
+ const char* contents_encryption_mode,
+ const char* filenames_encryption_mode, int policy_version);
static const char* fscrypt_unencrypted_folder = "/unencrypted";
static const char* fscrypt_key_ref = "/unencrypted/ref";