diff options
author | Eric Biggers <ebiggers@google.com> | 2019-09-13 11:07:42 -0700 |
---|---|---|
committer | Eric Biggers <ebiggers@google.com> | 2019-09-30 10:26:12 -0700 |
commit | 17115c7725f56b68f5117d6f4cd6eb68a99ad406 (patch) | |
tree | f96fe8d7ff4ba6939b7e0f093763b33dce3b2a55 /libfscrypt | |
parent | 0888874bb4a645dc5c6cacd0be17d83ae1cad168 (diff) | |
download | extras-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.cpp | 147 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 7 |
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"; |