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