Add RSA encryption and decryption support.
Change-Id: Iceefe0933c80a2169f87fbc01a6fa0fce9644649
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 9924a56..837e2ce 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -1078,5 +1078,208 @@
ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH, import_response.error);
}
+/**
+ * Test class that provides some infrastructure for generating keys and encrypting messages.
+ */
+class EncryptionOperationsTest : public KeymasterTest {
+ protected:
+ void GenerateKey(keymaster_algorithm_t algorithm, keymaster_padding_t padding,
+ uint32_t key_size) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT), Authorization(TAG_ALGORITHM, algorithm),
+ Authorization(TAG_KEY_SIZE, key_size), Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8), Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ GenerateKeyRequest generate_request;
+ generate_request.key_description.Reinitialize(params, array_length(params));
+ if (static_cast<int>(padding) != -1)
+ generate_request.key_description.push_back(TAG_PADDING, padding);
+ device.GenerateKey(generate_request, &generate_response_);
+ EXPECT_EQ(KM_ERROR_OK, generate_response_.error);
+ }
+
+ keymaster_error_t BeginOperation(keymaster_purpose_t purpose,
+ const keymaster_key_blob_t& key_blob, uint64_t* op_handle) {
+ BeginOperationRequest begin_request;
+ begin_request.SetKeyMaterial(key_blob);
+ begin_request.purpose = purpose;
+ AddClientParams(&begin_request.additional_params);
+
+ BeginOperationResponse begin_response;
+ device.BeginOperation(begin_request, &begin_response);
+ *op_handle = begin_response.op_handle;
+ return begin_response.error;
+ }
+
+ keymaster_error_t UpdateOperation(uint64_t op_handle, const void* message, size_t size,
+ string* output) {
+ UpdateOperationRequest update_request;
+ update_request.op_handle = op_handle;
+ update_request.input.Reinitialize(message, size);
+
+ UpdateOperationResponse update_response;
+ device.UpdateOperation(update_request, &update_response);
+ if (update_response.error == KM_ERROR_OK)
+ output->append(reinterpret_cast<const char*>(update_response.output.peek_read()),
+ update_response.output.available_read());
+ return update_response.error;
+ }
+
+ keymaster_error_t FinishOperation(uint64_t op_handle, string* output) {
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = op_handle;
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ if (finish_response.error == KM_ERROR_OK)
+ output->append(reinterpret_cast<const char*>(finish_response.output.peek_read()),
+ finish_response.output.available_read());
+ return finish_response.error;
+ }
+
+ string ProcessMessage(keymaster_purpose_t purpose, const keymaster_key_blob_t& key_blob,
+ const void* message, size_t size) {
+ uint64_t op_handle;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, key_blob, &op_handle));
+
+ string result;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, size, &result));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(op_handle, &result));
+ return result;
+ }
+
+ string EncryptMessage(const void* message, size_t size) {
+ return ProcessMessage(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, message, size);
+ }
+
+ string DecryptMessage(const void* ciphertext, size_t size) {
+ return ProcessMessage(KM_PURPOSE_DECRYPT, generate_response_.key_blob, ciphertext, size);
+ }
+
+ void AddClientParams(AuthorizationSet* set) { set->push_back(TAG_APPLICATION_ID, "app_id", 6); }
+
+ const keymaster_key_blob_t& key_blob() { return generate_response_.key_blob; }
+
+ const keymaster_key_blob_t& corrupt_key_blob() {
+ uint8_t* tmp = const_cast<uint8_t*>(generate_response_.key_blob.key_material);
+ ++tmp[generate_response_.key_blob.key_material_size / 2];
+ return generate_response_.key_blob;
+ }
+
+ protected:
+ GenerateKeyResponse generate_response_;
+};
+
+TEST_F(EncryptionOperationsTest, RsaOaepSuccess) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+ const char message[] = "Hello World!";
+ string ciphertext1 = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext2.size());
+
+ // OAEP randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepRoundTrip) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+ const char message[] = "Hello World!";
+ string ciphertext = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext.data(), ciphertext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepTooLarge) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+ const char message[] = "12345678901234567890123";
+ uint64_t op_handle;
+ string result;
+
+ EXPECT_EQ(KM_ERROR_OK,
+ BeginOperation(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, &op_handle));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, array_size(message), &result));
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(op_handle, &result));
+ EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaOaepCorruptedDecrypt) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_OAEP, 512);
+ const char message[] = "Hello World!";
+ string ciphertext = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext.size());
+
+ // Corrupt the ciphertext
+ ciphertext[512 / 8 / 2]++;
+
+ uint64_t op_handle;
+ string result;
+ EXPECT_EQ(KM_ERROR_OK,
+ BeginOperation(KM_PURPOSE_DECRYPT, generate_response_.key_blob, &op_handle));
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(op_handle, ciphertext.data(), ciphertext.size(), &result));
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(op_handle, &result));
+ EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1Success) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+ const char message[] = "Hello World!";
+ string ciphertext1 = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext2.size());
+
+ // PKCS1 v1.5 randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1RoundTrip) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+ const char message[] = "Hello World!";
+ string ciphertext = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext.data(), ciphertext.size());
+ EXPECT_EQ(message, plaintext);
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1TooLarge) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+ const char message[] = "1234567890123456789012345678901234567890123456789012";
+ uint64_t op_handle;
+ string result;
+
+ EXPECT_EQ(KM_ERROR_OK,
+ BeginOperation(KM_PURPOSE_ENCRYPT, generate_response_.key_blob, &op_handle));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(op_handle, message, array_size(message), &result));
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(op_handle, &result));
+ EXPECT_EQ(0, result.size());
+}
+
+TEST_F(EncryptionOperationsTest, RsaPkcs1CorruptedDecrypt) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, 512);
+ const char message[] = "Hello World!";
+ string ciphertext = EncryptMessage(message, strlen(message));
+ EXPECT_EQ(512 / 8, ciphertext.size());
+
+ // Corrupt the ciphertext
+ ciphertext[512 / 8 / 2]++;
+
+ uint64_t op_handle;
+ string result;
+ EXPECT_EQ(KM_ERROR_OK,
+ BeginOperation(KM_PURPOSE_DECRYPT, generate_response_.key_blob, &op_handle));
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(op_handle, ciphertext.data(), ciphertext.size(), &result));
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(op_handle, &result));
+ EXPECT_EQ(0, result.size());
+}
+
} // namespace test
} // namespace keymaster