Add key importing, RSA only.
Change-Id: I639e797939a28b2b2a815541c9926dc194657c54
diff --git a/Makefile b/Makefile
index 0841cdc..85fc547 100644
--- a/Makefile
+++ b/Makefile
@@ -140,7 +140,7 @@
clean:
rm -f $(OBJS) $(DEPS) $(BINARIES) \
- $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=massif) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
*gcno *gcda coverage.info
rm -rf coverage
diff --git a/asymmetric_key.cpp b/asymmetric_key.cpp
index 32d8fe9..8698ff4 100644
--- a/asymmetric_key.cpp
+++ b/asymmetric_key.cpp
@@ -154,6 +154,68 @@
return new_key;
}
+/* static */
+RsaKey* RsaKey::ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+ *error = KM_ERROR_UNKNOWN_ERROR;
+
+ UniquePtr<RSA, RSA_Delete> rsa_key(EVP_PKEY_get1_RSA(pkey));
+ if (!rsa_key.get())
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ uint64_t public_exponent;
+ if (authorizations.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &public_exponent)) {
+ // public_exponent specified, make sure it matches the key
+ UniquePtr<BIGNUM, BIGNUM_Delete> public_exponent_bn(BN_new());
+ if (!BN_set_word(public_exponent_bn.get(), public_exponent))
+ return NULL;
+ if (BN_cmp(public_exponent_bn.get(), rsa_key->e) != 0) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ // public_exponent not specified, use the one from the key.
+ public_exponent = BN_get_word(rsa_key->e);
+ if (public_exponent == 0xffffffffL) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ authorizations.push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+ }
+
+ uint32_t key_size;
+ if (authorizations.GetTagValue(TAG_KEY_SIZE, &key_size)) {
+ // key_size specified, make sure it matches the key.
+ if (RSA_size(rsa_key.get()) != (int)key_size) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ key_size = RSA_size(rsa_key.get()) * 8;
+ authorizations.push_back(TAG_KEY_SIZE, key_size);
+ }
+
+ keymaster_algorithm_t algorithm;
+ if (authorizations.GetTagValue(TAG_ALGORITHM, &algorithm)) {
+ if (algorithm != KM_ALGORITHM_RSA) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ authorizations.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ }
+
+ // Don't bother with the other parameters. If the necessary padding, digest, purpose, etc. are
+ // missing, the error will be diagnosed when the key is used (when auth checking is
+ // implemented).
+ *error = KM_ERROR_OK;
+ return new RsaKey(rsa_key.release(), authorizations);
+}
+
RsaKey::RsaKey(const KeyBlob& blob, keymaster_error_t* error) : AsymmetricKey(blob) {
if (error)
*error = LoadKey(blob);
diff --git a/asymmetric_key.h b/asymmetric_key.h
index bbe2527..2b54f6f 100644
--- a/asymmetric_key.h
+++ b/asymmetric_key.h
@@ -60,6 +60,8 @@
class RsaKey : public AsymmetricKey {
public:
static RsaKey* GenerateKey(const AuthorizationSet& key_description, keymaster_error_t* error);
+ static RsaKey* ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ keymaster_error_t* error);
RsaKey(const KeyBlob& blob, keymaster_error_t* error);
virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
diff --git a/authorization_set.cpp b/authorization_set.cpp
index 2b65ee1..2111b74 100644
--- a/authorization_set.cpp
+++ b/authorization_set.cpp
@@ -41,6 +41,9 @@
}
bool AuthorizationSet::reserve_elems(size_t count) {
+ if (is_valid() != OK)
+ return false;
+
if (count >= elems_capacity_) {
keymaster_key_param_t* new_elems = new keymaster_key_param_t[count];
if (new_elems == NULL) {
@@ -56,6 +59,9 @@
}
bool AuthorizationSet::reserve_indirect(size_t length) {
+ if (is_valid() != OK)
+ return false;
+
if (length > indirect_data_capacity_) {
uint8_t* new_data = new uint8_t[length];
if (new_data == NULL) {
@@ -98,6 +104,9 @@
}
int AuthorizationSet::find(keymaster_tag_t tag, int begin) const {
+ if (is_valid() != OK)
+ return -1;
+
int i = ++begin;
while (i < (int)elems_size_ && elems_[i].tag != tag)
++i;
@@ -109,7 +118,7 @@
keymaster_key_param_t empty;
keymaster_key_param_t AuthorizationSet::operator[](int at) const {
- if (at < (int)elems_size_) {
+ if (is_valid() == OK && at < (int)elems_size_) {
return elems_[at];
}
memset(&empty, 0, sizeof(empty));
@@ -171,6 +180,9 @@
}
bool AuthorizationSet::push_back(const AuthorizationSet& set) {
+ if (is_valid() != OK)
+ return false;
+
if (!reserve_elems(elems_size_ + set.elems_size_))
return false;
@@ -185,6 +197,9 @@
}
bool AuthorizationSet::push_back(keymaster_key_param_t elem) {
+ if (is_valid() != OK)
+ return false;
+
if (elems_size_ >= elems_capacity_)
if (!reserve_elems(elems_capacity_ ? elems_capacity_ * 2 : STARTING_ELEMS_CAPACITY))
return false;
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
index bd2adb1..5d8541b 100644
--- a/google_keymaster.cpp
+++ b/google_keymaster.cpp
@@ -50,16 +50,6 @@
};
typedef UniquePtr<ae_ctx, AE_CTX_Delete> Unique_ae_ctx;
-struct EVP_PKEY_Delete {
- void operator()(EVP_PKEY* p) { EVP_PKEY_free(p); }
-};
-typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
-
-struct PKCS8_PRIV_KEY_INFO_Delete {
- void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
-};
-typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO;
-
keymaster_algorithm_t supported_algorithms[] = {
KM_ALGORITHM_RSA, KM_ALGORITHM_DSA, KM_ALGORITHM_ECDSA,
};
@@ -176,22 +166,8 @@
if (response->error != KM_ERROR_OK)
return;
- if (!CopyAuthorizations(key->authorizations(), response))
- return;
-
- AuthorizationSet hidden_auths;
- response->error = BuildHiddenAuthorizations(key->authorizations(), &hidden_auths);
- if (response->error != KM_ERROR_OK)
- return;
-
- UniquePtr<uint8_t[]> key_material;
- size_t key_material_size;
- response->error = key->key_material(&key_material, &key_material_size);
- if (response->error != KM_ERROR_OK)
- return;
-
- response->error =
- SerializeKeyToResponse(key_material.get(), key_material_size, hidden_auths, response);
+ response->error = SerializeKey(key.get(), origin(), &response->key_blob, &response->enforced,
+ &response->unenforced);
}
void GoogleKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
@@ -308,67 +284,48 @@
}
void GoogleKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
-
- if (request.key_data == NULL || request.key_data_length <= 0) {
- response->error = KM_ERROR_INVALID_KEY_BLOB;
+ if (response == NULL)
return;
- }
- if (!is_supported_export_format(request.key_format)) {
- response->error = KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ UniquePtr<Key> key(Key::ImportKey(request.key_description, request.key_format, request.key_data,
+ request.key_data_length, &response->error));
+ if (response->error != KM_ERROR_OK)
return;
- }
- const uint8_t* key_data = request.key_data;
- Unique_PKCS8_PRIV_KEY_INFO pkcs8(
- d2i_PKCS8_PRIV_KEY_INFO(NULL, &key_data, request.key_data_length));
- if (pkcs8.get() == NULL) {
- response->error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return;
- }
-
- Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
- if (pkey.get() == NULL) {
- response->error = KM_ERROR_INVALID_KEY_BLOB;
- return;
- }
-
- int len = i2d_PrivateKey(pkey.get(), NULL);
- if (len <= 0) {
- response->error = KM_ERROR_INVALID_KEY_BLOB;
- return;
- }
-
- UniquePtr<uint8_t[]> der_encoded_key(new uint8_t[len]);
- if (der_encoded_key.get() == NULL) {
- response->error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return;
- }
-
- uint8_t* tmp_der = der_encoded_key.get();
- if (i2d_PrivateKey(pkey.get(), &tmp_der) != len) {
- response->error = KM_ERROR_INVALID_KEY_BLOB;
- return;
- }
-
- void* key_material = der_encoded_key.release();
- response->SetKeyMaterial(key_material, len);
-
- response->error = KM_ERROR_OK;
+ response->error = SerializeKey(key.get(), KM_ORIGIN_IMPORTED, &response->key_blob,
+ &response->enforced, &response->unenforced);
}
-keymaster_error_t GoogleKeymaster::SerializeKeyToResponse(uint8_t* key_bytes, size_t key_length,
- const AuthorizationSet& hidden_auths,
- GenerateKeyResponse* response) {
+keymaster_error_t GoogleKeymaster::SerializeKey(const Key* key, keymaster_key_origin_t origin,
+ keymaster_key_blob_t* keymaster_blob,
+ AuthorizationSet* enforced,
+ AuthorizationSet* unenforced) {
+
+ keymaster_error_t error;
+
+ error = SetAuthorizations(key->authorizations(), origin, enforced, unenforced);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ AuthorizationSet hidden_auths;
+ error = BuildHiddenAuthorizations(key->authorizations(), &hidden_auths);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ UniquePtr<uint8_t[]> key_material;
+ size_t key_material_size;
+ error = key->key_material(&key_material, &key_material_size);
+ if (error != KM_ERROR_OK)
+ return error;
+
uint8_t nonce[KeyBlob::NONCE_LENGTH];
GenerateNonce(nonce, array_size(nonce));
- keymaster_key_blob_t key_data = {key_bytes, key_length};
- UniquePtr<KeyBlob> blob(new KeyBlob(response->enforced, response->unenforced, hidden_auths,
- key_data, MasterKey(), nonce));
+ keymaster_key_blob_t key_data = {key_material.get(), key_material_size};
+ UniquePtr<KeyBlob> blob(
+ new KeyBlob(*enforced, *unenforced, hidden_auths, key_data, MasterKey(), nonce));
if (blob.get() == NULL)
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
if (blob->error() != KM_ERROR_OK)
return blob->error();
@@ -378,8 +335,8 @@
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
blob->Serialize(blob_bytes.get(), blob_bytes.get() + size);
- response->key_blob.key_material_size = size;
- response->key_blob.key_material = blob_bytes.release();
+ keymaster_blob->key_material_size = size;
+ keymaster_blob->key_material = blob_bytes.release();
return KM_ERROR_OK;
}
@@ -409,8 +366,8 @@
return blob.release();
}
-static keymaster_error_t CheckAuthorizationSet(const AuthorizationSet& set) {
- switch (set.is_valid()) {
+static keymaster_error_t TranslateAuthorizationSetError(AuthorizationSet::Error err) {
+ switch (err) {
case AuthorizationSet::OK:
return KM_ERROR_OK;
case AuthorizationSet::ALLOCATION_FAILURE:
@@ -421,37 +378,42 @@
return KM_ERROR_OK;
}
-bool GoogleKeymaster::CopyAuthorizations(const AuthorizationSet& key_description,
- GenerateKeyResponse* response) {
+keymaster_error_t GoogleKeymaster::SetAuthorizations(const AuthorizationSet& key_description,
+ keymaster_key_origin_t origin,
+ AuthorizationSet* enforced,
+ AuthorizationSet* unenforced) {
for (size_t i = 0; i < key_description.size(); ++i) {
switch (key_description[i].tag) {
+ // These cannot be specified by the client.
case KM_TAG_ROOT_OF_TRUST:
case KM_TAG_CREATION_DATETIME:
case KM_TAG_ORIGIN:
- response->error = KM_ERROR_INVALID_TAG;
- return false;
+ return KM_ERROR_INVALID_TAG;
+
+ // These don't work.
case KM_TAG_ROLLBACK_RESISTANT:
- response->error = KM_ERROR_UNSUPPORTED_TAG;
- return false;
+ return KM_ERROR_UNSUPPORTED_TAG;
+
+ // These are hidden.
+ case KM_TAG_APPLICATION_ID:
+ case KM_TAG_APPLICATION_DATA:
+ break;
+
+ // Everything else we just copy into the appropriate set.
default:
- if (!AddAuthorization(key_description[i], response))
- return false;
+ AddAuthorization(key_description[i], enforced, unenforced);
break;
}
}
- if (!AddAuthorization(Authorization(TAG_CREATION_DATETIME, java_time(time(NULL))), response) ||
- !AddAuthorization(Authorization(TAG_ORIGIN, origin()), response))
- return false;
+ AddAuthorization(Authorization(TAG_CREATION_DATETIME, java_time(time(NULL))), enforced,
+ unenforced);
+ AddAuthorization(Authorization(TAG_ORIGIN, origin), enforced, unenforced);
- response->error = CheckAuthorizationSet(response->enforced);
- if (response->error != KM_ERROR_OK)
- return false;
- response->error = CheckAuthorizationSet(response->unenforced);
- if (response->error != KM_ERROR_OK)
- return false;
+ if (enforced->is_valid() != AuthorizationSet::OK)
+ return TranslateAuthorizationSetError(enforced->is_valid());
- return true;
+ return TranslateAuthorizationSetError(unenforced->is_valid());
}
keymaster_error_t GoogleKeymaster::BuildHiddenAuthorizations(const AuthorizationSet& input_set,
@@ -463,23 +425,15 @@
hidden->push_back(TAG_APPLICATION_DATA, entry.data, entry.data_length);
hidden->push_back(RootOfTrustTag());
- return CheckAuthorizationSet(*hidden);
+ return TranslateAuthorizationSetError(hidden->is_valid());
}
-bool GoogleKeymaster::AddAuthorization(const keymaster_key_param_t& auth,
- GenerateKeyResponse* response) {
- switch (auth.tag) {
- case KM_TAG_ROOT_OF_TRUST:
- case KM_TAG_APPLICATION_ID:
- case KM_TAG_APPLICATION_DATA:
- // Skip. We handle these tags separately.
- return true;
- default:
- if (is_enforced(auth.tag))
- return response->enforced.push_back(auth);
- else
- return response->unenforced.push_back(auth);
- }
+void GoogleKeymaster::AddAuthorization(const keymaster_key_param_t& auth,
+ AuthorizationSet* enforced, AuthorizationSet* unenforced) {
+ if (is_enforced(auth.tag))
+ enforced->push_back(auth);
+ else
+ unenforced->push_back(auth);
}
keymaster_error_t GoogleKeymaster::AddOperation(Operation* operation,
diff --git a/google_keymaster.h b/google_keymaster.h
index 826549e..e1cc814 100644
--- a/google_keymaster.h
+++ b/google_keymaster.h
@@ -83,19 +83,22 @@
virtual keymaster_key_blob_t MasterKey() = 0;
virtual void GenerateNonce(uint8_t* nonce, size_t length) = 0;
- keymaster_error_t SerializeKeyToResponse(uint8_t* key_material, size_t key_length,
- const AuthorizationSet& hidden_auths,
- GenerateKeyResponse* response);
+ keymaster_error_t SerializeKey(const Key* key, keymaster_key_origin_t origin,
+ keymaster_key_blob_t* keymaster_blob, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
Key* LoadKey(const keymaster_key_blob_t& key, const AuthorizationSet& client_params,
keymaster_error_t* error);
KeyBlob* LoadKeyBlob(const keymaster_key_blob_t& key, const AuthorizationSet& client_params,
keymaster_error_t* error);
- bool CopyAuthorizations(const AuthorizationSet& key_description, GenerateKeyResponse* response);
+ keymaster_error_t SetAuthorizations(const AuthorizationSet& key_description,
+ keymaster_key_origin_t origin, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
keymaster_error_t BuildHiddenAuthorizations(const AuthorizationSet& input_set,
AuthorizationSet* hidden);
- bool AddAuthorization(const keymaster_key_param_t& auth, GenerateKeyResponse* response);
+ void AddAuthorization(const keymaster_key_param_t& auth, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
bool GenerateRsa(const AuthorizationSet& key_auths, GenerateKeyResponse* response,
AuthorizationSet* hidden_auths);
bool GenerateDsa(const AuthorizationSet& key_auths, GenerateKeyResponse* response,
diff --git a/google_keymaster_messages.cpp b/google_keymaster_messages.cpp
index 030cafd..109f355 100644
--- a/google_keymaster_messages.cpp
+++ b/google_keymaster_messages.cpp
@@ -228,14 +228,14 @@
}
size_t ImportKeyRequest::SerializedSize() const {
- return sizeof(uint32_t) /* additional_params size */ + additional_params.SerializedSize() +
+ return sizeof(uint32_t) /* additional_params size */ + key_description.SerializedSize() +
sizeof(uint32_t) /* key_format */ + sizeof(uint32_t) /* key_data_length */ +
key_data_length;
}
uint8_t* ImportKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
- buf = append_uint32_to_buf(buf, end, additional_params.SerializedSize());
- buf = additional_params.Serialize(buf, end);
+ buf = append_uint32_to_buf(buf, end, key_description.SerializedSize());
+ buf = key_description.Serialize(buf, end);
buf = append_uint32_to_buf(buf, end, key_format);
return append_size_and_data_to_buf(buf, end, key_data, key_data_length);
}
@@ -243,7 +243,7 @@
bool ImportKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
uint32_t additional_params_size;
return copy_uint32_from_buf(buf_ptr, end, &additional_params_size) &&
- additional_params.Deserialize(buf_ptr, *buf_ptr + additional_params_size) &&
+ key_description.Deserialize(buf_ptr, *buf_ptr + additional_params_size) &&
copy_uint32_from_buf(buf_ptr, end, &key_format) &&
copy_size_and_data_from_buf(buf_ptr, end, &key_data_length, &key_data);
}
diff --git a/google_keymaster_messages.h b/google_keymaster_messages.h
index 9b86d0c..f24a647 100644
--- a/google_keymaster_messages.h
+++ b/google_keymaster_messages.h
@@ -224,7 +224,7 @@
uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
- AuthorizationSet additional_params;
+ AuthorizationSet key_description;
keymaster_key_format_t key_format;
uint8_t* key_data;
size_t key_data_length;
diff --git a/google_keymaster_messages_test.cpp b/google_keymaster_messages_test.cpp
index 749a565..868e4f9 100644
--- a/google_keymaster_messages_test.cpp
+++ b/google_keymaster_messages_test.cpp
@@ -245,12 +245,12 @@
TEST(RoundTrip, ImportKeyRequest) {
ImportKeyRequest msg;
- msg.additional_params.Reinitialize(params, array_length(params));
+ msg.key_description.Reinitialize(params, array_length(params));
msg.key_format = KM_KEY_FORMAT_X509;
msg.SetKeyMaterial("foo", 3);
UniquePtr<ImportKeyRequest> deserialized(round_trip(msg, 93));
- EXPECT_EQ(msg.additional_params, deserialized->additional_params);
+ EXPECT_EQ(msg.key_description, deserialized->key_description);
EXPECT_EQ(msg.key_format, deserialized->key_format);
EXPECT_EQ(msg.key_data_length, deserialized->key_data_length);
EXPECT_EQ(0, memcmp(msg.key_data, deserialized->key_data, msg.key_data_length));
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 5f1f016..670810e 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <string>
+#include <fstream>
+
#include <gtest/gtest.h>
#include <openssl/engine.h>
@@ -23,6 +26,10 @@
#include "google_softkeymaster.h"
#include "keymaster_tags.h"
+using std::string;
+using std::ifstream;
+using std::istreambuf_iterator;
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
@@ -445,9 +452,13 @@
}
void SignMessage(const void* message, size_t size) {
+ SignMessage(generate_response_.key_blob, message, size);
+ }
+
+ void SignMessage(const keymaster_key_blob_t& key_blob, const void* message, size_t size) {
BeginOperationRequest begin_request;
BeginOperationResponse begin_response;
- begin_request.SetKeyMaterial(generate_response_.key_blob);
+ begin_request.SetKeyMaterial(key_blob);
begin_request.purpose = KM_PURPOSE_SIGN;
AddClientParams(&begin_request.additional_params);
@@ -883,5 +894,84 @@
ASSERT_TRUE(response.key_data == NULL);
}
+static string read_file(const string& file_name) {
+ ifstream file_stream(file_name, std::ios::binary);
+ istreambuf_iterator<char> file_begin(file_stream);
+ istreambuf_iterator<char> file_end;
+ return string(file_begin, file_end);
+}
+
+typedef SigningOperationsTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_DIGEST, KM_DIGEST_NONE),
+ Authorization(TAG_PADDING, KM_PAD_NONE),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+
+ string pk8_key = read_file("privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ ImportKeyRequest import_request;
+ import_request.key_description.Reinitialize(params, array_length(params));
+ import_request.key_format = KM_KEY_FORMAT_PKCS8;
+ import_request.SetKeyMaterial(pk8_key.data(), pk8_key.size());
+
+ ImportKeyResponse import_response;
+ device.ImportKey(import_request, &import_response);
+ ASSERT_EQ(KM_ERROR_OK, import_response.error);
+ EXPECT_EQ(0U, import_response.enforced.size());
+ EXPECT_GT(import_response.unenforced.size(), 0U);
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_KEY_SIZE, 1024));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ // And values provided by GoogleKeymaster
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(import_response.unenforced, KM_TAG_CREATION_DATETIME));
+
+ size_t message_len = 1024 / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::fill(message.get(), message.get() + message_len, 'a');
+ SignMessage(import_response.key_blob, message.get(), message_len);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(import_response.key_blob);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ 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(message.get(), message_len);
+ EXPECT_EQ(message_len, 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));
+}
+
} // namespace test
} // namespace keymaster
diff --git a/key.cpp b/key.cpp
index c814da0..0706ce7 100644
--- a/key.cpp
+++ b/key.cpp
@@ -14,13 +14,20 @@
* limitations under the License.
*/
+#include <openssl/x509.h>
+
#include "asymmetric_key.h"
#include "key_blob.h"
+#include "openssl_utils.h"
#include "key.h"
namespace keymaster {
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+
Key::Key(const KeyBlob& blob) {
authorizations_.push_back(blob.unenforced());
authorizations_.push_back(blob.enforced());
@@ -62,4 +69,47 @@
}
}
+/* static */
+Key* Key::ImportKey(const AuthorizationSet& key_description, keymaster_key_format_t key_format,
+ const uint8_t* key_data, size_t key_data_length, keymaster_error_t* error) {
+ *error = KM_ERROR_OK;
+
+ if (key_data == NULL || key_data_length <= 0) {
+ *error = KM_ERROR_INVALID_KEY_BLOB;
+ return NULL;
+ }
+
+ if (key_format != KM_KEY_FORMAT_PKCS8) {
+ *error = KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ return NULL;
+ }
+
+ UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
+ d2i_PKCS8_PRIV_KEY_INFO(NULL, &key_data, key_data_length));
+ if (pkcs8.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ *error = KM_ERROR_INVALID_KEY_BLOB;
+ return NULL;
+ }
+
+ UniquePtr<Key> key;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ return RsaKey::ImportKey(key_description, pkey.get(), error);
+ case EVP_PKEY_DSA:
+ case EVP_PKEY_EC:
+ default:
+ *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return NULL;
+ }
+
+ *error = KM_ERROR_UNIMPLEMENTED;
+ return NULL;
+}
+
} // namespace keymaster
diff --git a/key.h b/key.h
index 20a77e9..39e8d1f 100644
--- a/key.h
+++ b/key.h
@@ -29,6 +29,9 @@
public:
static Key* CreateKey(const KeyBlob& blob, keymaster_error_t* error);
static Key* GenerateKey(const AuthorizationSet& key_description, keymaster_error_t* error);
+ static Key* ImportKey(const AuthorizationSet& key_description,
+ keymaster_key_format_t key_format, const uint8_t* key_data,
+ size_t key_data_length, keymaster_error_t* error);
virtual ~Key() {}
virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) = 0;
diff --git a/keymaster_defs.h b/keymaster_defs.h
index 9dc3797..fac9876 100644
--- a/keymaster_defs.h
+++ b/keymaster_defs.h
@@ -360,6 +360,7 @@
KM_ERROR_MEMORY_ALLOCATION_FAILED = -41,
KM_ERROR_INVALID_RESCOPING = -42,
KM_ERROR_INVALID_DSA_PARAMS = -43,
+ KM_ERROR_IMPORT_PARAMETER_MISMATCH =-44,
KM_ERROR_UNIMPLEMENTED = -100,
/* Additional error codes may be added by implementations, but implementers should coordinate
diff --git a/privkey_pk8.der b/privkey_pk8.der
new file mode 100644
index 0000000..0336f80
--- /dev/null
+++ b/privkey_pk8.der
Binary files differ