Adding DropBox logging support for keystore functionality

This will allow us to track the actual usage patterns of keystore
functions and error occurences.

Test: DropBox logging works for keystore tags
Change-Id: Iadfba3afebaa0be753212b1111b68f50b77f9978
diff --git a/keystore/Android.bp b/keystore/Android.bp
index c98b78f..8af8717 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -27,6 +27,8 @@
         "confirmation_manager.cpp",
         "entropy.cpp",
         "grant_store.cpp",
+        "key_config.proto",
+        "key_proto_handler.cpp",
         "key_store_service.cpp",
         "keyblob_utils.cpp",
         "keymaster_enforcement.cpp",
@@ -35,6 +37,8 @@
         "keystore_utils.cpp",
         "legacy_keymaster_device_wrapper.cpp",
         "operation.cpp",
+        "operation_config.proto",
+        "operation_proto_handler.cpp",
         "permissions.cpp",
         "user_state.cpp",
     ],
@@ -57,7 +61,9 @@
         "libkeystore_binder",
         "libkeystore_parcelables",
         "liblog",
+        "libprotobuf-cpp-lite",
         "libselinux",
+        "libservices",
         "libsoftkeymasterdevice",
         "libutils",
         "libwifikeystorehal",
diff --git a/keystore/include/keystore/keymaster_types.h b/keystore/include/keystore/keymaster_types.h
index bd61294..f3c6907 100644
--- a/keystore/include/keystore/keymaster_types.h
+++ b/keystore/include/keystore/keymaster_types.h
@@ -66,6 +66,7 @@
 using keymaster::TAG_APPLICATION_ID;
 using keymaster::TAG_ATTESTATION_APPLICATION_ID;
 using keymaster::TAG_AUTH_TIMEOUT;
+using keymaster::TAG_BLOB_USAGE_REQUIREMENTS;
 using keymaster::TAG_BLOCK_MODE;
 using keymaster::TAG_DIGEST;
 using keymaster::TAG_EC_CURVE;
@@ -76,6 +77,7 @@
 using keymaster::TAG_MIN_SECONDS_BETWEEN_OPS;
 using keymaster::TAG_NO_AUTH_REQUIRED;
 using keymaster::TAG_NONCE;
+using keymaster::TAG_ORIGIN;
 using keymaster::TAG_ORIGINATION_EXPIRE_DATETIME;
 using keymaster::TAG_PADDING;
 using keymaster::TAG_PURPOSE;
diff --git a/keystore/key_config.proto b/keystore/key_config.proto
new file mode 100644
index 0000000..0b1a398
--- /dev/null
+++ b/keystore/key_config.proto
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package keystore;
+
+option optimize_for = LITE_RUNTIME;
+
+message KeyConfig {
+  // What type of encryption algorithm is this key being generated/imported for
+  // e.g. AES, RSA, etc
+  optional string algorithm = 1;
+
+  // Size of the key being generated/imported
+  optional int32 key_size = 2;
+
+  // Log whether the key was generated, imported, securely imported, or derived.
+  optional string origin = 3;
+
+  // What auth types does this key require? If none, then no auth required.
+  optional string user_auth_type = 4;
+
+  // If user authentication is required, is the requirement time based? If it
+  // is not time based then this field will not be used and the key is per
+  // operation. Per operation keys must be user authenticated on each usage.
+  optional int32 user_auth_key_timeout = 5;
+
+  // Track which padding modes this key supports.
+  repeated string padding = 6;
+
+  // Track which digests this key supports
+  repeated string digest = 7;
+
+  // Check what block mode is being used depending on the mode of encryption
+  repeated string block_mode = 8;
+
+  // Was the key generated/imported successfully?
+  optional bool was_creation_successful = 9;
+
+  // What purposes can this key be used for?
+  repeated string purpose = 10;
+
+  // Which ec curve was selected if elliptic curve cryptography is in use
+  optional string ec_curve = 11;
+
+  // Standalone or is a file system required
+  optional string key_blob_usage_reqs = 12;
+}
diff --git a/keystore/key_proto_handler.cpp b/keystore/key_proto_handler.cpp
new file mode 100644
index 0000000..3bf8c06
--- /dev/null
+++ b/keystore/key_proto_handler.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "KeystoreOperation"
+
+#include "key_proto_handler.h"
+
+#include <android/os/DropBoxManager.h>
+#include <google/protobuf/message_lite.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <keystore/keymaster_types.h>
+#include <utils/String16.h>
+
+#include "key_config.pb.h"
+
+namespace keystore {
+
+void checkEnforcedCharacteristics(const hidl_vec<KeyParameter>& keyParams, KeyConfig* keyConfig) {
+    for (auto& keyParam : keyParams) {
+        switch (keyParam.tag) {
+        case Tag::PURPOSE:
+            keyConfig->add_purpose(toString(accessTagValue(TAG_PURPOSE, keyParam)));
+            break;
+        case Tag::ALGORITHM:
+            keyConfig->set_algorithm(toString(accessTagValue(TAG_ALGORITHM, keyParam)));
+            break;
+        case Tag::KEY_SIZE:
+            keyConfig->set_key_size(accessTagValue(TAG_KEY_SIZE, keyParam));
+            break;
+        case Tag::BLOCK_MODE:
+            keyConfig->add_block_mode(toString(accessTagValue(TAG_BLOCK_MODE, keyParam)));
+            break;
+        case Tag::PADDING:
+            keyConfig->add_padding(toString(accessTagValue(TAG_PADDING, keyParam)));
+            break;
+        case Tag::DIGEST:
+            keyConfig->add_digest(toString(accessTagValue(TAG_DIGEST, keyParam)));
+            break;
+        case Tag::EC_CURVE:
+            keyConfig->set_ec_curve(toString(accessTagValue(TAG_EC_CURVE, keyParam)));
+            break;
+        case Tag::AUTH_TIMEOUT:
+            keyConfig->set_user_auth_key_timeout(accessTagValue(TAG_AUTH_TIMEOUT, keyParam));
+            break;
+        case Tag::ORIGIN:
+            keyConfig->set_origin(toString(accessTagValue(TAG_ORIGIN, keyParam)));
+            break;
+        case Tag::BLOB_USAGE_REQUIREMENTS:
+            keyConfig->set_key_blob_usage_reqs(
+                toString(accessTagValue(TAG_BLOB_USAGE_REQUIREMENTS, keyParam)));
+            break;
+        case Tag::USER_AUTH_TYPE:
+            keyConfig->set_user_auth_type(toString(accessTagValue(TAG_USER_AUTH_TYPE, keyParam)));
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void uploadKeyCharacteristicsAsProto(const hidl_vec<KeyParameter>& keyParams,
+                                     bool wasCreationSuccessful) {
+    KeyConfig keyConfig;
+    checkEnforcedCharacteristics(keyParams, &keyConfig);
+    auto dropbox = std::make_unique<android::os::DropBoxManager>();
+    keyConfig.set_was_creation_successful(wasCreationSuccessful);
+
+    size_t size = keyConfig.ByteSize();
+    auto data = std::make_unique<uint8_t[]>(size);
+    keyConfig.SerializeWithCachedSizesToArray(data.get());
+    dropbox->addData(android::String16("keymaster"), data.get(), size, 0);
+}
+
+}  // namespace keystore
diff --git a/keystore/key_proto_handler.h b/keystore/key_proto_handler.h
new file mode 100644
index 0000000..a2f6a24
--- /dev/null
+++ b/keystore/key_proto_handler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef KEYSTORE_KEY_PROTO_HANDLER_H_
+#define KEYSTORE_KEY_PROTO_HANDLER_H_
+
+#include <keystore/keystore_hidl_support.h>
+
+namespace keystore {
+
+void uploadKeyCharacteristicsAsProto(const hidl_vec<KeyParameter>& keyParams,
+                                     bool wasCreationSuccessful);
+
+}  // namespace keystore
+
+#endif  // KEYSTORE_KEY_PROTO_HANDLER_H_
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
index c8a8f84..fafa631 100644
--- a/keystore/key_store_service.cpp
+++ b/keystore/key_store_service.cpp
@@ -38,6 +38,7 @@
 #include <android/hardware/keymaster/3.0/IHwKeymasterDevice.h>
 
 #include "defaults.h"
+#include "key_proto_handler.h"
 #include "keystore_attestation_id.h"
 #include "keystore_keymaster_enforcement.h"
 #include "keystore_utils.h"
@@ -822,6 +823,7 @@
     }
     if (!error.isOk()) {
         ALOGE("Failed to generate key -> falling back to software keymaster");
+        uploadKeyCharacteristicsAsProto(params.getParameters(), false /* wasCreationSuccessful */);
         securityLevel = SecurityLevel::SOFTWARE;
 
         // No fall back for 3DES
@@ -847,6 +849,8 @@
             *aidl_return = static_cast<int32_t>(error);
             return Status::ok();
         }
+    } else {
+        uploadKeyCharacteristicsAsProto(params.getParameters(), true /* wasCreationSuccessful */);
     }
 
     if (!containsTag(params.getParameters(), Tag::USER_ID)) {
@@ -1053,6 +1057,7 @@
     // now check error from callback
     if (!error.isOk()) {
         ALOGE("Failed to import key -> falling back to software keymaster");
+        uploadKeyCharacteristicsAsProto(params.getParameters(), false /* wasCreationSuccessful */);
         securityLevel = SecurityLevel::SOFTWARE;
 
         // No fall back for 3DES
@@ -1081,6 +1086,8 @@
             *aidl_return = static_cast<int32_t>(error);
             return Status::ok();
         }
+    } else {
+        uploadKeyCharacteristicsAsProto(params.getParameters(), true /* wasCreationSuccessful */);
     }
 
     // Write the characteristics:
@@ -1338,8 +1345,9 @@
 
     // Note: The operation map takes possession of the contents of "characteristics".
     // It is safe to use characteristics after the following line but it will be empty.
-    sp<IBinder> operationToken = mOperationMap.addOperation(
-        result->handle, keyid, keyPurpose, dev, appToken, std::move(characteristics), pruneable);
+    sp<IBinder> operationToken =
+        mOperationMap.addOperation(result->handle, keyid, keyPurpose, dev, appToken,
+                                   std::move(characteristics), params.getParameters(), pruneable);
     assert(characteristics.hardwareEnforced.size() == 0);
     assert(characteristics.softwareEnforced.size() == 0);
     result->token = operationToken;
@@ -1479,20 +1487,23 @@
         op.device->finish(op.handle, inParams,
                           ::std::vector<uint8_t>() /* TODO(swillden): wire up input to finish() */,
                           signature, authToken, VerificationToken(), hidlCb));
-    // removeOperation() will free the memory 'op' used, so the order is important
-    mAuthTokenTable.MarkCompleted(op.handle);
-    mOperationMap.removeOperation(token);
 
+    bool wasOpSuccessful = true;
     // just a reminder: on success result->resultCode was set in the callback. So we only overwrite
     // it if there was a communication error indicated by the ErrorCode.
     if (!rc.isOk()) {
         result->resultCode = rc;
+        wasOpSuccessful = false;
     }
+
+    // removeOperation() will free the memory 'op' used, so the order is important
+    mAuthTokenTable.MarkCompleted(op.handle);
+    mOperationMap.removeOperation(token, wasOpSuccessful);
     return Status::ok();
 }
 
 Status KeyStoreService::abort(const sp<IBinder>& token, int32_t* aidl_return) {
-    auto getOpResult = mOperationMap.removeOperation(token);
+    auto getOpResult = mOperationMap.removeOperation(token, false /* wasOpSuccessful */);
     if (!getOpResult.isOk()) {
         *aidl_return = static_cast<int32_t>(ErrorCode::INVALID_OPERATION_HANDLE);
         return Status::ok();
diff --git a/keystore/operation.cpp b/keystore/operation.cpp
index e09d515..93b1e92 100644
--- a/keystore/operation.cpp
+++ b/keystore/operation.cpp
@@ -26,18 +26,18 @@
 
 sp<IBinder> OperationMap::addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
                                        const sp<Keymaster>& dev, const sp<IBinder>& appToken,
-                                       KeyCharacteristics&& characteristics, bool pruneable) {
+                                       KeyCharacteristics&& characteristics,
+                                       const hidl_vec<KeyParameter>& params, bool pruneable) {
     sp<IBinder> token = new ::android::BBinder();
-    mMap.emplace(token,
-                 Operation(handle, keyid, purpose, dev, std::move(characteristics), appToken));
+    mMap.emplace(token, Operation(handle, keyid, purpose, dev, std::move(characteristics), appToken,
+                                  params));
     if (pruneable) mLru.push_back(token);
     if (mAppTokenMap.find(appToken) == mAppTokenMap.end()) appToken->linkToDeath(mDeathRecipient);
     mAppTokenMap[appToken].push_back(token);
-
     return token;
 }
 
-NullOr<const OperationMap::Operation&> OperationMap::getOperation(const sp<IBinder>& token) {
+NullOr<const Operation&> OperationMap::getOperation(const sp<IBinder>& token) {
     auto entry = mMap.find(token);
     if (entry == mMap.end()) return {};
 
@@ -53,17 +53,17 @@
     }
 }
 
-NullOr<OperationMap::Operation> OperationMap::removeOperation(const sp<IBinder>& token) {
+NullOr<Operation> OperationMap::removeOperation(const sp<IBinder>& token, bool wasSuccessful) {
     auto entry = mMap.find(token);
     if (entry == mMap.end()) return {};
 
     Operation op = std::move(entry->second);
+    uploadOpAsProto(op, wasSuccessful);
     mMap.erase(entry);
 
     auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
     if (lruEntry != mLru.end()) mLru.erase(lruEntry);
     removeOperationTracking(token, op.appToken);
-
     return op;
 }
 
@@ -109,10 +109,4 @@
     return appEntry->second;
 }
 
-OperationMap::Operation::Operation(uint64_t handle_, uint64_t keyid_, KeyPurpose purpose_,
-                                   const sp<Keymaster>& device_,
-                                   KeyCharacteristics&& characteristics_, sp<IBinder> appToken_)
-    : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
-      characteristics(characteristics_), appToken(appToken_) {}
-
 }  // namespace keystore
diff --git a/keystore/operation.h b/keystore/operation.h
index 0acb70c..2d81f9c 100644
--- a/keystore/operation.h
+++ b/keystore/operation.h
@@ -26,6 +26,10 @@
 #include <utils/StrongPointer.h>
 
 #include <keystore/keymaster_types.h>
+#include <keystore/keystore_hidl_support.h>
+
+#include "operation_proto_handler.h"
+#include "operation_struct.h"
 
 namespace keystore {
 
@@ -42,30 +46,13 @@
 
 class OperationMap {
   public:
-    struct Operation {
-        Operation() = default;
-        Operation(uint64_t handle, uint64_t keyid, KeyPurpose purpose, const sp<Keymaster>& device,
-                  KeyCharacteristics&& characteristics, sp<IBinder> appToken);
-        Operation(Operation&&) = default;
-        Operation(const Operation&) = delete;
-
-        bool hasAuthToken() const { return authToken.mac.size() != 0; }
-
-        uint64_t handle;
-        uint64_t keyid;
-        KeyPurpose purpose;
-        sp<Keymaster> device;
-        KeyCharacteristics characteristics;
-        sp<IBinder> appToken;
-        HardwareAuthToken authToken;
-    };
-
     explicit OperationMap(IBinder::DeathRecipient* deathRecipient);
     sp<IBinder> addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
                              const sp<Keymaster>& dev, const sp<IBinder>& appToken,
-                             KeyCharacteristics&& characteristics, bool pruneable);
+                             KeyCharacteristics&& characteristics,
+                             const hidl_vec<KeyParameter>& params, bool pruneable);
     NullOr<const Operation&> getOperation(const sp<IBinder>& token);
-    NullOr<Operation> removeOperation(const sp<IBinder>& token);
+    NullOr<Operation> removeOperation(const sp<IBinder>& token, bool wasSuccessful);
     bool hasPruneableOperation() const;
     size_t getOperationCount() const { return mMap.size(); }
     size_t getPruneableOperationCount() const;
diff --git a/keystore/operation_config.proto b/keystore/operation_config.proto
new file mode 100644
index 0000000..37b4cbb
--- /dev/null
+++ b/keystore/operation_config.proto
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package keystore;
+
+option optimize_for = LITE_RUNTIME;
+
+message OperationConfig {
+  // What type of encryption algorithm is the key being used in the op for.
+  optional string algorithm = 1;
+
+  // Size of the key being used in this op
+  optional int32 key_size = 2;
+
+  // Log whether the key in this op was generated, imported,
+  // securely imported, or derived.
+  optional string origin = 3;
+
+  // What auth types does this op require? If none, then no auth required.
+  optional string user_auth_type = 4;
+
+  // If user authentication is required, is the requirement time based? If it
+  // is not time based then this field will not be used and the key is per
+  // operation. Per operation keys must be user authenticated on each usage.
+  optional int32 user_auth_key_timeout = 5;
+
+  // Track which padding mode was used for this operation.
+  optional string padding = 6;
+
+  // Keep track of the digest algorithm being used.
+  optional string digest = 7;
+
+  // Check what block mode is being used depending on the mode of encryption
+  optional string block_mode = 8;
+
+  // Did the operation succeed? If it didn't, this represents bugs or
+  // error cases occurring.
+  optional bool was_op_successful = 9;
+
+  // What purpose is this operation serving? Encrypt, decrypt, sign verify?
+  optional string purpose = 10;
+
+  // Which ec curve was selected if elliptic curve cryptography is in use
+  optional string ec_curve = 11;
+
+  // Standalone or is a file system required
+  optional string key_blob_usage_reqs = 12;
+}
diff --git a/keystore/operation_proto_handler.cpp b/keystore/operation_proto_handler.cpp
new file mode 100644
index 0000000..77e1b73
--- /dev/null
+++ b/keystore/operation_proto_handler.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "KeystoreOperation"
+
+#include "operation_proto_handler.h"
+
+#include <android/os/DropBoxManager.h>
+#include <google/protobuf/message_lite.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <keystore/keymaster_types.h>
+#include <keystore/keystore_hidl_support.h>
+#include <utils/String16.h>
+
+#include "operation_config.pb.h"
+
+namespace keystore {
+
+void determinePurpose(KeyPurpose purpose, OperationConfig* operationConfig) {
+    switch (purpose) {
+    case KeyPurpose::VERIFY:
+        operationConfig->set_purpose("verify");
+        break;
+    case KeyPurpose::ENCRYPT:
+        operationConfig->set_purpose("encrypt");
+        break;
+    case KeyPurpose::SIGN:
+        operationConfig->set_purpose("sign");
+        break;
+    case KeyPurpose::DECRYPT:
+        operationConfig->set_purpose("decrypt");
+        break;
+    case KeyPurpose::WRAP_KEY:
+        operationConfig->set_purpose("wrap");
+        break;
+    default:
+        break;
+    }
+}
+
+void checkKeyCharacteristics(const hidl_vec<KeyParameter>& characteristics,
+                             OperationConfig* operationConfig) {
+    for (auto& opParam : characteristics) {
+        switch (opParam.tag) {
+        case Tag::ALGORITHM:
+            operationConfig->set_algorithm(toString(accessTagValue(TAG_ALGORITHM, opParam)));
+            break;
+        case Tag::KEY_SIZE:
+            operationConfig->set_key_size(accessTagValue(TAG_KEY_SIZE, opParam));
+            break;
+        case Tag::EC_CURVE:
+            operationConfig->set_ec_curve(toString(accessTagValue(TAG_EC_CURVE, opParam)));
+            break;
+        case Tag::AUTH_TIMEOUT:
+            operationConfig->set_user_auth_key_timeout(accessTagValue(TAG_AUTH_TIMEOUT, opParam));
+            break;
+        case Tag::ORIGIN:
+            operationConfig->set_origin(toString(accessTagValue(TAG_ORIGIN, opParam)));
+            break;
+        case Tag::BLOB_USAGE_REQUIREMENTS:
+            operationConfig->set_key_blob_usage_reqs(
+                toString(accessTagValue(TAG_BLOB_USAGE_REQUIREMENTS, opParam)));
+            break;
+        case Tag::USER_AUTH_TYPE:
+            operationConfig->set_user_auth_type(
+                toString(accessTagValue(TAG_USER_AUTH_TYPE, opParam)));
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void checkOpCharacteristics(const hidl_vec<KeyParameter>& characteristics,
+                            OperationConfig* operationConfig) {
+    for (auto& opParam : characteristics) {
+        switch (opParam.tag) {
+        case Tag::BLOCK_MODE:
+            operationConfig->set_block_mode(toString(accessTagValue(TAG_BLOCK_MODE, opParam)));
+            break;
+        case Tag::PADDING:
+            operationConfig->set_padding(toString(accessTagValue(TAG_PADDING, opParam)));
+            break;
+        case Tag::DIGEST:
+            operationConfig->set_digest(toString(accessTagValue(TAG_DIGEST, opParam)));
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void uploadOpAsProto(Operation& op, bool wasOpSuccessful) {
+    OperationConfig operationConfig;
+    determinePurpose(op.purpose, &operationConfig);
+    checkKeyCharacteristics(op.characteristics.softwareEnforced, &operationConfig);
+    checkKeyCharacteristics(op.characteristics.hardwareEnforced, &operationConfig);
+    checkOpCharacteristics(op.params, &operationConfig);
+    auto dropbox = std::make_unique<android::os::DropBoxManager>();
+    operationConfig.set_was_op_successful(wasOpSuccessful);
+
+    size_t size = operationConfig.ByteSize();
+    auto data = std::make_unique<uint8_t[]>(size);
+    operationConfig.SerializeWithCachedSizesToArray(data.get());
+    dropbox->addData(android::String16("keymaster"), data.get(), size, 0);
+}
+
+}  // namespace keystore
diff --git a/keystore/operation_proto_handler.h b/keystore/operation_proto_handler.h
new file mode 100644
index 0000000..bf461b4
--- /dev/null
+++ b/keystore/operation_proto_handler.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef KEYSTORE_OPERATION_PROTO_HANDLER_H_
+#define KEYSTORE_OPERATION_PROTO_HANDLER_H_
+
+#include "operation_struct.h"
+
+namespace keystore {
+
+using ::android::IBinder;
+using keymaster::support::Keymaster;
+
+void uploadOpAsProto(Operation& op, bool wasOpSuccessful);
+
+}  // namespace keystore
+
+#endif  // KEYSTORE_OPERATION_PROTO_HANDLER_H_
diff --git a/keystore/operation_struct.h b/keystore/operation_struct.h
new file mode 100644
index 0000000..ea8a908
--- /dev/null
+++ b/keystore/operation_struct.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef KEYSTORE_OPERATION_STRUCT_H_
+#define KEYSTORE_OPERATION_STRUCT_H_
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <utils/StrongPointer.h>
+
+#include <keystore/keymaster_types.h>
+#include <keystore/keystore_hidl_support.h>
+
+namespace keystore {
+
+using ::android::IBinder;
+using ::android::sp;
+using keymaster::support::Keymaster;
+
+struct Operation {
+    Operation() = default;
+    Operation(uint64_t handle_, uint64_t keyid_, KeyPurpose purpose_, const sp<Keymaster>& device_,
+              KeyCharacteristics&& characteristics_, sp<IBinder> appToken_,
+              const hidl_vec<KeyParameter> params_)
+        : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
+          characteristics(characteristics_), appToken(appToken_), params(params_) {}
+    Operation(Operation&&) = default;
+    Operation(const Operation&) = delete;
+
+    bool hasAuthToken() const { return authToken.mac.size() != 0; }
+
+    uint64_t handle;
+    uint64_t keyid;
+    KeyPurpose purpose;
+    sp<Keymaster> device;
+    KeyCharacteristics characteristics;
+    sp<IBinder> appToken;
+    HardwareAuthToken authToken;
+    const hidl_vec<KeyParameter> params;
+};
+
+}  // namespace keystore
+
+#endif