summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--aes_operation.cpp196
-rw-r--r--aes_operation.h64
-rw-r--r--android_keymaster_test.cpp312
-rw-r--r--android_keymaster_test_utils.cpp25
-rw-r--r--android_keymaster_test_utils.h11
5 files changed, 518 insertions, 90 deletions
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 73549a8..1dcd3a6 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -30,6 +30,14 @@
namespace keymaster {
+static const size_t GCM_DEFAULT_NONCE_SIZE = 12;
+static const size_t GCM_MAX_TAG_LENGTH = 16;
+static const size_t GCM_MIN_TAG_LENGTH = 12;
+
+inline bool allows_padding(keymaster_block_mode_t block_mode) {
+ return (block_mode == KM_MODE_CTR || block_mode == KM_MODE_GCM);
+}
+
Operation* AesOperationFactory::CreateOperation(const Key& key,
const AuthorizationSet& begin_params,
keymaster_error_t* error) {
@@ -50,30 +58,38 @@ Operation* AesOperationFactory::CreateOperation(const Key& key,
if (!begin_params.GetTagValue(TAG_BLOCK_MODE, &block_mode)) {
LOG_E("%d block modes specified in begin params", begin_params.GetTagCount(TAG_BLOCK_MODE));
*error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
- return nullptr;
} else if (!supported(block_mode)) {
LOG_E("Block mode %d not supported", block_mode);
*error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
- return nullptr;
} else if (!key.authorizations().Contains(TAG_BLOCK_MODE, block_mode)) {
LOG_E("Block mode %d was specified, but not authorized by key", block_mode);
*error = KM_ERROR_INCOMPATIBLE_BLOCK_MODE;
- return nullptr;
+ }
+
+ size_t tag_length = 0;
+ if (block_mode == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
+ uint32_t tag_length_bits;
+ if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits))
+ *error = KM_ERROR_MISSING_MAC_LENGTH;
+ tag_length = tag_length_bits / 8;
+ if (tag_length_bits % 8 != 0 || tag_length > GCM_MAX_TAG_LENGTH ||
+ tag_length < GCM_MIN_TAG_LENGTH)
+ *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
}
keymaster_padding_t padding;
if (!begin_params.GetTagValue(TAG_PADDING, &padding)) {
LOG_E("%d padding modes specified in begin params", begin_params.GetTagCount(TAG_PADDING));
*error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
- return nullptr;
} else if (!supported(padding)) {
LOG_E("Padding mode %d not supported", padding);
*error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
- return nullptr;
+ } else if (allows_padding(block_mode) && padding != KM_PAD_NONE) {
+ LOG_E("Mode does not support padding", 0);
+ *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
} else if (!key.authorizations().Contains(TAG_PADDING, padding)) {
LOG_E("Padding mode %d was specified, but not authorized by key", padding);
*error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
- return nullptr;
}
bool caller_nonce = key.authorizations().GetTagValue(TAG_CALLER_NONCE);
@@ -81,30 +97,17 @@ Operation* AesOperationFactory::CreateOperation(const Key& key,
if (*error != KM_ERROR_OK)
return nullptr;
- switch (block_mode) {
- case KM_MODE_ECB:
- case KM_MODE_CBC:
- return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, error);
- case KM_MODE_CTR:
- if (padding != KM_PAD_NONE) {
- *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
- return nullptr;
- }
- return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, error);
- default:
- *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
- return nullptr;
- }
+ return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, tag_length, error);
}
Operation* AesOperationFactory::CreateEvpOperation(const SymmetricKey& key,
keymaster_block_mode_t block_mode,
keymaster_padding_t padding, bool caller_iv,
- keymaster_error_t* error) {
+ size_t tag_length, keymaster_error_t* error) {
Operation* op = NULL;
switch (purpose()) {
case KM_PURPOSE_ENCRYPT:
- op = new AesEvpEncryptOperation(block_mode, padding, caller_iv, key.key_data(),
+ op = new AesEvpEncryptOperation(block_mode, padding, caller_iv, tag_length, key.key_data(),
key.key_data_size());
break;
case KM_PURPOSE_DECRYPT:
@@ -121,7 +124,7 @@ Operation* AesOperationFactory::CreateEvpOperation(const SymmetricKey& key,
}
static const keymaster_block_mode_t supported_block_modes[] = {KM_MODE_ECB, KM_MODE_CBC,
- KM_MODE_CTR};
+ KM_MODE_CTR, KM_MODE_GCM};
const keymaster_block_mode_t*
AesOperationFactory::SupportedBlockModes(size_t* block_mode_count) const {
@@ -139,8 +142,8 @@ AesOperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const {
AesEvpOperation::AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
keymaster_padding_t padding, bool caller_iv, const uint8_t* key,
size_t key_size)
- : Operation(purpose), key_size_(key_size), block_mode_(block_mode), padding_(padding),
- caller_iv_(caller_iv) {
+ : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), data_started_(false),
+ key_size_(key_size), padding_(padding) {
memcpy(key_, key, key_size_);
EVP_CIPHER_CTX_init(&ctx_);
}
@@ -197,14 +200,26 @@ keymaster_error_t AesEvpOperation::InitializeCipher() {
return KM_ERROR_UNSUPPORTED_KEY_SIZE;
}
break;
+ case KM_MODE_GCM:
+ switch (key_size_) {
+ case 16:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 24:
+ cipher = EVP_aes_192_gcm();
+ break;
+ case 32:
+ cipher = EVP_aes_256_gcm();
+ 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_.get(), evp_encrypt_mode());
-
- if (!init_result)
+ if (!EVP_CipherInit_ex(&ctx_, cipher, NULL /* engine */, key_, iv_.get(), evp_encrypt_mode()))
return TranslateLastOpenSslError();
switch (padding_) {
@@ -225,6 +240,7 @@ bool AesEvpOperation::need_iv() const {
switch (block_mode_) {
case KM_MODE_CBC:
case KM_MODE_CTR:
+ case KM_MODE_GCM:
return true;
case KM_MODE_ECB:
return false;
@@ -235,38 +251,41 @@ bool AesEvpOperation::need_iv() const {
}
}
-keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& input_params,
- AuthorizationSet* output_params) {
+keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) {
if (!output_params)
return KM_ERROR_OUTPUT_PARAMETER_NULL;
- keymaster_error_t error = KM_ERROR_OK;
if (need_iv()) {
- switch (purpose()) {
- case KM_PURPOSE_ENCRYPT:
- if (input_params.find(TAG_NONCE) == -1)
- error = GenerateIv();
- else if (caller_iv_)
- error = GetIv(input_params);
- else
- error = KM_ERROR_CALLER_NONCE_PROHIBITED;
-
- if (error == KM_ERROR_OK)
- output_params->push_back(TAG_NONCE, iv_.get(), AES_BLOCK_SIZE);
- break;
+ keymaster_error_t error = GetIv(input_params);
+ if (error != KM_ERROR_OK)
+ return error;
+ }
- case KM_PURPOSE_DECRYPT:
+ return InitializeCipher();
+}
+
+keymaster_error_t AesEvpEncryptOperation::Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) {
+ if (!output_params)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ if (need_iv()) {
+ keymaster_error_t error = KM_ERROR_OK;
+ if (input_params.find(TAG_NONCE) == -1)
+ error = GenerateIv();
+ else if (caller_iv_)
error = GetIv(input_params);
- break;
- default:
- return KM_ERROR_UNSUPPORTED_PURPOSE;
- }
- }
+ else
+ error = KM_ERROR_CALLER_NONCE_PROHIBITED;
- if (error == KM_ERROR_OK)
- error = InitializeCipher();
+ if (error == KM_ERROR_OK)
+ output_params->push_back(TAG_NONCE, iv_.get(), iv_length_);
+ else
+ return error;
+ }
- return error;
+ return InitializeCipher();
}
keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
@@ -275,7 +294,7 @@ keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
LOG_E("No IV provided", 0);
return KM_ERROR_INVALID_ARGUMENT;
}
- if (iv_blob.data_length != AES_BLOCK_SIZE) {
+ if (block_mode_ != KM_MODE_GCM && iv_blob.data_length != AES_BLOCK_SIZE) {
LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE,
iv_blob.data_length);
return KM_ERROR_INVALID_NONCE;
@@ -283,14 +302,16 @@ keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
iv_.reset(dup_array(iv_blob.data, iv_blob.data_length));
if (!iv_.get())
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ iv_length_ = iv_blob.data_length;
return KM_ERROR_OK;
}
-keymaster_error_t AesEvpOperation::GenerateIv() {
- iv_.reset(new uint8_t[AES_BLOCK_SIZE]);
+keymaster_error_t AesEvpEncryptOperation::GenerateIv() {
+ iv_length_ = (block_mode_ == KM_MODE_GCM) ? GCM_DEFAULT_NONCE_SIZE : AES_BLOCK_SIZE;
+ iv_.reset(new uint8_t[iv_length_]);
if (!iv_.get())
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- if (RAND_bytes(iv_.get(), AES_BLOCK_SIZE) != 1)
+ if (RAND_bytes(iv_.get(), iv_length_) != 1)
return TranslateLastOpenSslError();
return KM_ERROR_OK;
}
@@ -301,10 +322,23 @@ inline size_t min(size_t a, size_t b) {
return b;
}
-keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& /* additional_params */,
+keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params,
const Buffer& input,
AuthorizationSet* /* output_params */, Buffer* output,
size_t* input_consumed) {
+ keymaster_blob_t aad;
+ if (block_mode_ == KM_MODE_GCM && additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
+ if (data_started_)
+ return KM_ERROR_INVALID_TAG;
+ // Incantation to add AAD is to call update with null output. Ugly.
+ int output_written;
+ if (!EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad.data, aad.data_length))
+ return TranslateLastOpenSslError();
+ }
+
+ if (input.available_read() > 0)
+ data_started_ = true;
+
output->reserve(input.available_read() + AES_BLOCK_SIZE);
const uint8_t* input_pos = input.peek_read();
@@ -322,6 +356,33 @@ keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& /* additional_
return KM_ERROR_OK;
}
+keymaster_error_t AesEvpDecryptOperation::Update(const AuthorizationSet& additional_params,
+ const Buffer& input,
+ AuthorizationSet* output_params, Buffer* output,
+ size_t* input_consumed) {
+ if (!tag_provided_ && block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_DECRYPT) {
+ keymaster_blob_t tag;
+ if (additional_params.GetTagValue(TAG_AEAD_TAG, &tag)) {
+ if (tag.data_length < GCM_MIN_TAG_LENGTH || tag.data_length > GCM_MAX_TAG_LENGTH)
+ return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+
+ if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag.data_length,
+ const_cast<uint8_t*>(tag.data)))
+ return TranslateLastOpenSslError();
+ tag_provided_ = true;
+ }
+ if (!tag_provided_)
+ return KM_ERROR_INVALID_TAG;
+ }
+
+ return AesEvpOperation::Update(additional_params, input, output_params, output, input_consumed);
+}
+
+inline bool is_bad_decrypt(unsigned long error) {
+ return (ERR_GET_LIB(error) == ERR_LIB_CIPHER && //
+ ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT);
+}
+
keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
const Buffer& /* signature */,
AuthorizationSet* /* output_params */, Buffer* output) {
@@ -329,6 +390,8 @@ keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_
int output_written = -1;
if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
+ if (block_mode_ == KM_MODE_GCM && is_bad_decrypt(ERR_peek_last_error()))
+ return KM_ERROR_VERIFICATION_FAILED;
LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
return TranslateLastOpenSslError();
}
@@ -338,6 +401,23 @@ keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_
return KM_ERROR_OK;
}
+keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params,
+ const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) {
+ keymaster_error_t error =
+ AesEvpOperation::Finish(additional_params, signature, output_params, output);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ if (block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
+ uint8_t tag[tag_length_];
+ if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, tag))
+ return TranslateLastOpenSslError();
+ output_params->push_back(TAG_AEAD_TAG, tag, tag_length_);
+ }
+
+ return KM_ERROR_OK;
+}
keymaster_error_t AesEvpOperation::Abort() {
return KM_ERROR_OK;
}
diff --git a/aes_operation.h b/aes_operation.h
index 726a599..760e0cf 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -34,39 +34,51 @@ class AesEvpOperation : public Operation {
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,
- AuthorizationSet* output_params, Buffer* output,
- size_t* input_consumed);
- virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
- const Buffer& signature, AuthorizationSet* output_params,
- Buffer* output);
- virtual keymaster_error_t Abort();
+ keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ AuthorizationSet* output_params, Buffer* output,
+ size_t* input_consumed) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) override;
+ keymaster_error_t Abort() override;
virtual int evp_encrypt_mode() = 0;
- private:
+ protected:
+ bool need_iv() const;
keymaster_error_t InitializeCipher();
keymaster_error_t GetIv(const AuthorizationSet& input_params);
- keymaster_error_t GenerateIv();
- bool need_iv() const;
+ const keymaster_block_mode_t block_mode_;
EVP_CIPHER_CTX ctx_;
+ UniquePtr<uint8_t[]> iv_;
+ size_t iv_length_;
+ const bool caller_iv_;
+
+ private:
+ bool data_started_;
const size_t key_size_;
- const keymaster_block_mode_t block_mode_;
const keymaster_padding_t padding_;
- const bool caller_iv_;
- UniquePtr<uint8_t[]> iv_;
uint8_t key_[MAX_EVP_KEY_SIZE];
};
class AesEvpEncryptOperation : public AesEvpOperation {
public:
AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
- bool caller_iv, const uint8_t* key, size_t key_size)
- : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, key, key_size) {}
- int evp_encrypt_mode() { return 1; }
+ bool caller_iv, size_t tag_length, const uint8_t* key, size_t key_size)
+ : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, key, key_size),
+ tag_length_(tag_length) {}
+
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) override;
+
+ int evp_encrypt_mode() override { return 1; }
+
+ private:
+ keymaster_error_t GenerateIv();
+
+ size_t tag_length_;
};
class AesEvpDecryptOperation : public AesEvpOperation {
@@ -74,9 +86,19 @@ class AesEvpDecryptOperation : public AesEvpOperation {
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,
- false /* caller_iv -- don't care */, key, key_size) {}
+ false /* caller_iv -- don't care */, key, key_size),
+ tag_provided_(false) {}
+
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
+ keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ AuthorizationSet* output_params, Buffer* output,
+ size_t* input_consumed) override;
- int evp_encrypt_mode() { return 0; }
+ int evp_encrypt_mode() override { return 0; }
+
+ private:
+ bool tag_provided_;
};
/**
@@ -98,7 +120,7 @@ class AesOperationFactory : public OperationFactory {
virtual Operation* CreateEvpOperation(const SymmetricKey& key,
keymaster_block_mode_t block_mode,
keymaster_padding_t padding, bool caller_iv,
- keymaster_error_t* error);
+ size_t tag_length, keymaster_error_t* error);
};
/**
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index 123f24f..ea59a73 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -138,7 +138,7 @@ TEST_P(CheckSupported, SupportedBlockModes) {
ASSERT_EQ(KM_ERROR_OK, device()->get_supported_block_modes(device(), KM_ALGORITHM_AES,
KM_PURPOSE_ENCRYPT, &modes, &len));
- EXPECT_TRUE(ResponseContains({KM_MODE_ECB, KM_MODE_CBC, KM_MODE_CTR}, modes, len));
+ EXPECT_TRUE(ResponseContains({KM_MODE_ECB, KM_MODE_CBC, KM_MODE_CTR, KM_MODE_GCM}, modes, len));
free(modes);
EXPECT_EQ(0, GetParam()->keymaster0_calls());
@@ -2203,6 +2203,316 @@ TEST_P(EncryptionOperationsTest, AesCbcPkcs7Padding) {
EXPECT_EQ(0, GetParam()->keymaster0_calls());
}
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ // Grab nonce & tag.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
+ begin_params.push_back(begin_out_params);
+ update_params.push_back(finish_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&discard));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foo", 3);
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+ // No data, AAD only.
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext,
+ &input_consumed));
+
+ // AAD and data.
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ // Grab nonce & tag.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
+ update_params.push_back(finish_out_params);
+
+ // All of the AAD in one.
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&discard));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "foobar";
+ string message = "12345678901234567890123456789012";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ // Grab nonce & tag.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
+ begin_params.push_back(begin_out_params);
+ update_params.Clear();
+ update_params.push_back(TAG_ASSOCIATED_DATA, "barfoo" /* Wrong AAD */, 6);
+ update_params.push_back(finish_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "foobar";
+ string message = "12345678901234567890123456789012";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
+ update_params.push_back(finish_out_params);
+ begin_params.push_back(TAG_NONCE, "123456789012", 12);
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+
+ // With wrong nonce, should have gotten garbage plaintext.
+ EXPECT_NE(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ // Grab nonce & tag; corrupt tag.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+ keymaster_blob_t tag;
+ EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
+ const_cast<uint8_t*>(tag.data)[tag.data_length / 2]++;
+ update_params.push_back(TAG_AEAD_TAG, tag);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmShortTag) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+ AuthorizationSet update_out_params;
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ string ciphertext;
+ string discard;
+ string plaintext;
+
+ size_t input_consumed;
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+ keymaster_blob_t tag;
+ EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
+ tag.data_length = 11;
+ update_params.push_back(TAG_AEAD_TAG, tag);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ UpdateOperation(update_params, ciphertext, &update_out_params, &plaintext,
+ &input_consumed));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
typedef Keymaster1Test AddEntropyTest;
INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AddEntropyTest, test_params);
diff --git a/android_keymaster_test_utils.cpp b/android_keymaster_test_utils.cpp
index 80f6822..69ca470 100644
--- a/android_keymaster_test_utils.cpp
+++ b/android_keymaster_test_utils.cpp
@@ -249,7 +249,8 @@ keymaster_error_t Keymaster1Test::UpdateOperation(const string& message, string*
}
keymaster_error_t Keymaster1Test::UpdateOperation(const AuthorizationSet& additional_params,
- const string& message, string* output,
+ const string& message,
+ AuthorizationSet* output_params, string* output,
size_t* input_consumed) {
EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL);
keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()};
@@ -260,6 +261,9 @@ keymaster_error_t Keymaster1Test::UpdateOperation(const AuthorizationSet& additi
if (error == KM_ERROR_OK && out_tmp.data)
output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
free((void*)out_tmp.data);
+ if (output_params)
+ output_params->Reinitialize(out_params);
+ keymaster_free_param_set(&out_params);
return error;
}
@@ -269,11 +273,13 @@ keymaster_error_t Keymaster1Test::FinishOperation(string* output) {
keymaster_error_t Keymaster1Test::FinishOperation(const string& signature, string* output) {
AuthorizationSet additional_params;
- return FinishOperation(additional_params, signature, output);
+ AuthorizationSet output_params;
+ return FinishOperation(additional_params, signature, &output_params, output);
}
keymaster_error_t Keymaster1Test::FinishOperation(const AuthorizationSet& additional_params,
- const string& signature, string* output) {
+ const string& signature,
+ AuthorizationSet* output_params, string* output) {
keymaster_blob_t sig = {reinterpret_cast<const uint8_t*>(signature.c_str()),
signature.length()};
keymaster_blob_t out_tmp;
@@ -289,6 +295,9 @@ keymaster_error_t Keymaster1Test::FinishOperation(const AuthorizationSet& additi
if (out_tmp.data)
output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
free((void*)out_tmp.data);
+ if (output_params)
+ output_params->Reinitialize(out_params);
+ keymaster_free_param_set(&out_params);
return error;
}
@@ -310,12 +319,13 @@ string Keymaster1Test::ProcessMessage(keymaster_purpose_t purpose, const string&
string Keymaster1Test::ProcessMessage(keymaster_purpose_t purpose, const string& message,
const AuthorizationSet& begin_params,
const AuthorizationSet& update_params,
- AuthorizationSet* output_params) {
- EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, output_params));
+ AuthorizationSet* begin_out_params) {
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, begin_out_params));
string result;
size_t input_consumed;
- EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+ &result, &input_consumed));
EXPECT_EQ(message.size(), input_consumed);
EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, "", &result));
return result;
@@ -329,7 +339,8 @@ string Keymaster1Test::ProcessMessage(keymaster_purpose_t purpose, const string&
string result;
size_t input_consumed;
- EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+ &result, &input_consumed));
EXPECT_EQ(message.size(), input_consumed);
EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, signature, &result));
return result;
diff --git a/android_keymaster_test_utils.h b/android_keymaster_test_utils.h
index ede14f5..cefd3d3 100644
--- a/android_keymaster_test_utils.h
+++ b/android_keymaster_test_utils.h
@@ -188,13 +188,18 @@ class Keymaster1Test : public testing::TestWithParam<InstanceCreatorPtr> {
keymaster_error_t UpdateOperation(const std::string& message, std::string* output,
size_t* input_consumed);
keymaster_error_t UpdateOperation(const AuthorizationSet& additional_params,
- const std::string& message, std::string* output,
- size_t* input_consumed);
+ const std::string& message, AuthorizationSet* output_params,
+ std::string* output, size_t* input_consumed);
keymaster_error_t FinishOperation(std::string* output);
keymaster_error_t FinishOperation(const std::string& signature, std::string* output);
keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
- const std::string& signature, std::string* output);
+ const std::string& signature, std::string* output) {
+ return FinishOperation(additional_params, signature, nullptr /* output_params */, output);
+ }
+ keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
+ const std::string& signature, AuthorizationSet* output_params,
+ std::string* output);
keymaster_error_t AbortOperation();