Refactor operation creation to use an operation factory registry.

Also modify GoogleKeymaster to query the operation factories to get
lists of supported modes and digests.

Change-Id: Ied30185df5dddaeaeb1106df63237757896d77db
diff --git a/Android.mk b/Android.mk
index 7f8b663..1dc16e3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -62,6 +62,7 @@
 		key.cpp \
 		key_blob.cpp \
 		ocb.c \
+		operation.cpp \
 		rsa_key.cpp \
 		rsa_operation.cpp \
 		serializable.cpp \
diff --git a/Makefile b/Makefile
index 97faa61..a4cf3f8 100644
--- a/Makefile
+++ b/Makefile
@@ -148,6 +148,7 @@
 	key.o \
 	key_blob.o \
 	ocb.o \
+	operation.o \
 	rsa_key.o \
 	rsa_operation.o \
 	serializable.o \
diff --git a/abstract_factory_registry.h b/abstract_factory_registry.h
index 719e5f0..954bc7d 100644
--- a/abstract_factory_registry.h
+++ b/abstract_factory_registry.h
@@ -45,9 +45,9 @@
  *     d.  Factory methods (likely all pure virtual).
  *
  * 2.  Create one or more concrete subclasses of AbstractFactory.  The concrete factories must have
- *     failure-proof no-argument constructors.  Note that by design it is impossible to register two
- *     factories which return the same value from registry_key().  Attempting to do so will cause
- *     both to be removed.
+ *     failure-proof, no-argument constructors.  Note that by design it is impossible to register
+ *     two factories which return the same value from registry_key().  Attempting to do so will
+ *     cause both to be removed.
  *
  * 3.  Define the registry instance pointer using the DEFINE_ABSTRACT_FACTORY_REGISTRY_INSTANCE
  *     macro.
diff --git a/aead_mode_operation.h b/aead_mode_operation.h
index 40196f7..cb976a2 100644
--- a/aead_mode_operation.h
+++ b/aead_mode_operation.h
@@ -23,6 +23,7 @@
 
 class AeadModeOperation : public Operation {
   public:
+    static const size_t MAX_CHUNK_LENGTH = 64 * 1024;
     static const size_t MAX_NONCE_LENGTH = 12;
     static const size_t MAX_TAG_LENGTH = 16;
     static const size_t MAX_KEY_LENGTH = 32;
diff --git a/aes_key.cpp b/aes_key.cpp
index e692711..87a63a7 100644
--- a/aes_key.cpp
+++ b/aes_key.cpp
@@ -39,68 +39,6 @@
         return new AesKey(auths, logger);
     }
 };
-
 static KeyFactoryRegistry::Registration<AesKeyFactory> registration;
 
-Operation* AesKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    keymaster_block_mode_t block_mode;
-    if (!authorizations().GetTagValue(TAG_BLOCK_MODE, &block_mode)) {
-        *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
-        return NULL;
-    }
-
-    switch (block_mode) {
-    case KM_MODE_OCB:
-        return CreateOcbOperation(purpose, error);
-    default:
-        *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
-        return NULL;
-    }
-}
-
-Operation* AesKey::CreateOcbOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    *error = KM_ERROR_OK;
-
-    if (key_data_size() != 16 && key_data_size() != 24 && key_data_size() != 32)
-        *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
-
-    uint32_t chunk_length;
-    if (!authorizations().GetTagValue(TAG_CHUNK_LENGTH, &chunk_length))
-        // TODO(swillden): Create and use a better return code.
-        *error = KM_ERROR_INVALID_ARGUMENT;
-
-    uint32_t tag_length;
-    if (!authorizations().GetTagValue(TAG_MAC_LENGTH, &tag_length))
-        // TODO(swillden): Create and use a better return code.
-        *error = KM_ERROR_INVALID_ARGUMENT;
-
-    keymaster_padding_t padding;
-    if (authorizations().GetTagValue(TAG_PADDING, &padding) && padding != KM_PAD_NONE) {
-        *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
-    }
-
-    if (*error != KM_ERROR_OK)
-        return NULL;
-
-    keymaster_blob_t additional_data = {0, 0};
-    authorizations().GetTagValue(TAG_ASSOCIATED_DATA, &additional_data);
-
-    UniquePtr<Operation> op;
-    switch (purpose) {
-    case KM_PURPOSE_ENCRYPT:
-    case KM_PURPOSE_DECRYPT:
-        op.reset(new AesOcbOperation(purpose, logger_, key_data(), key_data_size(), chunk_length,
-                                     tag_length, additional_data));
-        break;
-    default:
-        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
-        return NULL;
-    }
-
-    if (!op.get())
-        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-    return op.release();
-}
-
 }  // namespace keymaster
diff --git a/aes_key.h b/aes_key.h
index af80e12..bd6d905 100644
--- a/aes_key.h
+++ b/aes_key.h
@@ -28,12 +28,6 @@
     AesKey(const AuthorizationSet& auths, const Logger& logger) : SymmetricKey(auths, logger) {}
     AesKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error)
         : SymmetricKey(blob, logger, error) {}
-
-
-    virtual Operation* CreateOperation(keymaster_purpose_t, keymaster_error_t* error);
-
-  private:
-    Operation* CreateOcbOperation(keymaster_purpose_t, keymaster_error_t* error);
 };
 
 }  // namespace keymaster
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 0c7a86c..0b17249 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -19,13 +19,111 @@
 #include <openssl/aes.h>
 #include <openssl/rand.h>
 
+#include "aes_key.h"
 #include "aes_operation.h"
 
 namespace keymaster {
 
+/**
+ * Abstract base for AES OCB mode operation factories.  This class does all of the work to create
+ * OCB mode operations.
+ */
+class AesOcbOperationFactory : public OperationFactory {
+  public:
+    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_AES, purpose()); }
+
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error);
+    virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const;
+
+    virtual keymaster_purpose_t purpose() const = 0;
+};
+
+Operation* AesOcbOperationFactory::CreateOperation(const Key& key, const Logger& logger,
+                                                   keymaster_error_t* error) {
+    *error = KM_ERROR_OK;
+
+    keymaster_block_mode_t block_mode;
+    if (!key.authorizations().GetTagValue(TAG_BLOCK_MODE, &block_mode) ||
+        block_mode != KM_MODE_OCB) {
+        *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
+        return NULL;
+    }
+
+    uint32_t chunk_length;
+    if (!key.authorizations().GetTagValue(TAG_CHUNK_LENGTH, &chunk_length) ||
+        chunk_length > AeadModeOperation::MAX_CHUNK_LENGTH)
+        // TODO(swillden): Create and use a better return code.
+        *error = KM_ERROR_INVALID_ARGUMENT;
+
+    uint32_t tag_length;
+    if (!key.authorizations().GetTagValue(TAG_MAC_LENGTH, &tag_length) ||
+        tag_length > AeadModeOperation::MAX_TAG_LENGTH)
+        // TODO(swillden): Create and use a better return code.
+        *error = KM_ERROR_INVALID_ARGUMENT;
+
+    keymaster_padding_t padding;
+    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);
+
+    Operation* op = new AesOcbOperation(purpose(), logger, symmetric_key->key_data(),
+                                        symmetric_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};
+
+const keymaster_block_mode_t*
+AesOcbOperationFactory::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;
+
+/**
+ * Concrete factory for AES OCB mode decryption operations.
+ */
+class AesOcbDecryptionOperationFactory : public AesOcbOperationFactory {
+    keymaster_purpose_t purpose() const { return KM_PURPOSE_DECRYPT; }
+};
+static OperationFactoryRegistry::Registration<AesOcbDecryptionOperationFactory>
+    decrypt_registration;
+
 keymaster_error_t AesOcbOperation::Initialize(uint8_t* key, size_t key_size, size_t nonce_length,
                                               size_t tag_length) {
-    if (tag_length > MAX_TAG_LENGTH ||nonce_length > MAX_NONCE_LENGTH)
+    if (tag_length > MAX_TAG_LENGTH || nonce_length > MAX_NONCE_LENGTH)
         return KM_ERROR_INVALID_KEY_BLOB;
 
     if (ae_init(ctx(), key, key_size, nonce_length, tag_length) != AE_SUCCESS) {
diff --git a/ecdsa_key.cpp b/ecdsa_key.cpp
index b499fdb..448374f 100644
--- a/ecdsa_key.cpp
+++ b/ecdsa_key.cpp
@@ -191,23 +191,6 @@
         *error = LoadKey(blob);
 }
 
-Operation* EcdsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    Operation* op;
-    switch (purpose) {
-    case KM_PURPOSE_SIGN:
-        op = new EcdsaSignOperation(purpose, logger_, ecdsa_key_.release());
-        break;
-    case KM_PURPOSE_VERIFY:
-        op = new EcdsaVerifyOperation(purpose, logger_, ecdsa_key_.release());
-        break;
-    default:
-        *error = KM_ERROR_UNIMPLEMENTED;
-        return NULL;
-    }
-    *error = op ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    return op;
-}
-
 bool EcdsaKey::EvpToInternal(const EVP_PKEY* pkey) {
     ecdsa_key_.reset(EVP_PKEY_get1_EC_KEY(const_cast<EVP_PKEY*>(pkey)));
     return ecdsa_key_.get() != NULL;
diff --git a/ecdsa_key.h b/ecdsa_key.h
index 19df1ed..3b7b563 100644
--- a/ecdsa_key.h
+++ b/ecdsa_key.h
@@ -24,13 +24,14 @@
 namespace keymaster {
 
 class EcdsaKeyFactory;
+class EcdsaSignOperationFactory;
+class EcdsaVerifyOperationFactory;
 
 class EcdsaKey : public AsymmetricKey {
-  public:
-    virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error);
-
   private:
     friend EcdsaKeyFactory;
+    friend EcdsaSignOperationFactory;
+    friend EcdsaVerifyOperationFactory;
 
     EcdsaKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error);
     EcdsaKey(EC_KEY* ecdsa_key, const AuthorizationSet auths, const Logger& logger)
@@ -44,6 +45,8 @@
         void operator()(EC_KEY* p) { EC_KEY_free(p); }
     };
 
+    EC_KEY* key() const { return EC_KEY_dup(ecdsa_key_.get()); }
+
     UniquePtr<EC_KEY, ECDSA_Delete> ecdsa_key_;
 };
 
diff --git a/ecdsa_operation.cpp b/ecdsa_operation.cpp
index a2dbc4d..3bc0d88 100644
--- a/ecdsa_operation.cpp
+++ b/ecdsa_operation.cpp
@@ -16,11 +16,68 @@
 
 #include <openssl/ecdsa.h>
 
+#include "ecdsa_key.h"
 #include "ecdsa_operation.h"
 #include "openssl_utils.h"
 
 namespace keymaster {
 
+class EcdsaSignOperationFactory : public OperationFactory {
+  public:
+    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_ECDSA, KM_PURPOSE_SIGN); }
+
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error){
+        const EcdsaKey* ecdsa_key = static_cast<const EcdsaKey*>(&key);
+        if (!ecdsa_key) {
+            *error = KM_ERROR_UNKNOWN_ERROR;
+            return NULL;
+        }
+
+        Operation* op = new EcdsaSignOperation(KM_PURPOSE_SIGN, logger, ecdsa_key->key());
+        if (!op)
+            *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return op;
+    }
+};
+static OperationFactoryRegistry::Registration<EcdsaSignOperationFactory> sign_registration;
+
+class EcdsaVerifyOperationFactory : public OperationFactory {
+  public:
+    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_ECDSA, KM_PURPOSE_VERIFY); }
+
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error){
+        const EcdsaKey* ecdsa_key = static_cast<const EcdsaKey*>(&key);
+        if (!ecdsa_key) {
+            *error = KM_ERROR_UNKNOWN_ERROR;
+            return NULL;
+        }
+
+        Operation* op = new EcdsaVerifyOperation(KM_PURPOSE_VERIFY, logger, ecdsa_key->key());
+        if (!op)
+            *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return op;
+    }
+
+    // Informational methods
+    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const {
+        *padding_count = 0;
+        return NULL;
+    }
+
+    virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const {
+        *block_mode_count = 0;
+        return NULL;
+    }
+
+    virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const {
+        *digest_count = 0;
+        return NULL;
+    }
+};
+static OperationFactoryRegistry::Registration<EcdsaVerifyOperationFactory> verify_registration;
+
 EcdsaOperation::~EcdsaOperation() {
     if (ecdsa_key_ != NULL)
         EC_KEY_free(ecdsa_key_);
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
index 2024c90..cda181d 100644
--- a/google_keymaster.cpp
+++ b/google_keymaster.cpp
@@ -108,66 +108,54 @@
     response->error = KM_ERROR_OK;
 }
 
-void GoogleKeymaster::SupportedBlockModes(
-    keymaster_algorithm_t algorithm, keymaster_purpose_t /* purpose */,
-    SupportedResponse<keymaster_block_mode_t>* response) const {
-    if (response == NULL || !check_supported(algorithm, response))
-        return;
-    response->error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
+static OperationFactory* GetOperationFactory(keymaster_algorithm_t algorithm,
+                                             keymaster_purpose_t purpose,
+                                             keymaster_error_t* error) {
+    assert(error);
+    if (error)
+        *error = KM_ERROR_OK;
+
+    OperationFactory* factory =
+        OperationFactoryRegistry::Get(OperationFactory::KeyType(algorithm, purpose));
+    if (factory == NULL && error)
+        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
+
+    return factory;
 }
 
-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};
+template <typename T>
+void GetSupported(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose,
+                  const T* (OperationFactory::*get_supported_method)(size_t* count) const,
+                  SupportedResponse<T>* response) {
+    if (response == NULL || !check_supported(algorithm, response))
+        return;
+
+    OperationFactory* factory = GetOperationFactory(algorithm, purpose, &response->error);
+    if (!factory) {
+        response->error = KM_ERROR_UNSUPPORTED_PURPOSE;
+        return;
+    }
+
+    size_t count;
+    const T* supported = (factory->*get_supported_method)(&count);
+    response->SetResults(supported, count);
+}
+
+void GoogleKeymaster::SupportedBlockModes(
+    keymaster_algorithm_t algorithm, keymaster_purpose_t purpose,
+    SupportedResponse<keymaster_block_mode_t>* response) const {
+    GetSupported(algorithm, purpose, &OperationFactory::SupportedBlockModes, response);
+}
 
 void GoogleKeymaster::SupportedPaddingModes(
     keymaster_algorithm_t algorithm, keymaster_purpose_t purpose,
     SupportedResponse<keymaster_padding_t>* response) const {
-    if (response == NULL || !check_supported(algorithm, response))
-        return;
-
-    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);
-        break;
-    default:
-        response->results_length = 0;
-        break;
-    }
+    GetSupported(algorithm, purpose, &OperationFactory::SupportedPaddingModes, response);
 }
 
-keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE};
-void GoogleKeymaster::SupportedDigests(keymaster_algorithm_t algorithm,
-                                       keymaster_purpose_t /* purpose */,
+void GoogleKeymaster::SupportedDigests(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose,
                                        SupportedResponse<keymaster_digest_t>* response) const {
-    if (response == NULL || !check_supported(algorithm, response))
-        return;
-
-    response->error = KM_ERROR_OK;
-    switch (algorithm) {
-    case KM_ALGORITHM_RSA:
-    case KM_ALGORITHM_DSA:
-    case KM_ALGORITHM_ECDSA:
-        response->SetResults(supported_digests);
-        break;
-    default:
-        response->results_length = 0;
-        break;
-    }
+    GetSupported(algorithm, purpose, &OperationFactory::SupportedDigests, response);
 }
 
 void GoogleKeymaster::SupportedImportFormats(
@@ -235,11 +223,20 @@
         return;
     response->op_handle = 0;
 
-    UniquePtr<Key> key(LoadKey(request.key_blob, request.additional_params, &response->error));
+    keymaster_algorithm_t algorithm;
+    UniquePtr<Key> key(
+        LoadKey(request.key_blob, request.additional_params, &algorithm, &response->error));
     if (key.get() == NULL)
         return;
 
-    UniquePtr<Operation> operation(key->CreateOperation(request.purpose, &response->error));
+    OperationFactory::KeyType op_type(algorithm, request.purpose);
+    OperationFactory* factory = OperationFactoryRegistry::Get(op_type);
+    if (!factory) {
+        response->error = KM_ERROR_UNSUPPORTED_PURPOSE;
+        return;
+    }
+
+    UniquePtr<Operation> operation(factory->CreateOperation(*key, logger(), &response->error));
     if (operation.get() == NULL)
         return;
 
@@ -301,8 +298,9 @@
     if (response == NULL)
         return;
 
+    keymaster_algorithm_t algorithm;
     UniquePtr<Key> to_export(
-        LoadKey(request.key_blob, request.additional_params, &response->error));
+        LoadKey(request.key_blob, request.additional_params, &algorithm, &response->error));
     if (to_export.get() == NULL)
         return;
 
@@ -382,13 +380,16 @@
 }
 
 Key* GoogleKeymaster::LoadKey(const keymaster_key_blob_t& key,
-                              const AuthorizationSet& client_params, keymaster_error_t* error) {
+                              const AuthorizationSet& client_params,
+                              keymaster_algorithm_t* algorithm, keymaster_error_t* error) {
     UniquePtr<UnencryptedKeyBlob> blob(LoadKeyBlob(key, client_params, error));
     if (*error != KM_ERROR_OK)
         return NULL;
 
+    *algorithm = blob->algorithm();
+
     KeyFactory* factory = 0;
-    if ((factory = KeyFactoryRegistry::Get(blob->algorithm())))
+    if ((factory = KeyFactoryRegistry::Get(*algorithm)))
         return factory->LoadKey(*blob, logger(), error);
     *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
     return NULL;
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index f16b6f8..9306143 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -44,6 +44,18 @@
     return result;
 }
 
+template <typename T> std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) {
+    os << "{ ";
+    bool first = true;
+    for (T t : vec) {
+        os << (first ? "" : ", ") << t;
+        if (first)
+            first = false;
+    }
+    os << " }";
+    return os;
+}
+
 namespace keymaster {
 namespace test {
 
@@ -279,19 +291,23 @@
     }
 
     void SignMessage(const string& message, string* signature) {
+        SCOPED_TRACE("SignMessage");
         *signature = ProcessMessage(KM_PURPOSE_SIGN, message);
         EXPECT_GT(signature->size(), 0);
     }
 
     void VerifyMessage(const string& message, const string& signature) {
+        SCOPED_TRACE("VerifyMessage");
         ProcessMessage(KM_PURPOSE_VERIFY, message, signature);
     }
 
     string EncryptMessage(const string& message) {
+        SCOPED_TRACE("EncryptMessage");
         return ProcessMessage(KM_PURPOSE_ENCRYPT, message);
     }
 
     string DecryptMessage(const string& ciphertext) {
+        SCOPED_TRACE("DecryptMessage");
         return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext);
     }
 
@@ -392,21 +408,23 @@
 
     size_t len;
     keymaster_block_mode_t* modes;
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_BLOCK_MODE,
-              device()->get_supported_block_modes(device(), KM_ALGORITHM_RSA, KM_PURPOSE_ENCRYPT,
-                                                  &modes, &len));
+    EXPECT_EQ(KM_ERROR_OK, device()->get_supported_block_modes(device(), KM_ALGORITHM_RSA,
+                                                               KM_PURPOSE_ENCRYPT, &modes, &len));
+    EXPECT_EQ(0, len);
+    free(modes);
 
     EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM,
               device()->get_supported_block_modes(device(), KM_ALGORITHM_DSA, KM_PURPOSE_ENCRYPT,
                                                   &modes, &len));
 
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_BLOCK_MODE,
+    EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
               device()->get_supported_block_modes(device(), KM_ALGORITHM_ECDSA, KM_PURPOSE_ENCRYPT,
                                                   &modes, &len));
 
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_BLOCK_MODE,
-              device()->get_supported_block_modes(device(), KM_ALGORITHM_AES, KM_PURPOSE_ENCRYPT,
-                                                  &modes, &len));
+    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));
+    free(modes);
 }
 
 TEST_F(CheckSupported, SupportedPaddingModes) {
@@ -418,7 +436,12 @@
     keymaster_padding_t* modes;
     EXPECT_EQ(KM_ERROR_OK, device()->get_supported_padding_modes(device(), KM_ALGORITHM_RSA,
                                                                  KM_PURPOSE_SIGN, &modes, &len));
-    EXPECT_TRUE(ResponseContains(KM_PAD_NONE, modes, len));
+    EXPECT_TRUE(ResponseContains({KM_PAD_NONE}, modes, len));
+    free(modes);
+
+    EXPECT_EQ(KM_ERROR_OK, device()->get_supported_padding_modes(device(), KM_ALGORITHM_RSA,
+                                                                 KM_PURPOSE_ENCRYPT, &modes, &len));
+    EXPECT_TRUE(ResponseContains({KM_PAD_RSA_OAEP, KM_PAD_RSA_PKCS1_1_5_ENCRYPT}, modes, len));
     free(modes);
 
     EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM,
@@ -427,13 +450,12 @@
 
     EXPECT_EQ(KM_ERROR_OK, device()->get_supported_padding_modes(device(), KM_ALGORITHM_ECDSA,
                                                                  KM_PURPOSE_SIGN, &modes, &len));
-    EXPECT_TRUE(ResponseContains(KM_PAD_NONE, modes, len));
-    free(modes);
-
-    EXPECT_EQ(KM_ERROR_OK, device()->get_supported_padding_modes(device(), KM_ALGORITHM_AES,
-                                                                 KM_PURPOSE_SIGN, &modes, &len));
     EXPECT_EQ(0, len);
     free(modes);
+
+    EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
+              device()->get_supported_padding_modes(device(), KM_ALGORITHM_AES, KM_PURPOSE_SIGN,
+                                                    &modes, &len));
 }
 
 TEST_F(CheckSupported, SupportedDigests) {
@@ -445,7 +467,7 @@
     keymaster_digest_t* digests;
     EXPECT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_RSA,
                                                            KM_PURPOSE_SIGN, &digests, &len));
-    EXPECT_TRUE(ResponseContains(KM_DIGEST_NONE, digests, len));
+    EXPECT_TRUE(ResponseContains({KM_DIGEST_NONE}, digests, len));
     free(digests);
 
     EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM,
@@ -454,12 +476,18 @@
 
     EXPECT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_ECDSA,
                                                            KM_PURPOSE_SIGN, &digests, &len));
-    EXPECT_TRUE(ResponseContains(KM_DIGEST_NONE, digests, len));
+    EXPECT_EQ(0, len);
     free(digests);
 
-    EXPECT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_AES,
+    EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
+              device()->get_supported_digests(device(), KM_ALGORITHM_AES, KM_PURPOSE_SIGN, &digests,
+                                              &len));
+
+    EXPECT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_HMAC,
                                                            KM_PURPOSE_SIGN, &digests, &len));
-    EXPECT_EQ(0, len);
+    EXPECT_TRUE(ResponseContains({KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384,
+                                  KM_DIGEST_SHA_2_512, KM_DIGEST_SHA1},
+                                 digests, len));
     free(digests);
 }
 
@@ -473,19 +501,6 @@
               device()->get_supported_import_formats(device(), KM_ALGORITHM_RSA, &formats, &len));
     EXPECT_TRUE(ResponseContains(KM_KEY_FORMAT_PKCS8, formats, len));
     free(formats);
-
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM,
-              device()->get_supported_import_formats(device(), KM_ALGORITHM_DSA, &formats, &len));
-
-    EXPECT_EQ(KM_ERROR_OK,
-              device()->get_supported_import_formats(device(), KM_ALGORITHM_ECDSA, &formats, &len));
-    EXPECT_TRUE(ResponseContains(KM_KEY_FORMAT_PKCS8, formats, len));
-    free(formats);
-
-    EXPECT_EQ(KM_ERROR_OK,
-              device()->get_supported_import_formats(device(), KM_ALGORITHM_AES, &formats, &len));
-    EXPECT_EQ(0, len);
-    free(formats);
 }
 
 TEST_F(CheckSupported, SupportedExportFormats) {
@@ -1212,8 +1227,8 @@
 }
 
 TEST_F(EncryptionOperationsTest, AesOcbInvalidMacLength) {
-    GenerateKey(ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 17));
-    EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, BeginOperation(KM_PURPOSE_ENCRYPT));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 17)));
+    EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, BeginOperation(KM_PURPOSE_ENCRYPT));
 }
 
 }  // namespace test
diff --git a/hmac_key.cpp b/hmac_key.cpp
index d49f32c..278fa6d 100644
--- a/hmac_key.cpp
+++ b/hmac_key.cpp
@@ -39,36 +39,4 @@
 
 static KeyFactoryRegistry::Registration<HmacKeyFactory> registration;
 
-Operation* HmacKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    *error = KM_ERROR_OK;
-
-    uint32_t tag_length;
-    if (!authorizations().GetTagValue(TAG_MAC_LENGTH, &tag_length))
-        *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
-
-    keymaster_digest_t digest;
-    if (!authorizations().GetTagValue(TAG_DIGEST, &digest))
-        *error = KM_ERROR_UNSUPPORTED_DIGEST;
-
-    if (*error != KM_ERROR_OK)
-        return NULL;
-
-    UniquePtr<Operation> op;
-    switch (purpose) {
-    case KM_PURPOSE_SIGN:
-    case KM_PURPOSE_VERIFY:
-        op.reset(
-            new HmacOperation(purpose, logger_, key_data(), key_data_size(), digest, tag_length));
-        break;
-    default:
-        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
-        return NULL;
-    }
-
-    if (!op.get())
-        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-    return op.release();
-}
-
 }  // namespace keymaster
diff --git a/hmac_key.h b/hmac_key.h
index d60c848..afb877c 100644
--- a/hmac_key.h
+++ b/hmac_key.h
@@ -26,8 +26,6 @@
     HmacKey(const AuthorizationSet& auths, const Logger& logger) : SymmetricKey(auths, logger) {}
     HmacKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error)
         : SymmetricKey(blob, logger, error) {}
-
-    virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error);
 };
 
 }  // namespace keymaster
diff --git a/hmac_operation.cpp b/hmac_operation.cpp
index 74c65b9..8e67624 100644
--- a/hmac_operation.cpp
+++ b/hmac_operation.cpp
@@ -19,6 +19,8 @@
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 
+#include "symmetric_key.h"
+
 #if defined(OPENSSL_IS_BORINGSSL)
 typedef size_t openssl_size_t;
 #else
@@ -27,6 +29,74 @@
 
 namespace keymaster {
 
+/**
+ * Abstract base for HMAC operation factories.  This class does all of the work to create
+ * HMAC operations.
+ */
+class HmacOperationFactory : public OperationFactory {
+  public:
+    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_HMAC, purpose()); }
+
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error);
+
+    virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const;
+
+    virtual keymaster_purpose_t purpose() const = 0;
+};
+
+Operation* HmacOperationFactory::CreateOperation(const Key& key, const Logger& logger,
+                                                 keymaster_error_t* error) {
+    *error = KM_ERROR_OK;
+
+    uint32_t tag_length;
+    if (!key.authorizations().GetTagValue(TAG_MAC_LENGTH, &tag_length))
+        *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+
+    keymaster_digest_t digest;
+    if (!key.authorizations().GetTagValue(TAG_DIGEST, &digest))
+        *error = KM_ERROR_UNSUPPORTED_DIGEST;
+
+    if (*error != KM_ERROR_OK)
+        return NULL;
+
+    const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key);
+    if (!symmetric_key) {
+        *error = KM_ERROR_UNKNOWN_ERROR;
+        return NULL;
+    }
+
+    Operation* op = new HmacOperation(purpose(), logger, symmetric_key->key_data(),
+                                      symmetric_key->key_data_size(), digest, tag_length);
+    if (!op)
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    return op;
+}
+
+static keymaster_digest_t supported_digests[] = {KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224,
+                                                 KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384,
+                                                 KM_DIGEST_SHA_2_512};
+const keymaster_digest_t* HmacOperationFactory::SupportedDigests(size_t* digest_count) const {
+    *digest_count = array_length(supported_digests);
+    return supported_digests;
+}
+
+/**
+ * Concrete factory for creating HMAC signing operations.
+ */
+class HmacSignOperationFactory : public HmacOperationFactory {
+    keymaster_purpose_t purpose() const { return KM_PURPOSE_SIGN; }
+};
+static OperationFactoryRegistry::Registration<HmacSignOperationFactory> sign_registration;
+
+/**
+ * Concrete factory for creating HMAC verification operations.
+ */
+class HmacVerifyOperationFactory : public HmacOperationFactory {
+    keymaster_purpose_t purpose() const { return KM_PURPOSE_VERIFY; }
+};
+static OperationFactoryRegistry::Registration<HmacVerifyOperationFactory> verify_registration;
+
 HmacOperation::HmacOperation(keymaster_purpose_t purpose, const Logger& logger,
                              const uint8_t* key_data, size_t key_data_size,
                              keymaster_digest_t digest, size_t tag_length)
diff --git a/include/keymaster/google_keymaster.h b/include/keymaster/google_keymaster.h
index e340ff5..dfff076 100644
--- a/include/keymaster/google_keymaster.h
+++ b/include/keymaster/google_keymaster.h
@@ -91,7 +91,7 @@
                                    keymaster_key_blob_t* keymaster_blob, AuthorizationSet* enforced,
                                    AuthorizationSet* unenforced);
     Key* LoadKey(const keymaster_key_blob_t& key, const AuthorizationSet& client_params,
-                 keymaster_error_t* error);
+                 keymaster_algorithm_t* algorithm, keymaster_error_t* error);
     UnencryptedKeyBlob* LoadKeyBlob(const keymaster_key_blob_t& key,
                                     const AuthorizationSet& client_params,
                                     keymaster_error_t* error);
diff --git a/key.h b/key.h
index 77a8d96..96200bd 100644
--- a/key.h
+++ b/key.h
@@ -64,7 +64,6 @@
 class Key {
   public:
     virtual ~Key() {}
-    virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) = 0;
 
     /**
      * Return a copy of raw key material, in the key's preferred binary format.
diff --git a/operation.cpp b/operation.cpp
new file mode 100644
index 0000000..7c3e2af
--- /dev/null
+++ b/operation.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 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 "operation.h"
+
+namespace keymaster {
+
+DEFINE_ABSTRACT_FACTORY_REGISTRY_INSTANCE(OperationFactory);
+
+}  // namespace keymaster
diff --git a/operation.h b/operation.h
index 836b7d1..594adb4 100644
--- a/operation.h
+++ b/operation.h
@@ -25,9 +25,53 @@
 #include <hardware/keymaster_defs.h>
 #include <keymaster/logger.h>
 
+#include "abstract_factory_registry.h"
+
 namespace keymaster {
 
 class AuthorizationSet;
+class Key;
+class Operation;
+
+class OperationFactory {
+  public:
+    virtual ~OperationFactory() {}
+
+    // Required for registry
+    struct KeyType {
+        KeyType(keymaster_algorithm_t alg, keymaster_purpose_t purp)
+            : algorithm(alg), purpose(purp) {}
+
+        keymaster_algorithm_t algorithm;
+        keymaster_purpose_t purpose;
+
+        bool operator==(const KeyType& rhs) const {
+            return algorithm == rhs.algorithm && purpose == rhs.purpose;
+        }
+    };
+    virtual KeyType registry_key() const = 0;
+
+    // Factory methods
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error) = 0;
+
+    // Informational methods.  The returned arrays reference static memory and must not be
+    // deallocated or modified.
+    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const {
+        *padding_count = 0;
+        return NULL;
+    }
+    virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const {
+        *block_mode_count = 0;
+        return NULL;
+    }
+    virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const {
+        *digest_count = 0;
+        return NULL;
+    }
+};
+
+typedef AbstractFactoryRegistry<OperationFactory> OperationFactoryRegistry;
 
 /**
  * Abstract base for all cryptographic operations.
diff --git a/rsa_key.cpp b/rsa_key.cpp
index 35fe82e..f060274 100644
--- a/rsa_key.cpp
+++ b/rsa_key.cpp
@@ -155,46 +155,8 @@
         *error = LoadKey(blob);
 }
 
-Operation* RsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
-    *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;
-    }
-
-    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;
-    }
-
-    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;
+RSA* RsaKey::key() const {
+    return rsa_key_.get();
 }
 
 bool RsaKey::EvpToInternal(const EVP_PKEY* pkey) {
diff --git a/rsa_key.h b/rsa_key.h
index e98ece3..b218e89 100644
--- a/rsa_key.h
+++ b/rsa_key.h
@@ -24,13 +24,12 @@
 namespace keymaster {
 
 class RsaKeyFactory;
+class RsaOperationFactory;
 
 class RsaKey : public AsymmetricKey {
-  public:
-    virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error);
-
   private:
     friend class RsaKeyFactory;
+    friend class RsaOperationFactory;
 
     RsaKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error);
     RsaKey(RSA* rsa_key, const AuthorizationSet& auths, const Logger& logger)
@@ -47,7 +46,9 @@
         void operator()(RSA* p) { RSA_free(p); }
     };
 
-    UniquePtr<RSA, RSA_Delete> rsa_key_;
+    RSA* key() const;
+
+    mutable UniquePtr<RSA, RSA_Delete> rsa_key_;
 };
 
 }  // namespace keymaster
diff --git a/rsa_operation.cpp b/rsa_operation.cpp
index 4ca7812..27394a6 100644
--- a/rsa_operation.cpp
+++ b/rsa_operation.cpp
@@ -14,17 +14,212 @@
  * limitations under the License.
  */
 
+#include "rsa_operation.h"
+
 #include <limits.h>
 
 #include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/rsa.h>
 
-#include "rsa_operation.h"
 #include "openssl_utils.h"
+#include "rsa_key.h"
 
 namespace keymaster {
 
+/**
+ * Abstract base for all RSA operation factories.  This class exists mainly to centralize some code
+ * common to all RSA operation factories.
+ */
+class RsaOperationFactory : public OperationFactory {
+  public:
+    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_RSA, purpose()); }
+    virtual keymaster_purpose_t purpose() const = 0;
+
+  protected:
+    bool GetAndValidatePadding(const Key& key, keymaster_padding_t* padding,
+                               keymaster_error_t* error) const;
+    bool GetAndValidateDigest(const Key& key, keymaster_digest_t* digest,
+                              keymaster_error_t* error) const;
+    static RSA* GetRsaKey(const Key& key, keymaster_error_t* error);
+};
+
+bool RsaOperationFactory::GetAndValidatePadding(const Key& key, keymaster_padding_t* padding,
+                                                keymaster_error_t* error) const {
+    *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
+    if (!key.authorizations().GetTagValue(TAG_PADDING, padding))
+        return false;
+
+    size_t padding_count;
+    const keymaster_padding_t* supported_paddings = SupportedPaddingModes(&padding_count);
+    for (size_t i = 0; i < padding_count; ++i) {
+        if (*padding == supported_paddings[i]) {
+            *error = KM_ERROR_OK;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool RsaOperationFactory::GetAndValidateDigest(const Key& key, keymaster_digest_t* digest,
+                                               keymaster_error_t* error) const {
+    *error = KM_ERROR_UNSUPPORTED_DIGEST;
+    if (!key.authorizations().GetTagValue(TAG_DIGEST, digest))
+        return false;
+
+    size_t digest_count;
+    const keymaster_digest_t* supported_digests = SupportedDigests(&digest_count);
+    for (size_t i = 0; i < digest_count; ++i) {
+        if (*digest == supported_digests[i]) {
+            *error = KM_ERROR_OK;
+            return true;
+        }
+    }
+    return false;
+}
+
+/* static */
+RSA* RsaOperationFactory::GetRsaKey(const Key& key, keymaster_error_t* error) {
+    const RsaKey* rsa_key = static_cast<const RsaKey*>(&key);
+    assert(rsa_key);
+    if (!rsa_key || !rsa_key->key()) {
+        *error = KM_ERROR_UNKNOWN_ERROR;
+        return NULL;
+    }
+    return RSAPrivateKey_dup(rsa_key->key());
+}
+
+static const keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE};
+static const keymaster_padding_t supported_sig_padding[] = {KM_PAD_NONE};
+
+/**
+ * Abstract base for RSA operations that digest their input (signing and verification).  This class
+ * does most of the work of creation of RSA digesting operations, delegating only the actual
+ * operation instantiation.
+ */
+class RsaDigestingOperationFactory : public RsaOperationFactory {
+  public:
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error);
+
+    virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const {
+        *digest_count = array_length(supported_digests);
+        return supported_digests;
+    }
+
+    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const {
+        *padding_mode_count = array_length(supported_sig_padding);
+        return supported_sig_padding;
+    }
+
+  private:
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_digest_t digest,
+                                            keymaster_padding_t padding, RSA* key) = 0;
+};
+
+Operation* RsaDigestingOperationFactory::CreateOperation(const Key& key, const Logger& logger,
+                                                         keymaster_error_t* error) {
+    keymaster_padding_t padding;
+    keymaster_digest_t digest;
+    RSA* rsa;
+    if (!GetAndValidateDigest(key, &digest, error) ||
+        !GetAndValidatePadding(key, &padding, error) || !(rsa = GetRsaKey(key, error)))
+        return NULL;
+
+    Operation* op = InstantiateOperation(logger, digest, padding, rsa);
+    if (!op)
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    return op;
+}
+
+static const keymaster_padding_t supported_crypt_padding[] = {KM_PAD_RSA_OAEP,
+                                                              KM_PAD_RSA_PKCS1_1_5_ENCRYPT};
+
+/**
+ * Abstract base for en/de-crypting RSA operation factories.  This class does most of the work of
+ * creating such operations, delegating only the actual operation instantiation.
+ */
+class RsaCryptingOperationFactory : public RsaOperationFactory {
+  public:
+    virtual Operation* CreateOperation(const Key& key, const Logger& logger,
+                                       keymaster_error_t* error);
+
+    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const {
+        *padding_mode_count = array_length(supported_crypt_padding);
+        return supported_crypt_padding;
+    }
+
+    virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const {
+        *digest_count = 0;
+        return NULL;
+    }
+
+  private:
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_padding_t padding,
+                                            RSA* key) = 0;
+};
+
+Operation* RsaCryptingOperationFactory::CreateOperation(const Key& key, const Logger& logger,
+                                                        keymaster_error_t* error) {
+    keymaster_padding_t padding;
+    RSA* rsa;
+    if (!GetAndValidatePadding(key, &padding, error) || !(rsa = GetRsaKey(key, error)))
+        return NULL;
+
+    Operation* op = InstantiateOperation(logger, padding, rsa);
+    if (!op)
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    return op;
+}
+
+/**
+ * Concrete factory for RSA signing operations.
+ */
+class RsaSigningOperationFactory : public RsaDigestingOperationFactory {
+  public:
+    virtual keymaster_purpose_t purpose() const { return KM_PURPOSE_SIGN; }
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_digest_t digest,
+                                            keymaster_padding_t padding, RSA* key) {
+        return new RsaSignOperation(logger, digest, padding, key);
+    }
+};
+static OperationFactoryRegistry::Registration<RsaSigningOperationFactory> sign_registration;
+
+/**
+ * Concrete factory for RSA signing operations.
+ */
+class RsaVerificationOperationFactory : public RsaDigestingOperationFactory {
+    virtual keymaster_purpose_t purpose() const { return KM_PURPOSE_VERIFY; }
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_digest_t digest,
+                                            keymaster_padding_t padding, RSA* key) {
+        return new RsaVerifyOperation(logger, digest, padding, key);
+    }
+};
+static OperationFactoryRegistry::Registration<RsaVerificationOperationFactory> verify_registration;
+
+/**
+ * Concrete factory for RSA signing operations.
+ */
+class RsaEncryptionOperationFactory : public RsaCryptingOperationFactory {
+    virtual keymaster_purpose_t purpose() const { return KM_PURPOSE_ENCRYPT; }
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_padding_t padding,
+                                            RSA* key) {
+        return new RsaEncryptOperation(logger, padding, key);
+    }
+};
+static OperationFactoryRegistry::Registration<RsaEncryptionOperationFactory> encrypt_registration;
+
+/**
+ * Concrete factory for RSA signing operations.
+ */
+class RsaDecryptionOperationFactory : public RsaCryptingOperationFactory {
+    virtual keymaster_purpose_t purpose() const { return KM_PURPOSE_DECRYPT; }
+    virtual Operation* InstantiateOperation(const Logger& logger, keymaster_padding_t padding,
+                                            RSA* key) {
+        return new RsaDecryptOperation(logger, padding, key);
+    }
+};
+
+static OperationFactoryRegistry::Registration<RsaDecryptionOperationFactory> decrypt_registration;
+
 struct RSA_Delete {
     void operator()(RSA* p) const { RSA_free(p); }
 };
diff --git a/rsa_operation.h b/rsa_operation.h
index 14a66c1..2c9680f 100644
--- a/rsa_operation.h
+++ b/rsa_operation.h
@@ -19,6 +19,9 @@
 
 #include <UniquePtr.h>
 
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+
 #include <keymaster/key_blob.h>
 
 #include "operation.h"
diff --git a/symmetric_key.h b/symmetric_key.h
index bef921f..48895d8 100644
--- a/symmetric_key.h
+++ b/symmetric_key.h
@@ -61,14 +61,11 @@
         return KM_ERROR_UNIMPLEMENTED;
     }
 
-  protected:
-    keymaster_error_t error_;
-
-    SymmetricKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error);
-
     const uint8_t* key_data() const { return key_data_; }
     size_t key_data_size() const { return key_data_size_; }
 
+  protected:
+    SymmetricKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error);
     SymmetricKey(const AuthorizationSet& auths, const Logger& logger) : Key(auths, logger) {}
 
   private: