/* * 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. */ #define LOG_TAG "TrustyKeymaster" // TODO: make this generic in libtrusty #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0" static int handle_ = -1; static const int timeout_ms = 10 * 1000; static const int max_timeout_ms = 60 * 1000; int trusty_keymaster_connect() { int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT); if (rc < 0) { return rc; } handle_ = rc; return 0; } class VectorEraser { public: VectorEraser(std::vector* v) : _v(v) {} ~VectorEraser() { if (_v) { std::fill(const_cast(_v->data()), const_cast(_v->data() + _v->size()), 0); } } void disarm() { _v = nullptr; } VectorEraser(const VectorEraser&) = delete; VectorEraser& operator=(const VectorEraser&) = delete; VectorEraser(VectorEraser&& other) = delete; VectorEraser& operator=(VectorEraser&&) = delete; private: std::vector* _v; }; std::variant> trusty_keymaster_call_2(uint32_t cmd, void* in, uint32_t in_size) { if (handle_ < 0) { ALOGE("not connected\n"); return -EINVAL; } size_t msg_size = in_size + sizeof(struct keymaster_message); struct keymaster_message* msg = reinterpret_cast(malloc(msg_size)); if (!msg) { ALOGE("failed to allocate msg buffer\n"); return -EINVAL; } msg->cmd = cmd; memcpy(msg->payload, in, in_size); nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC); bool timed_out = false; int poll_timeout_ms = timeout_ms; while (true) { struct pollfd pfd; pfd.fd = handle_; pfd.events = POLLOUT; pfd.revents = 0; int p = poll(&pfd, 1, poll_timeout_ms); if (p == 0) { ALOGW("write for cmd %d is taking more than %lld nsecs", cmd, (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns)); timed_out = true; poll_timeout_ms *= 2; if (poll_timeout_ms > max_timeout_ms) { poll_timeout_ms = max_timeout_ms; } continue; } else if (p < 0) { ALOGE("write poll error: %d", errno); } else if (pfd.revents != POLLOUT) { ALOGW("unexpected poll() result: %d", pfd.revents); } break; } ssize_t rc = write(handle_, msg, msg_size); if (timed_out) { ALOGW("write for cmd %d finished after %lld nsecs", cmd, (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns)); } free(msg); if (rc < 0) { ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno)); return -errno; } std::vector out(TRUSTY_KEYMASTER_RECV_BUF_SIZE); VectorEraser out_eraser(&out); uint8_t* write_pos = out.data(); uint8_t* out_end = out.data() + out.size(); struct iovec iov[2]; struct keymaster_message header; iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)}; while (true) { if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) { // In stead of using std::vector.resize(), allocate a new one to have chance // at zeroing the old buffer. std::vector new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH); // After the swap below this erases the old out buffer. VectorEraser new_out_eraser(&new_out); std::copy(out.data(), write_pos, new_out.begin()); auto write_offset = write_pos - out.data(); std::swap(new_out, out); write_pos = out.data() + write_offset; out_end = out.data() + out.size(); } size_t buffer_size = 0; if (__builtin_sub_overflow(reinterpret_cast(out_end), reinterpret_cast(write_pos), &buffer_size)) { return -EOVERFLOW; } iov[1] = {.iov_base = write_pos, .iov_len = buffer_size}; start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC); timed_out = false; poll_timeout_ms = timeout_ms; while (true) { struct pollfd pfd; pfd.fd = handle_; pfd.events = POLLIN; pfd.revents = 0; int p = poll(&pfd, 1, poll_timeout_ms); if (p == 0) { ALOGW("readv for cmd %d is taking more than %lld nsecs", cmd, (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns)); timed_out = true; poll_timeout_ms *= 2; if (poll_timeout_ms > max_timeout_ms) { poll_timeout_ms = max_timeout_ms; } continue; } else if (p < 0) { ALOGE("read poll error: %d", errno); } else if (pfd.revents != POLLIN) { ALOGW("unexpected poll() result: %d", pfd.revents); } break; } rc = readv(handle_, iov, 2); if (timed_out) { ALOGW("readv for cmd %d finished after %lld nsecs", cmd, (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns)); } 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; } if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) { ALOGE("invalid command (%d)", header.cmd); return -EINVAL; } write_pos += ((size_t)rc - sizeof(struct keymaster_message)); if (header.cmd & KEYMASTER_STOP_BIT) { break; } } out.resize(write_pos - out.data()); out_eraser.disarm(); return out; } int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out, uint32_t* out_size) { auto result = trusty_keymaster_call_2(cmd, in, in_size); if (auto out_buffer = std::get_if>(&result)) { if (out_buffer->size() <= *out_size) { std::copy(out_buffer->begin(), out_buffer->end(), out); std::fill(const_cast(&*out_buffer->begin()), const_cast(&*out_buffer->end()), 0); *out_size = out_buffer->size(); return 0; } else { ALOGE("Message was to large (%zu) for the provided buffer (%u)", out_buffer->size(), *out_size); return -EMSGSIZE; } } else { return std::get(result); } } void trusty_keymaster_disconnect() { if (handle_ >= 0) { tipc_close(handle_); } handle_ = -1; } 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; } } keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req, keymaster::KeymasterResponse* rsp) { uint32_t req_size = req.SerializedSize(); if (req_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) { ALOGE("Request too big: %u Max size: %u", req_size, TRUSTY_KEYMASTER_SEND_BUF_SIZE); return KM_ERROR_INVALID_INPUT_LENGTH; } uint8_t send_buf[TRUSTY_KEYMASTER_SEND_BUF_SIZE]; keymaster::Eraser send_buf_eraser(send_buf, TRUSTY_KEYMASTER_SEND_BUF_SIZE); req.Serialize(send_buf, send_buf + req_size); // Send it auto response = trusty_keymaster_call_2(command, send_buf, req_size); if (auto response_buffer = std::get_if>(&response)) { keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size()); ALOGV("Received %zu byte response\n", response_buffer->size()); const uint8_t* p = response_buffer->data(); if (!rsp->Deserialize(&p, p + response_buffer->size())) { ALOGE("Error deserializing response of size %zu\n", response_buffer->size()); return KM_ERROR_UNKNOWN_ERROR; } else if (rsp->error != KM_ERROR_OK) { ALOGE("Response of size %zu contained error code %d\n", response_buffer->size(), (int)rsp->error); } return rsp->error; } else { auto rc = std::get(response); // Reset the connection on tipc error trusty_keymaster_disconnect(); trusty_keymaster_connect(); ALOGE("tipc error: %d\n", rc); // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately. return translate_error(rc); } }