diff options
author | Paul Crowley <paulcrowley@google.com> | 2020-02-18 21:30:03 -0800 |
---|---|---|
committer | Paul Crowley <paulcrowley@google.com> | 2020-02-19 12:21:32 -0800 |
commit | 47212f0c707c0e81f8de2ab19e9c661addb9eed0 (patch) | |
tree | 685a7d1b162f60e04800a5f88621f58e342fff8e /libfscrypt | |
parent | da95b3f323f1ba535be8d583b366f1cfdba8e1c8 (diff) | |
download | extras-47212f0c707c0e81f8de2ab19e9c661addb9eed0.tar.gz |
fscrypt option parsing depends on ro.product.first_api_level
We'd like to change what the defaults are for fscrypt as we get new
and better things, but we don't want to break old devices. So we
arrange for the behavior to depend on the API version the device
launched with.
In addition, we can now supply a default if any of the three fields
are blank, meaning that clients like get_volume_file_encryption_options
don't have to separately specify defaults.
Right now we use the API level to choose between version 1 and version
2 being default, and as a further guard against anyone using the
deprecated FSCRYPT_POLICY_FLAGS_PAD_4 on new devices.
Bug: 147107322
Test: atest fscrypt
Test: Various Cuttlefish configurations
Change-Id: I43c94c1051c61d2b051355dcd428c44c279a3c75
Diffstat (limited to 'libfscrypt')
-rw-r--r-- | libfscrypt/fscrypt.cpp | 63 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 8 | ||||
-rw-r--r-- | libfscrypt/tests/fscrypt_test.cpp | 237 |
3 files changed, 188 insertions, 120 deletions
diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp index 9ea8cd33..b8e6ddcf 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/properties.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <asm/ioctl.h> @@ -137,7 +138,16 @@ bool operator!=(const EncryptionOptions& lhs, const EncryptionOptions& rhs) { (lhs.use_hw_wrapped_key == rhs.use_hw_wrapped_key)); } +unsigned int GetFirstApiLevel() { + return android::base::GetUintProperty<unsigned int>("ro.product.first_api_level", 0); +} + bool OptionsToString(const EncryptionOptions& options, std::string* options_string) { + return OptionsToStringForApiLevel(GetFirstApiLevel(), options, options_string); +} + +bool OptionsToStringForApiLevel(unsigned int first_api_level, const EncryptionOptions& options, + std::string* options_string) { std::string contents_mode, filenames_mode; if (!LookupModeById(contents_modes, options.contents_mode, &contents_mode)) { return false; @@ -152,8 +162,9 @@ bool OptionsToString(const EncryptionOptions& options, std::string* options_stri if (options.use_hw_wrapped_key) { *options_string += "+wrappedkey_v0"; } + EncryptionOptions options_check; - if (!ParseOptions(*options_string, &options_check)) { + if (!ParseOptionsForApiLevel(first_api_level, *options_string, &options_check)) { LOG(ERROR) << "Internal error serializing options as string: " << *options_string; return false; } @@ -166,28 +177,41 @@ bool OptionsToString(const EncryptionOptions& options, std::string* options_stri } bool ParseOptions(const std::string& options_string, EncryptionOptions* options) { - memset(options, '\0', sizeof(*options)); + return ParseOptionsForApiLevel(GetFirstApiLevel(), options_string, options); +} + +bool ParseOptionsForApiLevel(unsigned int first_api_level, const std::string& options_string, + EncryptionOptions* options) { auto parts = android::base::Split(options_string, ":"); - if (parts.size() < 1 || parts.size() > 3) { + if (parts.size() > 3) { + LOG(ERROR) << "Invalid encryption options: " << options; 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]; + options->contents_mode = FSCRYPT_MODE_AES_256_XTS; + if (parts.size() > 0 && !parts[0].empty()) { + if (!LookupModeByName(contents_modes, parts[0], &options->contents_mode)) { + LOG(ERROR) << "Invalid file contents encryption mode: " << parts[0]; return false; } - } else if (options->contents_mode == FSCRYPT_MODE_ADIANTUM) { + } + if (options->contents_mode == FSCRYPT_MODE_ADIANTUM) { options->filenames_mode = FSCRYPT_MODE_ADIANTUM; } else { options->filenames_mode = FSCRYPT_MODE_AES_256_CTS; } - options->version = 1; + if (parts.size() > 1 && !parts[1].empty()){ + if (!LookupModeByName(filenames_modes, parts[1], &options->filenames_mode)) { + LOG(ERROR) << "Invalid file names encryption mode: " << parts[1]; + return false; + } + } + // Default to v2 after Q + constexpr unsigned int pre_gki_level = 29; + auto is_gki = first_api_level > pre_gki_level; + options->version = is_gki ? 2 : 1; options->flags = 0; - if (parts.size() >= 3) { + options->use_hw_wrapped_key = false; + if (parts.size() > 2 && !parts[2].empty()) { auto flags = android::base::Split(parts[2], "+"); for (const auto& flag : flags) { if (flag == "v1") { @@ -206,12 +230,12 @@ bool ParseOptions(const std::string& options_string, EncryptionOptions* options) } // 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. + // padding of filenames, so retain that on old first_api_levels. // // 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 (options->version == 1 && options->filenames_mode == FSCRYPT_MODE_AES_256_CTS) { + if (!is_gki && options->version == 1 && options->filenames_mode == FSCRYPT_MODE_AES_256_CTS) { options->flags |= FSCRYPT_POLICY_FLAGS_PAD_4; } else { options->flags |= FSCRYPT_POLICY_FLAGS_PAD_16; @@ -220,8 +244,15 @@ bool ParseOptions(const std::string& options_string, EncryptionOptions* options) // 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 (options->filenames_mode == FSCRYPT_MODE_ADIANTUM) { + if (options->contents_mode == FSCRYPT_MODE_ADIANTUM) { + if (options->filenames_mode != FSCRYPT_MODE_ADIANTUM) { + LOG(ERROR) << "Adiantum must be both contents and filenames mode or neither, invalid options: " << options_string; + return false; + } options->flags |= FSCRYPT_POLICY_FLAG_DIRECT_KEY; + } else if (options->filenames_mode == FSCRYPT_MODE_ADIANTUM) { + LOG(ERROR) << "Adiantum must be both contents and filenames mode or neither, invalid options: " << options_string; + return false; } return true; } diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h index 18fb4fc3..c780c7ce 100644 --- a/libfscrypt/include/fscrypt/fscrypt.h +++ b/libfscrypt/include/fscrypt/fscrypt.h @@ -47,10 +47,18 @@ struct EncryptionPolicy { void BytesToHex(const std::string& bytes, std::string* hex); +unsigned int GetFirstApiLevel(); + bool OptionsToString(const EncryptionOptions& options, std::string* options_string); +bool OptionsToStringForApiLevel(unsigned int first_api_level, const EncryptionOptions& options, + std::string* options_string); + bool ParseOptions(const std::string& options_string, EncryptionOptions* options); +bool ParseOptionsForApiLevel(unsigned int first_api_level, const std::string& options_string, + EncryptionOptions* options); + bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory); } // namespace fscrypt diff --git a/libfscrypt/tests/fscrypt_test.cpp b/libfscrypt/tests/fscrypt_test.cpp index 379e827b..7149e7ca 100644 --- a/libfscrypt/tests/fscrypt_test.cpp +++ b/libfscrypt/tests/fscrypt_test.cpp @@ -26,113 +26,142 @@ using namespace android::fscrypt; #define FSCRYPT_MODE_AES_256_HEH 126 #define FSCRYPT_MODE_PRIVATE 127 -TEST(fscrypt, ParseOptions) { +const EncryptionOptions TestString(unsigned int first_api_level, const std::string instring, + const std::string outstring) { 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(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_ADIANTUM, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.filenames_mode); - EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_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(FSCRYPT_MODE_ADIANTUM, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_HEH, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_HEH, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.filenames_mode); - EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_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(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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(ParseOptionsForApiLevel(first_api_level, instring, &options)); + EXPECT_TRUE(OptionsToStringForApiLevel(first_api_level, options, &options_string)); + EXPECT_EQ(outstring, options_string); + return options; +} - EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", &options)); - EXPECT_EQ(2, options.version); - EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FSCRYPT_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); +#define TEST_STRING(first_api_level, instring, outstring) \ + SCOPED_TRACE(instring); \ + auto options = TestString(first_api_level, instring, outstring); - 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)); +TEST(fscrypt, ParseOptions) { + EncryptionOptions dummy_options; + + std::vector<std::string> defaults = { + "software", + "", + ":", + "::", + "aes-256-xts", + "aes-256-xts:", + "aes-256-xts::", + "aes-256-xts:aes-256-cts", + "aes-256-xts:aes-256-cts:", + ":aes-256-cts", + ":aes-256-cts:", + }; + for (const auto& d : defaults) { + TEST_STRING(29, d, "aes-256-xts:aes-256-cts:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_4, options.flags); + } + for (const auto& d : defaults) { + TEST_STRING(30, d, "aes-256-xts:aes-256-cts:v2"); + EXPECT_TRUE(ParseOptionsForApiLevel(30, d, &dummy_options)); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + } + + EXPECT_FALSE(ParseOptionsForApiLevel(29, "blah", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "blah", &dummy_options)); + + { + TEST_STRING(29, "::v1", "aes-256-xts:aes-256-cts:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_4, options.flags); + } + { + TEST_STRING(30, "::v1", "aes-256-xts:aes-256-cts:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + } + { + TEST_STRING(29, "::v2", "aes-256-xts:aes-256-cts:v2"); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + } + { + TEST_STRING(29, "ice", "ice:aes-256-cts:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_4, options.flags); + } + EXPECT_FALSE(ParseOptionsForApiLevel(29, "ice:blah", &dummy_options)); + + { + TEST_STRING(29, "ice:aes-256-cts", "ice:aes-256-cts:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_4, options.flags); + } + + { + TEST_STRING(29, "ice:aes-256-heh", "ice:aes-256-heh:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_PRIVATE, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_HEH, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16, options.flags); + } + { + TEST_STRING(29, "adiantum", "adiantum:adiantum:v1"); + EXPECT_EQ(1, options.version); + EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_DIRECT_KEY, options.flags); + } + { + TEST_STRING(30, "adiantum", "adiantum:adiantum:v2"); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_ADIANTUM, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_DIRECT_KEY, options.flags); + } + EXPECT_FALSE(ParseOptionsForApiLevel(29, "adiantum:aes-256-cts", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "adiantum:aes-256-cts", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(29, "aes-256-xts:adiantum", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:adiantum", &dummy_options)); + { + TEST_STRING(30, "::inlinecrypt_optimized", + "aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized"); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64, options.flags); + } + { + TEST_STRING(30, "aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", + "aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized"); + EXPECT_EQ(2, options.version); + EXPECT_EQ(FSCRYPT_MODE_AES_256_XTS, options.contents_mode); + EXPECT_EQ(FSCRYPT_MODE_AES_256_CTS, options.filenames_mode); + EXPECT_EQ(FSCRYPT_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64, options.flags); + } + + EXPECT_FALSE(ParseOptionsForApiLevel(29, "aes-256-xts:aes-256-cts:v2:", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(29, "aes-256-xts:aes-256-cts:v2:foo", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(29, "aes-256-xts:aes-256-cts:blah", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(29, "aes-256-xts:aes-256-cts:vblah", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:v2:", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:v2:foo", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:blah", &dummy_options)); + EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:vblah", &dummy_options)); } |