diff options
author | Eric Biggers <ebiggers@google.com> | 2019-10-07 19:09:44 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-10-07 19:09:44 +0000 |
commit | c5f7c38480a268da19b43dfa52914822c5e091d1 (patch) | |
tree | 9290ad25770079f002272240383d5815f3f17bca | |
parent | cf29d5254c2b7e8dfc25005775cf0bdb893c54e8 (diff) | |
parent | 17115c7725f56b68f5117d6f4cd6eb68a99ad406 (diff) | |
download | extras-c5f7c38480a268da19b43dfa52914822c5e091d1.tar.gz |
Merge "libfscrypt: support setting v2 encryption policies"
-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"; |