diff --git a/CleanSpec.mk b/CleanSpec.mk index b3661e4e7..3cad4272a 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -56,3 +56,5 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) $(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/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk new file mode 100644 index 000000000..3982c8fd9 --- /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 000000000..b05dcd848 --- /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 000000000..0ee3c2f28 --- /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 + +#include +#include +#include + +#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 000000000..d24f44f2e --- /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 +#include +#include +#include + +#include "trusty_gatekeeper.h" +#include "trusty_gatekeeper_ipc.h" +#include "gatekeeper_ipc.h" + +#define LOG_TAG "TrustyGateKeeper" +#include + +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::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(this) == &device_); + assert(reinterpret_cast(this) == &(device_.common)); +#endif + + memset(&device_, 0, sizeof(device_)); + device_.common.tag = HARDWARE_DEVICE_TAG; + device_.common.version = 1; + device_.common.module = const_cast(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(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(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(const_cast(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 000000000..82108dc04 --- /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 +#include + +#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 000000000..a1c319e5a --- /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 +#include +#include + +#define LOG_TAG "TrustyGateKeeper" +#include +#include + +#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 000000000..f8de7f873 --- /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 000000000..0ebf52d8a --- /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 000000000..f57538189 --- /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 000000000..48fa53d15 --- /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 000000000..81597d9b0 --- /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 +#include + +#include +#include + +#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 000000000..069b4fedb --- /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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define LOG_TAG "TrustyKeymaster" +#include +#include + +#include + +#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::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(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(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(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(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(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( + 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 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(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(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(const_cast(dev)); +} + +/* static */ +int TrustyKeymasterDevice::close_device(hw_device_t* dev) { + delete reinterpret_cast(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 pub_key(pub_key_data); + + const uint8_t* p = pub_key_data; + UniquePtr 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(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(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 000000000..cb74386b9 --- /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 + +#include + +#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 000000000..3bb543015 --- /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 +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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(priv_); +} + +template +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(in_buf); + req.Deserialize(&in, in + in_size); + Rsp rsp; + (device->*method)(req, &rsp); + + *out_size = rsp.SerializedSize(); + uint8_t* out = static_cast(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 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 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 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 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 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 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 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 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 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 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 file_begin(file_stream); + istreambuf_iterator 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(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 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(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 pkey(d2i_PUBKEY(NULL, &key, key_len)); + ASSERT_TRUE(pkey.get() != NULL); + UniquePtr 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 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 000000000..b68209ef3 --- /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 +#include +#include + +#define LOG_TAG "TrustyKeymaster" +#include + +#include + +#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 000000000..978524766 --- /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 000000000..7ed880e6d --- /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 + +#include +#include + +#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 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 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 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 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 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 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 pkey(d2i_PUBKEY(NULL, &tmp, exported_size)); + UniquePtr 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 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 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 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 deleter(key); + + printf("=== Signing with ECDSA key === \n"); + keymaster_ec_sign_params_t sign_params = {DIGEST_NONE}; + size_t message_len = 30 /* arbitrary */; + UniquePtr 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 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 pkey(d2i_PUBKEY(NULL, &tmp, exported_size)); + UniquePtr 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 000000000..15cb6f3d5 --- /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 000000000..b196d88b3 --- /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 + +/* + * 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 000000000..9fc73d3a2 --- /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 000000000..b4748e209 --- /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 +#include +#include +#include +#include +#include +#include + +#include + +#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 000000000..2e366bbb9 --- /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 +#include + +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 000000000..471cb500f --- /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 + diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c new file mode 100644 index 000000000..d645ac01c --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 -p -r \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 000000000..91304583a --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 000000000..85cff44d9 --- /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 +#include + +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 000000000..c61e89d78 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000..5a670d4b7 --- /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 +#include + +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 000000000..9c3a7df0d --- /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 000000000..3f263167c --- /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 \