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