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