Add RSA verification.
Change-Id: Ie9ac37dba7ead62b0ca17054bbf6d2744cea5946
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
index 9849d2b..d841b25 100644
--- a/google_keymaster.cpp
+++ b/google_keymaster.cpp
@@ -298,15 +298,15 @@
}
}
-void GoogleKeymaster::FinishOperation(const keymaster_operation_handle_t op_handle,
+void GoogleKeymaster::FinishOperation(const FinishOperationRequest& request,
FinishOperationResponse* response) {
- OpTableEntry* entry = FindOperation(op_handle);
+ OpTableEntry* entry = FindOperation(request.op_handle);
if (entry == NULL) {
response->error = KM_ERROR_INVALID_OPERATION_HANDLE;
return;
}
- response->error = entry->operation->Finish(&response->signature, &response->output);
+ response->error = entry->operation->Finish(request.signature, &response->output);
DeleteOperation(entry);
}
diff --git a/google_keymaster.h b/google_keymaster.h
index cf5bcb1..d8543e8 100644
--- a/google_keymaster.h
+++ b/google_keymaster.h
@@ -75,8 +75,7 @@
}
void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
- void FinishOperation(const keymaster_operation_handle_t op_handle,
- FinishOperationResponse* response);
+ void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
keymaster_error_t AbortOperation(const keymaster_operation_handle_t op_handle);
private:
diff --git a/google_keymaster_messages.h b/google_keymaster_messages.h
index d116280..2825ab7 100644
--- a/google_keymaster_messages.h
+++ b/google_keymaster_messages.h
@@ -127,9 +127,13 @@
Buffer output;
};
+struct FinishOperationRequest {
+ keymaster_operation_handle_t op_handle;
+ Buffer signature;
+};
+
struct FinishOperationResponse {
keymaster_error_t error;
- Buffer signature;
Buffer output;
};
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 4c8c28e..cb9c32e 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -277,9 +277,9 @@
};
GenerateKeyRequest generate_request;
generate_request.key_description.Reinitialize(params, array_length(params));
- if (digest != -1)
+ if (static_cast<int>(digest) != -1)
generate_request.key_description.push_back(TAG_DIGEST, digest);
- if (padding != -1)
+ 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);
@@ -316,11 +316,12 @@
ASSERT_EQ(KM_ERROR_OK, update_response.error);
EXPECT_EQ(0U, update_response.output.available_read());
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
FinishOperationResponse finish_response;
- device.FinishOperation(begin_response.op_handle, &finish_response);
+ device.FinishOperation(finish_request, &finish_response);
ASSERT_EQ(KM_ERROR_OK, finish_response.error);
- EXPECT_GT(finish_response.signature.available_read(), 0U);
- EXPECT_EQ(0U, finish_response.output.available_read());
+ EXPECT_GT(finish_response.output.available_read(), 0U);
EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
}
@@ -433,10 +434,117 @@
ASSERT_EQ(KM_ERROR_OK, update_response.error);
EXPECT_EQ(0U, update_response.output.available_read());
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
FinishOperationResponse finish_response;
- device.FinishOperation(begin_response.op_handle, &finish_response);
+ device.FinishOperation(finish_request, &finish_response);
ASSERT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, finish_response.error);
- EXPECT_EQ(0U, finish_response.signature.available_read());
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+class VerificationOperationsTest : public KeymasterTest {
+ protected:
+ VerificationOperationsTest() {
+ generate_response_.error = KM_ERROR_UNKNOWN_ERROR;
+ finish_response_.error = KM_ERROR_UNKNOWN_ERROR;
+ }
+
+ void GenerateKey(keymaster_digest_t digest, keymaster_padding_t padding,
+ uint32_t key_size) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ 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>(digest) != -1)
+ generate_request.key_description.push_back(TAG_DIGEST, digest);
+ 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);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.key_blob = generate_response_.key_blob;
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.additional_params.push_back(TAG_APPLICATION_ID, "app_id", 6);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize("012345678901234567890123456789012", 32);
+ EXPECT_EQ(32U, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ device.FinishOperation(finish_request, &finish_response_);
+ ASSERT_EQ(KM_ERROR_OK, finish_response_.error);
+ EXPECT_GT(finish_response_.output.available_read(), 0U);
+ }
+
+ keymaster_key_blob_t* key_blob() {
+ if (generate_response_.error == KM_ERROR_OK)
+ return &generate_response_.key_blob;
+ return NULL;
+ }
+
+ Buffer* signature() {
+ if (finish_response_.error == KM_ERROR_OK)
+ return &finish_response_.output;
+ return NULL;
+ }
+
+ private:
+ GenerateKeyResponse generate_response_;
+ FinishOperationResponse finish_response_;
+};
+
+TEST_F(VerificationOperationsTest, RsaSuccess) {
+ GenerateKey(KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+ ASSERT_TRUE(key_blob() != NULL);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.key_blob = *key_blob();
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ begin_request.additional_params.push_back(TAG_APPLICATION_ID, "app_id", 6);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize("012345678901234567890123456789012", 32);
+ EXPECT_EQ(32U, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
EXPECT_EQ(0U, finish_response.output.available_read());
EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
diff --git a/google_keymaster_utils.cpp b/google_keymaster_utils.cpp
index bd44cfd..89e6968 100644
--- a/google_keymaster_utils.cpp
+++ b/google_keymaster_utils.cpp
@@ -71,4 +71,13 @@
return true;
}
+int memcmp_s(const void* p1, const void* p2, size_t length) {
+ const uint8_t* s1 = static_cast<const uint8_t*>(p1);
+ const uint8_t* s2 = static_cast<const uint8_t*>(p2);
+ uint8_t result = 0;
+ while (length-- > 0)
+ result |= *s1++ ^ *s2++;
+ return result == 0 ? 0 : 1;
+}
+
} // namespace keymaster
diff --git a/google_keymaster_utils.h b/google_keymaster_utils.h
index abef785..5d4c677 100644
--- a/google_keymaster_utils.h
+++ b/google_keymaster_utils.h
@@ -96,16 +96,22 @@
* optimized away. This is important because we often need to wipe blocks of sensitive data from
* memory.
*/
-#ifndef KEYMASTER_CLANG_TEST_BUILD
-#pragma GCC push_options
-#pragma GCC optimize("O0")
-#endif // not KEYMASTER_CLANG_TEST_BUILD
-inline void* memset_s(void* s, int c, size_t n) {
+#ifdef KEYMASTER_CLANG_TEST_BUILD
+#define OPTIMIZE(x)
+#else // not KEYMASTER_CLANG_TEST_BUILD
+#define OPTIMIZE(x) __attribute__((optimize(x)))
+#endif // not KEYMASTER_CLANG_TEST_BUILD
+inline OPTIMIZE("O0") void* memset_s(void* s, int c, size_t n) {
return memset(s, c, n);
}
-#ifndef KEYMASTER_CLANG_TEST_BUILD
-#pragma GCC pop_options
-#endif // not KEYMASTER_CLANG_TEST_BUILD
+#undef OPTIMIZE
+
+/**
+ * Variant of memcmp that has the same runtime regardless of whether the data matches (i.e. doesn't
+ * short-circuit). Not an exact equivalent to memcmp because it doesn't return <0 if p1 < p2, just
+ * 0 for match and non-zero for non-match.
+ */
+int memcmp_s(const void* p1, const void* p2, size_t length);
/**
* Eraser clears buffers. Construct it with a buffer or object and the destructor will ensure that
@@ -156,6 +162,11 @@
bool Reinitialize(size_t size);
bool Reinitialize(const void* buf, size_t size);
+ // Reinitialize with a copy of the provided buffer's readable data.
+ bool Reinitialize(const Buffer& buffer) {
+ return Reinitialize(buffer.peek_read(), buffer.available_read());
+ }
+
size_t available_write() const;
size_t available_read() const;
size_t buffer_size() const {
@@ -178,6 +189,10 @@
}
private:
+ // Disallow copy construction and assignment.
+ void operator=(const Buffer& other);
+ Buffer(const Buffer&);
+
uint8_t* buffer_;
size_t buffer_size_;
int read_position_;
diff --git a/operation.h b/operation.h
index 222c24f..64ca40b 100644
--- a/operation.h
+++ b/operation.h
@@ -42,7 +42,7 @@
virtual keymaster_error_t Begin() = 0;
virtual keymaster_error_t Update(const Buffer& input, Buffer* output) = 0;
- virtual keymaster_error_t Finish(Buffer* signature, Buffer* output) = 0;
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output) = 0;
virtual keymaster_error_t Abort() = 0;
private:
diff --git a/rsa_operation.cpp b/rsa_operation.cpp
index 4635154..620df65 100644
--- a/rsa_operation.cpp
+++ b/rsa_operation.cpp
@@ -66,7 +66,7 @@
// Since we're not using a digest function, we just need to store the text, up to the key
// size, until Finish is called, so we allocate a place to put it.
- if (!data_to_sign_.Reinitialize(RSA_size(rsa_key_))) {
+ if (!data_.Reinitialize(RSA_size(rsa_key_))) {
error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
return;
}
@@ -78,37 +78,54 @@
RSA_free(rsa_key_);
}
-keymaster_error_t RsaOperation::Update(const Buffer& input, Buffer* output) {
+keymaster_error_t RsaOperation::Update(const Buffer& input, Buffer* /* output */) {
switch (purpose()) {
default:
return KM_ERROR_UNIMPLEMENTED;
case KM_PURPOSE_SIGN:
- return Sign(input, output);
+ case KM_PURPOSE_VERIFY:
+ return StoreData(input);
}
}
-keymaster_error_t RsaOperation::Sign(const Buffer& input, Buffer* /* output */) {
- if (!data_to_sign_.write(input.peek_read(), input.available_read()))
+keymaster_error_t RsaOperation::StoreData(const Buffer& input) {
+ if (!data_.write(input.peek_read(), input.available_read()))
return KM_ERROR_INVALID_INPUT_LENGTH;
return KM_ERROR_OK;
}
-keymaster_error_t RsaOperation::Finish(Buffer* signature, Buffer* /* output */) {
+keymaster_error_t RsaOperation::Finish(const Buffer& signature, Buffer* output) {
switch (purpose()) {
case KM_PURPOSE_SIGN: {
- signature->Reinitialize(RSA_size(rsa_key_));
- if (data_to_sign_.available_read() != signature->buffer_size())
+ output->Reinitialize(RSA_size(rsa_key_));
+ if (data_.available_read() != output->buffer_size())
return KM_ERROR_INVALID_INPUT_LENGTH;
- int bytes_encrypted =
- RSA_private_encrypt(data_to_sign_.available_read(), data_to_sign_.peek_read(),
- signature->peek_write(), rsa_key_, RSA_NO_PADDING);
+ int bytes_encrypted = RSA_private_encrypt(data_.available_read(), data_.peek_read(),
+ output->peek_write(), rsa_key_, RSA_NO_PADDING);
if (bytes_encrypted < 0)
return KM_ERROR_UNKNOWN_ERROR;
assert(bytes_encrypted == RSA_size(rsa_key_));
- signature->advance_write(bytes_encrypted);
+ output->advance_write(bytes_encrypted);
return KM_ERROR_OK;
}
+ case KM_PURPOSE_VERIFY: {
+ if ((int)data_.available_read() != RSA_size(rsa_key_))
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ if (data_.available_read() != signature.available_read())
+ return KM_ERROR_VERIFICATION_FAILED;
+
+ UniquePtr<uint8_t[]> decrypted_data(new uint8_t[RSA_size(rsa_key_)]);
+ int bytes_decrypted = RSA_public_decrypt(signature.available_read(), signature.peek_read(),
+ decrypted_data.get(), rsa_key_, RSA_NO_PADDING);
+ if (bytes_decrypted < 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ assert(bytes_decrypted == RSA_size(rsa_key_));
+
+ if (memcmp_s(decrypted_data.get(), data_.peek_read(), data_.available_read()) == 0)
+ return KM_ERROR_OK;
+ return KM_ERROR_VERIFICATION_FAILED;
+ }
default:
return KM_ERROR_UNIMPLEMENTED;
}
diff --git a/rsa_operation.h b/rsa_operation.h
index 1c88131..7884798 100644
--- a/rsa_operation.h
+++ b/rsa_operation.h
@@ -34,20 +34,20 @@
return error_;
}
virtual keymaster_error_t Update(const Buffer& input, Buffer* output);
- virtual keymaster_error_t Finish(Buffer* signature, Buffer* output);
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
virtual keymaster_error_t Abort() {
// Nothing to do.
return KM_ERROR_OK;
}
private:
- keymaster_error_t Sign(const Buffer& input, Buffer* output);
+ keymaster_error_t StoreData(const Buffer& input);
keymaster_error_t error_;
keymaster_digest_t digest_;
keymaster_padding_t padding_;
RSA* rsa_key_;
- Buffer data_to_sign_;
+ Buffer data_;
};
} // namespace keymaster