Add initial support for rescoping.
This code does not yet validate that rescoping is authorized. A future
CL will integrate rescoping enforcement.
Change-Id: Iff66860630eef717562bce7c534a09d80b85a7a3
diff --git a/ecdsa_key.cpp b/ecdsa_key.cpp
index f90cf80..4593f7e 100644
--- a/ecdsa_key.cpp
+++ b/ecdsa_key.cpp
@@ -35,6 +35,8 @@
virtual Key* LoadKey(const UnencryptedKeyBlob& blob, keymaster_error_t* error) {
return new EcdsaKey(blob, error);
}
+ virtual Key* RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations, keymaster_error_t* error);
private:
static EC_GROUP* choose_group(size_t key_size_bits);
@@ -141,6 +143,21 @@
return new EcdsaKey(ecdsa_key.release(), authorizations);
}
+Key* EcdsaKeyFactory::RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ EcdsaKey original_key(blob, error);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ EcdsaKey* new_key = new EcdsaKey(original_key.ecdsa_key_.release(), new_authorizations);
+ *error = new_key ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return new_key;
+}
+
/* static */
EC_GROUP* EcdsaKeyFactory::choose_group(size_t key_size_bits) {
switch (key_size_bits) {
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
index 7bf43fa..0778887 100644
--- a/google_keymaster.cpp
+++ b/google_keymaster.cpp
@@ -228,6 +228,12 @@
if (key.get() == NULL)
return;
+ if (key->rescopable()) {
+ // TODO(swillden): Create a better error code for this.
+ response->error = KM_ERROR_INVALID_KEY_BLOB;
+ return;
+ }
+
OperationFactory::KeyType op_type(algorithm, request.purpose);
OperationFactory* factory = OperationFactoryRegistry::Get(op_type);
if (!factory) {
@@ -312,6 +318,29 @@
}
}
+void GoogleKeymaster::Rescope(const RescopeRequest& request, RescopeResponse* response) {
+ if (response == NULL)
+ return;
+
+ UniquePtr<UnencryptedKeyBlob> blob(
+ LoadKeyBlob(request.key_blob, request.additional_params, &response->error));
+ if (response->error != KM_ERROR_OK)
+ return;
+
+ KeyFactory* factory = KeyFactoryRegistry::Get(blob->algorithm());
+ if (!factory) {
+ response->error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return;
+ }
+
+ UniquePtr<Key> key;
+ key.reset(factory->RescopeKey(*blob, request.new_authorizations, &response->error));
+ if (response->error != KM_ERROR_OK)
+ return;
+
+ response->error = SerializeKey(key.get(), blob->origin(), &response->key_blob,
+ &response->enforced, &response->unenforced);
+}
void GoogleKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
if (response == NULL)
return;
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 6fc5b0b..2b94ed9 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -467,6 +467,14 @@
return response.error;
}
+ keymaster_error_t Rescope(const AuthorizationSet& new_params,
+ keymaster_key_blob_t* rescoped_blob,
+ keymaster_key_characteristics_t** rescoped_characteristics) {
+ return device()->rescope(device(), new_params.data(), new_params.size(), &blob_,
+ &client_id_, NULL /* app data */, rescoped_blob,
+ rescoped_characteristics);
+ }
+
void CheckHmacTestVector(string key, string message, keymaster_digest_t digest,
string expected_mac) {
ASSERT_EQ(KM_ERROR_OK,
@@ -2368,5 +2376,75 @@
device()->add_rng_entropy(device(), reinterpret_cast<const uint8_t*>("foo"), 3));
}
+typedef KeymasterTest RescopingTest;
+TEST_F(RescopingTest, KeyWithRescopingNotUsable) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 16).Option(
+ TAG_RESCOPING_ADD, KM_TAG_MAC_LENGTH)));
+ // TODO(swillden): Add a better error code for this.
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, BeginOperation(KM_PURPOSE_ENCRYPT));
+}
+
+TEST_F(RescopingTest, RescopeSymmetric) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .AesEncryptionKey(128)
+ .OcbMode(4096, 16)
+ .Option(TAG_RESCOPING_ADD, KM_TAG_MAC_LENGTH)
+ .Option(TAG_RESCOPING_DEL, KM_TAG_MAC_LENGTH)));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_MAC_LENGTH, 15));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_MAC_LENGTH, 16));
+
+ keymaster_key_blob_t rescoped_blob;
+ keymaster_key_characteristics_t* rescoped_characteristics;
+ AuthorizationSet new_params =
+ ParamBuilder().AesEncryptionKey(128).OcbMode(4096, 15 /* note changed */).build();
+
+ ASSERT_EQ(KM_ERROR_OK, Rescope(new_params, &rescoped_blob, &rescoped_characteristics));
+ ASSERT_TRUE(rescoped_characteristics != NULL);
+
+ EXPECT_EQ(0, rescoped_characteristics->hw_enforced.length);
+ AuthorizationSet auths(rescoped_characteristics->sw_enforced);
+ keymaster_free_characteristics(rescoped_characteristics);
+ free(rescoped_characteristics);
+ free(const_cast<uint8_t*>(rescoped_blob.key_material));
+
+ EXPECT_TRUE(contains(auths, TAG_ALGORITHM, KM_ALGORITHM_AES));
+ EXPECT_TRUE(contains(auths, TAG_MAC_LENGTH, 15));
+ EXPECT_FALSE(contains(auths, TAG_MAC_LENGTH, 16));
+}
+
+TEST_F(RescopingTest, RescopeRsa) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(ParamBuilder()
+ .RsaEncryptionKey(256)
+ .Option(TAG_RESCOPING_ADD, KM_TAG_PURPOSE)
+ .Option(TAG_RESCOPING_DEL, KM_TAG_PURPOSE)));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_VERIFY));
+
+ keymaster_key_blob_t rescoped_blob;
+ keymaster_key_characteristics_t* rescoped_characteristics;
+ AuthorizationSet new_params = ParamBuilder().RsaSigningKey(256).build();
+
+ ASSERT_EQ(KM_ERROR_OK, Rescope(new_params, &rescoped_blob, &rescoped_characteristics));
+ ASSERT_TRUE(rescoped_characteristics != NULL);
+
+ EXPECT_EQ(0, rescoped_characteristics->hw_enforced.length);
+ AuthorizationSet auths(rescoped_characteristics->sw_enforced);
+ keymaster_free_characteristics(rescoped_characteristics);
+ free(rescoped_characteristics);
+ free(const_cast<uint8_t*>(rescoped_blob.key_material));
+
+ EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_VERIFY));
+}
+
+// TODO(swillden): When adding rescoping enforcement, include tests that verify that tags
+// corresponding to intrinsic attributes of keys, like RSA public exponent, or symmetric key size,
+// may not be changed.
+
} // namespace test
} // namespace keymaster
diff --git a/include/keymaster/google_keymaster.h b/include/keymaster/google_keymaster.h
index c78dbee..2b49f59 100644
--- a/include/keymaster/google_keymaster.h
+++ b/include/keymaster/google_keymaster.h
@@ -62,10 +62,7 @@
void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response);
- void Rescope(const RescopeRequest& /* request */, RescopeResponse* response) {
- // Not going to implement until post-L.
- response->error = KM_ERROR_UNIMPLEMENTED;
- }
+ void Rescope(const RescopeRequest& request, RescopeResponse* response);
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
diff --git a/key.h b/key.h
index dddf6f7..2c972be 100644
--- a/key.h
+++ b/key.h
@@ -46,6 +46,9 @@
keymaster_key_format_t key_format, const uint8_t* key_data,
size_t key_data_length, keymaster_error_t* error) = 0;
virtual Key* LoadKey(const UnencryptedKeyBlob& blob, keymaster_error_t* error) = 0;
+ virtual Key* RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations,
+ keymaster_error_t* error) = 0;
// Informational methods.
virtual const keymaster_key_format_t* SupportedImportFormats(size_t* format_count) = 0;
diff --git a/rsa_key.cpp b/rsa_key.cpp
index f10b51b..aec0410 100644
--- a/rsa_key.cpp
+++ b/rsa_key.cpp
@@ -42,6 +42,8 @@
virtual Key* LoadKey(const UnencryptedKeyBlob& blob, keymaster_error_t* error) {
return new RsaKey(blob, error);
}
+ virtual Key* RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations, keymaster_error_t* error);
};
static KeyFactoryRegistry::Registration<RsaKeyFactory> registration;
@@ -147,6 +149,21 @@
return new RsaKey(rsa_key.release(), authorizations);
}
+Key* RsaKeyFactory::RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ RsaKey original_key(blob, error);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ RsaKey* new_key = new RsaKey(original_key.rsa_key_.release(), new_authorizations);
+ *error = new_key ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return new_key;
+}
+
RsaKey::RsaKey(const UnencryptedKeyBlob& blob, keymaster_error_t* error) : AsymmetricKey(blob) {
if (error)
*error = LoadKey(blob);
diff --git a/soft_keymaster_device.cpp b/soft_keymaster_device.cpp
index a4a28df..c3ad466 100644
--- a/soft_keymaster_device.cpp
+++ b/soft_keymaster_device.cpp
@@ -576,12 +576,38 @@
/* static */
keymaster_error_t SoftKeymasterDevice::rescope(
- const keymaster1_device_t* /* dev */, const keymaster_key_param_t* /* new_params */,
- size_t /* new_params_count */, const keymaster_key_blob_t* /* key_blob */,
- const keymaster_blob_t* /* client_id */, const keymaster_blob_t* /* app_data */,
- keymaster_key_blob_t* /* rescoped_key_blob */,
- keymaster_key_characteristics_t** /* characteristics */) {
- return KM_ERROR_UNIMPLEMENTED;
+ const keymaster1_device_t* dev, const keymaster_key_param_t* new_params,
+ size_t new_params_count, const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ keymaster_key_blob_t* rescoped_key_blob, keymaster_key_characteristics_t** characteristics) {
+ if (!key_blob)
+ return KM_ERROR_INVALID_KEY_BLOB;
+
+ if (!new_params)
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ if (!rescoped_key_blob)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ RescopeRequest request;
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+ request.new_authorizations.Reinitialize(new_params, new_params_count);
+
+ RescopeResponse response;
+ convert_device(dev)->impl_->Rescope(request, &response);
+ if (response.error != KM_ERROR_OK)
+ return response.error;
+
+ rescoped_key_blob->key_material_size = response.key_blob.key_material_size;
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(rescoped_key_blob->key_material_size));
+ memcpy(tmp, response.key_blob.key_material, response.key_blob.key_material_size);
+ rescoped_key_blob->key_material = tmp;
+
+ if (characteristics)
+ *characteristics = BuildCharacteristics(response.enforced, response.unenforced);
+
+ return KM_ERROR_OK;
}
/* static */
diff --git a/symmetric_key.cpp b/symmetric_key.cpp
index 41983ac..680c08a 100644
--- a/symmetric_key.cpp
+++ b/symmetric_key.cpp
@@ -95,6 +95,21 @@
return key.release();
}
+Key* SymmetricKeyFactory::RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ UniquePtr<SymmetricKey> key(CreateKey(new_authorizations));
+ key->key_data_size_ = blob.unencrypted_key_material_length();
+ key->key_data_.reset(new uint8_t[key->key_data_size_]);
+ memcpy(key->key_data_.get(), blob.unencrypted_key_material(), key->key_data_size_);
+
+ *error = KM_ERROR_OK;
+ return key.release();
+}
+
SymmetricKey::SymmetricKey(const UnencryptedKeyBlob& blob, keymaster_error_t* error)
: Key(blob), key_data_size_(blob.unencrypted_key_material_length()) {
key_data_.reset(new uint8_t[key_data_size_]);
diff --git a/symmetric_key.h b/symmetric_key.h
index 7f78a86..46f4f3f 100644
--- a/symmetric_key.h
+++ b/symmetric_key.h
@@ -27,6 +27,8 @@
virtual Key* GenerateKey(const AuthorizationSet& key_description, keymaster_error_t* error);
virtual Key* ImportKey(const AuthorizationSet&, keymaster_key_format_t, const uint8_t*, size_t,
keymaster_error_t* error);
+ virtual Key* RescopeKey(const UnencryptedKeyBlob& blob,
+ const AuthorizationSet& new_authorizations, keymaster_error_t* error);
virtual const keymaster_key_format_t* SupportedImportFormats(size_t* format_count);
virtual const keymaster_key_format_t* SupportedExportFormats(size_t* format_count) {