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