Implement key upgrade in keystore.

Change-Id: I0cf169d9366aee1de32f1cc4501af76e6e1bc505
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
index e02ef8b..ba0182c 100644
--- a/keystore/key_store_service.cpp
+++ b/keystore/key_store_service.cpp
@@ -30,6 +30,11 @@
 #include "defaults.h"
 #include "keystore_utils.h"
 
+using keymaster::AuthorizationSet;
+using keymaster::AuthorizationSetBuilder;
+using keymaster::TAG_APPLICATION_DATA;
+using keymaster::TAG_APPLICATION_ID;
+
 namespace android {
 
 const size_t MAX_OPERATIONS = 15;
@@ -39,6 +44,10 @@
 };
 typedef UniquePtr<BIGNUM, BIGNUM_Delete> Unique_BIGNUM;
 
+struct Malloc_Delete {
+    void operator()(uint8_t* p) const { free(p); }
+};
+
 void KeyStoreService::binderDied(const wp<IBinder>& who) {
     auto operations = mOperationMap.getOperationsForToken(who.unsafe_get());
     for (auto token : operations) {
@@ -680,16 +689,29 @@
     if (responseCode != ::NO_ERROR) {
         return responseCode;
     }
-    keymaster_key_blob_t key;
-    key.key_material_size = keyBlob.getLength();
-    key.key_material = keyBlob.getValue();
+    keymaster_key_blob_t key = {keyBlob.getValue(), static_cast<size_t>(keyBlob.getLength())};
     auto* dev = mKeyStore->getDeviceForBlob(keyBlob);
-    keymaster_key_characteristics_t out = {{nullptr, 0}, {nullptr, 0}};
+    keymaster_key_characteristics_t out = {};
     if (!dev->get_key_characteristics) {
         ALOGE("device does not implement get_key_characteristics");
         return KM_ERROR_UNIMPLEMENTED;
     }
     rc = dev->get_key_characteristics(dev, &key, clientId, appData, &out);
+    if (rc == KM_ERROR_KEY_REQUIRES_UPGRADE) {
+        AuthorizationSet upgradeParams;
+        if (clientId && clientId->data && clientId->data_length) {
+            upgradeParams.push_back(TAG_APPLICATION_ID, *clientId);
+        }
+        if (appData && appData->data && appData->data_length) {
+            upgradeParams.push_back(TAG_APPLICATION_DATA, *appData);
+        }
+        rc = upgradeKeyBlob(name, targetUid, upgradeParams, &keyBlob);
+        if (rc != ::NO_ERROR) {
+            return rc;
+        }
+        key = {keyBlob.getValue(), static_cast<size_t>(keyBlob.getLength())};
+        rc = dev->get_key_characteristics(dev, &key, clientId, appData, &out);
+    }
     if (rc != KM_ERROR_OK) {
         return rc;
     }
@@ -827,6 +849,16 @@
     Unique_keymaster_key_characteristics characteristics;
     characteristics.reset(new keymaster_key_characteristics_t);
     err = getOperationCharacteristics(key, dev, opParams, characteristics.get());
+    if (err == KM_ERROR_KEY_REQUIRES_UPGRADE) {
+        int32_t rc = upgradeKeyBlob(name, targetUid,
+                                    AuthorizationSet(opParams.data(), opParams.size()), &keyBlob);
+        if (rc != ::NO_ERROR) {
+            result->resultCode = rc;
+            return;
+        }
+        key = {keyBlob.getValue(), static_cast<size_t>(keyBlob.getLength())};
+        err = getOperationCharacteristics(key, dev, opParams, characteristics.get());
+    }
     if (err) {
         result->resultCode = err;
         return;
@@ -1504,4 +1536,45 @@
     return ::NO_ERROR;
 }
 
+int32_t KeyStoreService::upgradeKeyBlob(const String16& name, uid_t uid,
+                                        const AuthorizationSet& params, Blob* blob) {
+    // Read the blob rather than assuming the caller provided the right name/uid/blob triplet.
+    String8 name8(name);
+    ResponseCode responseCode = mKeyStore->getKeyForName(blob, name8, uid, TYPE_KEYMASTER_10);
+    if (responseCode != ::NO_ERROR) {
+        return responseCode;
+    }
+
+    keymaster_key_blob_t key = {blob->getValue(), static_cast<size_t>(blob->getLength())};
+    auto* dev = mKeyStore->getDeviceForBlob(*blob);
+    keymaster_key_blob_t upgraded_key;
+    int32_t rc = dev->upgrade_key(dev, &key, &params, &upgraded_key);
+    if (rc != KM_ERROR_OK) {
+        return rc;
+    }
+    UniquePtr<uint8_t, Malloc_Delete> upgraded_key_deleter(
+        const_cast<uint8_t*>(upgraded_key.key_material));
+
+    rc = del(name, uid);
+    if (rc != ::NO_ERROR) {
+        return rc;
+    }
+
+    String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
+    Blob newBlob(upgraded_key.key_material, upgraded_key.key_material_size, nullptr /* info */,
+                 0 /* infoLength */, ::TYPE_KEYMASTER_10);
+    newBlob.setFallback(blob->isFallback());
+    newBlob.setEncrypted(blob->isEncrypted());
+
+    rc = mKeyStore->put(filename.string(), &newBlob, get_user_id(uid));
+
+    // Re-read blob for caller.  We can't use newBlob because writing it modified it.
+    responseCode = mKeyStore->getKeyForName(blob, name8, uid, TYPE_KEYMASTER_10);
+    if (responseCode != ::NO_ERROR) {
+        return responseCode;
+    }
+
+    return rc;
+}
+
 }  // namespace android
diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h
index 12a342e..7d55919 100644
--- a/keystore/key_store_service.h
+++ b/keystore/key_store_service.h
@@ -19,6 +19,8 @@
 
 #include <keystore/IKeystoreService.h>
 
+#include <keymaster/authorization_set.h>
+
 #include "auth_token_table.h"
 #include "keystore.h"
 #include "keystore_keymaster_enforcement.h"
@@ -222,6 +224,17 @@
                                uint8_t** out, size_t* outLength, const uint8_t* signature,
                                size_t signatureLength, keymaster_purpose_t purpose);
 
+    /**
+     * Upgrade a key blob under alias "name", returning the new blob in "blob".  If "blob"
+     * previously contained data, it will be overwritten.
+     *
+     * Returns ::NO_ERROR if the key was upgraded successfully.
+     *         KM_ERROR_VERSION_MISMATCH if called on a key whose patch level is greater than or
+     *         equal to the current system patch level.
+     */
+    int32_t upgradeKeyBlob(const String16& name, uid_t targetUid,
+                           const keymaster::AuthorizationSet& params, Blob* blob);
+
     ::KeyStore* mKeyStore;
     OperationMap mOperationMap;
     keymaster::AuthTokenTable mAuthTokenTable;