Add RSA encryption and decryption support.

This change was already reviewed, merged and reverted, so I'm skipping
the review step this time.

Change-Id: Ie5b7dba86a7ae7f62eedbdb6eec7b61ef83d0c73
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
index da606e5..47f22cf 100644
--- a/google_keymaster.cpp
+++ b/google_keymaster.cpp
@@ -93,10 +93,12 @@
     response->error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
 }
 
+keymaster_padding_t supported_rsa_crypt_padding[] = {KM_PAD_RSA_OAEP, KM_PAD_RSA_PKCS1_1_5_ENCRYPT};
+keymaster_padding_t supported_rsa_sign_padding[] = {KM_PAD_NONE};
 keymaster_padding_t supported_padding[] = {KM_PAD_NONE};
 
 void GoogleKeymaster::SupportedPaddingModes(
-    keymaster_algorithm_t algorithm, keymaster_purpose_t /* purpose */,
+    keymaster_algorithm_t algorithm, keymaster_purpose_t purpose,
     SupportedResponse<keymaster_padding_t>* response) const {
     if (response == NULL || !check_supported(algorithm, response))
         return;
@@ -104,6 +106,17 @@
     response->error = KM_ERROR_OK;
     switch (algorithm) {
     case KM_ALGORITHM_RSA:
+        switch (purpose) {
+        case KM_PURPOSE_ENCRYPT:
+        case KM_PURPOSE_DECRYPT:
+            response->SetResults(supported_rsa_crypt_padding);
+            break;
+        case KM_PURPOSE_SIGN:
+        case KM_PURPOSE_VERIFY:
+            response->SetResults(supported_rsa_sign_padding);
+            break;
+        }
+        break;
     case KM_ALGORITHM_DSA:
     case KM_ALGORITHM_ECDSA:
         response->SetResults(supported_padding);
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index a124126..810f1f3 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -1089,5 +1089,208 @@
     EXPECT_EQ(0, rsp.subminor_ver);
 }
 
+/**
+ * Test class that provides some infrastructure for generating keys and encrypting messages.
+ */
+class EncryptionOperationsTest : public KeymasterTest {
+  protected:
+    void GenerateKey(keymaster_algorithm_t algorithm, keymaster_padding_t padding,
+                     uint32_t key_size) {
+        keymaster_key_param_t params[] = {
+            Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT),
+            Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT), Authorization(TAG_ALGORITHM, algorithm),
+            Authorization(TAG_KEY_SIZE, key_size), Authorization(TAG_USER_ID, 7),
+            Authorization(TAG_USER_AUTH_ID, 8), Authorization(TAG_APPLICATION_ID, "app_id", 6),
+            Authorization(TAG_AUTH_TIMEOUT, 300),
+        };
+        GenerateKeyRequest generate_request;
+        generate_request.key_description.Reinitialize(params, array_length(params));
+        if (static_cast<int>(padding) != -1)
+            generate_request.key_description.push_back(TAG_PADDING, padding);
+        device.GenerateKey(generate_request, &generate_response_);
+        EXPECT_EQ(KM_ERROR_OK, generate_response_.error);
+    }
+
+    keymaster_error_t BeginOperation(keymaster_purpose_t purpose,
+                                     const keymaster_key_blob_t& key_blob, uint64_t* op_handle) {
+        BeginOperationRequest begin_request;
+        begin_request.SetKeyMaterial(key_blob);
+        begin_request.purpose = purpose;
+        AddClientParams(&begin_request.additional_params);
+
+        BeginOperationResponse begin_response;
+        device.BeginOperation(begin_request, &begin_response);
+        *op_handle = begin_response.op_handle;
+        return begin_response.error;
+    }
+
+    keymaster_error_t UpdateOperation(uint64_t op_handle, const void* message, size_t size,
+                                      string* output) {
+        UpdateOperationRequest update_request;
+        update_request.op_handle = op_handle;
+        update_request.input.Reinitialize(message, size);
+
+        UpdateOperationResponse update_response;
+        device.UpdateOperation(update_request, &update_response);
+        if (update_response.error == KM_ERROR_OK)
+            output->append(reinterpret_cast<const char*>(update_response.output.peek_read()),
+                           update_response.output.available_read());
+        return update_response.error;
+    }
+
+    keymaster_error_t FinishOperation(uint64_t op_handle, string* output) {
+        FinishOperationRequest finish_request;
+        finish_request.op_handle = op_handle;
+        FinishOperationResponse finish_response;
+        device.FinishOperation(finish_request, &finish_response);
+        if (finish_response.error == KM_ERROR_OK)
+            output->append(reinterpret_cast<const char*>(finish_response.output.peek_read()),
+                           finish_response.output.available_read());
+        return finish_response.error;
+    }
+
+    string ProcessMessage(keymaster_purpose_t purpose, const keymaster_key_blob_t& key_blob,
+                          const void* message, size_t size) {
+        uint64_t op_handle;
+        EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, key_blob, &op_handle));
+
+        string result;
+        EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, size, &result));
+        EXPECT_EQ(KM_ERROR_OK, FinishOperation(op_handle, &result));
+        return result;
+    }
+
+    string EncryptMessage(const void* message, size_t size) {
+        return ProcessMessage(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, message, size);
+    }
+
+    string DecryptMessage(const void* ciphertext, size_t size) {
+        return ProcessMessage(KM_PURPOSE_DECRYPT, generate_response_.key_blob, ciphertext, size);
+    }
+
+    void AddClientParams(AuthorizationSet* set) { set->push_back(TAG_APPLICATION_ID, "app_id", 6); }
+
+    const keymaster_key_blob_t& key_blob() { return generate_response_.key_blob; }
+
+    const keymaster_key_blob_t& corrupt_key_blob() {
+        uint8_t* tmp = const_cast<uint8_t*>(generate_response_.key_blob.key_material);
+        ++tmp[generate_response_.key_blob.key_material_size / 2];
+        return generate_response_.key_blob;
+    }
+
+  protected:
+    GenerateKeyResponse generate_response_;
+};
+
+TEST_F(EncryptionOperationsTest, RsaOaepSuccess) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+    const char message[] = "Hello World!";
+    string ciphertext1 = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext2.size());
+
+    // OAEP randomizes padding so every result should be different.
+    EXPECT_NE(ciphertext1, ciphertext2);
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepRoundTrip) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+    const char message[] = "Hello World!";
+    string ciphertext = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext.size());
+
+    string plaintext = DecryptMessage(ciphertext.data(), ciphertext.size());
+    EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepTooLarge) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+    const char message[] = "12345678901234567890123";
+    uint64_t op_handle;
+    string result;
+
+    EXPECT_EQ(KM_ERROR_OK,
+              BeginOperation(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, &op_handle));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, array_size(message), &result));
+    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(op_handle, &result));
+    EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepCorruptedDecrypt) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+    const char message[] = "Hello World!";
+    string ciphertext = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext.size());
+
+    // Corrupt the ciphertext
+    ciphertext[512 / 8 / 2]++;
+
+    uint64_t op_handle;
+    string result;
+    EXPECT_EQ(KM_ERROR_OK,
+              BeginOperation(KM_PURPOSE_DECRYPT, generate_response_.key_blob, &op_handle));
+    EXPECT_EQ(KM_ERROR_OK,
+              UpdateOperation(op_handle, ciphertext.data(), ciphertext.size(), &result));
+    EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(op_handle, &result));
+    EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1Success) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+    const char message[] = "Hello World!";
+    string ciphertext1 = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext2.size());
+
+    // PKCS1 v1.5 randomizes padding so every result should be different.
+    EXPECT_NE(ciphertext1, ciphertext2);
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1RoundTrip) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+    const char message[] = "Hello World!";
+    string ciphertext = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext.size());
+
+    string plaintext = DecryptMessage(ciphertext.data(), ciphertext.size());
+    EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1TooLarge) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+    const char message[] = "1234567890123456789012345678901234567890123456789012";
+    uint64_t op_handle;
+    string result;
+
+    EXPECT_EQ(KM_ERROR_OK,
+              BeginOperation(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, &op_handle));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, array_size(message), &result));
+    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(op_handle, &result));
+    EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1CorruptedDecrypt) {
+    GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+    const char message[] = "Hello World!";
+    string ciphertext = EncryptMessage(message, strlen(message));
+    EXPECT_EQ(512 / 8, ciphertext.size());
+
+    // Corrupt the ciphertext
+    ciphertext[512 / 8 / 2]++;
+
+    uint64_t op_handle;
+    string result;
+    EXPECT_EQ(KM_ERROR_OK,
+              BeginOperation(KM_PURPOSE_DECRYPT, generate_response_.key_blob, &op_handle));
+    EXPECT_EQ(KM_ERROR_OK,
+              UpdateOperation(op_handle, ciphertext.data(), ciphertext.size(), &result));
+    EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(op_handle, &result));
+    EXPECT_EQ(0, result.size());
+}
+
 }  // namespace test
 }  // namespace keymaster
diff --git a/rsa_key.cpp b/rsa_key.cpp
index db50930..c904c19 100644
--- a/rsa_key.cpp
+++ b/rsa_key.cpp
@@ -128,30 +128,44 @@
 }
 
 Operation* RsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    keymaster_digest_t digest = KM_DIGEST_NONE;
-    if (!authorizations().GetTagValue(TAG_DIGEST, &digest) || digest != KM_DIGEST_NONE) {
-        *error = KM_ERROR_UNSUPPORTED_DIGEST;
-        return NULL;
-    }
-    keymaster_padding_t padding = KM_PAD_NONE;
-    if (!authorizations().GetTagValue(TAG_PADDING, &padding) || padding != KM_PAD_NONE) {
+    *error = KM_ERROR_OK;
+
+    keymaster_padding_t padding = static_cast<keymaster_padding_t>(-1);
+    authorizations().GetTagValue(TAG_PADDING, &padding);
+    if (!SupportedMode(purpose, padding)) {
         *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
         return NULL;
     }
 
-    Operation* op;
-    switch (purpose) {
-    case KM_PURPOSE_SIGN:
-        op = new RsaSignOperation(purpose, logger_, digest, padding, rsa_key_.release());
-        break;
-    case KM_PURPOSE_VERIFY:
-        op = new RsaVerifyOperation(purpose, logger_, digest, padding, rsa_key_.release());
-        break;
-    default:
-        *error = KM_ERROR_UNIMPLEMENTED;
+    keymaster_digest_t digest = static_cast<keymaster_digest_t>(-1);
+    authorizations().GetTagValue(TAG_DIGEST, &digest);
+    if (!SupportedMode(purpose, digest)) {
+        *error = KM_ERROR_UNSUPPORTED_DIGEST;
         return NULL;
     }
-    *error = op ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+    Operation* op = NULL;
+    switch (purpose) {
+    case KM_PURPOSE_SIGN:
+        op = new RsaSignOperation(logger_, digest, padding, rsa_key_.release());
+        break;
+    case KM_PURPOSE_VERIFY:
+        op = new RsaVerifyOperation(logger_, digest, padding, rsa_key_.release());
+        break;
+    case KM_PURPOSE_ENCRYPT:
+        op = new RsaEncryptOperation(logger_, padding, rsa_key_.release());
+        break;
+    case KM_PURPOSE_DECRYPT:
+        op = new RsaDecryptOperation(logger_, padding, rsa_key_.release());
+        break;
+    default:
+        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
+        return NULL;
+    }
+
+    if (!op)
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
     return op;
 }
 
@@ -164,4 +178,32 @@
     return EVP_PKEY_set1_RSA(pkey, rsa_key_.get()) == 1;
 }
 
+bool RsaKey::SupportedMode(keymaster_purpose_t purpose, keymaster_padding_t padding) {
+    switch (purpose) {
+    case KM_PURPOSE_SIGN:
+    case KM_PURPOSE_VERIFY:
+        return padding == KM_PAD_NONE;
+        break;
+    case KM_PURPOSE_ENCRYPT:
+    case KM_PURPOSE_DECRYPT:
+        return padding == KM_PAD_RSA_OAEP || padding == KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+        break;
+    };
+    return false;
+}
+
+bool RsaKey::SupportedMode(keymaster_purpose_t purpose, keymaster_digest_t digest) {
+    switch (purpose) {
+    case KM_PURPOSE_SIGN:
+    case KM_PURPOSE_VERIFY:
+        return digest == KM_DIGEST_NONE;
+        break;
+    case KM_PURPOSE_ENCRYPT:
+    case KM_PURPOSE_DECRYPT:
+        /* Don't care */
+        break;
+    };
+    return true;
+}
+
 }  // namespace keymaster
diff --git a/rsa_key.h b/rsa_key.h
index 4002996..cb341e2 100644
--- a/rsa_key.h
+++ b/rsa_key.h
@@ -41,6 +41,9 @@
     virtual bool InternalToEvp(EVP_PKEY* pkey) const;
     virtual bool EvpToInternal(const EVP_PKEY* pkey);
 
+    bool SupportedMode(keymaster_purpose_t purpose, keymaster_padding_t padding);
+    bool SupportedMode(keymaster_purpose_t purpose, keymaster_digest_t digest);
+
     struct RSA_Delete {
         void operator()(RSA* p) { RSA_free(p); }
     };
diff --git a/rsa_operation.cpp b/rsa_operation.cpp
index a013b78..df39006 100644
--- a/rsa_operation.cpp
+++ b/rsa_operation.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-#include <openssl/rsa.h>
+#include <limits.h>
+
+#include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/rsa.h>
 
 #include "rsa_operation.h"
 #include "openssl_utils.h"
@@ -37,6 +40,8 @@
         return KM_ERROR_UNIMPLEMENTED;
     case KM_PURPOSE_SIGN:
     case KM_PURPOSE_VERIFY:
+    case KM_PURPOSE_ENCRYPT:
+    case KM_PURPOSE_DECRYPT:
         return StoreData(input);
     }
 }
@@ -61,13 +66,16 @@
 
 keymaster_error_t RsaVerifyOperation::Finish(const Buffer& signature, Buffer* /* output */) {
 #if defined(OPENSSL_IS_BORINGSSL)
-    if (data_.available_read() != RSA_size(rsa_key_))
-        return KM_ERROR_INVALID_INPUT_LENGTH;
+    size_t message_size = data_.available_read();
 #else
-    if ((int)data_.available_read() != RSA_size(rsa_key_))
+    if (data_.available_read() > INT_MAX)
         return KM_ERROR_INVALID_INPUT_LENGTH;
+    int message_size = (int)data_.available_read();
 #endif
 
+    if (message_size != RSA_size(rsa_key_))
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+
     if (data_.available_read() != signature.available_read())
         return KM_ERROR_VERIFICATION_FAILED;
 
@@ -83,4 +91,81 @@
     return KM_ERROR_VERIFICATION_FAILED;
 }
 
+const int OAEP_PADDING_OVERHEAD = 41;
+const int PKCS1_PADDING_OVERHEAD = 11;
+
+keymaster_error_t RsaEncryptOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+    int openssl_padding;
+
+#if defined(OPENSSL_IS_BORINGSSL)
+    size_t message_size = data_.available_read();
+#else
+    if (data_.available_read() > INT_MAX)
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    int message_size = (int)data_.available_read();
+#endif
+
+    switch (padding_) {
+    case KM_PAD_RSA_OAEP:
+        openssl_padding = RSA_PKCS1_OAEP_PADDING;
+        if (message_size >= RSA_size(rsa_key_) - OAEP_PADDING_OVERHEAD) {
+            logger().error("Cannot encrypt %d bytes with %d-byte key and OAEP padding",
+                           data_.available_read(), RSA_size(rsa_key_));
+            return KM_ERROR_INVALID_INPUT_LENGTH;
+        }
+        break;
+    case KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+        openssl_padding = RSA_PKCS1_PADDING;
+        if (message_size >= RSA_size(rsa_key_) - PKCS1_PADDING_OVERHEAD) {
+            logger().error("Cannot encrypt %d bytes with %d-byte key and PKCS1 padding",
+                           data_.available_read(), RSA_size(rsa_key_));
+            return KM_ERROR_INVALID_INPUT_LENGTH;
+        }
+        break;
+    default:
+        logger().error("Padding mode %d not supported", padding_);
+        return KM_ERROR_UNSUPPORTED_PADDING_MODE;
+    }
+
+    output->Reinitialize(RSA_size(rsa_key_));
+    int bytes_encrypted = RSA_public_encrypt(data_.available_read(), data_.peek_read(),
+                                             output->peek_write(), rsa_key_, openssl_padding);
+
+    if (bytes_encrypted < 0) {
+        logger().error("Error %d encrypting data with RSA", ERR_get_error());
+        return KM_ERROR_UNKNOWN_ERROR;
+    }
+    assert(bytes_encrypted == RSA_size(rsa_key_));
+    output->advance_write(bytes_encrypted);
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t RsaDecryptOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+    int openssl_padding;
+    switch (padding_) {
+    case KM_PAD_RSA_OAEP:
+        openssl_padding = RSA_PKCS1_OAEP_PADDING;
+        break;
+    case KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+        openssl_padding = RSA_PKCS1_PADDING;
+        break;
+    default:
+        logger().error("Padding mode %d not supported", padding_);
+        return KM_ERROR_UNSUPPORTED_PADDING_MODE;
+    }
+
+    output->Reinitialize(RSA_size(rsa_key_));
+    int bytes_decrypted = RSA_private_decrypt(data_.available_read(), data_.peek_read(),
+                                              output->peek_write(), rsa_key_, openssl_padding);
+
+    if (bytes_decrypted < 0) {
+        logger().error("Error %d decrypting data with RSA", ERR_get_error());
+        return KM_ERROR_UNKNOWN_ERROR;
+    }
+    output->advance_write(bytes_decrypted);
+
+    return KM_ERROR_OK;
+}
+
 }  // namespace keymaster
diff --git a/rsa_operation.h b/rsa_operation.h
index 4697866..fb417a7 100644
--- a/rsa_operation.h
+++ b/rsa_operation.h
@@ -27,9 +27,9 @@
 
 class RsaOperation : public Operation {
   public:
-    RsaOperation(keymaster_purpose_t purpose, const Logger& logger, keymaster_digest_t digest,
-                 keymaster_padding_t padding, RSA* key)
-        : Operation(purpose, logger), rsa_key_(key), digest_(digest), padding_(padding) {}
+    RsaOperation(keymaster_purpose_t purpose, const Logger& logger, keymaster_padding_t padding,
+                 RSA* key)
+        : Operation(purpose, logger), rsa_key_(key), padding_(padding) {}
     ~RsaOperation();
 
     virtual keymaster_error_t Begin() { return KM_ERROR_OK; }
@@ -40,24 +40,43 @@
     keymaster_error_t StoreData(const Buffer& input);
 
     RSA* rsa_key_;
-    keymaster_digest_t digest_;
     keymaster_padding_t padding_;
     Buffer data_;
 };
 
 class RsaSignOperation : public RsaOperation {
   public:
-    RsaSignOperation(keymaster_purpose_t purpose, const Logger& logger, keymaster_digest_t digest,
-                     keymaster_padding_t padding, RSA* key)
-        : RsaOperation(purpose, logger, digest, padding, key) {}
+    RsaSignOperation(const Logger& logger, keymaster_digest_t digest, keymaster_padding_t padding,
+                     RSA* key)
+        : RsaOperation(KM_PURPOSE_SIGN, logger, padding, key), digest_(digest) {}
     virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+
+  private:
+    keymaster_digest_t digest_;
 };
 
 class RsaVerifyOperation : public RsaOperation {
   public:
-    RsaVerifyOperation(keymaster_purpose_t purpose, const Logger& logger, keymaster_digest_t digest,
-                       keymaster_padding_t padding, RSA* key)
-        : RsaOperation(purpose, logger, digest, padding, key) {}
+    RsaVerifyOperation(const Logger& logger, keymaster_digest_t digest, keymaster_padding_t padding,
+                       RSA* key)
+        : RsaOperation(KM_PURPOSE_VERIFY, logger, padding, key), digest_(digest) {}
+    virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+
+  private:
+    keymaster_digest_t digest_;
+};
+
+class RsaEncryptOperation : public RsaOperation {
+  public:
+    RsaEncryptOperation(const Logger& logger, keymaster_padding_t padding, RSA* key)
+        : RsaOperation(KM_PURPOSE_ENCRYPT, logger, padding, key) {}
+    virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+class RsaDecryptOperation : public RsaOperation {
+  public:
+    RsaDecryptOperation(const Logger& logger, keymaster_padding_t padding, RSA* key)
+        : RsaOperation(KM_PURPOSE_DECRYPT, logger, padding, key) {}
     virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
 };