1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <inttypes.h> 6#include <stddef.h> 7#include <stdint.h> 8#include <threads.h> 9 10#include <crypto/cipher.h> 11#include <ddk/debug.h> 12#include <fbl/auto_call.h> 13#include <lib/zx/port.h> 14#include <zircon/listnode.h> 15#include <zircon/status.h> 16#include <zircon/syscalls/port.h> 17#include <zircon/types.h> 18#include <zxcrypt/volume.h> 19 20#include "debug.h" 21#include "device.h" 22#include "extra.h" 23#include "worker.h" 24 25namespace zxcrypt { 26 27Worker::Worker() : device_(nullptr) { 28 LOG_ENTRY(); 29} 30 31Worker::~Worker() { 32 LOG_ENTRY(); 33} 34 35void Worker::MakeRequest(zx_port_packet_t* packet, uint64_t op, void* arg) { 36 static_assert(sizeof(uintptr_t) <= sizeof(uint64_t), "cannot store pointer as uint64_t"); 37 ZX_DEBUG_ASSERT(packet); 38 packet->key = 0; 39 packet->type = ZX_PKT_TYPE_USER; 40 packet->status = ZX_OK; 41 packet->user.u64[0] = op; 42 packet->user.u64[1] = reinterpret_cast<uint64_t>(arg); 43} 44 45zx_status_t Worker::Start(Device* device, const Volume& volume, zx::port&& port) { 46 LOG_ENTRY_ARGS("device=%p, volume=%p, port=%p", device, &volume, &port); 47 zx_status_t rc; 48 49 if (!device) { 50 zxlogf(ERROR, "bad parameters: device=%p\n", device); 51 return ZX_ERR_INVALID_ARGS; 52 } 53 device_ = device; 54 55 if ((rc = volume.Bind(crypto::Cipher::kEncrypt, &encrypt_)) != ZX_OK || 56 (rc = volume.Bind(crypto::Cipher::kDecrypt, &decrypt_)) != ZX_OK) { 57 zxlogf(ERROR, "failed to bind ciphers: %s\n", zx_status_get_string(rc)); 58 return rc; 59 } 60 61 port_ = fbl::move(port); 62 63 if (thrd_create(&thrd_, WorkerRun, this) != thrd_success) { 64 zxlogf(ERROR, "failed to start thread\n"); 65 return ZX_ERR_INTERNAL; 66 } 67 68 return ZX_OK; 69} 70 71zx_status_t Worker::Run() { 72 LOG_ENTRY(); 73 ZX_DEBUG_ASSERT(device_); 74 zx_status_t rc; 75 76 zx_port_packet_t packet; 77 while (true) { 78 // Read request 79 if ((rc = port_.wait(zx::time::infinite(), &packet)) != ZX_OK) { 80 zxlogf(ERROR, "failed to read request: %s\n", zx_status_get_string(rc)); 81 return rc; 82 } 83 ZX_DEBUG_ASSERT(packet.key == 0); 84 ZX_DEBUG_ASSERT(packet.type == ZX_PKT_TYPE_USER); 85 ZX_DEBUG_ASSERT(packet.status == ZX_OK); 86 87 // Handle control messages 88 switch (packet.user.u64[0]) { 89 case kBlockRequest: 90 break; 91 case kStopRequest: 92 zxlogf(TRACE, "worker %p stopping.\n", this); 93 return ZX_OK; 94 default: 95 zxlogf(ERROR, "unknown request: 0x%016" PRIx64 "\n", packet.user.u64[0]); 96 return ZX_ERR_NOT_SUPPORTED; 97 } 98 99 // Dispatch block request 100 block_op_t* block = reinterpret_cast<block_op_t*>(packet.user.u64[1]); 101 switch (block->command & BLOCK_OP_MASK) { 102 case BLOCK_OP_WRITE: 103 device_->BlockForward(block, EncryptWrite(block)); 104 break; 105 106 case BLOCK_OP_READ: 107 device_->BlockComplete(block, DecryptRead(block)); 108 break; 109 110 default: 111 device_->BlockComplete(block, ZX_ERR_NOT_SUPPORTED); 112 } 113 } 114} 115 116zx_status_t Worker::EncryptWrite(block_op_t* block) { 117 LOG_ENTRY_ARGS("block=%p", block); 118 zx_status_t rc; 119 120 // Convert blocks to bytes 121 extra_op_t* extra = BlockToExtra(block, device_->op_size()); 122 uint32_t length; 123 uint64_t offset_dev, offset_vmo; 124 if (mul_overflow(block->rw.length, device_->block_size(), &length) || 125 mul_overflow(block->rw.offset_dev, device_->block_size(), &offset_dev) || 126 mul_overflow(extra->offset_vmo, device_->block_size(), &offset_vmo)) { 127 zxlogf(ERROR, 128 "overflow; length=%" PRIu32 "; offset_dev=%" PRIu64 "; offset_vmo=%" PRIu64 "\n", 129 block->rw.length, block->rw.offset_dev, extra->offset_vmo); 130 return ZX_ERR_OUT_OF_RANGE; 131 } 132 133 // Copy and encrypt the plaintext 134 if ((rc = zx_vmo_read(extra->vmo, extra->data, offset_vmo, length)) != ZX_OK) { 135 zxlogf(ERROR, "zx_vmo_read() failed: %s\n", zx_status_get_string(rc)); 136 return rc; 137 } 138 if ((rc = encrypt_.Encrypt(extra->data, offset_dev, length, extra->data) != ZX_OK)) { 139 zxlogf(ERROR, "failed to encrypt: %s\n", zx_status_get_string(rc)); 140 return rc; 141 } 142 143 return ZX_OK; 144} 145 146zx_status_t Worker::DecryptRead(block_op_t* block) { 147 LOG_ENTRY_ARGS("block=%p", block); 148 zx_status_t rc; 149 150 // Convert blocks to bytes 151 uint32_t length; 152 uint64_t offset_dev, offset_vmo; 153 if (mul_overflow(block->rw.length, device_->block_size(), &length) || 154 mul_overflow(block->rw.offset_dev, device_->block_size(), &offset_dev) || 155 mul_overflow(block->rw.offset_vmo, device_->block_size(), &offset_vmo)) { 156 zxlogf(ERROR, 157 "overflow; length=%" PRIu32 "; offset_dev=%" PRIu64 "; offset_vmo=%" PRIu64 "\n", 158 block->rw.length, block->rw.offset_dev, block->rw.offset_vmo); 159 return ZX_ERR_OUT_OF_RANGE; 160 } 161 162 // Map the ciphertext 163 zx_handle_t root = zx_vmar_root_self(); 164 uintptr_t address; 165 constexpr uint32_t flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 166 if ((rc = zx_vmar_map(root, flags, 0, block->rw.vmo, offset_vmo, length, &address)) != ZX_OK) { 167 zxlogf(ERROR, "zx::vmar::root_self()->map() failed: %s\n", zx_status_get_string(rc)); 168 return rc; 169 } 170 auto cleanup = 171 fbl::MakeAutoCall([root, address, length]() { zx_vmar_unmap(root, address, length); }); 172 173 // Decrypt in place 174 uint8_t* data = reinterpret_cast<uint8_t*>(address); 175 if ((rc = decrypt_.Decrypt(data, offset_dev, length, data)) != ZX_OK) { 176 zxlogf(ERROR, "failed to decrypt: %s\n", zx_status_get_string(rc)); 177 return rc; 178 } 179 180 return ZX_OK; 181} 182 183zx_status_t Worker::Stop() { 184 LOG_ENTRY(); 185 zx_status_t rc; 186 187 thrd_join(thrd_, &rc); 188 189 if (rc != ZX_OK) { 190 zxlogf(WARN, "worker exited with error: %s\n", zx_status_get_string(rc)); 191 return rc; 192 } 193 194 return ZX_OK; 195} 196 197} // namespace zxcrypt 198