1 // Copyright 2017 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 <arpa/inet.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <inttypes.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include <crypto/bytes.h>
15 #include <crypto/cipher.h>
16 #include <crypto/hkdf.h>
17 #include <crypto/secret.h>
18 #include <ddk/device.h>
19 #include <ddk/driver.h>
20 #include <ddk/protocol/block.h>
21 #include <fbl/algorithm.h>
22 #include <fbl/auto_call.h>
23 #include <fbl/macros.h>
24 #include <fbl/string_buffer.h>
25 #include <fbl/unique_fd.h>
26 #include <fbl/unique_ptr.h>
27 #include <fs-management/mount.h>
28 #include <fs-management/ramdisk.h>
29 #include <lib/fdio/debug.h>
30 #include <lib/sync/completion.h>
31 #include <lib/zx/vmo.h>
32 #include <zircon/compiler.h>
33 #include <zircon/device/block.h>
34 #include <zircon/errors.h>
35 #include <zircon/status.h>
36 #include <zircon/types.h>
37 #include <zxcrypt/volume.h>
38
39 #include <utility>
40
41 #define ZXDEBUG 0
42
43 namespace zxcrypt {
44
45 // Several copies of the metadata for a zxcrypt volume is saved at the beginning and end of the
46 // devices. The number of copies is given by |kMetadataBlocks * kReservedSlices|, and the locations
47 // of each block can be iterated through using |Begin| and |Next|. The metadata block, or
48 // superblock, consists of a fixed type GUID, an instance GUID, a 32-bit version, a set of "key
49 // slots" The key slots are data cipher key material encrypted with a wrapping crypto::AEAD key
50 // derived from the caller-provided root key and specific slot.
51
52 // Determines what algorithms are in use when creating new zxcrypt devices.
53 const Volume::Version Volume::kDefaultVersion = Volume::kAES256_XTS_SHA256;
54
55 // The amount of data that can "in-flight" to the underlying block device before the zxcrypt
56 // driver begins queuing transactions
57 //
58 // TODO(aarongreen): See ZX-1616. Tune this value. Possibly break into several smaller VMOs if we
59 // want to allow some to be recycled; support for this doesn't currently exist. Up to 64 MB may be
60 // in flight at once. The device's max_transfer_size will be capped at 1/4 of this value.
61 const uint32_t Volume::kBufferSize = 1U << 24;
62 static_assert(Volume::kBufferSize % PAGE_SIZE == 0, "kBufferSize must be page aligned");
63
64 namespace {
65
66 // The zxcrypt driver
67 const char* kDriverLib = "/boot/driver/zxcrypt.so";
68
69 // The number of metadata blocks in a reserved metadata slice, each holding a copy of the
70 // superblock.
71 const size_t kMetadataBlocks = 2;
72
73 // HKDF labels
74 const size_t kMaxLabelLen = 16;
75 const char* kWrapKeyLabel = "wrap key %" PRIu64;
76 const char* kWrapIvLabel = "wrap iv %" PRIu64;
77
78 // Header is type GUID | instance GUID | version.
79 const size_t kHeaderLen = sizeof(zxcrypt_magic) + GUID_LEN + sizeof(uint32_t);
80
SyncComplete(void * cookie,zx_status_t status,block_op_t * block)81 void SyncComplete(void* cookie, zx_status_t status, block_op_t* block) {
82 // Use the 32bit command field to shuttle the response back to the callsite that's waiting on
83 // the completion
84 block->command = status;
85 sync_completion_signal(static_cast<sync_completion_t*>(cookie));
86 }
87
88 // Performs synchronous I/O
SyncIO(zx_device_t * dev,uint32_t cmd,void * buf,size_t off,size_t len)89 zx_status_t SyncIO(zx_device_t* dev, uint32_t cmd, void* buf, size_t off, size_t len) {
90 zx_status_t rc;
91
92 if (!dev || !buf || len == 0) {
93 xprintf("bad parameter(s): dev=%p, buf=%p, len=%zu\n", dev, buf, len);
94 return ZX_ERR_INVALID_ARGS;
95 }
96
97 block_impl_protocol_t proto;
98 if ((rc = device_get_protocol(dev, ZX_PROTOCOL_BLOCK, &proto)) != ZX_OK) {
99 xprintf("block protocol not support\n");
100 return ZX_ERR_NOT_SUPPORTED;
101 }
102
103 zx::vmo vmo;
104 if ((rc = zx::vmo::create(len, 0, &vmo)) != ZX_OK) {
105 xprintf("zx::vmo::create failed: %s\n", zx_status_get_string(rc));
106 return rc;
107 }
108
109 block_info_t info;
110 size_t op_size;
111 block_impl_query(&proto, &info, &op_size);
112
113 size_t bsz = info.block_size;
114 ZX_DEBUG_ASSERT(off / bsz <= UINT32_MAX);
115 ZX_DEBUG_ASSERT(len / bsz <= UINT32_MAX);
116
117 char raw[op_size];
118 block_op_t* block = reinterpret_cast<block_op_t*>(raw);
119
120 sync_completion_t completion;
121 sync_completion_reset(&completion);
122
123 block->command = cmd;
124 block->rw.vmo = vmo.get();
125 block->rw.length = static_cast<uint32_t>(len / bsz);
126 block->rw.offset_dev = static_cast<uint32_t>(off / bsz);
127 block->rw.offset_vmo = 0;
128
129 if (cmd == BLOCK_OP_WRITE && (rc = vmo.write(buf, 0, len)) != ZX_OK) {
130 xprintf("zx::vmo::write failed: %s\n", zx_status_get_string(rc));
131 return rc;
132 }
133
134 block_impl_queue(&proto, block, SyncComplete, &completion);
135 sync_completion_wait(&completion, ZX_TIME_INFINITE);
136
137 rc = block->command;
138 if (rc != ZX_OK) {
139 xprintf("Block I/O failed: %s\n", zx_status_get_string(rc));
140 return rc;
141 }
142
143 if (cmd == BLOCK_OP_READ && (rc = vmo.read(buf, 0, len)) != ZX_OK) {
144 xprintf("zx::vmo::read failed: %s\n", zx_status_get_string(rc));
145 return rc;
146 }
147
148 return ZX_OK;
149 }
150
151 } // namespace
152
Volume(fbl::unique_fd && fd)153 Volume::Volume(fbl::unique_fd&& fd) {
154 Reset();
155 fd_ = std::move(fd);
156 dev_ = nullptr;
157 }
158
Volume(zx_device_t * dev)159 Volume::Volume(zx_device_t* dev) {
160 Reset();
161 dev_ = dev;
162 }
163
~Volume()164 Volume::~Volume() {}
165
Reset()166 void Volume::Reset() {
167 reserved_blocks_ = 0;
168 reserved_slices_ = 0;
169 block_.Resize(0);
170 offset_ = UINT64_MAX;
171 guid_.Resize(0);
172 header_.Resize(0);
173 aead_ = crypto::AEAD::kUninitialized;
174 wrap_key_.Clear();
175 wrap_iv_.Resize(0);
176 cipher_ = crypto::Cipher::kUninitialized;
177 data_key_.Clear();
178 data_iv_.Resize(0);
179 slot_len_ = 0;
180 num_key_slots_ = 0;
181 digest_ = crypto::digest::kUninitialized;
182 }
183
Init(fbl::unique_fd fd,fbl::unique_ptr<Volume> * out)184 zx_status_t Volume::Init(fbl::unique_fd fd, fbl::unique_ptr<Volume>* out) {
185 zx_status_t rc;
186
187 if (!fd || !out) {
188 xprintf("bad parameter(s): fd=%d, out=%p\n", fd.get(), out);
189 return ZX_ERR_INVALID_ARGS;
190 }
191
192 fbl::AllocChecker ac;
193 fbl::unique_ptr<Volume> volume(new (&ac) Volume(std::move(fd)));
194 if (!ac.check()) {
195 xprintf("allocation failed: %zu bytes\n", sizeof(Volume));
196 return ZX_ERR_NO_MEMORY;
197 }
198
199 if ((rc = volume->Init()) != ZX_OK) {
200 return rc;
201 }
202
203 *out = std::move(volume);
204 return ZX_OK;
205 }
206
Create(fbl::unique_fd fd,const crypto::Secret & key,fbl::unique_ptr<Volume> * out)207 zx_status_t Volume::Create(fbl::unique_fd fd, const crypto::Secret& key,
208 fbl::unique_ptr<Volume>* out) {
209 zx_status_t rc;
210
211 fbl::unique_ptr<Volume> volume;
212 if ((rc = Volume::Init(std::move(fd), &volume)) != ZX_OK ||
213 (rc = volume->CreateBlock()) != ZX_OK || (rc = volume->SealBlock(key, 0)) != ZX_OK ||
214 (rc = volume->CommitBlock()) != ZX_OK) {
215 return rc;
216 }
217
218 if (out) {
219 *out = std::move(volume);
220 }
221 return ZX_OK;
222 }
223
Unlock(fbl::unique_fd fd,const crypto::Secret & key,key_slot_t slot,fbl::unique_ptr<Volume> * out)224 zx_status_t Volume::Unlock(fbl::unique_fd fd, const crypto::Secret& key, key_slot_t slot,
225 fbl::unique_ptr<Volume>* out) {
226 zx_status_t rc;
227
228 fbl::unique_ptr<Volume> volume;
229 if ((rc = Volume::Init(std::move(fd), &volume)) != ZX_OK ||
230 (rc = volume->Unlock(key, slot)) != ZX_OK) {
231 return rc;
232 }
233
234 *out = std::move(volume);
235 return ZX_OK;
236 }
237
Unlock(zx_device_t * dev,const crypto::Secret & key,key_slot_t slot,fbl::unique_ptr<Volume> * out)238 zx_status_t Volume::Unlock(zx_device_t* dev, const crypto::Secret& key, key_slot_t slot,
239 fbl::unique_ptr<Volume>* out) {
240 zx_status_t rc;
241
242 if (!dev || !out) {
243 xprintf("bad parameter(s): dev=%p, out=%p\n", dev, out);
244 return ZX_ERR_INVALID_ARGS;
245 }
246 fbl::AllocChecker ac;
247 fbl::unique_ptr<Volume> volume(new (&ac) Volume(dev));
248 if (!ac.check()) {
249 xprintf("allocation failed: %zu bytes\n", sizeof(Volume));
250 return ZX_ERR_NO_MEMORY;
251 }
252 if ((rc = volume->Init()) != ZX_OK || (rc = volume->Unlock(key, slot)) != ZX_OK) {
253 return rc;
254 }
255
256 *out = std::move(volume);
257 return ZX_OK;
258 }
259
Unlock(const crypto::Secret & key,key_slot_t slot)260 zx_status_t Volume::Unlock(const crypto::Secret& key, key_slot_t slot) {
261 zx_status_t rc;
262
263 for (rc = Begin(); rc == ZX_ERR_NEXT; rc = Next()) {
264 if ((rc = Read()) != ZX_OK) {
265 xprintf("failed to read block at %" PRIu64 ": %d\n", offset_, rc);
266 } else if ((rc = UnsealBlock(key, slot)) != ZX_OK) {
267 xprintf("failed to open block at %" PRIu64 ": %d\n", offset_, rc);
268 } else {
269 return ZX_OK;
270 }
271 }
272
273 return ZX_ERR_ACCESS_DENIED;
274 }
275
Open(const zx::duration & timeout,fbl::unique_fd * out)276 zx_status_t Volume::Open(const zx::duration& timeout, fbl::unique_fd* out) {
277 zx_status_t rc;
278 ssize_t res;
279
280 // Get the full device path
281 fbl::StringBuffer<PATH_MAX> path;
282 path.Resize(path.capacity());
283 if ((res = ioctl_device_get_topo_path(fd_.get(), path.data(), path.capacity())) < 0) {
284 rc = static_cast<zx_status_t>(res);
285 xprintf("could not find parent device: %s\n", zx_status_get_string(rc));
286 return rc;
287 }
288 path.Resize(strlen(path.c_str()));
289 path.Append("/zxcrypt/block");
290
291 // Early return if already bound
292 fbl::unique_fd fd(open(path.c_str(), O_RDWR));
293 if (fd) {
294 out->reset(fd.release());
295 return ZX_OK;
296 }
297
298 // Bind the device
299 if ((res = ioctl_device_bind(fd_.get(), kDriverLib, strlen(kDriverLib))) < 0) {
300 rc = static_cast<zx_status_t>(res);
301 xprintf("could not bind zxcrypt driver: %s\n", zx_status_get_string(rc));
302 return rc;
303 }
304 if ((rc = wait_for_device(path.c_str(), timeout.get())) != ZX_OK) {
305 xprintf("zxcrypt driver failed to bind: %s\n", zx_status_get_string(rc));
306 return rc;
307 }
308 fd.reset(open(path.c_str(), O_RDWR));
309 if (!fd) {
310 xprintf("failed to open zxcrypt volume\n");
311 return ZX_ERR_NOT_FOUND;
312 }
313
314 out->reset(fd.release());
315 return ZX_OK;
316 }
317
Bind(crypto::Cipher::Direction direction,crypto::Cipher * cipher) const318 zx_status_t Volume::Bind(crypto::Cipher::Direction direction, crypto::Cipher* cipher) const {
319 zx_status_t rc;
320 ZX_DEBUG_ASSERT(dev_); // Cannot bind from library
321
322 if (!cipher) {
323 xprintf("bad parameter(s): cipher=%p\n", cipher);
324 return ZX_ERR_INVALID_ARGS;
325 }
326 if (!block_.get()) {
327 xprintf("not initialized\n");
328 return ZX_ERR_BAD_STATE;
329 }
330 if ((rc = cipher->Init(cipher_, direction, data_key_, data_iv_, block_.len())) != ZX_OK) {
331 return rc;
332 }
333
334 return ZX_OK;
335 }
336
Enroll(const crypto::Secret & key,key_slot_t slot)337 zx_status_t Volume::Enroll(const crypto::Secret& key, key_slot_t slot) {
338 zx_status_t rc;
339 ZX_DEBUG_ASSERT(!dev_); // Cannot enroll from driver
340
341 if ((rc = SealBlock(key, slot)) != ZX_OK || (rc = CommitBlock()) != ZX_OK) {
342 return rc;
343 }
344
345 return ZX_OK;
346 }
347
Revoke(key_slot_t slot)348 zx_status_t Volume::Revoke(key_slot_t slot) {
349 zx_status_t rc;
350 ZX_DEBUG_ASSERT(!dev_); // Cannot revoke from driver
351
352 zx_off_t off;
353 crypto::Bytes invalid;
354 if ((rc = GetSlotOffset(slot, &off)) != ZX_OK || (rc = invalid.Randomize(slot_len_)) != ZX_OK ||
355 (rc = block_.Copy(invalid, off)) != ZX_OK || (rc = CommitBlock()) != ZX_OK) {
356 return rc;
357 }
358
359 return ZX_OK;
360 }
361
Shred()362 zx_status_t Volume::Shred() {
363 zx_status_t rc;
364 ZX_DEBUG_ASSERT(!dev_); // Cannot shred from driver
365
366 if (!block_.get()) {
367 xprintf("not initialized\n");
368 return ZX_ERR_BAD_STATE;
369 }
370 if ((rc = block_.Randomize()) != ZX_OK) {
371 return rc;
372 }
373 for (rc = Begin(); rc == ZX_ERR_NEXT; rc = Next()) {
374 if ((rc = Write()) != ZX_OK) {
375 return rc;
376 }
377 }
378 Reset();
379
380 return ZX_OK;
381 }
382
383 // Configuration methods
384
GetSlotOffset(key_slot_t slot,zx_off_t * out) const385 zx_status_t Volume::GetSlotOffset(key_slot_t slot, zx_off_t* out) const {
386 if (!block_.get()) {
387 xprintf("not initialized\n");
388 return ZX_ERR_BAD_STATE;
389 }
390
391 zx_off_t off;
392 if (mul_overflow(slot, slot_len_, &off) || add_overflow(kHeaderLen, off, &off) ||
393 off > block_.len() - slot_len_) {
394 xprintf("bad key slot: %" PRIu64 "\n", slot);
395 return ZX_ERR_INVALID_ARGS;
396 }
397
398 if (out) {
399 *out = off;
400 }
401 return ZX_OK;
402 }
403
Init()404 zx_status_t Volume::Init() {
405 zx_status_t rc;
406
407 // Get block info; align our blocks to pages
408 block_info_t blk;
409 if ((rc = Ioctl(IOCTL_BLOCK_GET_INFO, nullptr, 0, &blk, sizeof(blk))) < 0) {
410 xprintf("failed to get block info: %s\n", zx_status_get_string(rc));
411 return rc;
412 }
413 // Check that we meet the minimum size.
414 if (blk.block_count < kMetadataBlocks) {
415 xprintf("device is too small; have %" PRIu64 " blocks, need %" PRIu64 "\n", blk.block_count,
416 kMetadataBlocks);
417 return ZX_ERR_NOT_SUPPORTED;
418 }
419 reserved_blocks_ = kMetadataBlocks;
420 // Allocate block buffer
421 if ((rc = block_.Resize(blk.block_size)) != ZX_OK) {
422 return rc;
423 }
424 // Get FVM info
425 fvm_info_t fvm;
426 switch ((rc = Ioctl(IOCTL_BLOCK_FVM_QUERY, nullptr, 0, &fvm, sizeof(fvm)))) {
427 case ZX_OK: {
428 // This *IS* an FVM partition.
429 // Ensure first kReservedSlices + 1 slices are allocated
430 size_t blocks_per_slice = fvm.slice_size / blk.block_size;
431 reserved_blocks_ = fbl::round_up(reserved_blocks_, blocks_per_slice);
432 reserved_slices_ = reserved_blocks_ / blocks_per_slice;
433 size_t required = reserved_slices_ + 1;
434 size_t range = 1;
435 query_request_t request;
436 query_response_t response;
437 extend_request_t extend;
438 for (size_t i = 0; i < required; i += range) {
439 // Ask about the next contiguous range
440 request.count = 1;
441 request.vslice_start[0] = i + 1;
442 if ((rc = Ioctl(IOCTL_BLOCK_FVM_VSLICE_QUERY, &request, sizeof(request), &response,
443 sizeof(response))) < 0 ||
444 response.count == 0 || (range = response.vslice_range[0].count) == 0) {
445 xprintf("ioctl_block_fvm_vslice_query failed: %s\n", zx_status_get_string(rc));
446 return rc;
447 }
448 // If already allocated, continue
449 if (response.vslice_range[0].allocated) {
450 continue;
451 };
452 // Otherwise, allocate it
453 extend.offset = i + 1;
454 extend.length = fbl::min(required - i, range);
455 if ((rc = Ioctl(IOCTL_BLOCK_FVM_EXTEND, &extend, sizeof(extend), nullptr, 0)) < 0) {
456 xprintf("failed to extend FVM partition: %s\n", zx_status_get_string(rc));
457 return rc;
458 }
459 }
460 break;
461 }
462 case ZX_ERR_NOT_SUPPORTED:
463 // This is *NOT* an FVM partition.
464 break;
465 default:
466 // An error occurred
467 return rc;
468 }
469
470 return ZX_OK;
471 }
472
Configure(Volume::Version version)473 zx_status_t Volume::Configure(Volume::Version version) {
474 zx_status_t rc;
475
476 switch (version) {
477 case Volume::kAES256_XTS_SHA256:
478 aead_ = crypto::AEAD::kAES128_GCM_SIV;
479 cipher_ = crypto::Cipher::kAES256_XTS;
480 digest_ = crypto::digest::kSHA256;
481 break;
482
483 default:
484 xprintf("unknown version: %u\n", version);
485 return ZX_ERR_NOT_SUPPORTED;
486 }
487
488 size_t key_len, iv_len, tag_len;
489 if ((rc = crypto::Cipher::GetKeyLen(cipher_, &key_len)) != ZX_OK ||
490 (rc = crypto::Cipher::GetIVLen(cipher_, &iv_len)) != ZX_OK ||
491 (rc = crypto::AEAD::GetTagLen(aead_, &tag_len)) != ZX_OK) {
492 return rc;
493 }
494
495 slot_len_ = key_len + iv_len + tag_len;
496 num_key_slots_ = (block_.len() - kHeaderLen) / slot_len_;
497 if (num_key_slots_ == 0) {
498 xprintf("block size is too small; have %zu, need %zu\n", block_.len(),
499 kHeaderLen + slot_len_);
500 return ZX_ERR_NOT_SUPPORTED;
501 }
502
503 return ZX_OK;
504 }
505
DeriveSlotKeys(const crypto::Secret & key,key_slot_t slot)506 zx_status_t Volume::DeriveSlotKeys(const crypto::Secret& key, key_slot_t slot) {
507 zx_status_t rc;
508
509 crypto::HKDF hkdf;
510 char label[kMaxLabelLen];
511 if ((rc = hkdf.Init(digest_, key, guid_)) != ZX_OK) {
512 return rc;
513 }
514 snprintf(label, kMaxLabelLen, kWrapKeyLabel, slot);
515 size_t len;
516 if ((rc = crypto::AEAD::GetKeyLen(aead_, &len)) != ZX_OK ||
517 (rc = hkdf.Derive(label, len, &wrap_key_)) != ZX_OK) {
518 xprintf("failed to derive wrap key: %s\n", zx_status_get_string(rc));
519 return rc;
520 }
521 snprintf(label, kMaxLabelLen, kWrapIvLabel, slot);
522 crypto::Secret wrap_iv;
523 if ((rc = crypto::AEAD::GetIVLen(aead_, &len)) != ZX_OK ||
524 (rc = hkdf.Derive(label, len, &wrap_iv_)) != ZX_OK) {
525 xprintf("failed to derive wrap IV: %s\n", zx_status_get_string(rc));
526 return rc;
527 }
528
529 return ZX_OK;
530 }
531
532 // Block methods
533
Begin()534 zx_status_t Volume::Begin() {
535 offset_ = 0;
536 return ZX_ERR_NEXT;
537 }
538
Next()539 zx_status_t Volume::Next() {
540 offset_ += block_.len();
541 return (offset_ / block_.len()) < kMetadataBlocks ? ZX_ERR_NEXT : ZX_ERR_STOP;
542 }
543
CreateBlock()544 zx_status_t Volume::CreateBlock() {
545 zx_status_t rc;
546
547 // Create a "backdrop" of random data
548 if ((rc = block_.Randomize()) != ZX_OK) {
549 return rc;
550 }
551
552 // Write the variant 1/version 1 type GUID according to RFC 4122.
553 // TODO(aarongreen): ZX-2106. This and other magic numbers should be moved to a public/zircon
554 // header, and the dependency removed.
555 uint8_t* out = block_.get();
556 memcpy(out, zxcrypt_magic, sizeof(zxcrypt_magic));
557 out += sizeof(zxcrypt_magic);
558
559 // Create a variant 1/version 4 instance GUID according to RFC 4122.
560 if ((rc = guid_.Randomize(GUID_LEN)) != ZX_OK) {
561 return rc;
562 }
563 guid_[6] = (guid_[6] & 0x0F) | 0x40;
564 guid_[8] = (guid_[8] & 0x3F) | 0x80;
565 memcpy(out, guid_.get(), GUID_LEN);
566 out += GUID_LEN;
567
568 // Write the 32-bit version.
569 if ((rc = Configure(kDefaultVersion)) != ZX_OK) {
570 return rc;
571 }
572 uint32_t version = htonl(kDefaultVersion);
573 memcpy(out, &version, sizeof(version));
574
575 // Generate the data key and IV, and save the AAD.
576 size_t key_len, iv_len;
577 if ((rc = crypto::Cipher::GetKeyLen(cipher_, &key_len)) != ZX_OK ||
578 (rc = crypto::Cipher::GetIVLen(cipher_, &iv_len)) != ZX_OK ||
579 (rc = data_key_.Generate(key_len)) != ZX_OK || (rc = data_iv_.Resize(iv_len)) != ZX_OK ||
580 (rc = data_iv_.Randomize()) != ZX_OK ||
581 (rc = header_.Copy(block_.get(), kHeaderLen)) != ZX_OK) {
582 return rc;
583 }
584
585 return ZX_OK;
586 }
587
CommitBlock()588 zx_status_t Volume::CommitBlock() {
589 zx_status_t rc;
590
591 // Make a copy to compare the read result to; this reduces the number of
592 // writes we must do.
593 crypto::Bytes block;
594 if ((rc = block.Copy(block_)) != ZX_OK) {
595 xprintf("zxcrypt: Cannot copy block: %s\n", zx_status_get_string(rc));
596 return rc;
597 }
598 for (rc = Begin(); rc == ZX_ERR_NEXT; rc = Next()) {
599 if ((rc = Read()) != ZX_OK) {
600 xprintf("zxcrypt: CommitBlock Read failed: %s\n", zx_status_get_string(rc));
601 return rc;
602 }
603 // Only write back blocks that don't match.
604 if (block_ == block) {
605 continue;
606 }
607 if ((rc = block_.Copy(block)) != ZX_OK || (rc = Write()) != ZX_OK) {
608 xprintf("zxcrypt: CommitBlock Write failed for offset %" PRIu64 ": %s\n", offset_,
609 zx_status_get_string(rc));
610 }
611 }
612 return ZX_OK;
613 }
614
SealBlock(const crypto::Secret & key,key_slot_t slot)615 zx_status_t Volume::SealBlock(const crypto::Secret& key, key_slot_t slot) {
616 zx_status_t rc;
617
618 // Encrypt the data key
619 zx_off_t nonce;
620 crypto::AEAD aead;
621 crypto::Bytes ptext, ctext;
622 zx_off_t off;
623 zx_off_t data_key_off = 0;
624 zx_off_t data_iv_off = data_key_.len();
625 if ((rc = GetSlotOffset(slot, &off)) != ZX_OK ||
626 (rc = ptext.Copy(data_key_.get(), data_key_.len(), data_key_off)) != ZX_OK ||
627 (rc = ptext.Copy(data_iv_.get(), data_iv_.len(), data_iv_off)) != ZX_OK ||
628 (rc = DeriveSlotKeys(key, slot)) != ZX_OK ||
629 (rc = aead.InitSeal(aead_, wrap_key_, wrap_iv_)) != ZX_OK ||
630 (rc = aead.Seal(ptext, header_, &nonce, &ctext)) != ZX_OK) {
631 return rc;
632 }
633 // Check that we'll be able to unseal.
634 if (memcmp(&nonce, wrap_iv_.get(), sizeof(nonce)) != 0) {
635 xprintf("unexpected nonce: %" PRIu64 "\n", nonce);
636 return ZX_ERR_INTERNAL;
637 }
638
639 memcpy(block_.get() + off, ctext.get(), ctext.len());
640 return ZX_OK;
641 }
642
UnsealBlock(const crypto::Secret & key,key_slot_t slot)643 zx_status_t Volume::UnsealBlock(const crypto::Secret& key, key_slot_t slot) {
644 zx_status_t rc;
645
646 // Check the type GUID matches |kTypeGuid|.
647 const uint8_t* in = block_.get();
648 if (memcmp(in, zxcrypt_magic, sizeof(zxcrypt_magic)) != 0) {
649 xprintf("not a zxcrypt device\n");
650 return ZX_ERR_NOT_SUPPORTED;
651 }
652 in += sizeof(zxcrypt_magic);
653
654 // Save the instance GUID
655 if ((rc = guid_.Copy(in, GUID_LEN)) != ZX_OK) {
656 return rc;
657 }
658 in += GUID_LEN;
659
660 // Read the version
661 uint32_t version;
662 memcpy(&version, in, sizeof(version));
663 in += sizeof(version);
664
665 // Read in the data
666 zx_off_t off;
667 size_t key_len, iv_len;
668 uint8_t* key_buf;
669 crypto::AEAD aead;
670 crypto::Bytes ctext, ptext;
671 if ((rc = Configure(Version(ntohl(version)))) != ZX_OK ||
672 (rc = GetSlotOffset(slot, &off)) != ZX_OK || (rc = DeriveSlotKeys(key, slot)) != ZX_OK ||
673 (rc = crypto::Cipher::GetKeyLen(cipher_, &key_len)) != ZX_OK ||
674 (rc = crypto::Cipher::GetIVLen(cipher_, &iv_len)) != ZX_OK ||
675 (rc = data_key_.Allocate(key_len, &key_buf)) != ZX_OK ||
676 (rc = ctext.Copy(block_.get() + off, slot_len_)) != ZX_OK ||
677 (rc = aead.InitOpen(aead_, wrap_key_, wrap_iv_)) != ZX_OK ||
678 (rc = header_.Copy(block_.get(), kHeaderLen)) != ZX_OK) {
679 return rc;
680 }
681
682 // Extract nonce from IV.
683 zx_off_t nonce;
684 memcpy(&nonce, wrap_iv_.get(), sizeof(nonce));
685 if ((rc = aead.Open(nonce, ctext, header_, &ptext)) != ZX_OK ||
686 (rc = data_iv_.Copy(ptext.get() + key_len, iv_len)) != ZX_OK) {
687 return rc;
688 }
689 memcpy(key_buf, ptext.get(), key_len);
690
691 return ZX_OK;
692 }
693
694 // Device methods
695
Ioctl(int op,const void * in,size_t in_len,void * out,size_t out_len)696 zx_status_t Volume::Ioctl(int op, const void* in, size_t in_len, void* out, size_t out_len) {
697 // Don't include debug messages here; some errors (e.g. ZX_ERR_NOT_SUPPORTED)
698 // are expected under certain conditions (e.g. calling FVM ioctls on a non-FVM
699 // device). Handle error reporting at the call sites instead.
700 if (dev_) {
701 size_t actual;
702 return device_ioctl(dev_, op, in, in_len, out, out_len, &actual);
703 }
704
705 ssize_t res;
706 if ((res = fdio_ioctl(fd_.get(), op, in, in_len, out, out_len)) < 0) {
707 return static_cast<zx_status_t>(res);
708 }
709
710 return ZX_OK;
711 }
712
Read()713 zx_status_t Volume::Read() {
714 if (dev_) {
715 return SyncIO(dev_, BLOCK_OP_READ, block_.get(), offset_, block_.len());
716 }
717
718 if (lseek(fd_.get(), offset_, SEEK_SET) < 0) {
719 xprintf("lseek(%d, %" PRIu64 ", SEEK_SET) failed: %s\n", fd_.get(), offset_,
720 strerror(errno));
721 return ZX_ERR_IO;
722 }
723 ssize_t res;
724 if ((res = read(fd_.get(), block_.get(), block_.len())) < 0) {
725 xprintf("read(%d, %p, %zu) failed: %s\n", fd_.get(), block_.get(), block_.len(),
726 strerror(errno));
727 return ZX_ERR_IO;
728 }
729 if (static_cast<size_t>(res) != block_.len()) {
730 xprintf("short read: have %zd, need %zu\n", res, block_.len());
731 return ZX_ERR_IO;
732 }
733
734 return ZX_OK;
735 }
736
Write()737 zx_status_t Volume::Write() {
738 if (dev_) {
739 return SyncIO(dev_, BLOCK_OP_WRITE, block_.get(), offset_, block_.len());
740 }
741
742 if (lseek(fd_.get(), offset_, SEEK_SET) < 0) {
743 xprintf("lseek(%d, %" PRIu64 ", SEEK_SET) failed: %s\n", fd_.get(), offset_,
744 strerror(errno));
745 return ZX_ERR_IO;
746 }
747 ssize_t res;
748 if ((res = write(fd_.get(), block_.get(), block_.len())) < 0) {
749 xprintf("write(%d, %p, %zu) failed: %s\n", fd_.get(), block_.get(), block_.len(),
750 strerror(errno));
751 return ZX_ERR_IO;
752 }
753 if (static_cast<size_t>(res) != block_.len()) {
754 xprintf("short write: have %zd, need %zu\n", res, block_.len());
755 return ZX_ERR_IO;
756 }
757 return ZX_OK;
758 }
759
760 } // namespace zxcrypt
761