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/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) {