diff options
author | Paul Crowley <paulcrowley@google.com> | 2019-10-24 14:51:44 -0700 |
---|---|---|
committer | Paul Crowley <paulcrowley@google.com> | 2019-10-24 20:45:40 -0700 |
commit | f6ca2c34097e997bf0c1201f808153538368b8ea (patch) | |
tree | 8e7e2a4d43ce7678f4e564892c776e1f4bfb51d5 /libfscrypt | |
parent | b0afbc964fc5647b7ae76a8b5179966051f939bb (diff) | |
download | extras-f6ca2c34097e997bf0c1201f808153538368b8ea.tar.gz |
Provide struct-based C++ interface
Replace many-parameter C interface with a cleaner and more complete
C++ interface for passing around policies in a struct.
Bug: 143307095
Test: treehugger
Change-Id: I95aeaae211f7ad698854bcc66c6d25a0bcb24cfe
Diffstat (limited to 'libfscrypt')
l--------- | libfscrypt/.clang-format | 1 | ||||
-rw-r--r-- | libfscrypt/fscrypt.cpp | 213 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 44 |
3 files changed, 178 insertions, 80 deletions
diff --git a/libfscrypt/.clang-format b/libfscrypt/.clang-format new file mode 120000 index 00000000..973b2fab --- /dev/null +++ b/libfscrypt/.clang-format @@ -0,0 +1 @@ +../../../build/soong/scripts/system-clang-format
\ No newline at end of file diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp index 66a43200..c29354f3 100644 --- a/libfscrypt/fscrypt.cpp +++ b/libfscrypt/fscrypt.cpp @@ -18,6 +18,7 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <asm/ioctl.h> #include <cutils/properties.h> @@ -32,6 +33,10 @@ #include <utils/misc.h> #include <array> +#include <string> +#include <vector> + +using namespace std::string_literals; // TODO: switch to <linux/fscrypt.h> once it's in Bionic #ifndef FSCRYPT_POLICY_V1 @@ -69,12 +74,55 @@ struct fscrypt_policy_v2 { #define MAX_KEY_REF_SIZE_HEX (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1) +struct ModeLookupEntry { + std::string name; + int id; +}; + +static const auto contents_modes = std::vector<ModeLookupEntry>{ + {"aes-256-xts"s, FS_ENCRYPTION_MODE_AES_256_XTS}, + {"software"s, FS_ENCRYPTION_MODE_AES_256_XTS}, + {"adiantum"s, FS_ENCRYPTION_MODE_ADIANTUM}, + {"ice"s, FS_ENCRYPTION_MODE_PRIVATE}, +}; + +static const auto filenames_modes = std::vector<ModeLookupEntry>{ + {"aes-256-cts"s, FS_ENCRYPTION_MODE_AES_256_CTS}, + {"aes-256-heh"s, FS_ENCRYPTION_MODE_AES_256_HEH}, + {"adiantum"s, FS_ENCRYPTION_MODE_ADIANTUM}, +}; + +static bool LookupModeByName(const std::vector<struct ModeLookupEntry>& modes, + const std::string& name, int* result) { + for (const auto& e : modes) { + if (e.name == name) { + *result = e.id; + return true; + } + } + return false; +} + +static bool LookupModeById(const std::vector<struct ModeLookupEntry>& modes, int id, + std::string* result) { + for (const auto& e : modes) { + if (e.id == id) { + *result = e.name; + return true; + } + } + return false; +} + bool fscrypt_is_native() { char value[PROPERTY_VALUE_MAX]; property_get("ro.crypto.type", value, "none"); return !strcmp(value, "file"); } +namespace android { +namespace fscrypt { + static void log_ls(const char* dirname) { std::array<const char*, 3> argv = {"ls", "-laZ", dirname}; int status = 0; @@ -96,16 +144,15 @@ static void log_ls(const char* dirname) { } } -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]; +void BytesToHex(const std::string& bytes, std::string* hex) { + hex->clear(); + for (char c : bytes) { + *hex += HEX_LOOKUP[(c & 0xF0) >> 4]; + *hex += HEX_LOOKUP[c & 0x0F]; } - hex[j] = '\0'; } -static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode, int policy_version) { +static uint8_t fscrypt_get_policy_flags(const EncryptionOptions& options) { uint8_t flags = 0; // In the original setting of v1 policies and AES-256-CTS we used 4-byte @@ -114,7 +161,7 @@ static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode, int polic // 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) { + if (options.version == 1 && options.filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { flags |= FS_POLICY_FLAGS_PAD_4; } else { flags |= FS_POLICY_FLAGS_PAD_16; @@ -123,7 +170,7 @@ static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode, int polic // 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) { + if (options.filenames_mode == FS_ENCRYPTION_MODE_ADIANTUM) { flags |= FS_POLICY_FLAG_DIRECT_KEY; } @@ -139,80 +186,105 @@ static bool fscrypt_is_encrypted(int fd) { return ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) == 0 || errno == EINVAL; } -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; - - if (!strcmp(contents_encryption_mode, "software") || - !strcmp(contents_encryption_mode, "aes-256-xts")) { - contents_mode = FS_ENCRYPTION_MODE_AES_256_XTS; - } else if (!strcmp(contents_encryption_mode, "adiantum")) { - contents_mode = FS_ENCRYPTION_MODE_ADIANTUM; - } else if (!strcmp(contents_encryption_mode, "ice")) { - contents_mode = FS_ENCRYPTION_MODE_PRIVATE; - } else { - LOG(ERROR) << "Invalid file contents encryption mode: " - << contents_encryption_mode; - return -1; +bool OptionsToString(const EncryptionOptions& options, std::string* options_string) { + std::string contents_mode, filenames_mode; + if (!LookupModeById(contents_modes, options.contents_mode, &contents_mode)) { + return false; } + if (!LookupModeById(filenames_modes, options.filenames_mode, &filenames_mode)) { + return false; + } + *options_string = contents_mode + ":" + filenames_mode + ":v" + std::to_string(options.version); + return true; +} + +bool ParseOptions(const std::string& options_string, EncryptionOptions* options) { + auto parts = android::base::Split(options_string, ":"); + + if (parts.size() != 3) return false; + + return ParseOptionsParts(parts[0], parts[1], parts[2], options); +} - if (!strcmp(filenames_encryption_mode, "aes-256-cts")) { - filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS; - } else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) { - filenames_mode = FS_ENCRYPTION_MODE_AES_256_HEH; - } else if (!strcmp(filenames_encryption_mode, "adiantum")) { - filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM; +bool ParseOptionsParts(const std::string& contents_mode, const std::string& filenames_mode, + const std::string& flags, EncryptionOptions* options) { + int policy_version; + if (flags == "v1") { + policy_version = 1; + } else if (flags == "v2") { + policy_version = 2; } else { - LOG(ERROR) << "Invalid file names encryption mode: " - << filenames_encryption_mode; - return -1; + LOG(ERROR) << "Unknown flag: " << flags; + return false; + } + return ParseOptionsParts(contents_mode, filenames_mode, policy_version, options); +} + +bool ParseOptionsParts(const std::string& contents_mode, const std::string& filenames_mode, + int policy_version, EncryptionOptions* options) { + if (!LookupModeByName(contents_modes, contents_mode, &options->contents_mode)) { + LOG(ERROR) << "Invalid file contents encryption mode: " << contents_mode; + return false; + } + if (!LookupModeByName(filenames_modes, filenames_mode, &options->filenames_mode)) { + LOG(ERROR) << "Invalid file names encryption mode: " << filenames_mode; + return false; } + options->version = policy_version; + return true; +} + +bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory) { union { fscrypt_policy_v1 v1; fscrypt_policy_v2 v2; - } policy; - memset(&policy, 0, sizeof(policy)); + } kern_policy; + memset(&kern_policy, 0, sizeof(kern_policy)); - switch (policy_version) { + switch (policy.options.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; + if (policy.key_raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Invalid key descriptor length for v1 policy: " + << policy.key_raw_ref.size(); + return false; } // 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); + kern_policy.v1.version = FSCRYPT_POLICY_V1; + kern_policy.v1.contents_encryption_mode = policy.options.contents_mode; + kern_policy.v1.filenames_encryption_mode = policy.options.filenames_mode; + kern_policy.v1.flags = fscrypt_get_policy_flags(policy.options); + policy.key_raw_ref.copy(reinterpret_cast<char*>(kern_policy.v1.master_key_descriptor), + 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; + if (policy.key_raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) { + LOG(ERROR) << "Invalid key identifier length for v2 policy: " + << policy.key_raw_ref.size(); + return false; } - 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); + kern_policy.v2.version = FSCRYPT_POLICY_V2; + kern_policy.v2.contents_encryption_mode = policy.options.contents_mode; + kern_policy.v2.filenames_encryption_mode = policy.options.filenames_mode; + kern_policy.v2.flags = fscrypt_get_policy_flags(policy.options); + policy.key_raw_ref.copy(reinterpret_cast<char*>(kern_policy.v2.master_key_identifier), + FSCRYPT_KEY_IDENTIFIER_SIZE); break; default: - LOG(ERROR) << "Invalid encryption policy version: " << policy_version; - return -1; + LOG(ERROR) << "Invalid encryption policy version: " << policy.options.version; + return false; } - char ref[MAX_KEY_REF_SIZE_HEX]; - keyrefstring(key_raw_ref, key_raw_ref_length, ref); + std::string policy_descr; + BytesToHex(policy.key_raw_ref, &policy_descr); + policy_descr += " modes "s + std::to_string(policy.options.contents_mode) + "/" + + std::to_string(policy.options.filenames_mode); - android::base::unique_fd fd(open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); + android::base::unique_fd fd(open(directory.c_str(), O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); if (fd == -1) { PLOG(ERROR) << "Failed to open directory " << directory; - return -1; + return false; } bool already_encrypted = fscrypt_is_encrypted(fd); @@ -220,7 +292,7 @@ int fscrypt_policy_ensure(const char* directory, const char* key_raw_ref, size_t // 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, &policy) != 0) { + if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &kern_policy) != 0) { std::string reason; switch (errno) { case EEXIST: @@ -230,20 +302,21 @@ int fscrypt_policy_ensure(const char* directory, const char* key_raw_ref, size_t reason = strerror(errno); break; } - LOG(ERROR) << "Failed to set encryption policy of " << directory << " to " << ref - << " modes " << contents_mode << "/" << filenames_mode << ": " << reason; + LOG(ERROR) << "Failed to set encryption policy of " << directory << " to " << policy_descr + << ": " << reason; if (errno == ENOTEMPTY) { - log_ls(directory); + log_ls(directory.c_str()); } - return -1; + return false; } if (already_encrypted) { - LOG(INFO) << "Verified that " << directory << " has the encryption policy " << ref - << " modes " << contents_mode << "/" << filenames_mode; + LOG(INFO) << "Verified that " << directory << " has the encryption policy " << policy_descr; } else { - LOG(INFO) << "Encryption policy of " << directory << " set to " << ref << " modes " - << contents_mode << "/" << filenames_mode; + LOG(INFO) << "Encryption policy of " << directory << " set to " << policy_descr; } - return 0; + return true; } + +} // namespace fscrypt +} // namespace android diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h index 13358bb7..29617ad5 100644 --- a/libfscrypt/include/fscrypt/fscrypt.h +++ b/libfscrypt/include/fscrypt/fscrypt.h @@ -17,23 +17,47 @@ #ifndef _FSCRYPT_H_ #define _FSCRYPT_H_ -#include <sys/cdefs.h> -#include <stdbool.h> -#include <cutils/multiuser.h> - -__BEGIN_DECLS +#include <string> bool fscrypt_is_native(); -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"; static const char* fscrypt_key_per_boot_ref = "/unencrypted/per_boot_ref"; static const char* fscrypt_key_mode = "/unencrypted/mode"; -__END_DECLS +namespace android { +namespace fscrypt { + +struct EncryptionOptions { + int version; + int contents_mode; + int filenames_mode; + + EncryptionOptions() : version(0) {} +}; + +struct EncryptionPolicy { + EncryptionOptions options; + std::string key_raw_ref; +}; + +void BytesToHex(const std::string& bytes, std::string* hex); + +bool OptionsToString(const EncryptionOptions& options, std::string* options_string); + +// Note that right now this parses only the output from OptionsToString, not the +// more general format that appears in fstabs. +bool ParseOptions(const std::string& options_string, EncryptionOptions* options); + +bool ParseOptionsParts(const std::string& contents_mode, const std::string& filenames_mode, + const std::string& flags, EncryptionOptions* options); + +bool ParseOptionsParts(const std::string& contents_mode, const std::string& filenames_mode, + int policy_version, EncryptionOptions* options); +bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory); + +} // namespace fscrypt +} // namespace android #endif // _FSCRYPT_H_ |