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