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