Add AES ECB, CBC, OFB and CFB support.

Change-Id: I7a4e8eaa3be5f20e87ab1f16b0b6bfc1fa47b74c
diff --git a/aes_operation.cpp b/aes_operation.cpp
index e605d2a..a54c8d0 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -17,38 +17,88 @@
 #include <stdio.h>
 
 #include <openssl/aes.h>
+#include <openssl/err.h>
 #include <openssl/rand.h>
 
 #include <keymaster/logger.h>
 
 #include "aes_key.h"
 #include "aes_operation.h"
+#include "openssl_err.h"
 
 namespace keymaster {
 
 /**
- * Abstract base for AES OCB mode operation factories.  This class does all of the work to create
- * OCB mode operations.
+ * Abstract base for AES operation factories.  This class does all of the work to create
+ * AES operations.
  */
-class AesOcbOperationFactory : public OperationFactory {
+class AesOperationFactory : public OperationFactory {
   public:
     virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_AES, purpose()); }
 
     virtual Operation* CreateOperation(const Key& key, keymaster_error_t* error);
     virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const;
+    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const;
 
     virtual keymaster_purpose_t purpose() const = 0;
+
+  private:
+    virtual Operation* CreateOcbOperation(const SymmetricKey& key, keymaster_error_t* error);
+    virtual Operation* CreateEvpOperation(const SymmetricKey& key,
+                                          keymaster_block_mode_t block_mode,
+                                          keymaster_padding_t padding, keymaster_error_t* error);
 };
 
-Operation* AesOcbOperationFactory::CreateOperation(const Key& key, keymaster_error_t* error) {
+Operation* AesOperationFactory::CreateOperation(const Key& key, keymaster_error_t* error) {
     *error = KM_ERROR_OK;
 
+    const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key);
+    if (!symmetric_key) {
+        *error = KM_ERROR_UNKNOWN_ERROR;
+        return NULL;
+    }
+
+    switch (symmetric_key->key_data_size()) {
+    case 16:
+    case 24:
+    case 32:
+        break;
+    default:
+        *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        return NULL;
+    }
+
     keymaster_block_mode_t block_mode;
-    if (!key.authorizations().GetTagValue(TAG_BLOCK_MODE, &block_mode) ||
-        block_mode != KM_MODE_OCB) {
+    if (!key.authorizations().GetTagValue(TAG_BLOCK_MODE, &block_mode) || !supported(block_mode))
+        *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
+
+    keymaster_padding_t padding = KM_PAD_NONE;
+    key.authorizations().GetTagValue(TAG_PADDING, &padding);
+
+    if (*error != KM_ERROR_OK)
+        return NULL;
+
+    switch (block_mode) {
+    case KM_MODE_OCB:
+        if (padding != KM_PAD_NONE) {
+            *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
+            return NULL;
+        }
+        return CreateOcbOperation(*symmetric_key, error);
+    case KM_MODE_ECB:
+    case KM_MODE_CBC:
+    case KM_MODE_OFB:
+    case KM_MODE_CFB:
+        return CreateEvpOperation(*symmetric_key, block_mode, padding, error);
+    default:
         *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
         return NULL;
     }
+}
+
+Operation* AesOperationFactory::CreateOcbOperation(const SymmetricKey& key,
+                                                   keymaster_error_t* error) {
+    *error = KM_ERROR_OK;
 
     uint32_t chunk_length;
     if (!key.authorizations().GetTagValue(TAG_CHUNK_LENGTH, &chunk_length) ||
@@ -66,60 +116,72 @@
     if (key.authorizations().GetTagValue(TAG_PADDING, &padding) && padding != KM_PAD_NONE)
         *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
 
-    const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key);
-    if (!symmetric_key) {
-        *error = KM_ERROR_UNKNOWN_ERROR;
-        return NULL;
-    }
-
-    switch (symmetric_key->key_data_size()) {
-    case 16:
-    case 24:
-    case 32:
-        break;
-    default:
-        *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
-    }
-
     if (*error != KM_ERROR_OK)
         return NULL;
 
     keymaster_blob_t additional_data = {0, 0};
-    symmetric_key->authorizations().GetTagValue(TAG_ASSOCIATED_DATA, &additional_data);
+    key.authorizations().GetTagValue(TAG_ASSOCIATED_DATA, &additional_data);
 
-    Operation* op =
-        new AesOcbOperation(purpose(), symmetric_key->key_data(), symmetric_key->key_data_size(),
-                            chunk_length, tag_length, additional_data);
+    Operation* op = new AesOcbOperation(purpose(), key.key_data(), key.key_data_size(),
+                                        chunk_length, tag_length, additional_data);
     if (!op)
         *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
     return op;
 }
 
-static const keymaster_block_mode_t supported_block_modes[] = {KM_MODE_OCB};
+Operation* AesOperationFactory::CreateEvpOperation(const SymmetricKey& key,
+                                                   keymaster_block_mode_t block_mode,
+                                                   keymaster_padding_t padding,
+                                                   keymaster_error_t* error) {
+    Operation* op = NULL;
+    switch (purpose()) {
+    case KM_PURPOSE_ENCRYPT:
+        op = new AesEvpEncryptOperation(block_mode, padding, key.key_data(), key.key_data_size());
+        break;
+    case KM_PURPOSE_DECRYPT:
+        op = new AesEvpDecryptOperation(block_mode, padding, key.key_data(), key.key_data_size());
+        break;
+    default:
+        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
+        return NULL;
+    }
+
+    if (!op)
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    return op;
+}
+
+static const keymaster_block_mode_t supported_block_modes[] = {
+    KM_MODE_OCB, KM_MODE_ECB, KM_MODE_CBC, KM_MODE_OFB, KM_MODE_CFB};
 
 const keymaster_block_mode_t*
-AesOcbOperationFactory::SupportedBlockModes(size_t* block_mode_count) const {
+AesOperationFactory::SupportedBlockModes(size_t* block_mode_count) const {
     *block_mode_count = array_length(supported_block_modes);
     return supported_block_modes;
 }
 
-/**
- * Concrete factory for AES OCB mode encryption operations.
- */
-class AesOcbEncryptionOperationFactory : public AesOcbOperationFactory {
-    keymaster_purpose_t purpose() const { return KM_PURPOSE_ENCRYPT; }
-};
-static OperationFactoryRegistry::Registration<AesOcbEncryptionOperationFactory>
-    encrypt_registration;
+static const keymaster_padding_t supported_padding_modes[] = {KM_PAD_NONE, KM_PAD_PKCS7};
+const keymaster_padding_t*
+AesOperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const {
+    *padding_mode_count = array_length(supported_padding_modes);
+    return supported_padding_modes;
+}
 
 /**
- * Concrete factory for AES OCB mode decryption operations.
+ * Concrete factory for AES encryption operations.
  */
-class AesOcbDecryptionOperationFactory : public AesOcbOperationFactory {
+class AesEncryptionOperationFactory : public AesOperationFactory {
+    keymaster_purpose_t purpose() const { return KM_PURPOSE_ENCRYPT; }
+};
+static OperationFactoryRegistry::Registration<AesEncryptionOperationFactory> encrypt_registration;
+
+/**
+ * Concrete factory for AES decryption operations.
+ */
+class AesDecryptionOperationFactory : public AesOperationFactory {
     keymaster_purpose_t purpose() const { return KM_PURPOSE_DECRYPT; }
 };
-static OperationFactoryRegistry::Registration<AesOcbDecryptionOperationFactory>
-    decrypt_registration;
+static OperationFactoryRegistry::Registration<AesDecryptionOperationFactory> decrypt_registration;
 
 keymaster_error_t AesOcbOperation::Initialize(uint8_t* key, size_t key_size, size_t nonce_length,
                                               size_t tag_length) {
@@ -175,4 +237,198 @@
     return KM_ERROR_OK;
 }
 
+AesEvpOperation::AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
+                                 keymaster_padding_t padding, const uint8_t* key, size_t key_size)
+    : Operation(purpose), key_size_(key_size), block_mode_(block_mode), padding_(padding),
+      cipher_initialized_(false), iv_buffered_(0) {
+    memcpy(key_, key, key_size_);
+    EVP_CIPHER_CTX_init(&ctx_);
+}
+
+AesEvpOperation::~AesEvpOperation() {
+    EVP_CIPHER_CTX_cleanup(&ctx_);
+}
+
+keymaster_error_t AesEvpOperation::InitializeCipher() {
+    const EVP_CIPHER* cipher;
+    switch (block_mode_) {
+    case KM_MODE_ECB:
+        switch (key_size_) {
+        case 16:
+            cipher = EVP_aes_128_ecb();
+            break;
+        case 24:
+            cipher = EVP_aes_192_ecb();
+            break;
+        case 32:
+            cipher = EVP_aes_256_ecb();
+            break;
+        default:
+            return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        }
+        break;
+    case KM_MODE_CBC:
+        switch (key_size_) {
+        case 16:
+            cipher = EVP_aes_128_cbc();
+            break;
+        case 24:
+            cipher = EVP_aes_192_cbc();
+            break;
+        case 32:
+            cipher = EVP_aes_256_cbc();
+            break;
+        default:
+            return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        }
+        break;
+    case KM_MODE_OFB:
+        switch (key_size_) {
+        case 16:
+            cipher = EVP_aes_128_ofb();
+            break;
+        case 24:
+            cipher = EVP_aes_192_ofb();
+            break;
+        case 32:
+            cipher = EVP_aes_256_ofb();
+            break;
+        default:
+            return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        }
+        break;
+    case KM_MODE_CFB:
+        switch (key_size_) {
+        case 16:
+            cipher = EVP_aes_128_cfb();
+            break;
+        case 24:
+            cipher = EVP_aes_192_cfb();
+            break;
+        case 32:
+            cipher = EVP_aes_256_cfb();
+            break;
+        default:
+            return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        }
+        break;
+    default:
+        return KM_ERROR_UNSUPPORTED_BLOCK_MODE;
+    }
+
+    int init_result =
+        EVP_CipherInit_ex(&ctx_, cipher, NULL /* engine */, key_, iv_, evp_encrypt_mode());
+
+    if (!init_result)
+        return KM_ERROR_UNKNOWN_ERROR;
+
+    switch (padding_) {
+    case KM_PAD_NONE:
+        EVP_CIPHER_CTX_set_padding(&ctx_, 0 /* disable padding */);
+        break;
+    case KM_PAD_PKCS7:
+        // This is the default for OpenSSL EVP cipher operations.
+        break;
+    default:
+        return KM_ERROR_UNSUPPORTED_PADDING_MODE;
+    }
+
+    cipher_initialized_ = true;
+    return KM_ERROR_OK;
+}
+
+bool AesEvpOperation::need_iv() const {
+    switch (block_mode_) {
+    case KM_MODE_CBC:
+    case KM_MODE_OFB:
+    case KM_MODE_CFB:
+        return true;
+    case KM_MODE_ECB:
+        return false;
+    default:
+        // Shouldn't get here.
+        assert(false);
+        return false;
+    }
+}
+
+keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& /* input_params */,
+                                         AuthorizationSet* /* output_params */) {
+    return KM_ERROR_OK;
+}
+
+inline size_t min(size_t a, size_t b) {
+    if (a < b)
+        return a;
+    return b;
+}
+
+keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& /* additional_params */,
+                                          const Buffer& input, Buffer* output,
+                                          size_t* input_consumed) {
+    output->reserve(input.available_read() + AES_BLOCK_SIZE);
+
+    const uint8_t* input_pos = input.peek_read();
+    const uint8_t* input_end = input_pos + input.available_read();
+
+    if (!cipher_initialized_) {
+        if (need_iv()) {
+            switch (purpose()) {
+            case KM_PURPOSE_DECRYPT: {
+                size_t iv_bytes_to_copy = min(input_end - input_pos, AES_BLOCK_SIZE - iv_buffered_);
+                memcpy(iv_ + iv_buffered_, input_pos, iv_bytes_to_copy);
+                input_pos += iv_bytes_to_copy;
+                iv_buffered_ += iv_bytes_to_copy;
+
+                if (iv_buffered_ < AES_BLOCK_SIZE) {
+                    // Don't yet have enough IV bytes.  Wait for another update.
+                    return KM_ERROR_OK;
+                }
+            } break;
+            case KM_PURPOSE_ENCRYPT:
+                if (!RAND_bytes(iv_, AES_BLOCK_SIZE))
+                    return TranslateLastOpenSslError();
+                output->write(iv_, AES_BLOCK_SIZE);
+                break;
+            default:
+                return KM_ERROR_UNSUPPORTED_BLOCK_MODE;
+            }
+        }
+
+        keymaster_error_t error = InitializeCipher();
+        if (error != KM_ERROR_OK)
+            return error;
+    }
+
+    int output_written = -1;
+    if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input_pos,
+                          input_end - input_pos))
+        return TranslateLastOpenSslError();
+
+    assert(output_written >= 0);
+    assert(output_written <= (int)output->available_write());
+    output->advance_write(output_written);
+    *input_consumed = input.available_read();
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
+                                          const Buffer& /* signature */, Buffer* output) {
+    output->reserve(AES_BLOCK_SIZE);
+
+    int output_written = -1;
+    if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
+        LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
+        return TranslateLastOpenSslError();
+    }
+
+    assert(output_written <= AES_BLOCK_SIZE);
+    output->advance_write(output_written);
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t AesEvpOperation::Abort() {
+    return KM_ERROR_OK;
+}
+
 }  // namespace keymaster
diff --git a/aes_operation.h b/aes_operation.h
index 0ceb631..aaa53a4 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -17,6 +17,8 @@
 #ifndef SYSTEM_KEYMASTER_AES_OPERATION_H_
 #define SYSTEM_KEYMASTER_AES_OPERATION_H_
 
+#include <openssl/evp.h>
+
 #include "aead_mode_operation.h"
 #include "ocb_utils.h"
 #include "operation.h"
@@ -26,8 +28,6 @@
 class AesOcbOperation : public AeadModeOperation {
   public:
     static const size_t NONCE_LENGTH = 12;
-    static const size_t MAX_TAG_LENGTH = 16;
-    static const size_t MAX_KEY_LENGTH = 32;
 
     AesOcbOperation(keymaster_purpose_t purpose, const uint8_t* key, size_t key_size,
                     size_t chunk_length, size_t tag_length, keymaster_blob_t additional_data)
@@ -56,6 +56,53 @@
     AeCtx ctx_;
 };
 
+class AesEvpOperation : public Operation {
+  public:
+    AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
+                    keymaster_padding_t padding, const uint8_t* key, size_t key_size);
+    ~AesEvpOperation();
+
+    virtual keymaster_error_t Begin(const AuthorizationSet& input_params,
+                                    AuthorizationSet* output_params);
+    virtual keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+                                     Buffer* output, size_t* input_consumed);
+    virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
+                                     const Buffer& /* signature */, Buffer* output);
+    virtual keymaster_error_t Abort();
+
+    virtual int evp_encrypt_mode() = 0;
+
+  private:
+    keymaster_error_t InitializeCipher();
+    bool need_iv() const;
+
+    EVP_CIPHER_CTX ctx_;
+    const size_t key_size_;
+    const keymaster_block_mode_t block_mode_;
+    const keymaster_padding_t padding_;
+    bool cipher_initialized_;
+    uint8_t iv_buffered_;
+    uint8_t iv_[AES_BLOCK_SIZE];
+    uint8_t key_[SymmetricKey::MAX_KEY_SIZE];
+};
+
+class AesEvpEncryptOperation : public AesEvpOperation {
+  public:
+    AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+                           const uint8_t* key, size_t key_size)
+        : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, key, key_size) {}
+    int evp_encrypt_mode() { return 1; }
+};
+
+class AesEvpDecryptOperation : public AesEvpOperation {
+  public:
+    AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+                           const uint8_t* key, size_t key_size)
+        : AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding, key, key_size) {}
+
+    int evp_encrypt_mode() { return 0; }
+};
+
 }  // namespace keymaster
 
 #endif  // SYSTEM_KEYMASTER_AES_OPERATION_H_
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