Add AES-GCM mode.
Cherry-picked from internal.
Bug: 19919114
Change-Id: I9c82fb50d2372b2ef7a84d74f9f4868994d80869
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 73549a8..7c62d74 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -30,6 +30,10 @@
namespace keymaster {
+static const size_t GCM_NONCE_SIZE = 12;
+static const size_t GCM_MAX_TAG_LENGTH = 16;
+static const size_t GCM_MIN_TAG_LENGTH = 12;
+
Operation* AesOperationFactory::CreateOperation(const Key& key,
const AuthorizationSet& begin_params,
keymaster_error_t* error) {
@@ -50,30 +54,38 @@
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 (block_mode == KM_MODE_CTR && padding != KM_PAD_NONE) {
+ LOG_E("CTR 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 +93,17 @@
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 +120,7 @@
}
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 +138,8 @@
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), aad_provided_(false),
+ key_size_(key_size), padding_(padding) {
memcpy(key_, key, key_size_);
EVP_CIPHER_CTX_init(&ctx_);
}
@@ -197,14 +196,26 @@
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 +236,7 @@
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 +247,41 @@
}
}
-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;
-
- case KM_PURPOSE_DECRYPT:
- error = GetIv(input_params);
- break;
- default:
- return KM_ERROR_UNSUPPORTED_PURPOSE;
- }
+ keymaster_error_t error = GetIv(input_params);
+ if (error != KM_ERROR_OK)
+ return error;
}
- if (error == KM_ERROR_OK)
- error = InitializeCipher();
+ return InitializeCipher();
+}
- return error;
+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);
+ else
+ error = KM_ERROR_CALLER_NONCE_PROHIBITED;
+
+ if (error == KM_ERROR_OK)
+ output_params->push_back(TAG_NONCE, iv_.get(), iv_length_);
+ else
+ return error;
+ }
+
+ return InitializeCipher();
}
keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
@@ -275,7 +290,13 @@
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) {
+ if (iv_blob.data_length != GCM_NONCE_SIZE) {
+ LOG_E("Expected %d-byte nonce for AES-GCM operation, but got %d bytes", GCM_NONCE_SIZE,
+ iv_blob.data_length);
+ return KM_ERROR_INVALID_NONCE;
+ }
+ } else if (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 +304,16 @@
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_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 +324,22 @@
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) {
+ if (!aad_provided_ && block_mode_ == KM_MODE_GCM) {
+ keymaster_blob_t aad;
+ if (additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
+ // 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();
+ }
+ aad_provided_ = true;
+ }
+
output->reserve(input.available_read() + AES_BLOCK_SIZE);
const uint8_t* input_pos = input.peek_read();
@@ -322,6 +357,33 @@
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 +391,8 @@
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 +402,23 @@
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..dc902a7 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -34,39 +34,51 @@
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;
- EVP_CIPHER_CTX ctx_;
- const size_t key_size_;
const keymaster_block_mode_t block_mode_;
- const keymaster_padding_t padding_;
- const bool caller_iv_;
+ EVP_CIPHER_CTX ctx_;
UniquePtr<uint8_t[]> iv_;
+ size_t iv_length_;
+ const bool caller_iv_;
+
+ private:
+ bool aad_provided_;
+ const size_t key_size_;
+ const keymaster_padding_t padding_;
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 @@
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) {}
- int evp_encrypt_mode() { return 0; }
+ 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() override { return 0; }
+
+ private:
+ bool tag_provided_;
};
/**
@@ -98,7 +120,7 @@
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..4b86a2d 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -138,7 +138,7 @@
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,268 @@
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, 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));
+
+ // Try with wrong-length nonce. Should fail immediately.
+ AuthorizationSet long_nonce(begin_params);
+ long_nonce.push_back(TAG_NONCE, "1234567890123", 13);
+ EXPECT_EQ(KM_ERROR_INVALID_NONCE,
+ BeginOperation(KM_PURPOSE_DECRYPT, long_nonce, &begin_out_params));
+ AuthorizationSet short_nonce(begin_params);
+ short_nonce.push_back(TAG_NONCE, "12345678901", 11);
+ EXPECT_EQ(KM_ERROR_INVALID_NONCE,
+ BeginOperation(KM_PURPOSE_DECRYPT, short_nonce, &begin_out_params));
+
+ // Now with correct length, but incorrect content.
+ 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 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 @@
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(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 @@
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& 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 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 @@
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();