Add AES ECB, CBC, OFB and CFB support.
Change-Id: I7a4e8eaa3be5f20e87ab1f16b0b6bfc1fa47b74c
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 96081ea..b0c47d8 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -35,6 +35,7 @@
using std::vector;
int main(int argc, char** argv) {
+ ERR_load_crypto_strings();
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
// Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
@@ -425,7 +426,8 @@
EXPECT_EQ(KM_ERROR_OK, device()->get_supported_block_modes(device(), KM_ALGORITHM_AES,
KM_PURPOSE_ENCRYPT, &modes, &len));
- EXPECT_TRUE(ResponseContains({KM_MODE_OCB}, modes, len));
+ EXPECT_TRUE(ResponseContains({KM_MODE_OCB, KM_MODE_ECB, KM_MODE_CBC, KM_MODE_OFB, KM_MODE_CFB},
+ modes, len));
free(modes);
}
@@ -1456,14 +1458,10 @@
EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, BeginOperation(KM_PURPOSE_ENCRYPT));
}
-TEST_F(EncryptionOperationsTest, AesEcbUnsupported) {
- GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE, KM_MODE_ECB));
- EXPECT_EQ(KM_ERROR_UNSUPPORTED_BLOCK_MODE, BeginOperation(KM_PURPOSE_ENCRYPT));
-}
-
TEST_F(EncryptionOperationsTest, AesOcbPaddingUnsupported) {
- GenerateKey(
- ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 16).Option(TAG_PADDING, KM_PAD_ZERO));
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 16).Option(
+ TAG_PADDING, KM_PAD_ZERO)));
EXPECT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_ENCRYPT));
}
@@ -1472,5 +1470,194 @@
EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, BeginOperation(KM_PURPOSE_ENCRYPT));
}
+TEST_F(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_ECB)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // ECB is deterministic.
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_ECB)));
+ // Message is slightly shorter than two blocks.
+ string message = "1234567890123456789012345678901";
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &ciphertext, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&ciphertext));
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ string message = "a";
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(16, ciphertext.size());
+ EXPECT_NE(ciphertext, message);
+ ++ciphertext[ciphertext.size() / 2];
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&plaintext));
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size() + 16, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size() + 16, ciphertext2.size());
+
+ // CBC uses random IVs, so ciphertexts shouldn't match.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcIncrementalNoPadding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+
+ int increment = 15;
+ string message(240, 'a');
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size() + 16, ciphertext.size());
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size() - 16, plaintext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 32 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CFB)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size() + 16, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size() + 16, ciphertext2.size());
+
+ // CBC uses random IVs, so ciphertexts shouldn't match.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbIncrementalNoPadding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_CFB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+
+ int increment = 15;
+ string message(240, 'a');
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size() + 16, ciphertext.size());
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size() - 16, plaintext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CFB)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 16, ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
} // namespace test
} // namespace keymaster