Allow entropy to be provided to some operations

generateKey and begin can now optionally take an array of bytes to add
to the rng entropy of the device before the operation. If entropy is
specified and the device does not support add_rng_entropy or the call
fails then that device will not be used, leading to fallback or error
depending on the situation.

Change-Id: Id7d33e3cc959594dfa5483d002993ba35c1fb134
diff --git a/keystore/IKeystoreService.cpp b/keystore/IKeystoreService.cpp
index 3a23059..7be3a97 100644
--- a/keystore/IKeystoreService.cpp
+++ b/keystore/IKeystoreService.cpp
@@ -999,13 +999,15 @@
     };
 
     virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                                int uid, int flags, KeyCharacteristics* outCharacteristics)
+                                const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                                KeyCharacteristics* outCharacteristics)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
         data.writeString16(name);
         data.writeInt32(1);
         params.writeToParcel(&data);
+        data.writeByteArray(entropyLength, entropy);
         data.writeInt32(uid);
         data.writeInt32(flags);
         status_t status = remote()->transact(BnKeystoreService::GENERATE_KEY, data, &reply);
@@ -1131,7 +1133,8 @@
 
     virtual void begin(const sp<IBinder>& appToken, const String16& name,
                        keymaster_purpose_t purpose, bool pruneable,
-                       const KeymasterArguments& params, KeymasterArguments* outParams,
+                       const KeymasterArguments& params, const uint8_t* entropy,
+                       size_t entropyLength, KeymasterArguments* outParams,
                        OperationResult* result)
     {
         if (!result || !outParams) {
@@ -1145,6 +1148,7 @@
         data.writeInt32(pruneable ? 1 : 0);
         data.writeInt32(1);
         params.writeToParcel(&data);
+        data.writeByteArray(entropyLength, entropy);
         status_t status = remote()->transact(BnKeystoreService::BEGIN, data, &reply);
         if (status != NO_ERROR) {
             ALOGD("begin() could not contact remote: %d\n", status);
@@ -1584,10 +1588,14 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
+            const uint8_t* entropy = NULL;
+            size_t entropyLength = 0;
+            readByteArray(data, &entropy, &entropyLength);
             int32_t uid = data.readInt32();
             int32_t flags = data.readInt32();
             KeyCharacteristics outCharacteristics;
-            int32_t ret = generateKey(name, args, uid, flags, &outCharacteristics);
+            int32_t ret = generateKey(name, args, entropy, entropyLength, uid, flags,
+                                      &outCharacteristics);
             reply->writeNoException();
             reply->writeInt32(ret);
             reply->writeInt32(1);
@@ -1655,9 +1663,13 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
+            const uint8_t* entropy = NULL;
+            size_t entropyLength = 0;
+            readByteArray(data, &entropy, &entropyLength);
             KeymasterArguments outArgs;
             OperationResult result;
-            begin(token, name, purpose, pruneable, args, &outArgs, &result);
+            begin(token, name, purpose, pruneable, args, entropy, entropyLength, &outArgs,
+                  &result);
             reply->writeNoException();
             reply->writeInt32(1);
             result.writeToParcel(reply);
diff --git a/keystore/include/keystore/IKeystoreService.h b/keystore/include/keystore/IKeystoreService.h
index f7f1295..6cae3cb 100644
--- a/keystore/include/keystore/IKeystoreService.h
+++ b/keystore/include/keystore/IKeystoreService.h
@@ -197,11 +197,14 @@
     virtual int32_t addRngEntropy(const uint8_t* data, size_t dataLength) = 0;
 
     virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                                int uid, int flags, KeyCharacteristics* outCharacteristics) = 0;
+                                const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                                KeyCharacteristics* outCharacteristics) = 0;
+
     virtual int32_t getKeyCharacteristics(const String16& name,
                                           const keymaster_blob_t* clientId,
                                           const keymaster_blob_t* appData,
                                           KeyCharacteristics* outCharacteristics) = 0;
+
     virtual int32_t importKey(const String16& name, const KeymasterArguments&  params,
                               keymaster_key_format_t format, const uint8_t *keyData,
                               size_t keyLength, int uid, int flags,
@@ -213,7 +216,8 @@
 
     virtual void begin(const sp<IBinder>& apptoken, const String16& name,
                        keymaster_purpose_t purpose, bool pruneable,
-                       const KeymasterArguments& params, KeymasterArguments* outParams,
+                       const KeymasterArguments& params, const uint8_t* entropy,
+                       size_t entropyLength, KeymasterArguments* outParams,
                        OperationResult* result) = 0;
 
     virtual void update(const sp<IBinder>& token, const KeymasterArguments& params,
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index a949ffb..8523db6 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -2469,7 +2469,8 @@
     }
 
     int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                        int uid, int flags, KeyCharacteristics* outCharacteristics) {
+                        const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                        KeyCharacteristics* outCharacteristics) {
         uid_t callingUid = IPCThreadState::self()->getCallingUid();
         pid_t callingPid = IPCThreadState::self()->getCallingPid();
         if (!has_permission(callingUid, P_INSERT, callingPid)) {
@@ -2499,19 +2500,37 @@
         if (device == NULL) {
             return ::SYSTEM_ERROR;
         }
-        // TODO: Seed from Linux RNG either before this or periodically
+        // TODO: Seed from Linux RNG before this.
         if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
                 device->generate_key != NULL) {
-            rc = device->generate_key(device, params.params.data(), params.params.size(), &blob,
-                                      &out);
+            if (!entropy) {
+                rc = KM_ERROR_OK;
+            } else if (device->add_rng_entropy) {
+                rc = device->add_rng_entropy(device, entropy, entropyLength);
+            } else {
+                rc = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (rc == KM_ERROR_OK) {
+                rc = device->generate_key(device, params.params.data(), params.params.size(),
+                                          &blob, &out);
+            }
         }
         // If the HW device didn't support generate_key or generate_key failed
         // fall back to the software implementation.
         if (rc && fallback->generate_key != NULL) {
             isFallback = true;
-            rc = fallback->generate_key(fallback, params.params.data(), params.params.size(),
-                                      &blob,
-                                      &out);
+            if (!entropy) {
+                rc = KM_ERROR_OK;
+            } else if (fallback->add_rng_entropy) {
+                rc = fallback->add_rng_entropy(fallback, entropy, entropyLength);
+            } else {
+                rc = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (rc == KM_ERROR_OK) {
+                rc = fallback->generate_key(fallback, params.params.data(), params.params.size(),
+                                            &blob,
+                                            &out);
+            }
         }
 
         if (out) {
@@ -2676,8 +2695,8 @@
     }
 
     void begin(const sp<IBinder>& appToken, const String16& name, keymaster_purpose_t purpose,
-               bool pruneable, const KeymasterArguments& params,
-               KeymasterArguments* outParams, OperationResult* result) {
+               bool pruneable, const KeymasterArguments& params, const uint8_t* entropy,
+               size_t entropyLength, KeymasterArguments* outParams, OperationResult* result) {
         if (!result || !outParams) {
             ALOGE("Unexpected null arguments to begin()");
             return;
@@ -2703,10 +2722,22 @@
         size_t outSize;
         keymaster_operation_handle_t handle;
         keymaster1_device_t* dev = mKeyStore->getDeviceForBlob(keyBlob);
+        keymaster_error_t err = KM_ERROR_UNIMPLEMENTED;
+        // Add entropy to the device first.
+        if (entropy) {
+            if (dev->add_rng_entropy) {
+                err = dev->add_rng_entropy(dev, entropy, entropyLength);
+            } else {
+                err = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (err) {
+                result->resultCode = err;
+                return;
+            }
+        }
         // TODO: Check authorization.
-        keymaster_error_t err = dev->begin(dev, purpose, &key, params.params.data(),
-                                           params.params.size(), &out, &outSize,
-                                           &handle);
+        err = dev->begin(dev, purpose, &key, params.params.data(), params.params.size(), &out,
+                         &outSize, &handle);
 
         // If there are too many operations abort the oldest operation that was
         // started as pruneable and try again.