diff options
Diffstat (limited to 'libfscrypt')
l--------- | libfscrypt/.clang-format | 1 | ||||
-rw-r--r-- | libfscrypt/fscrypt.cpp | 269 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 40 | ||||
-rw-r--r-- | libfscrypt/tests/Android.bp | 33 | ||||
-rw-r--r-- | libfscrypt/tests/fscrypt_test.cpp | 138 |
5 files changed, 382 insertions, 99 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..b76f0b17 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 @@ -67,7 +72,45 @@ struct fscrypt_policy_v2 { #define HEX_LOOKUP "0123456789abcdef" -#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]; @@ -75,6 +118,9 @@ bool fscrypt_is_native() { 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,17 +142,85 @@ 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) { - uint8_t flags = 0; +static bool fscrypt_is_encrypted(int fd) { + 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; +} + +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); + if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64)) { + *options_string += "+inlinecrypt_optimized"; + } + EncryptionOptions options_check; + if (!ParseOptions(*options_string, &options_check)) { + LOG(ERROR) << "Internal error serializing options as string: " << *options_string; + return false; + } + if (memcmp(&options, &options_check, sizeof(options_check)) != 0) { + LOG(ERROR) << "Internal error serializing options as string, round trip failed: " + << *options_string; + return false; + } + return true; +} + +bool ParseOptions(const std::string& options_string, EncryptionOptions* options) { + memset(options, '\0', sizeof(*options)); + auto parts = android::base::Split(options_string, ":"); + if (parts.size() < 1 || parts.size() > 3) { + return false; + } + if (!LookupModeByName(contents_modes, parts[0], &options->contents_mode)) { + LOG(ERROR) << "Invalid file contents encryption mode: " << parts[0]; + return false; + } + if (parts.size() >= 2) { + if (!LookupModeByName(filenames_modes, parts[1], &options->filenames_mode)) { + LOG(ERROR) << "Invalid file names encryption mode: " << parts[1]; + return false; + } + } else if (options->contents_mode == FS_ENCRYPTION_MODE_ADIANTUM) { + options->filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM; + } else { + options->filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + } + options->version = 1; + options->flags = 0; + if (parts.size() >= 3) { + auto flags = android::base::Split(parts[2], "+"); + for (const auto& flag : flags) { + if (flag == "v1") { + options->version = 1; + } else if (flag == "v2") { + options->version = 2; + } else if (flag == "inlinecrypt_optimized") { + options->flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64; + } else { + LOG(ERROR) << "Unknown flag: " << flag; + return false; + } + } + } // 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. @@ -114,105 +228,77 @@ 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) { - flags |= FS_POLICY_FLAGS_PAD_4; + if (options->version == 1 && options->filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { + options->flags |= FS_POLICY_FLAGS_PAD_4; } else { - flags |= FS_POLICY_FLAGS_PAD_16; + options->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; + if (options->filenames_mode == FS_ENCRYPTION_MODE_ADIANTUM) { + options->flags |= FS_POLICY_FLAG_DIRECT_KEY; } - - return flags; + return true; } -static bool fscrypt_is_encrypted(int fd) { - 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; +static std::string PolicyDebugString(const EncryptionPolicy& policy) { + std::stringstream ss; + std::string ref_hex; + BytesToHex(policy.key_raw_ref, &ref_hex); + ss << ref_hex; + ss << " v" << policy.options.version; + ss << " modes " << policy.options.contents_mode << "/" << policy.options.filenames_mode; + ss << std::hex << " flags 0x" << policy.options.flags; + return ss.str(); } -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; - } - - 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; - } else { - LOG(ERROR) << "Invalid file names encryption mode: " - << filenames_encryption_mode; - return -1; - } - +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 = policy.options.flags; + 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 = policy.options.flags; + 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); - - 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 +306,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 +316,23 @@ 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 " + << PolicyDebugString(policy) << ": " << 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 " + << PolicyDebugString(policy); } else { - LOG(INFO) << "Encryption policy of " << directory << " set to " << ref << " modes " - << contents_mode << "/" << filenames_mode; + LOG(INFO) << "Encryption policy of " << directory << " set to " + << PolicyDebugString(policy); } - return 0; + return true; } + +} // namespace fscrypt +} // namespace android diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h index 13358bb7..2b809866 100644 --- a/libfscrypt/include/fscrypt/fscrypt.h +++ b/libfscrypt/include/fscrypt/fscrypt.h @@ -17,23 +17,45 @@ #ifndef _FSCRYPT_H_ #define _FSCRYPT_H_ -#include <sys/cdefs.h> -#include <stdbool.h> -#include <cutils/multiuser.h> +#include <string> -__BEGIN_DECLS +// TODO: switch to <linux/fscrypt.h> once it's in Bionic +#define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 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; + int flags; + + // Ensure that "version" is not valid on creation and so must be explicitly set + 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); + +bool ParseOptions(const std::string& options_string, EncryptionOptions* options); + +bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory); + +} // namespace fscrypt +} // namespace android #endif // _FSCRYPT_H_ diff --git a/libfscrypt/tests/Android.bp b/libfscrypt/tests/Android.bp new file mode 100644 index 00000000..985b425f --- /dev/null +++ b/libfscrypt/tests/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libfscrypt_unit_test", + + shared_libs: [ + "libbase", + ], + static_libs: [ + "libfscrypt", + ], + srcs: [ + "fscrypt_test.cpp", + ], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], +} diff --git a/libfscrypt/tests/fscrypt_test.cpp b/libfscrypt/tests/fscrypt_test.cpp new file mode 100644 index 00000000..677f0f22 --- /dev/null +++ b/libfscrypt/tests/fscrypt_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/fs.h> + +#include <fscrypt/fscrypt.h> + +#include <gtest/gtest.h> + +using namespace android::fscrypt; + +/* modes not supported by upstream kernel, so not in <linux/fs.h> */ +#define FS_ENCRYPTION_MODE_AES_256_HEH 126 +#define FS_ENCRYPTION_MODE_PRIVATE 127 + +TEST(fscrypt, ParseOptions) { + EncryptionOptions options; + std::string options_string; + + EXPECT_FALSE(ParseOptions("", &options)); + EXPECT_FALSE(ParseOptions("blah", &options)); + + EXPECT_TRUE(ParseOptions("software", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); + + EXPECT_TRUE(ParseOptions("aes-256-xts", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); + + EXPECT_TRUE(ParseOptions("adiantum", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("adiantum:adiantum:v1", options_string); + + EXPECT_TRUE(ParseOptions("adiantum:aes-256-heh", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_HEH, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("adiantum:aes-256-heh:v1", options_string); + + EXPECT_TRUE(ParseOptions("ice", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("ice:aes-256-cts:v1", options_string); + + EXPECT_FALSE(ParseOptions("ice:blah", &options)); + + EXPECT_TRUE(ParseOptions("ice:aes-256-cts", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("ice:aes-256-cts:v1", options_string); + + EXPECT_TRUE(ParseOptions("ice:aes-256-heh", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_HEH, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("ice:aes-256-heh:v1", options_string); + + EXPECT_TRUE(ParseOptions("ice:adiantum", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("ice:adiantum:v1", options_string); + + EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); + + EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v1", &options)); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); + + EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v2", &options)); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v2", options_string); + + EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", &options)); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64, options.flags); + EXPECT_TRUE(OptionsToString(options, &options_string)); + EXPECT_EQ("aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", options_string); + + EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:v2:", &options)); + EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:v2:foo", &options)); + EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:blah", &options)); + EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:vblah", &options)); +} |