Merge "nativeloader: Add CloseNativeLibrary() method"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b3661e4..3cad427 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -56,3 +56,5 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.$(TARGET_DEVICE).so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h
deleted file mode 100644
index 0854775..0000000
--- a/include/utils/ashmem.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* utils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
- */
-
-#ifndef _UTILS_ASHMEM_H
-#define _UTILS_ASHMEM_H
-
-#include <linux/limits.h>
-#include <linux/ioctl.h>
-
-#define ASHMEM_NAME_LEN 256
-
-#define ASHMEM_NAME_DEF "dev/ashmem"
-
-/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
-#define ASHMEM_NOT_REAPED 0
-#define ASHMEM_WAS_REAPED 1
-
-/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
-#define ASHMEM_NOW_UNPINNED 0
-#define ASHMEM_NOW_PINNED 1
-
-#define __ASHMEMIOC 0x77
-
-#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
-#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
-#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
-#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
-#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
-#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
-#define ASHMEM_PIN _IO(__ASHMEMIOC, 7)
-#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8)
-#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9)
-#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
-
-#endif /* _UTILS_ASHMEM_H */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index d147bb9..2a6aaec 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -53,6 +53,9 @@
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
#endif
+__attribute__((visibility("default")))
+void ResetNativeLoader();
+
}; // namespace android
#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 20606b9..7d9bb56 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -121,6 +121,10 @@
public_libraries_ = base::Join(sonames, ':');
}
+ void Reset() {
+ namespaces_.clear();
+ }
+
private:
bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames) {
// Read list of public native libraries from the config file.
@@ -172,6 +176,12 @@
#endif
}
+void ResetNativeLoader() {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->Reset();
+#endif
+}
jstring CreateClassLoaderNamespace(JNIEnv* env,
int32_t target_sdk_version,
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
new file mode 100644
index 0000000..3982c8f
--- /dev/null
+++ b/trusty/gatekeeper/Android.mk
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2015 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.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK. Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gatekeeper.trusty
+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SRC_FILES := \
+ module.cpp \
+ trusty_gatekeeper_ipc.c \
+ trusty_gatekeeper.cpp
+
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ libgatekeeper \
+ liblog \
+ libcutils \
+ libtrusty
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/gatekeeper/gatekeeper_ipc.h b/trusty/gatekeeper/gatekeeper_ipc.h
new file mode 100644
index 0000000..b05dcd8
--- /dev/null
+++ b/trusty/gatekeeper/gatekeeper_ipc.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma once
+
+#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MAX_BUFFER_LENGTH 1024
+
+enum gatekeeper_command {
+ GK_REQ_SHIFT = 1,
+ GK_RESP_BIT = 1,
+
+ GK_ENROLL = (0 << GK_REQ_SHIFT),
+ GK_VERIFY = (1 << GK_REQ_SHIFT),
+};
+
+/**
+ * gatekeeper_message - Serial header for communicating with GK server
+ * @cmd: the command, one of ENROLL, VERIFY. Payload must be a serialized
+ * buffer of the corresponding request object.
+ * @payload: start of the serialized command specific payload
+ */
+struct gatekeeper_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
new file mode 100644
index 0000000..0ee3c2f
--- /dev/null
+++ b/trusty/gatekeeper/module.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <hardware/hardware.h>
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "trusty_gatekeeper.h"
+
+using gatekeeper::TrustyGateKeeperDevice;
+
+static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
+ hw_device_t **device) {
+
+ if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
+ return -EINVAL;
+ }
+
+ TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
+ if (gatekeeper == NULL) return -ENOMEM;
+ *device = gatekeeper->hw_device();
+
+ return 0;
+}
+
+static struct hw_module_methods_t gatekeeper_module_methods = {
+ .open = trusty_gatekeeper_open,
+};
+
+struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = GATEKEEPER_HARDWARE_MODULE_ID,
+ .name = "Trusty GateKeeper HAL",
+ .author = "The Android Open Source Project",
+ .methods = &gatekeeper_module_methods,
+ .dso = 0,
+ .reserved = {}
+ },
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
new file mode 100644
index 0000000..d24f44f
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <type_traits>
+
+#include "trusty_gatekeeper.h"
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+
+namespace gatekeeper {
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
+ "TrustyGateKeeperDevice must be standard layout");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
+ "device_ must be the first member of TrustyGateKeeperDevice");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
+ "common must be the first member of gatekeeper_device");
+#else
+ assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
+ assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
+#endif
+
+ memset(&device_, 0, sizeof(device_));
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t *>(module);
+ device_.common.close = close_device;
+
+ device_.enroll = enroll;
+ device_.verify = verify;
+ device_.delete_user = nullptr;
+ device_.delete_all_users = nullptr;
+
+ int rc = trusty_gatekeeper_connect();
+ if (rc < 0) {
+ ALOGE("Error initializing trusty session: %d", rc);
+ }
+
+ error_ = rc;
+
+}
+
+hw_device_t* TrustyGateKeeperDevice::hw_device() {
+ return &device_.common;
+}
+
+int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
+ return 0;
+}
+
+TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
+ trusty_gatekeeper_disconnect();
+}
+
+int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length) {
+
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer desired_password_buffer(desired_password_length);
+ memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
+
+ SizedBuffer current_password_handle_buffer(current_password_handle_length);
+ if (current_password_handle) {
+ memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
+ current_password_handle_length);
+ }
+
+ SizedBuffer current_password_buffer(current_password_length);
+ if (current_password) {
+ memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
+ }
+
+ EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
+ ¤t_password_buffer);
+ EnrollResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+ *enrolled_password_handle_length = response.enrolled_password_handle.length;
+
+
+ return 0;
+}
+
+int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer password_handle_buffer(enrolled_password_handle_length);
+ memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
+ enrolled_password_handle_length);
+ SizedBuffer provided_password_buffer(provided_password_length);
+ memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+
+ VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+ VerifyResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ if (auth_token != NULL && auth_token_length != NULL) {
+ *auth_token = response.auth_token.buffer.release();
+ *auth_token_length = response.auth_token.length;
+ }
+
+ if (request_reenroll != NULL) {
+ *request_reenroll = response.request_reenroll;
+ }
+
+ return 0;
+}
+
+gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage *response) {
+ uint32_t request_size = request.GetSerializedSize();
+ if (request_size > SEND_BUF_SIZE)
+ return ERROR_INVALID;
+ uint8_t send_buf[SEND_BUF_SIZE];
+ request.Serialize(send_buf, send_buf + request_size);
+
+ // Send it
+ uint8_t recv_buf[RECV_BUF_SIZE];
+ uint32_t response_size = RECV_BUF_SIZE;
+ int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
+ if (rc < 0) {
+ ALOGE("error (%d) calling gatekeeper TA", rc);
+ return ERROR_INVALID;
+ }
+
+ const gatekeeper_message *msg = reinterpret_cast<gatekeeper_message *>(recv_buf);
+ const uint8_t *payload = msg->payload;
+
+ return response->Deserialize(payload, payload + response_size);
+}
+
+static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
+ return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
+}
+
+/* static */
+int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+
+ if (dev == NULL ||
+ enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
+ desired_password == NULL || desired_password_length == 0)
+ return -EINVAL;
+
+ // Current password and current password handle go together
+ if (current_password_handle == NULL || current_password_handle_length == 0 ||
+ current_password == NULL || current_password_length == 0) {
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
+ current_password, current_password_length, desired_password, desired_password_length,
+ enrolled_password_handle, enrolled_password_handle_length);
+
+}
+
+/* static */
+int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
+ uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll) {
+
+ if (dev == NULL || enrolled_password_handle == NULL ||
+ provided_password == NULL) {
+ return -EINVAL;
+ }
+
+ return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
+ enrolled_password_handle_length, provided_password, provided_password_length,
+ auth_token, auth_token_length, request_reenroll);
+}
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
new file mode 100644
index 0000000..82108dc
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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 TRUSTY_GATEKEEPER_H
+#define TRUSTY_GATEKEEPER_H
+
+#include <hardware/gatekeeper.h>
+#include <gatekeeper/gatekeeper_messages.h>
+
+#include "gatekeeper_ipc.h"
+
+namespace gatekeeper {
+
+class TrustyGateKeeperDevice {
+ public:
+
+ TrustyGateKeeperDevice(const hw_module_t* module);
+ ~TrustyGateKeeperDevice();
+
+ hw_device_t* hw_device();
+
+ /**
+ * Enrolls password_payload, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password will not be allocated.
+ */
+ int Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches expected_password after enrolling
+ * with the authentication factor private key.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to verification_token,
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll);
+
+ private:
+
+ gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage* response);
+
+ gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+ return Send(GK_ENROLL, request, response);
+ }
+
+ gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+ return Send(GK_VERIFY, request, response);
+ }
+
+ // Static methods interfacing the HAL API with the TrustyGateKeeper device
+
+ /**
+ * Enrolls desired_password, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * If there was already a password enrolled, it should be provided in
+ * current_password_handle, along with the current password in current_password
+ * that should validate against current_password_handle.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+
+ static int close_device(hw_device_t* dev);
+
+ gatekeeper_device device_;
+ int error_;
+
+};
+}
+
+#endif
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
new file mode 100644
index 0000000..a1c319e
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+#include <trusty/tipc.h>
+
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_gatekeeper_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, GATEKEEPER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size) {
+ if (handle_ == 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct gatekeeper_message);
+ struct gatekeeper_message *msg = malloc(msg_size);
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+ GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ rc = read(handle_, out, *out_size);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+ cmd, GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t) rc < sizeof(struct gatekeeper_message)) {
+ ALOGE("invalid response size (%d)\n", (int) rc);
+ return -EINVAL;
+ }
+
+ msg = (struct gatekeeper_message *) out;
+
+ if ((cmd | GK_RESP_BIT) != msg->cmd) {
+ ALOGE("invalid command (%d)\n", msg->cmd);
+ return -EINVAL;
+ }
+
+ *out_size = ((size_t) rc) - sizeof(struct gatekeeper_message);
+ return rc;
+}
+
+void trusty_gatekeeper_disconnect() {
+ if (handle_ != 0) {
+ tipc_close(handle_);
+ }
+}
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.h b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
new file mode 100644
index 0000000..f8de7f8
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+__BEGIN_DECLS
+
+int trusty_gatekeeper_connect();
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size);
+void trusty_gatekeeper_disconnect();
+
+__END_DECLS
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
new file mode 100644
index 0000000..0ebf52d
--- /dev/null
+++ b/trusty/keymaster/Android.mk
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2015 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.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK. Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+###
+# trusty_keymaster is a binary used only for on-device testing. It
+# runs Trusty Keymaster through a basic set of operations with RSA
+# and ECDSA keys.
+###
+LOCAL_MODULE := trusty_keymaster_tipc
+LOCAL_SRC_FILES := \
+ trusty_keymaster_device.cpp \
+ trusty_keymaster_ipc.c \
+ trusty_keymaster_main.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libcutils \
+ libkeymaster1 \
+ libtrusty \
+ libkeymaster_messages \
+ liblog
+
+include $(BUILD_EXECUTABLE)
+
+###
+# keystore.trusty is the HAL used by keystore on Trusty devices.
+##
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := keystore.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := module.cpp \
+ trusty_keymaster_ipc.c \
+ trusty_keymaster_device.cpp
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libkeymaster_messages \
+ libtrusty \
+ liblog \
+ libcutils
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Makefile b/trusty/keymaster/Makefile
new file mode 100644
index 0000000..f575381
--- /dev/null
+++ b/trusty/keymaster/Makefile
@@ -0,0 +1,199 @@
+#####
+# Local unit test Makefile
+#
+# This makefile builds and runs the trusty_keymaster unit tests locally on the development
+# machine, not on an Android device.
+#
+# To build and run these tests, one pre-requisite must be manually installed: BoringSSL.
+# This Makefile expects to find BoringSSL in a directory adjacent to $ANDROID_BUILD_TOP.
+# To get and build it, first install the Ninja build tool (e.g. apt-get install
+# ninja-build), then do:
+#
+# cd $ANDROID_BUILD_TOP/..
+# git clone https://boringssl.googlesource.com/boringssl
+# cd boringssl
+# mdkir build
+# cd build
+# cmake -GNinja ..
+# ninja
+#
+# Then return to $ANDROID_BUILD_TOP/system/keymaster and run "make".
+#####
+
+BASE=../../../..
+SUBS=system/core \
+ system/keymaster \
+ hardware/libhardware \
+ external/gtest
+GTEST=$(BASE)/external/gtest
+KM=$(BASE)/system/keymaster
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+ -I $(BASE)/libnativehelper/include/nativehelper \
+ -I ../tipc/include \
+ -I $(BASE)/system/keymaster \
+ -I $(GTEST) \
+ -I$(BASE)/../boringssl/include
+
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
+ -Wmissing-declarations -ftest-coverage \
+ -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+ $(COMPILER_SPECIFIC_ARGS)
+LDLIBS=-L$(BASE)/../boringssl/build/crypto -lcrypto -lpthread -lstdc++
+
+CPPSRCS=\
+ $(KM)/aead_mode_operation.cpp \
+ $(KM)/aes_key.cpp \
+ $(KM)/aes_operation.cpp \
+ $(KM)/android_keymaster.cpp \
+ $(KM)/android_keymaster_messages.cpp \
+ $(KM)/android_keymaster_messages_test.cpp \
+ $(KM)/android_keymaster_test.cpp \
+ $(KM)/android_keymaster_test_utils.cpp \
+ $(KM)/android_keymaster_utils.cpp \
+ $(KM)/asymmetric_key.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/authorization_set.cpp \
+ $(KM)/authorization_set_test.cpp \
+ $(KM)/ec_key.cpp \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.cpp \
+ $(KM)/hmac_key.cpp \
+ $(KM)/hmac_operation.cpp \
+ $(KM)/integrity_assured_key_blob.cpp \
+ $(KM)/key.cpp \
+ $(KM)/key_blob_test.cpp \
+ $(KM)/keymaster0_engine.cpp \
+ $(KM)/logger.cpp \
+ $(KM)/ocb_utils.cpp \
+ $(KM)/openssl_err.cpp \
+ $(KM)/openssl_utils.cpp \
+ $(KM)/operation.cpp \
+ $(KM)/operation_table.cpp \
+ $(KM)/rsa_key.cpp \
+ $(KM)/rsa_keymaster0_key.cpp \
+ $(KM)/rsa_operation.cpp \
+ $(KM)/serializable.cpp \
+ $(KM)/soft_keymaster_context.cpp \
+ $(KM)/symmetric_key.cpp \
+ $(KM)/unencrypted_key_blob.cpp \
+ trusty_keymaster_device.cpp \
+ trusty_keymaster_device_test.cpp
+CCSRCS=$(GTEST)/src/gtest-all.cc
+CSRCS=ocb.c
+
+OBJS=$(CPPSRCS:.cpp=.o) $(CCSRCS:.cc=.o) $(CSRCS:.c=.o)
+DEPS=$(CPPSRCS:.cpp=.d) $(CCSRCS:.cc=.d) $(CSRCS:.c=.d)
+GCDA=$(CPPSRCS:.cpp=.gcda) $(CCSRCS:.cc=.gcda) $(CSRCS:.c=.gcda)
+GCNO=$(CPPSRCS:.cpp=.gcno) $(CCSRCS:.cc=.gcno) $(CSRCS:.c=.gcno)
+
+LINK.o=$(LINK.cc)
+
+BINARIES=trusty_keymaster_device_test
+
+ifdef TRUSTY
+BINARIES += trusty_keymaster_device_test
+endif # TRUSTY
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+ ./$<
+ touch $@
+
+run: $(BINARIES:=.run)
+
+coverage: coverage.info
+ genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+ lcov --capture --directory=. --output-file coverage.info
+
+%.coverage : %
+ $(MAKE) clean && $(MAKE) $<
+ ./$<
+ lcov --capture --directory=. --output-file coverage.info
+ genhtml coverage.info --output-directory coverage
+
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+ --show-reachable=yes \
+ --vgdb=full \
+ $(UNINIT_OPTS) \
+ --error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+ --stacks=yes
+
+%.memcheck : %
+ valgrind $(MEMCHECK_OPTS) ./$< && \
+ touch $@
+
+%.massif : %
+ valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+trusty_keymaster_device_test: trusty_keymaster_device_test.o \
+ trusty_keymaster_device.o \
+ $(KM)/aead_mode_operation.o \
+ $(KM)/aes_key.o \
+ $(KM)/aes_operation.o \
+ $(KM)/android_keymaster.o \
+ $(KM)/android_keymaster_messages.o \
+ $(KM)/android_keymaster_test_utils.o \
+ $(KM)/android_keymaster_utils.o \
+ $(KM)/asymmetric_key.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/authorization_set.o \
+ $(KM)/ec_key.o \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.o \
+ $(KM)/hmac_key.o \
+ $(KM)/hmac_operation.o \
+ $(KM)/integrity_assured_key_blob.o \
+ $(KM)/key.o \
+ $(KM)/keymaster0_engine.o \
+ $(KM)/logger.o \
+ $(KM)/ocb.o \
+ $(KM)/ocb_utils.o \
+ $(KM)/openssl_err.o \
+ $(KM)/openssl_utils.o \
+ $(KM)/operation.o \
+ $(KM)/operation_table.o \
+ $(KM)/rsa_key.o \
+ $(KM)/rsa_keymaster0_key.o \
+ $(KM)/rsa_operation.o \
+ $(KM)/serializable.o \
+ $(KM)/soft_keymaster_context.o \
+ $(KM)/symmetric_key.o \
+ $(GTEST)/src/gtest-all.o
+
+$(GTEST)/src/gtest-all.o: CXXFLAGS:=$(subst -Wmissing-declarations,,$(CXXFLAGS))
+ocb.o: CFLAGS=$(CLANG_TEST_DEFINE)
+
+clean:
+ rm -f $(OBJS) $(DEPS) $(GCDA) $(GCNO) $(BINARIES) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+ coverage.info
+ rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
new file mode 100644
index 0000000..48fa53d
--- /dev/null
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#pragma once
+
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MAX_BUFFER_LENGTH 4096
+
+// Commands
+enum keymaster_command {
+ KEYMASTER_RESP_BIT = 1,
+ KEYMASTER_REQ_SHIFT = 1,
+
+ KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
+ KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
+ KM_UPDATE_OPERATION = (2 << KEYMASTER_REQ_SHIFT),
+ KM_FINISH_OPERATION = (3 << KEYMASTER_REQ_SHIFT),
+ KM_ABORT_OPERATION = (4 << KEYMASTER_REQ_SHIFT),
+ KM_IMPORT_KEY = (5 << KEYMASTER_REQ_SHIFT),
+ KM_EXPORT_KEY = (6 << KEYMASTER_REQ_SHIFT),
+ KM_GET_VERSION = (7 << KEYMASTER_REQ_SHIFT),
+ KM_ADD_RNG_ENTROPY = (8 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_ALGORITHMS = (9 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_BLOCK_MODES = (10 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_PADDING_MODES = (11 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_DIGESTS = (12 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
+ KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT),
+};
+
+#ifdef __ANDROID__
+
+/**
+ * keymaster_message - Serial header for communicating with KM server
+ * @cmd: the command, one of keymaster_command.
+ * @payload: start of the serialized command specific payload
+ */
+struct keymaster_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
+#endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
new file mode 100644
index 0000000..81597d9
--- /dev/null
+++ b/trusty/keymaster/module.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include "trusty_keymaster_device.h"
+
+using keymaster::TrustyKeymasterDevice;
+
+/*
+ * Generic device handling
+ */
+static int trusty_keymaster_open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+ return -EINVAL;
+
+ TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
+ if (dev == NULL)
+ return -ENOMEM;
+ *device = dev->hw_device();
+ // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
+ // exist until then.
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ .open = trusty_keymaster_open,
+};
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Trusty Keymaster HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
+ },
+};
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
new file mode 100644
index 0000000..069b4fe
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include "trusty_keymaster_device.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stddef.h>
+
+#include <type_traits>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#define LOG_TAG "TrustyKeymaster"
+#include <cutils/log.h>
+#include <hardware/keymaster0.h>
+
+#include <keymaster/authorization_set.h>
+
+#include "trusty_keymaster_ipc.h"
+#include "keymaster_ipc.h"
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+namespace keymaster {
+
+static keymaster_error_t translate_error(int err) {
+ switch (err) {
+ case 0:
+ return KM_ERROR_OK;
+ case -EPERM:
+ case -EACCES:
+ return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+
+ case -ECANCELED:
+ return KM_ERROR_OPERATION_CANCELLED;
+
+ case -ENODEV:
+ return KM_ERROR_UNIMPLEMENTED;
+
+ case -ENOMEM:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ case -EBUSY:
+ return KM_ERROR_SECURE_HW_BUSY;
+
+ case -EIO:
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+
+ case -EOVERFLOW:
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ default:
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+}
+
+TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
+ static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
+ "TrustyKeymasterDevice must be standard layout");
+ static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
+ "device_ must be the first member of KeymasterOpenSsl");
+ static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
+ "common must be the first member of keymaster_device");
+
+ ALOGI("Creating device");
+ ALOGD("Device address: %p", this);
+
+ memset(&device_, 0, sizeof(device_));
+
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t*>(module);
+ device_.common.close = close_device;
+
+ device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+
+ device_.generate_keypair = generate_keypair;
+ device_.import_keypair = import_keypair;
+ device_.get_keypair_public = get_keypair_public;
+ device_.delete_keypair = NULL;
+ device_.delete_all = NULL;
+ device_.sign_data = sign_data;
+ device_.verify_data = verify_data;
+
+ device_.context = NULL;
+
+ int rc = trusty_keymaster_connect();
+ error_ = translate_error(rc);
+ if (rc < 0) {
+ ALOGE("failed to connect to keymaster (%d)", rc);
+ return;
+ }
+
+ GetVersionRequest version_request;
+ GetVersionResponse version_response;
+ error_ = Send(version_request, &version_response);
+ if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
+ ALOGI("\"Bad parameters\" error on GetVersion call. Assuming version 0.");
+ message_version_ = 0;
+ error_ = KM_ERROR_OK;
+ }
+ message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
+ version_response.subminor_ver);
+ if (message_version_ < 0) {
+ // Can't translate version? Keymaster implementation must be newer.
+ ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
+ version_response.minor_ver, version_response.subminor_ver);
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ }
+}
+
+TrustyKeymasterDevice::~TrustyKeymasterDevice() {
+ trusty_keymaster_disconnect();
+}
+
+const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+
+int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** key_blob,
+ size_t* key_blob_length) {
+ ALOGD("Device received generate_keypair");
+
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ GenerateKeyRequest req(message_version_);
+ StoreNewKeyParams(&req.key_description);
+
+ switch (key_type) {
+ case TYPE_RSA: {
+ req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ const keymaster_rsa_keygen_params_t* rsa_params =
+ static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
+ ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
+ rsa_params->modulus_size, rsa_params->public_exponent);
+ req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
+ req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
+ break;
+ }
+
+ case TYPE_EC: {
+ req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
+ const keymaster_ec_keygen_params_t* ec_params =
+ static_cast<const keymaster_ec_keygen_params_t*>(key_params);
+ ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
+ req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
+ break;
+ }
+ default:
+ ALOGD("Received request for unsuported key type %d", key_type);
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+
+ GenerateKeyResponse rsp(message_version_);
+ ALOGD("Sending generate request");
+ keymaster_error_t err = Send(req, &rsp);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Got error %d from send", err);
+ return err;
+ }
+
+ *key_blob_length = rsp.key_blob.key_material_size;
+ *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
+ memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
+ ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
+
+ return KM_ERROR_OK;
+}
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+
+int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
+ uint8_t** key_blob, size_t* key_blob_length) {
+ ALOGD("Device received import_keypair");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ if (!key)
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ if (!key_blob || !key_blob_length)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ ImportKeyRequest request(message_version_);
+ StoreNewKeyParams(&request.key_description);
+ keymaster_algorithm_t algorithm;
+ keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
+ if (err != KM_ERROR_OK)
+ return err;
+ request.key_description.push_back(TAG_ALGORITHM, algorithm);
+
+ request.SetKeyMaterial(key, key_length);
+ request.key_format = KM_KEY_FORMAT_PKCS8;
+ ImportKeyResponse response(message_version_);
+ err = Send(request, &response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ *key_blob_length = response.key_blob.key_material_size;
+ *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
+ memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
+ printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
+ keymaster_algorithm_t* algorithm) {
+ if (key == NULL) {
+ ALOGE("No key specified for import");
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+
+ UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
+ d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
+ if (pkcs8.get() == NULL) {
+ ALOGE("Could not parse PKCS8 key blob");
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ ALOGE("Could not extract key from PKCS8 key blob");
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ *algorithm = KM_ALGORITHM_RSA;
+ break;
+ case EVP_PKEY_EC:
+ *algorithm = KM_ALGORITHM_EC;
+ break;
+ default:
+ ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+ ALOGD("Device received get_keypair_public");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ ExportKeyRequest request(message_version_);
+ request.SetKeyMaterial(key_blob, key_blob_length);
+ request.key_format = KM_KEY_FORMAT_X509;
+ ExportKeyResponse response(message_version_);
+ keymaster_error_t err = Send(request, &response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ *x509_data_length = response.key_data_length;
+ *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
+ memcpy(*x509_data, response.key_data, *x509_data_length);
+ printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* data,
+ const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length) {
+ ALOGD("Device received sign_data, %d", error_);
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ BeginOperationRequest begin_request(message_version_);
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob, key_blob_length);
+ keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
+ &begin_request.additional_params);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error extracting signing params: %d", err);
+ return err;
+ }
+
+ BeginOperationResponse begin_response(message_version_);
+ ALOGD("Sending signing request begin");
+ err = Send(begin_request, &begin_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign begin: %d", err);
+ return err;
+ }
+
+ UpdateOperationRequest update_request(message_version_);
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(data, data_length);
+ UpdateOperationResponse update_response(message_version_);
+ ALOGD("Sending signing request update");
+ err = Send(update_request, &update_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign update: %d", err);
+ return err;
+ }
+
+ FinishOperationRequest finish_request(message_version_);
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response(message_version_);
+ ALOGD("Sending signing request finish");
+ err = Send(finish_request, &finish_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign finish: %d", err);
+ return err;
+ }
+
+ *signed_data_length = finish_response.output.available_read();
+ *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
+ if (!finish_response.output.read(*signed_data, *signed_data_length)) {
+ ALOGE("Error reading response data: %d", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* signed_data,
+ const size_t signed_data_length, const uint8_t* signature,
+ const size_t signature_length) {
+ ALOGD("Device received verify_data");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ BeginOperationRequest begin_request(message_version_);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ begin_request.SetKeyMaterial(key_blob, key_blob_length);
+ keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
+ &begin_request.additional_params);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ BeginOperationResponse begin_response(message_version_);
+ err = Send(begin_request, &begin_response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ UpdateOperationRequest update_request(message_version_);
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(signed_data, signed_data_length);
+ UpdateOperationResponse update_response(message_version_);
+ err = Send(update_request, &update_response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ FinishOperationRequest finish_request(message_version_);
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(signature, signature_length);
+ FinishOperationResponse finish_response(message_version_);
+ err = Send(finish_request, &finish_response);
+ if (err != KM_ERROR_OK)
+ return err;
+ return KM_ERROR_OK;
+}
+
+hw_device_t* TrustyKeymasterDevice::hw_device() {
+ return &device_.common;
+}
+
+static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
+ return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+}
+
+/* static */
+int TrustyKeymasterDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyKeymasterDevice*>(dev);
+ return 0;
+}
+
+/* static */
+int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
+ const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** keyBlob,
+ size_t* keyBlobLength) {
+ ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
+ return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+}
+
+/* static */
+int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob,
+ size_t* key_blob_length) {
+ return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+}
+
+/* static */
+int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+ return convert_device(dev)
+ ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+}
+
+/* static */
+int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
+ const uint8_t* keyBlob, const size_t keyBlobLength,
+ const uint8_t* data, const size_t dataLength,
+ uint8_t** signedData, size_t* signedDataLength) {
+ return convert_device(dev)
+ ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+}
+
+/* static */
+int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
+ const uint8_t* keyBlob, const size_t keyBlobLength,
+ const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
+ signedDataLength, signature, signatureLength);
+}
+
+keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
+ KeymasterResponse* rsp) {
+ uint32_t req_size = req.SerializedSize();
+ if (req_size > SEND_BUF_SIZE)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ uint8_t send_buf[SEND_BUF_SIZE];
+ Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
+ req.Serialize(send_buf, send_buf + req_size);
+
+ // Send it
+ uint8_t recv_buf[RECV_BUF_SIZE];
+ Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
+ uint32_t rsp_size = RECV_BUF_SIZE;
+ printf("Sending %d byte request\n", (int)req.SerializedSize());
+ int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
+ if (rc < 0) {
+ ALOGE("tipc error: %d\n", rc);
+ // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
+ return translate_error(rc);
+ } else {
+ ALOGV("Received %d byte response\n", rsp_size);
+ }
+
+ const keymaster_message* msg = (keymaster_message *) recv_buf;
+ const uint8_t *p = msg->payload;
+ if (!rsp->Deserialize(&p, p + rsp_size)) {
+ ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (rsp->error != KM_ERROR_OK) {
+ ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+ return rsp->error;
+ }
+ return rsp->error;
+}
+
+keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
+ const uint8_t* key_blob,
+ size_t key_blob_length,
+ AuthorizationSet* auth_set) {
+ uint8_t* pub_key_data;
+ size_t pub_key_data_length;
+ int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
+ &pub_key_data_length);
+ if (err < 0) {
+ ALOGE("Error %d extracting public key to determine algorithm", err);
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+ UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
+
+ const uint8_t* p = pub_key_data;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA: {
+ const keymaster_rsa_sign_params_t* rsa_params =
+ reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
+ if (rsa_params->digest_type != DIGEST_NONE)
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ if (rsa_params->padding_type != PADDING_NONE)
+ return KM_ERROR_UNSUPPORTED_PADDING_MODE;
+ if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
+ !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ } break;
+ case EVP_PKEY_EC: {
+ const keymaster_ec_sign_params_t* ecdsa_params =
+ reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
+ if (ecdsa_params->digest_type != DIGEST_NONE)
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ } break;
+ default:
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+ return KM_ERROR_OK;
+}
+
+void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
+ auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
+ auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
+ auth_set->push_back(TAG_ALL_USERS);
+ auth_set->push_back(TAG_NO_AUTH_REQUIRED);
+ uint64_t now = java_time(time(NULL));
+ auth_set->push_back(TAG_CREATION_DATETIME, now);
+ auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
+ if (message_version_ == 0) {
+ auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
+ auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
+ } else {
+ auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
+ }
+}
+
+} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
new file mode 100644
index 0000000..cb74386
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 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 EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster_messages.h>
+
+#include "keymaster_ipc.h"
+
+namespace keymaster {
+
+/**
+ * Software OpenSSL-based Keymaster device.
+ *
+ * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
+ * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
+ * no data members which aren't standard layout), and device_ must be the first data member.
+ * Assertions in the constructor validate compliance with those constraints.
+ */
+class TrustyKeymasterDevice {
+ public:
+ /*
+ * These are the only symbols that will be exported by libtrustykeymaster. All functionality
+ * can be reached via the function pointers in device_.
+ */
+ __attribute__((visibility("default"))) TrustyKeymasterDevice(const hw_module_t* module);
+ __attribute__((visibility("default"))) hw_device_t* hw_device();
+
+ ~TrustyKeymasterDevice();
+
+ keymaster_error_t session_error() { return error_; }
+
+ int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
+ uint8_t** key_blob, size_t* key_blob_length);
+ int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
+ size_t* key_blob_length);
+ int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length);
+ int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* data, const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length);
+ int verify_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* signed_data,
+ const size_t signed_data_length, const uint8_t* signature,
+ const size_t signature_length);
+
+ private:
+ keymaster_error_t Send(uint32_t command, const Serializable& request,
+ KeymasterResponse* response);
+ keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
+ return Send(KM_GENERATE_KEY, request, response);
+ }
+ keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
+ return Send(KM_BEGIN_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const UpdateOperationRequest& request,
+ UpdateOperationResponse* response) {
+ return Send(KM_UPDATE_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const FinishOperationRequest& request,
+ FinishOperationResponse* response) {
+ return Send(KM_FINISH_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
+ return Send(KM_IMPORT_KEY, request, response);
+ }
+ keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
+ return Send(KM_EXPORT_KEY, request, response);
+ }
+ keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
+ return Send(KM_GET_VERSION, request, response);
+ }
+
+ keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
+ size_t key_blob_length, AuthorizationSet* auth_set);
+ void StoreNewKeyParams(AuthorizationSet* auth_set);
+ keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
+ keymaster_algorithm_t* algorithm);
+
+ /*
+ * These static methods are the functions referenced through the function pointers in
+ * keymaster_device. They're all trivial wrappers.
+ */
+ static int close_device(hw_device_t* dev);
+ static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
+ static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
+ static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
+ const size_t key_blob_length, uint8_t** x509_data,
+ size_t* x509_data_length);
+ static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
+ const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length);
+ static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* signed_data, const size_t signed_data_length,
+ const uint8_t* signature, const size_t signature_length);
+
+ keymaster0_device_t device_;
+ keymaster_error_t error_;
+ int32_t message_version_;
+};
+
+} // namespace keymaster
+
+#endif // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
new file mode 100644
index 0000000..3bb5430
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include <algorithm>
+#include <fstream>
+
+#include <UniquePtr.h>
+#include <gtest/gtest.h>
+#include <openssl/engine.h>
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/soft_keymaster_context.h>
+
+#include "android_keymaster_test_utils.h"
+#include "trusty_keymaster_device.h"
+#include "openssl_utils.h"
+
+using std::string;
+using std::ifstream;
+using std::istreambuf_iterator;
+
+static keymaster::AndroidKeymaster *impl_ = nullptr;
+
+extern "C" {
+int __android_log_print();
+}
+
+int __android_log_print() {
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+int trusty_keymaster_connect() {
+ impl_ = new keymaster::AndroidKeymaster(new keymaster::SoftKeymasterContext(nullptr), 16);
+}
+
+void trusty_keymaster_disconnect() {
+ delete static_cast<keymaster::AndroidKeymaster*>(priv_);
+}
+
+template <typename Req, typename Rsp>
+static int fake_call(keymaster::AndroidKeymaster* device,
+ void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
+ uint32_t in_size, void* out_buf, uint32_t* out_size) {
+ Req req;
+ const uint8_t* in = static_cast<uint8_t*>(in_buf);
+ req.Deserialize(&in, in + in_size);
+ Rsp rsp;
+ (device->*method)(req, &rsp);
+
+ *out_size = rsp.SerializedSize();
+ uint8_t* out = static_cast<uint8_t*>(out_buf);
+ rsp.Serialize(out, out + *out_size);
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
+ uint32_t* out_size) {
+ switch (cmd) {
+ case KM_GENERATE_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
+ out_buf, out_size);
+ case KM_BEGIN_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_UPDATE_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_FINISH_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_IMPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size, out_buf,
+ out_size);
+ case KM_EXPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size, out_buf,
+ out_size);
+ }
+ return -EINVAL;
+
+}
+
+namespace keymaster {
+namespace test {
+
+class TrustyKeymasterTest : public testing::Test {
+ protected:
+ TrustyKeymasterTest() : device(NULL) {}
+
+ keymaster_rsa_keygen_params_t build_rsa_params() {
+ keymaster_rsa_keygen_params_t rsa_params;
+ rsa_params.public_exponent = 65537;
+ rsa_params.modulus_size = 2048;
+ return rsa_params;
+ }
+
+ uint8_t* build_message(size_t length) {
+ uint8_t* msg = new uint8_t[length];
+ memset(msg, 'a', length);
+ return msg;
+ }
+
+ size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
+ switch (params.key_size) {
+ case 256:
+ case 1024:
+ return 48;
+ case 2048:
+ case 4096:
+ return 72;
+ default:
+ // Oops.
+ return 0;
+ }
+ }
+
+ TrustyKeymasterDevice device;
+};
+
+class Malloc_Delete {
+ public:
+ Malloc_Delete(void* p) : p_(p) {}
+ ~Malloc_Delete() { free(p_); }
+
+ private:
+ void* p_;
+};
+
+typedef TrustyKeymasterTest KeyGenTest;
+TEST_F(KeyGenTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+TEST_F(KeyGenTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t ec_params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &ec_params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+typedef TrustyKeymasterTest SigningTest;
+TEST_F(SigningTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+}
+
+TEST_F(SigningTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 - 1;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 + 1;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaEmptyMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+typedef TrustyKeymasterTest VerificationTest;
+TEST_F(VerificationTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadSignature) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ signature[siglen / 2]++;
+ EXPECT_EQ(
+ KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ message[0]++;
+ EXPECT_EQ(
+ KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len - 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len + 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+TEST_F(VerificationTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+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 TrustyKeymasterTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_size = 1024 /* key size */ / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+ memset(message.get(), 'a', message_size);
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message.get(), message_size,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message.get(), message_size,
+ signature, siglen));
+}
+
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
+ size_t signature_len, const uint8_t* message, size_t message_len) {
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+ ASSERT_TRUE(pkey.get() != NULL);
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ ASSERT_TRUE(ctx.get() != NULL);
+ ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
+ if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
+ ASSERT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING));
+ EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), signature, signature_len, message, message_len));
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(ptr, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+ const uint8_t* tmp = exported;
+
+ VerifySignature(exported, exported_size, signature, siglen, message.get(), message_len);
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &key, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(key);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(key, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+
+ VerifySignature(exported, exported_size, signature, siglen, message, array_size(message) - 1);
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.c
new file mode 100644
index 0000000..b68209e
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_ipc.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// TODO: make this generic in libtrusty
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "TrustyKeymaster"
+#include <cutils/log.h>
+
+#include <trusty/tipc.h>
+
+#include "trusty_keymaster_ipc.h"
+#include "keymaster_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_keymaster_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size) {
+ if (handle_ == 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct keymaster_message);
+ struct keymaster_message *msg = malloc(msg_size);
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+ KEYMASTER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ rc = read(handle_, out, *out_size);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+ cmd, KEYMASTER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t) rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int) rc);
+ return -EINVAL;
+ }
+
+ msg = (struct keymaster_message *) out;
+
+ if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
+ ALOGE("invalid command (%d)", msg->cmd);
+ return -EINVAL;
+ }
+
+ *out_size = ((size_t) rc) - sizeof(struct keymaster_message);
+ return rc;
+}
+
+void trusty_keymaster_disconnect() {
+ if (handle_ != 0) {
+ tipc_close(handle_);
+ }
+}
+
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
new file mode 100644
index 0000000..9785247
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+__BEGIN_DECLS
+
+int trusty_keymaster_connect(void);
+int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size);
+void trusty_keymaster_disconnect(void);
+
+__END_DECLS
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
new file mode 100644
index 0000000..7ed880e
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <stdio.h>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include "trusty_keymaster_device.h"
+
+using keymaster::TrustyKeymasterDevice;
+
+unsigned char rsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34, 0x81, 0x2d, 0x5a,
+ 0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01, 0xf2, 0x34, 0x22, 0x6c,
+ 0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41, 0x7b, 0x71, 0xc0, 0xb6, 0xa4,
+ 0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9, 0xda, 0x29, 0x35, 0xad, 0xb1, 0xff,
+ 0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7, 0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57,
+ 0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e, 0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5,
+ 0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12, 0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f,
+ 0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d, 0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28,
+ 0x07, 0x45, 0xea, 0x6d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0,
+ 0x4d, 0x9c, 0xae, 0x37, 0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55,
+ 0x89, 0x9f, 0xfb, 0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab,
+ 0x02, 0x97, 0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed,
+ 0x0f, 0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
+ 0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0, 0x80,
+ 0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac, 0xe7, 0x24,
+ 0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a, 0xb5, 0x91, 0x2c,
+ 0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80, 0x81, 0x02, 0x41, 0x00,
+ 0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0, 0x1a, 0xce, 0xaa, 0xf1, 0x30,
+ 0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf, 0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d,
+ 0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb, 0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c,
+ 0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85, 0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55,
+ 0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83, 0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31,
+ 0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a, 0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b,
+ 0xc9, 0x30, 0xdb, 0xe5, 0x63, 0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6,
+ 0xcd, 0xef, 0xd3, 0x24, 0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5,
+ 0x01, 0xfd, 0x91, 0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1,
+ 0x44, 0x11, 0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78,
+ 0xcc, 0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
+ 0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f, 0xa8,
+ 0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d, 0x15, 0x18,
+ 0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc, 0x86, 0x94, 0x04,
+ 0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45, 0x26, 0xd3, 0x28, 0xc1,
+ 0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d, 0xec, 0x25, 0x08, 0x92, 0xdb,
+ 0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77, 0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d,
+ 0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d, 0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f,
+ 0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24, 0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a,
+ 0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98, 0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c,
+ 0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3, 0x34, 0x92, 0xd6};
+unsigned int rsa_privkey_pk8_der_len = 633;
+
+unsigned char dsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86, 0x48,
+ 0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3, 0xe9, 0xb6,
+ 0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad, 0xbc, 0xc9, 0xd1,
+ 0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8, 0xe0, 0x26, 0x44, 0x19,
+ 0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde, 0xe5, 0x4f, 0x48, 0x15, 0x01,
+ 0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8, 0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c,
+ 0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d, 0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b,
+ 0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2, 0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7,
+ 0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda, 0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec,
+ 0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24, 0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb,
+ 0xea, 0x17, 0xd2, 0x09, 0xb3, 0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71,
+ 0x68, 0xf7, 0xe3, 0x02, 0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b,
+ 0xf6, 0xcd, 0xd6, 0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06,
+ 0x88, 0xb1, 0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8,
+ 0x11, 0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
+ 0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80, 0xca,
+ 0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62, 0x75, 0x8b,
+ 0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf, 0x72, 0x9a, 0x67,
+ 0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a, 0xba, 0x3b, 0xa8, 0x00,
+ 0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00, 0x81, 0x9d, 0xfd, 0x53, 0x0c,
+ 0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33, 0x91, 0x84, 0xbe, 0xad, 0x81};
+unsigned int dsa_privkey_pk8_der_len = 335;
+
+unsigned char ec_privkey_pk8_der[] = {
+ 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02,
+ 0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d, 0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa,
+ 0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09, 0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81,
+ 0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07,
+ 0xc2, 0x54, 0x61, 0x68, 0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e,
+ 0x3b, 0xdd, 0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
+ 0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf, 0x33, 0x76,
+ 0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
+unsigned int ec_privkey_pk8_der_len = 138;
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static bool test_import_rsa(TrustyKeymasterDevice* device) {
+ printf("===================\n");
+ printf("= RSA Import Test =\n");
+ printf("===================\n\n");
+
+ printf("=== Importing RSA keypair === \n");
+ uint8_t* key;
+ size_t size;
+ int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> key_deleter(key);
+
+ printf("=== Signing with imported RSA key ===\n");
+ keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = 1024 / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with imported RSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with imported RSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with imported RSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_rsa(TrustyKeymasterDevice* device) {
+ printf("============\n");
+ printf("= RSA Test =\n");
+ printf("============\n\n");
+
+ printf("=== Generating RSA key pair ===\n");
+ keymaster_rsa_keygen_params_t params;
+ params.public_exponent = 65537;
+ params.modulus_size = 2048;
+
+ uint8_t* key;
+ size_t size;
+ int error = device->generate_keypair(TYPE_RSA, ¶ms, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error generating RSA key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with RSA key === \n");
+ keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with RSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with RSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with RSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Exporting RSA public key ===\n");
+ uint8_t* exported_key;
+ size_t exported_size;
+ error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting RSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openss EVP context\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ printf("Exported key was the wrong type?!?\n");
+ return false;
+ }
+
+ EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+ if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
+ printf("=====================\n");
+ printf("= ECDSA Import Test =\n");
+ printf("=====================\n\n");
+
+ printf("=== Importing ECDSA keypair === \n");
+ uint8_t* key;
+ size_t size;
+ int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with imported ECDSA key ===\n");
+ keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
+ size_t message_len = 30 /* arbitrary */;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with imported ECDSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with imported ECDSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_ecdsa(TrustyKeymasterDevice* device) {
+ printf("==============\n");
+ printf("= ECDSA Test =\n");
+ printf("==============\n\n");
+
+ printf("=== Generating ECDSA key pair ===\n");
+ keymaster_ec_keygen_params_t params;
+ params.field_size = 521;
+ uint8_t* key;
+ size_t size;
+ int error = device->generate_keypair(TYPE_EC, ¶ms, &key, &size);
+ if (error != 0) {
+ printf("Error generating ECDSA key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with ECDSA key === \n");
+ keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
+ size_t message_len = 30 /* arbitrary */;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with ECDSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with ECDSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with ECDSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Exporting ECDSA public key ===\n");
+ uint8_t* exported_key;
+ size_t exported_size;
+ error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting ECDSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openss EVP context\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
+ printf("Exported key was the wrong type?!?\n");
+ return false;
+ }
+
+ if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+int main(void) {
+
+ TrustyKeymasterDevice device(NULL);
+ if (device.session_error() != KM_ERROR_OK) {
+ printf("Failed to initialize Trusty session: %d\n", device.session_error());
+ return 1;
+ }
+ printf("Trusty session initialized\n");
+
+ bool success = true;
+ success &= test_rsa(&device);
+ success &= test_import_rsa(&device);
+ success &= test_ecdsa(&device);
+ success &= test_import_ecdsa(&device);
+
+ if (success) {
+ printf("\nTESTS PASSED!\n");
+ } else {
+ printf("\n!!!!TESTS FAILED!!!\n");
+ }
+
+ return success ? 0 : 1;
+}
diff --git a/trusty/storage/interface/Android.mk b/trusty/storage/interface/Android.mk
new file mode 100644
index 0000000..15cb6f3
--- /dev/null
+++ b/trusty/storage/interface/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2015 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrustystorageinterface
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
new file mode 100644
index 0000000..b196d88
--- /dev/null
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015-2016 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*
+ * Storage port names
+ * @STORAGE_CLIENT_TD_PORT: Port used by clients that require tamper and
+ * rollback detection.
+ * @STORAGE_CLIENT_TDEA_PORT: Port used by clients that require storage before
+ * the non-secure os has booted.
+ * @STORAGE_CLIENT_TP_PORT: Port used by clients that require tamper proof
+ * storage. Note that non-secure code can prevent
+ read and write operations from succeeding, but
+ it cannot modify on-disk data.
+ * @STORAGE_DISK_PROXY_PORT: Port used by non-secure proxy server
+ */
+#define STORAGE_CLIENT_TD_PORT "com.android.trusty.storage.client.td"
+#define STORAGE_CLIENT_TDEA_PORT "com.android.trusty.storage.client.tdea"
+#define STORAGE_CLIENT_TP_PORT "com.android.trusty.storage.client.tp"
+#define STORAGE_DISK_PROXY_PORT "com.android.trusty.storage.proxy"
+
+enum storage_cmd {
+ STORAGE_REQ_SHIFT = 1,
+ STORAGE_RESP_BIT = 1,
+
+ STORAGE_RESP_MSG_ERR = STORAGE_RESP_BIT,
+
+ STORAGE_FILE_DELETE = 1 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_OPEN = 2 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_CLOSE = 3 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_READ = 4 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_WRITE = 5 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_GET_SIZE = 6 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_SET_SIZE = 7 << STORAGE_REQ_SHIFT,
+
+ STORAGE_RPMB_SEND = 8 << STORAGE_REQ_SHIFT,
+
+ /* transaction support */
+ STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT,
+};
+
+/**
+ * enum storage_err - error codes for storage protocol
+ * @STORAGE_NO_ERROR: all OK
+ * @STORAGE_ERR_GENERIC: unknown error. Can occur when there's an internal server
+ * error, e.g. the server runs out of memory or is in a bad state.
+ * @STORAGE_ERR_NOT_VALID: input not valid. May occur if the arguments passed
+ * into the command are not valid, for example if the file handle
+ * passed in is not a valid one.
+ * @STORAGE_ERR_UNIMPLEMENTED: the command passed in is not recognized
+ * @STORAGE_ERR_ACCESS: the file is not accessible in the requested mode
+ * @STORAGE_ERR_NOT_FOUND: the file was not found
+ * @STORAGE_ERR_EXIST the file exists when it shouldn't as in with OPEN_CREATE | OPEN_EXCLUSIVE.
+ * @STORAGE_ERR_TRANSACT returned by various operations to indicate that current transaction
+ * is in error state. Such state could be only cleared by sending
+ * STORAGE_END_TRANSACTION message.
+ */
+enum storage_err {
+ STORAGE_NO_ERROR = 0,
+ STORAGE_ERR_GENERIC = 1,
+ STORAGE_ERR_NOT_VALID = 2,
+ STORAGE_ERR_UNIMPLEMENTED = 3,
+ STORAGE_ERR_ACCESS = 4,
+ STORAGE_ERR_NOT_FOUND = 5,
+ STORAGE_ERR_EXIST = 6,
+ STORAGE_ERR_TRANSACT = 7,
+};
+
+/**
+ * storage_delete_flag - flags for controlling delete semantics
+ */
+enum storage_file_delete_flag {
+ STORAGE_FILE_DELETE_MASK = 0,
+};
+
+/**
+ * storage_file_flag - Flags to control 'open' semantics.
+ * @STORAGE_FILE_OPEN_CREATE: if this file does not exist, create it.
+ * @STORAGE_FILE_OPEN_CREATE_EXCLUSIVE: causes STORAGE_FILE_OPEN_CREATE to fail if the file
+ * already exists. Only meaningful if used in combination
+ * with STORAGE_FILE_OPEN_CREATE.
+ * @STORAGE_FILE_OPEN_TRUNCATE: if this file already exists, discard existing content
+ * and open it as a new file. No change in semantics if the
+ * file does not exist.
+ * @STORAGE_FILE_OPEN_MASK: mask for all open flags supported in current protocol.
+ * All other bits must be set to 0.
+ */
+enum storage_file_open_flag {
+ STORAGE_FILE_OPEN_CREATE = (1 << 0),
+ STORAGE_FILE_OPEN_CREATE_EXCLUSIVE = (1 << 1),
+ STORAGE_FILE_OPEN_TRUNCATE = (1 << 2),
+ STORAGE_FILE_OPEN_MASK = STORAGE_FILE_OPEN_CREATE |
+ STORAGE_FILE_OPEN_TRUNCATE |
+ STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+};
+
+/**
+ * enum storage_msg_flag - protocol-level flags in struct storage_msg
+ * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction.
+ * No response will be sent by the server until
+ * it receives a command with this flag unset, at
+ * which point a cummulative result for all messages
+ * sent with STORAGE_MSG_FLAG_BATCH will be sent.
+ * This is only supported by the non-secure disk proxy
+ * server.
+ * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit
+ * pending changes before processing this message.
+ * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit
+ * pending changes after processing this message.
+ * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit
+ * current transaction after processing this message.
+ * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
+ */
+enum storage_msg_flag {
+ STORAGE_MSG_FLAG_BATCH = 0x1,
+ STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
+ STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
+ STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
+};
+
+/*
+ * The following declarations are the message-specific contents of
+ * the 'payload' element inside struct storage_msg.
+ */
+
+/**
+ * struct storage_file_delete_req - request format for STORAGE_FILE_DELETE
+ * @flags: currently unused, must be set to 0.
+ * @name: the name of the file
+ */
+struct storage_file_delete_req {
+ uint32_t flags;
+ char name[0];
+};
+
+/**
+ * struct storage_file_open_req - request format for STORAGE_FILE_OPEN
+ * @flags: any of enum storage_file_flag or'ed together
+ * @name: the name of the file
+ */
+struct storage_file_open_req {
+ uint32_t flags;
+ char name[0];
+};
+
+/**
+ * struct storage_file_open_resp - response format for STORAGE_FILE_OPEN
+ * @handle: opaque handle to the opened file. Only present on success.
+ */
+struct storage_file_open_resp {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_close_req - request format for STORAGE_FILE_CLOSE
+ * @handle: the handle for the file to close
+ */
+struct storage_file_close_req {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_read_req - request format for STORAGE_FILE_READ
+ * @handle: the handle for the file from which to read
+ * @size: the quantity of bytes to read from the file
+ * @offset: the offset in the file from whence to read
+ */
+struct storage_file_read_req {
+ uint32_t handle;
+ uint32_t size;
+ uint64_t offset;
+};
+
+/**
+ * struct storage_file_read_resp - response format for STORAGE_FILE_READ
+ * @data: beginning of data retrieved from file
+ */
+struct storage_file_read_resp {
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_file_write_req - request format for STORAGE_FILE_WRITE
+ * @handle: the handle for the file to write to
+ * @offset: the offset in the file from whence to write
+ * @__reserved: unused, must be set to 0.
+ * @data: beginning of the data to be written
+ */
+struct storage_file_write_req {
+ uint64_t offset;
+ uint32_t handle;
+ uint32_t __reserved;
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_file_get_size_req - request format for STORAGE_FILE_GET_SIZE
+ * @handle: handle for which the size is requested
+ */
+struct storage_file_get_size_req {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_get_size_resp - response format for STORAGE_FILE_GET_SIZE
+ * @size: the size of the file
+ */
+struct storage_file_get_size_resp {
+ uint64_t size;
+};
+
+/**
+ * struct storage_file_set_size_req - request format for STORAGE_FILE_SET_SIZE
+ * @handle: the file handle
+ * @size: the desired size of the file
+ */
+struct storage_file_set_size_req {
+ uint64_t size;
+ uint32_t handle;
+};
+
+/**
+ * struct storage_rpmb_send_req - request format for STORAGE_RPMB_SEND
+ * @reliable_write_size: size in bytes of reliable write region
+ * @write_size: size in bytes of write region
+ * @read_size: number of bytes to read for a read request
+ * @__reserved: unused, must be set to 0
+ * @payload: start of reliable write region, followed by
+ * write region.
+ *
+ * Only used in proxy<->server interface.
+ */
+struct storage_rpmb_send_req {
+ uint32_t reliable_write_size;
+ uint32_t write_size;
+ uint32_t read_size;
+ uint32_t __reserved;
+ uint8_t payload[0];
+};
+
+/**
+ * struct storage_rpmb_send_resp: response type for STORAGE_RPMB_SEND
+ * @data: the data frames frames retrieved from the MMC.
+ */
+struct storage_rpmb_send_resp {
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_msg - generic req/resp format for all storage commands
+ * @cmd: one of enum storage_cmd
+ * @op_id: client chosen operation identifier for an instance
+ * of a command or atomic grouping of commands (transaction).
+ * @flags: one or many of enum storage_msg_flag or'ed together.
+ * @size: total size of the message including this header
+ * @result: one of enum storage_err
+ * @__reserved: unused, must be set to 0.
+ * @payload: beginning of command specific message format
+ */
+struct storage_msg {
+ uint32_t cmd;
+ uint32_t op_id;
+ uint32_t flags;
+ uint32_t size;
+ int32_t result;
+ uint32_t __reserved;
+ uint8_t payload[0];
+};
+
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
new file mode 100644
index 0000000..9fc73d3
--- /dev/null
+++ b/trusty/storage/proxy/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storageproxyd
+
+LOCAL_SRC_FILES := \
+ ipc.c \
+ rpmb.c \
+ storage.c \
+ proxy.c
+
+LOCAL_CLFAGS = -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtrustystorageinterface \
+ libtrusty
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/trusty/storage/proxy/ipc.c b/trusty/storage/proxy/ipc.c
new file mode 100644
index 0000000..b4748e2
--- /dev/null
+++ b/trusty/storage/proxy/ipc.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include <trusty/tipc.h>
+
+#include "ipc.h"
+#include "log.h"
+
+#define MAX_RECONNECT_RETRY_COUNT 5
+#define TRUSTY_RECONNECT_TIMEOUT_SEC 5
+
+static int tipc_fd = -1;
+
+int ipc_connect(const char *device, const char *port)
+{
+ int rc;
+ uint retry_cnt = 0;
+
+ assert(tipc_fd == -1);
+
+ while(true) {
+ rc = tipc_connect(device, port);
+ if (rc >= 0)
+ break;
+
+ ALOGE("failed (%d) to connect to storage server\n", rc);
+ if (++retry_cnt > MAX_RECONNECT_RETRY_COUNT) {
+ ALOGE("max number of reconnect retries (%d) has been reached\n",
+ retry_cnt);
+ return -1;
+ }
+ sleep(TRUSTY_RECONNECT_TIMEOUT_SEC);
+ }
+ tipc_fd = rc;
+ return 0;
+}
+
+void ipc_disconnect(void)
+{
+ assert(tipc_fd >= 0);
+
+ tipc_close(tipc_fd);
+ tipc_fd = -1;
+}
+
+ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len)
+{
+ ssize_t rc;
+ struct iovec iovs[2] = {{msg, sizeof(*msg)}, {req_buf, req_buf_len}};
+
+ assert(tipc_fd >= 0);
+
+ rc = readv(tipc_fd, iovs, 2);
+ if (rc < 0) {
+ ALOGE("failed to read request: %s\n", strerror(errno));
+ return rc;
+ }
+
+ /* check for minimum size */
+ if ((size_t)rc < sizeof(*msg)) {
+ ALOGE("message is too short (%zu bytes received)\n", rc);
+ return -1;
+ }
+
+ /* check for message completeness */
+ if (msg->size != (uint32_t)rc) {
+ ALOGE("inconsistent message size [cmd=%d] (%u != %u)\n",
+ msg->cmd, msg->size, (uint32_t)rc);
+ return -1;
+ }
+
+ return rc - sizeof(*msg);
+}
+
+int ipc_respond(struct storage_msg *msg, void *out, size_t out_size)
+{
+ ssize_t rc;
+ struct iovec iovs[2] = {{msg, sizeof(*msg)}, {out, out_size}};
+
+ assert(tipc_fd >= 0);
+
+ msg->cmd |= STORAGE_RESP_BIT;
+
+ rc = writev(tipc_fd, iovs, out ? 2 : 1);
+ if (rc < 0) {
+ ALOGE("error sending response 0x%x: %s\n",
+ msg->cmd, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
diff --git a/trusty/storage/proxy/ipc.h b/trusty/storage/proxy/ipc.h
new file mode 100644
index 0000000..2e366bb
--- /dev/null
+++ b/trusty/storage/proxy/ipc.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int ipc_connect(const char *device, const char *service_name);
+void ipc_disconnect(void);
+ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len);
+int ipc_respond(struct storage_msg *msg, void *out, size_t out_size);
diff --git a/trusty/storage/proxy/log.h b/trusty/storage/proxy/log.h
new file mode 100644
index 0000000..471cb50
--- /dev/null
+++ b/trusty/storage/proxy/log.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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 "storageproxyd"
+#include <cutils/log.h>
+
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
new file mode 100644
index 0000000..d645ac0
--- /dev/null
+++ b/trusty/storage/proxy/proxy.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "ipc.h"
+#include "log.h"
+#include "rpmb.h"
+#include "storage.h"
+
+#define REQ_BUFFER_SIZE 4096
+static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
+
+static const char *ss_data_root;
+static const char *trusty_devname;
+static const char *rpmb_devname;
+static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+
+static const char *_sopts = "hp:d:r:";
+static const struct option _lopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"trusty_dev", required_argument, NULL, 'd'},
+ {"data_path", required_argument, NULL, 'p'},
+ {"rpmb_dev", required_argument, NULL, 'r'},
+ {0, 0, 0, 0}
+};
+
+static void show_usage_and_exit(int code)
+{
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+ exit(code);
+}
+
+static int drop_privs(void)
+{
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ return -1;
+ }
+
+ /*
+ * ensure we're running as the system user
+ */
+ if (setgid(AID_SYSTEM) != 0) {
+ return -1;
+ }
+
+ if (setuid(AID_SYSTEM) != 0) {
+ return -1;
+ }
+
+ /*
+ * drop all capabilities except SYS_RAWIO
+ */
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
+ capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ return -1;
+ }
+
+ /* no-execute for user, no access for group and other */
+ umask(S_IXUSR | S_IRWXG | S_IRWXO);
+
+ return 0;
+}
+
+static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
+{
+ int rc;
+
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
+ (msg->cmd != STORAGE_RPMB_SEND)) {
+ /*
+ * handling post commit messages on non rpmb commands are not
+ * implemented as there is no use case for this yet.
+ */
+ ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ return ipc_respond(msg, NULL, 0);
+ }
+
+ if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
+ rc = storage_sync_checkpoint();
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ return ipc_respond(msg, NULL, 0);
+ }
+ }
+
+ switch (msg->cmd) {
+ case STORAGE_FILE_DELETE:
+ rc = storage_file_delete(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_OPEN:
+ rc = storage_file_open(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_CLOSE:
+ rc = storage_file_close(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_WRITE:
+ rc = storage_file_write(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_READ:
+ rc = storage_file_read(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_GET_SIZE:
+ rc = storage_file_get_size(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_SET_SIZE:
+ rc = storage_file_set_size(msg, req, req_len);
+ break;
+
+ case STORAGE_RPMB_SEND:
+ rc = rpmb_send(msg, req, req_len);
+ break;
+
+ default:
+ ALOGE("unhandled command 0x%x\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ rc = 1;
+ }
+
+ if (rc > 0) {
+ /* still need to send response */
+ rc = ipc_respond(msg, NULL, 0);
+ }
+ return rc;
+}
+
+static int proxy_loop(void)
+{
+ ssize_t rc;
+ struct storage_msg msg;
+
+ /* enter main message handling loop */
+ while (true) {
+
+ /* get incoming message */
+ rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
+ if (rc < 0)
+ return rc;
+
+ /* handle request */
+ req_buffer[rc] = 0; /* force zero termination */
+ rc = handle_req(&msg, req_buffer, rc);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static void parse_args(int argc, char *argv[])
+{
+ int opt;
+ int oidx = 0;
+
+ while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
+ switch (opt) {
+
+ case 'd':
+ trusty_devname = strdup(optarg);
+ break;
+
+ case 'p':
+ ss_data_root = strdup(optarg);
+ break;
+
+ case 'r':
+ rpmb_devname = strdup(optarg);
+ break;
+
+ default:
+ ALOGE("unrecognized option (%c):\n", opt);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ss_data_root == NULL ||
+ trusty_devname == NULL ||
+ rpmb_devname == NULL) {
+ ALOGE("missing required argument(s)\n");
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+
+ ALOGI("starting storageproxyd\n");
+ ALOGI("storage data root: %s\n", ss_data_root);
+ ALOGI("trusty dev: %s\n", trusty_devname);
+ ALOGI("rpmb dev: %s\n", rpmb_devname);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ uint retry_cnt;
+
+ /* drop privileges */
+ if (drop_privs() < 0)
+ return EXIT_FAILURE;
+
+ /* parse arguments */
+ parse_args(argc, argv);
+
+ /* initialize secure storage directory */
+ rc = storage_init(ss_data_root);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* open rpmb device */
+ rc = rpmb_open(rpmb_devname);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* connect to Trusty secure storage server */
+ rc = ipc_connect(trusty_devname, ss_srv_name);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* enter main loop */
+ rc = proxy_loop();
+ ALOGE("exiting proxy loop with status (%d)\n", rc);
+
+ ipc_disconnect();
+ rpmb_close();
+
+ return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
new file mode 100644
index 0000000..9130458
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <linux/major.h>
+#include <linux/mmc/ioctl.h>
+
+#include "ipc.h"
+#include "log.h"
+#include "rpmb.h"
+#include "storage.h"
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+#define MMC_WRITE_FLAG_R 0
+#define MMC_WRITE_FLAG_W 1
+#define MMC_WRITE_FLAG_RELW (MMC_WRITE_FLAG_W | MMC_RELIABLE_WRITE_FLAG)
+
+#define MMC_BLOCK_SIZE 512
+
+static int rpmb_fd = -1;
+static uint8_t read_buf[4096];
+
+#ifdef RPMB_DEBUG
+
+static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
+{
+ size_t i;
+
+ printf("%s @%p [%zu]", prefix, buf, size);
+ for (i = 0; i < size; i++) {
+ if (i && i % 32 == 0)
+ printf("\n%*s", (int) strlen(prefix), "");
+ printf(" %02x", buf[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#endif
+
+
+int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
+{
+ int rc;
+ struct {
+ struct mmc_ioc_multi_cmd multi;
+ struct mmc_ioc_cmd cmd_buf[3];
+ } mmc = {};
+ struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
+ const struct storage_rpmb_send_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
+ req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len =
+ sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
+ req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ const uint8_t *write_buf = req->payload;
+ if (req->reliable_write_size) {
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_RELW;
+ cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->reliable_write_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), write_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+ print_buf("request: ", write_buf, req->reliable_write_size);
+#endif
+ write_buf += req->reliable_write_size;
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ if (req->write_size) {
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_W;
+ cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->write_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), write_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+ print_buf("request: ", write_buf, req->write_size);
+#endif
+ write_buf += req->write_size;
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ if (req->read_size) {
+ if (req->read_size % MMC_BLOCK_SIZE != 0 ||
+ req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_R;
+ cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), read_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+#endif
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ if (rc < 0) {
+ ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+#ifdef RPMB_DEBUG
+ if (req->read_size)
+ print_buf("response: ", read_buf, req->read_size);
+#endif
+
+ if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+ /*
+ * Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD
+ * is fully synchronous in this implementation.
+ */
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, read_buf, req->read_size);
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int rpmb_open(const char *rpmb_devname)
+{
+ int rc;
+
+ rc = open(rpmb_devname, O_RDWR, 0);
+ if (rc < 0) {
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n",
+ errno, rpmb_devname, strerror(errno));
+ return rc;
+ }
+ rpmb_fd = rc;
+ return 0;
+}
+
+void rpmb_close(void)
+{
+ close(rpmb_fd);
+ rpmb_fd = -1;
+}
+
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
new file mode 100644
index 0000000..85cff44
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int rpmb_open(const char *rpmb_devname);
+int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+void rpmb_close(void);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
new file mode 100644
index 0000000..c61e89d
--- /dev/null
+++ b/trusty/storage/proxy/storage.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "ipc.h"
+#include "storage.h"
+
+#define FD_TBL_SIZE 64
+#define MAX_READ_SIZE 4096
+
+enum sync_state {
+ SS_UNUSED = -1,
+ SS_CLEAN = 0,
+ SS_DIRTY = 1,
+};
+
+static int ssdir_fd = -1;
+static const char *ssdir_name;
+
+static enum sync_state fs_state;
+static enum sync_state dir_state;
+static enum sync_state fd_state[FD_TBL_SIZE];
+
+static struct {
+ struct storage_file_read_resp hdr;
+ uint8_t data[MAX_READ_SIZE];
+} read_rsp;
+
+static uint32_t insert_fd(int open_flags, int fd)
+{
+ uint32_t handle = fd;
+
+ if (open_flags & O_CREAT) {
+ dir_state = SS_DIRTY;
+ }
+
+ if (handle < FD_TBL_SIZE) {
+ fd_state[fd] = SS_CLEAN; /* fd clean */
+ if (open_flags & O_TRUNC) {
+ fd_state[fd] = SS_DIRTY; /* set fd dirty */
+ }
+ } else {
+ ALOGW("%s: untracked fd %u\n", __func__, fd);
+ if (open_flags & (O_TRUNC | O_CREAT)) {
+ fs_state = SS_DIRTY;
+ }
+ }
+ return handle;
+}
+
+static int lookup_fd(uint32_t handle, bool dirty)
+{
+ if (dirty) {
+ if (handle < FD_TBL_SIZE) {
+ fd_state[handle] = SS_DIRTY;
+ } else {
+ fs_state = SS_DIRTY;
+ }
+ }
+ return handle;
+}
+
+static int remove_fd(uint32_t handle)
+{
+ if (handle < FD_TBL_SIZE) {
+ fd_state[handle] = SS_UNUSED; /* set to uninstalled */
+ }
+ return handle;
+}
+
+static enum storage_err translate_errno(int error)
+{
+ enum storage_err result;
+ switch (error) {
+ case 0:
+ result = STORAGE_NO_ERROR;
+ break;
+ case EBADF:
+ case EINVAL:
+ case ENOTDIR:
+ case EISDIR:
+ case ENAMETOOLONG:
+ result = STORAGE_ERR_NOT_VALID;
+ break;
+ case ENOENT:
+ result = STORAGE_ERR_NOT_FOUND;
+ break;
+ case EEXIST:
+ result = STORAGE_ERR_EXIST;
+ break;
+ case EPERM:
+ case EACCES:
+ result = STORAGE_ERR_ACCESS;
+ break;
+ default:
+ result = STORAGE_ERR_GENERIC;
+ break;
+ }
+
+ return result;
+}
+
+static ssize_t write_with_retry(int fd, const void *buf_, size_t size, off_t offset)
+{
+ ssize_t rc;
+ const uint8_t *buf = buf_;
+
+ while (size > 0) {
+ rc = TEMP_FAILURE_RETRY(pwrite(fd, buf, size, offset));
+ if (rc < 0)
+ return rc;
+ size -= rc;
+ buf += rc;
+ offset += rc;
+ }
+ return 0;
+}
+
+static ssize_t read_with_retry(int fd, void *buf_, size_t size, off_t offset)
+{
+ ssize_t rc;
+ size_t rcnt = 0;
+ uint8_t *buf = buf_;
+
+ while (size > 0) {
+ rc = TEMP_FAILURE_RETRY(pread(fd, buf, size, offset));
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ break;
+ size -= rc;
+ buf += rc;
+ offset += rc;
+ rcnt += rc;
+ }
+ return rcnt;
+}
+
+int storage_file_delete(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ char *path = NULL;
+ const struct storage_file_delete_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t fname_len = strlen(req->name);
+ if (fname_len != req_len - sizeof(*req)) {
+ ALOGE("%s: invalid filename length (%zd != %zd)\n",
+ __func__, fname_len, req_len - sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
+ if (rc < 0) {
+ ALOGE("%s: asprintf failed\n", __func__);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+
+ dir_state = SS_DIRTY;
+ rc = unlink(path);
+ if (rc < 0) {
+ rc = errno;
+ if (errno == ENOENT) {
+ ALOGV("%s: error (%d) unlinking file '%s'\n",
+ __func__, rc, path);
+ } else {
+ ALOGE("%s: error (%d) unlinking file '%s'\n",
+ __func__, rc, path);
+ }
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ ALOGV("%s: \"%s\"\n", __func__, path);
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ if (path)
+ free(path);
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_open(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ char *path = NULL;
+ const struct storage_file_open_req *req = r;
+ struct storage_file_open_resp resp = {0};
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t fname_len = strlen(req->name);
+ if (fname_len != req_len - sizeof(*req)) {
+ ALOGE("%s: invalid filename length (%zd != %zd)\n",
+ __func__, fname_len, req_len - sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
+ if (rc < 0) {
+ ALOGE("%s: asprintf failed\n", __func__);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+
+ int open_flags = O_RDWR;
+
+ if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)
+ open_flags |= O_TRUNC;
+
+ if (req->flags & STORAGE_FILE_OPEN_CREATE) {
+ /* open or create */
+ if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
+ /* create exclusive */
+ open_flags |= O_CREAT | O_EXCL;
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ } else {
+ /* try open first */
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ if (rc == -1 && errno == ENOENT) {
+ /* then try open with O_CREATE */
+ open_flags |= O_CREAT;
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ }
+
+ }
+ } else {
+ /* open an existing file */
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ }
+
+ if (rc < 0) {
+ rc = errno;
+ if (errno == EEXIST || errno == ENOENT) {
+ ALOGV("%s: failed to open file \"%s\": %s\n",
+ __func__, path, strerror(errno));
+ } else {
+ ALOGE("%s: failed to open file \"%s\": %s\n",
+ __func__, path, strerror(errno));
+ }
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+ free(path);
+
+ /* at this point rc contains storage file fd */
+ msg->result = STORAGE_NO_ERROR;
+ resp.handle = insert_fd(open_flags, rc);
+ ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
+ __func__, path, rc, resp.handle);
+
+ return ipc_respond(msg, &resp, sizeof(resp));
+
+err_response:
+ if (path)
+ free(path);
+ return ipc_respond(msg, NULL, 0);
+}
+
+int storage_file_close(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_close_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = remove_fd(req->handle);
+ ALOGV("%s: handle = %u: fd = %u\n", __func__, req->handle, fd);
+
+ int rc = fsync(fd);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: fsync failed for fd=%u: %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ rc = close(fd);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: close failed for fd=%u: %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_write(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ int rc;
+ const struct storage_file_write_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, true);
+ if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
+ req->offset) < 0) {
+ rc = errno;
+ ALOGW("%s: error writing file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_read(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ int rc;
+ const struct storage_file_read_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->size > MAX_READ_SIZE) {
+ ALOGW("%s: request is too large (%zd > %zd) - refusing\n",
+ __func__, req->size, MAX_READ_SIZE);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, false);
+ ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
+ (off_t)req->offset);
+ if (read_res < 0) {
+ rc = errno;
+ ALOGW("%s: error reading file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_get_size(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_get_size_req *req = r;
+ struct storage_file_get_size_resp resp = {0};
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ struct stat stat;
+ int fd = lookup_fd(req->handle, false);
+ int rc = fstat(fd, &stat);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: error stat'ing file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ resp.size = stat.st_size;
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, &resp, sizeof(resp));
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_set_size(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_set_size_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, true);
+ int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: error truncating file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+int storage_init(const char *dirname)
+{
+ fs_state = SS_CLEAN;
+ dir_state = SS_CLEAN;
+ for (uint i = 0; i < FD_TBL_SIZE; i++) {
+ fd_state[i] = SS_UNUSED; /* uninstalled */
+ }
+
+ ssdir_fd = open(dirname, O_RDONLY);
+ if (ssdir_fd < 0) {
+ ALOGE("failed to open ss root dir \"%s\": %s\n",
+ dirname, strerror(errno));
+ return -1;
+ }
+ ssdir_name = dirname;
+ return 0;
+}
+
+int storage_sync_checkpoint(void)
+{
+ int rc;
+
+ /* sync fd table and reset it to clean state first */
+ for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
+ if (fd_state[fd] == SS_DIRTY) {
+ if (fs_state == SS_CLEAN) {
+ /* need to sync individual fd */
+ rc = fsync(fd);
+ if (rc < 0) {
+ ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
+ return rc;
+ }
+ }
+ fd_state[fd] = SS_CLEAN; /* set to clean */
+ }
+ }
+
+ /* check if we need to sync the directory */
+ if (dir_state == SS_DIRTY) {
+ if (fs_state == SS_CLEAN) {
+ rc = fsync(ssdir_fd);
+ if (rc < 0) {
+ ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
+ return rc;
+ }
+ }
+ dir_state = SS_CLEAN; /* set to clean */
+ }
+
+ /* check if we need to sync the whole fs */
+ if (fs_state == SS_DIRTY) {
+ rc = syscall(SYS_syncfs, ssdir_fd);
+ if (rc < 0) {
+ ALOGE("syncfs failed: %s\n", strerror(errno));
+ return rc;
+ }
+ fs_state = SS_CLEAN;
+ }
+
+ return 0;
+}
+
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
new file mode 100644
index 0000000..5a670d4
--- /dev/null
+++ b/trusty/storage/proxy/storage.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int storage_file_delete(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_open(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_close(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_write(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_read(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_get_size(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_set_size(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_init(const char *dirname);
+
+int storage_sync_checkpoint(void);
+
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
new file mode 100644
index 0000000..9c3a7df
--- /dev/null
+++ b/trusty/trusty-base.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2016 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.
+#
+
+#
+# This makefile should be included by devices that use Trusty TEE
+# to pull in the baseline set of Trusty specific modules.
+#
+
+PRODUCT_PACKAGES += \
+ keystore.trusty \
+ gatekeeper.trusty
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ ro.hardware.keystore=trusty \
+ ro.hardware.gatekeeper=trusty
diff --git a/trusty/trusty-storage.mk b/trusty/trusty-storage.mk
new file mode 100644
index 0000000..3f26316
--- /dev/null
+++ b/trusty/trusty-storage.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2015 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.
+#
+
+PRODUCT_PACKAGES += \
+ storageproxyd \