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
diff --git a/aes_operation.h b/aes_operation.h
index 0ceb631..aaa53a4 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -17,6 +17,8 @@
#ifndef SYSTEM_KEYMASTER_AES_OPERATION_H_
#define SYSTEM_KEYMASTER_AES_OPERATION_H_
+#include <openssl/evp.h>
+
#include "aead_mode_operation.h"
#include "ocb_utils.h"
#include "operation.h"
@@ -26,8 +28,6 @@
class AesOcbOperation : public AeadModeOperation {
public:
static const size_t NONCE_LENGTH = 12;
- static const size_t MAX_TAG_LENGTH = 16;
- static const size_t MAX_KEY_LENGTH = 32;
AesOcbOperation(keymaster_purpose_t purpose, const uint8_t* key, size_t key_size,
size_t chunk_length, size_t tag_length, keymaster_blob_t additional_data)
@@ -56,6 +56,53 @@
AeCtx ctx_;
};
+class AesEvpOperation : public Operation {
+ public:
+ AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, const uint8_t* key, size_t key_size);
+ ~AesEvpOperation();
+
+ virtual keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params);
+ virtual keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ Buffer* output, size_t* input_consumed);
+ virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
+ const Buffer& /* signature */, Buffer* output);
+ virtual keymaster_error_t Abort();
+
+ virtual int evp_encrypt_mode() = 0;
+
+ private:
+ keymaster_error_t InitializeCipher();
+ bool need_iv() const;
+
+ EVP_CIPHER_CTX ctx_;
+ const size_t key_size_;
+ const keymaster_block_mode_t block_mode_;
+ const keymaster_padding_t padding_;
+ bool cipher_initialized_;
+ uint8_t iv_buffered_;
+ uint8_t iv_[AES_BLOCK_SIZE];
+ uint8_t key_[SymmetricKey::MAX_KEY_SIZE];
+};
+
+class AesEvpEncryptOperation : public AesEvpOperation {
+ public:
+ AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+ const uint8_t* key, size_t key_size)
+ : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, key, key_size) {}
+ int evp_encrypt_mode() { return 1; }
+};
+
+class AesEvpDecryptOperation : public AesEvpOperation {
+ public:
+ AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+ const uint8_t* key, size_t key_size)
+ : AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding, key, key_size) {}
+
+ int evp_encrypt_mode() { return 0; }
+};
+
} // namespace keymaster
#endif // SYSTEM_KEYMASTER_AES_OPERATION_H_
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 96081ea..b0c47d8 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -35,6 +35,7 @@
using std::vector;
int main(int argc, char** argv) {
+ ERR_load_crypto_strings();
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
// Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
@@ -425,7 +426,8 @@
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));
+ EXPECT_TRUE(ResponseContains({KM_MODE_OCB, KM_MODE_ECB, KM_MODE_CBC, KM_MODE_OFB, KM_MODE_CFB},
+ modes, len));
free(modes);
}
@@ -1456,14 +1458,10 @@
EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, BeginOperation(KM_PURPOSE_ENCRYPT));
}
-TEST_F(EncryptionOperationsTest, AesEcbUnsupported) {
- GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE, KM_MODE_ECB));
- EXPECT_EQ(KM_ERROR_UNSUPPORTED_BLOCK_MODE, BeginOperation(KM_PURPOSE_ENCRYPT));
-}
-
TEST_F(EncryptionOperationsTest, AesOcbPaddingUnsupported) {
- GenerateKey(
- ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 16).Option(TAG_PADDING, KM_PAD_ZERO));
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 16).Option(
+ TAG_PADDING, KM_PAD_ZERO)));
EXPECT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_ENCRYPT));
}
@@ -1472,5 +1470,194 @@
EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, BeginOperation(KM_PURPOSE_ENCRYPT));
}
+TEST_F(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_ECB)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // ECB is deterministic.
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_ECB)));
+ // Message is slightly shorter than two blocks.
+ string message = "1234567890123456789012345678901";
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &ciphertext, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&ciphertext));
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ string message = "a";
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(16, ciphertext.size());
+ EXPECT_NE(ciphertext, message);
+ ++ciphertext[ciphertext.size() / 2];
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&plaintext));
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size() + 16, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size() + 16, ciphertext2.size());
+
+ // CBC uses random IVs, so ciphertexts shouldn't match.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcIncrementalNoPadding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+
+ int increment = 15;
+ string message(240, 'a');
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size() + 16, ciphertext.size());
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size() - 16, plaintext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCbcPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 32 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CFB)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message);
+ EXPECT_EQ(message.size() + 16, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message));
+ EXPECT_EQ(message.size() + 16, ciphertext2.size());
+
+ // CBC uses random IVs, so ciphertexts shouldn't match.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1);
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbIncrementalNoPadding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .Option(TAG_BLOCK_MODE, KM_MODE_CFB)
+ .Option(TAG_PADDING, KM_PAD_PKCS7)));
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CBC)));
+
+ int increment = 15;
+ string message(240, 'a');
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT));
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size() + 16, ciphertext.size());
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size() - 16, plaintext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, AesCfbPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder().AesEncryptionKey(128).Option(TAG_BLOCK_MODE,
+ KM_MODE_CFB)));
+
+ // Try various message lengths; all should work.
+ for (int i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message);
+ EXPECT_EQ(i + 16, ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
} // namespace test
} // namespace keymaster