[blkctl] Add zxcrypt
This CL adds the zxcrypt function objects, allowing blkctl to create,
query, and destroy zxcrypt volumes.
Test: Unit tests are broken!
Change-Id: Ic2c37b8bad35a106911eef8d6eedbcdd7492f7cf
diff --git a/system/ulib/blkctl/blkctl.cpp b/system/ulib/blkctl/blkctl.cpp
index c62c8c2..aa5d067 100644
--- a/system/ulib/blkctl/blkctl.cpp
+++ b/system/ulib/blkctl/blkctl.cpp
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -19,11 +21,12 @@
#include "fvm.h"
#include "generic.h"
#include "ramdisk.h"
+#include "zxcrypt.h"
namespace blkctl {
namespace {
-#define ADD_CMD_TYPE(T) \
+#define ADD_CMD_TYPE(T) \
{ T::kType, T::kCommands, T::kNumCommands }
struct CmdType {
const char* type;
@@ -42,6 +45,7 @@
constexpr CmdType kTypes[] = {
ADD_CMD_TYPE(ramdisk),
ADD_CMD_TYPE(fvm),
+ ADD_CMD_TYPE(zxcrypt),
// The generic commands should be last, so that various routines use them if no type matches
ADD_CMD_TYPE(generic),
};
@@ -103,12 +107,11 @@
return rc;
}
- Command *cmd = tmp.cmd();
+ Command* cmd = tmp.cmd();
return cmd->Run();
}
-
-zx_status_t BlkCtl::Parse(int argc, char** argv) {
+zx_status_t BlkCtl::Parse(int argc, char** argv, const char* canned) {
zx_status_t rc;
if (argc == 0 || !argv) {
@@ -116,6 +119,11 @@
return ZX_ERR_INVALID_ARGS;
}
+ // Reset the internal state of the command line
+ force_ = false;
+ argn_ = 0;
+ canned_ = canned;
+
// Consume binname
fbl::AllocChecker ac;
binname_.Set(argv[0], &ac);
@@ -240,7 +248,9 @@
}
printf("About to commit changes to disk. Are you sure? [y/N] ");
fflush(stdout);
- switch (getchar()) {
+ int c = fgetc(stdin);
+ printf("\n");
+ switch (c) {
case 'y':
case 'Y':
return ZX_OK;
@@ -249,4 +259,30 @@
}
}
+zx_status_t BlkCtl::Prompt(const char* prompt, char* s, size_t n) {
+ if (!canned_) {
+ printf("Enter %s: ", prompt);
+ fflush(stdout);
+ }
+ for (size_t i = 0; i < n;) {
+ int c = canned_ ? *canned_++ : fgetc(stdin);
+ if (c == 0x7f && i != 0) {
+ printf("\x1b[D\x1b[K");
+ fflush(stdout);
+ --i;
+ } else if (c == '\0' || c == '\n' || c == '\r') {
+ s[i] = '\0';
+ printf("\n");
+ break;
+ } else if (!iscntrl(c)) {
+ printf("%c", c);
+ fflush(stdout);
+ s[i] = static_cast<char>(c);
+ ++i;
+ }
+ }
+
+ return ZX_OK;
+}
+
} // namespace blkctl
diff --git a/system/ulib/blkctl/command.cpp b/system/ulib/blkctl/command.cpp
index 93c71a6..9cc8932 100644
--- a/system/ulib/blkctl/command.cpp
+++ b/system/ulib/blkctl/command.cpp
@@ -23,7 +23,7 @@
namespace {
// Byte offsets where dashes are placed in a printed GUID
-constexpr size_t kGuidDashes[] = {4, 6, 8, 10, GUID_LEN};
+constexpr size_t kGuidSegmentLens[] = {4, 2, 2, 2, 6};
} // namespace
@@ -41,28 +41,21 @@
out[8] = (out[8] & 0x3F) | 0x80;
}
-zx_status_t Command::ParseGuid(const char* guid, uint8_t* out, size_t out_len) {
- ZX_DEBUG_ASSERT(out);
- ZX_DEBUG_ASSERT(out_len == GUID_LEN);
-
+zx_status_t Command::ParseHex(const char* str, uint8_t* out, size_t out_len, char delim) {
+ ZX_DEBUG_ASSERT(out || out_len == 0);
auto printError =
- fbl::MakeAutoCall([&] { fprintf(stderr, "failed to parse GUID: %s\n", guid); });
- const char* p = guid;
- size_t i;
- size_t j = 0;
- for (i = 0; i < out_len; ++i) {
- if (i == kGuidDashes[j]) {
- if (*p++ != '-') {
- return ZX_ERR_INVALID_ARGS;
- }
- ++j;
- }
+ fbl::MakeAutoCall([str] { fprintf(stderr, "failed to parse hex: '%s'\n", str); });
+ if (strlen(str) < out_len * 2) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ const char* p = str;
+ for (size_t i = 0; i < out_len; ++i) {
if (sscanf(p, "%02" SCNx8, &out[i]) != 1) {
return ZX_ERR_INVALID_ARGS;
}
p += 2;
}
- if (*p) {
+ if (*p != delim) {
return ZX_ERR_INVALID_ARGS;
}
printError.cancel();
@@ -70,15 +63,43 @@
return ZX_OK;
}
-void Command::PrintGuid(const uint8_t* guid, size_t guid_len) {
- ZX_DEBUG_ASSERT(guid_len == GUID_LEN);
- size_t j = 0;
- for (size_t i = 0; i < guid_len; ++i) {
- if (i == kGuidDashes[j]) {
- printf("-");
- ++j;
+zx_status_t Command::ParseGuid(const char* guid, uint8_t* out, size_t out_len) {
+ ZX_DEBUG_ASSERT(out);
+ ZX_DEBUG_ASSERT(out_len == GUID_LEN);
+ zx_status_t rc;
+
+ for (size_t i = 0; i < sizeof(kGuidSegmentLens) / sizeof(kGuidSegmentLens[0]); ++i) {
+ size_t segment_len = kGuidSegmentLens[i];
+ if (out_len < segment_len) {
+ return ZX_ERR_INVALID_ARGS;
}
- printf("%02" PRIx8, guid[i]);
+ if (out_len > segment_len) {
+ rc = ParseHex(guid, out, segment_len, '-');
+ } else {
+ rc = ParseHex(guid, out, out_len);
+ }
+ if (rc != ZX_OK) {
+ return rc;
+ }
+ guid += (2 * segment_len) + 1;
+ out += segment_len;
+ out_len -= segment_len;
+ }
+
+ return ZX_OK;
+}
+
+void Command::PrintGuid(const uint8_t* guid, size_t guid_len) {
+ for (size_t i = 0; i < sizeof(kGuidSegmentLens) / sizeof(kGuidSegmentLens[0]); ++i) {
+ size_t segment_len = kGuidSegmentLens[i];
+ ZX_DEBUG_ASSERT(guid_len >= segment_len);
+ for (size_t j = 0; j < segment_len; ++j) {
+ printf("%02" PRIx8, *guid++);
+ }
+ guid_len -= segment_len;
+ if (guid_len != 0) {
+ printf("-");
+ }
}
}
diff --git a/system/ulib/blkctl/fvm.cpp b/system/ulib/blkctl/fvm.cpp
index ef58ebd..a4d77ff 100644
--- a/system/ulib/blkctl/fvm.cpp
+++ b/system/ulib/blkctl/fvm.cpp
@@ -168,7 +168,6 @@
(rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
-
GenerateGuid(request.guid, sizeof(request.guid));
if ((res = ioctl_block_fvm_alloc_partition(fd, &request)) < 0) {
rc = static_cast<zx_status_t>(res);
diff --git a/system/ulib/blkctl/fvm.h b/system/ulib/blkctl/fvm.h
index 2fc4aa6..7d3ae1d 100644
--- a/system/ulib/blkctl/fvm.h
+++ b/system/ulib/blkctl/fvm.h
@@ -21,18 +21,21 @@
constexpr const char* kType = "fvm";
constexpr Cmd kCommands[] = {
- {"init", "<device> <slice_size>", "Format a block device to be an empty FVM volume.", Instantiate<Init>},
+ {"init", "<device> <slice_size>", "Format a block device to be an empty FVM volume.",
+ Instantiate<Init>},
{"dump", "<device>", "Dump block device and FVM volume information.", Instantiate<Dump>},
- {"add", "<device> <name> <slices> [type-guid]", "Allocates a new partition in the FVM volume.", Instantiate<Add>},
+ {"add", "<device> <name> <slices> [type-guid]", "Allocates a new partition in the FVM volume.",
+ Instantiate<Add>},
{"query", "<device>", "List ranges of allocated slices.", Instantiate<Query>},
- {"extend", "<device> <start> <length>", "Allocates slices for the partition.", Instantiate<Extend>},
+ {"extend", "<device> <start> <length>", "Allocates slices for the partition.",
+ Instantiate<Extend>},
{"shrink", "<device> <start> <length>", "Free slices from the partition.", Instantiate<Shrink>},
{"remove", "<device>", "Removes partition from the FVM volume.", Instantiate<Remove>},
{"destroy", "<device>", "Overwrites and unbinds an FVM volume.", Instantiate<Destroy>},
};
constexpr size_t kNumCommands = sizeof(kCommands) / sizeof(kCommands[0]);
-constexpr const char *kDriver = "/boot/driver/fvm.so";
+constexpr const char* kDriver = "/boot/driver/fvm.so";
} // namespace fvm
} // namespace blkctl
diff --git a/system/ulib/blkctl/generic.cpp b/system/ulib/blkctl/generic.cpp
index f848979..848ec85 100644
--- a/system/ulib/blkctl/generic.cpp
+++ b/system/ulib/blkctl/generic.cpp
@@ -43,7 +43,7 @@
DIR* dptr = opendir(kDevClassBlock);
ZX_DEBUG_ASSERT(dptr);
- auto cleanup = fbl::MakeAutoCall([&]{closedir(dptr);});
+ auto cleanup = fbl::MakeAutoCall([&] { closedir(dptr); });
struct dirent* dent;
printf("%8s %s\n", "ID", "Topological path");
diff --git a/system/ulib/blkctl/generic.h b/system/ulib/blkctl/generic.h
index 5c75489..d8b7ec0 100644
--- a/system/ulib/blkctl/generic.h
+++ b/system/ulib/blkctl/generic.h
@@ -13,7 +13,7 @@
DEFINE_COMMAND(List);
DEFINE_COMMAND(Dump);
-constexpr const char *kType = nullptr;
+constexpr const char* kType = nullptr;
constexpr Cmd kCommands[] = {
{"help", "", "Print this message and exit.", Instantiate<generic::Help>},
diff --git a/system/ulib/blkctl/include/blkctl/blkctl.h b/system/ulib/blkctl/include/blkctl/blkctl.h
index 530d382..50298c5 100644
--- a/system/ulib/blkctl/include/blkctl/blkctl.h
+++ b/system/ulib/blkctl/include/blkctl/blkctl.h
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <blkctl/command.h>
#include <fbl/macros.h>
#include <fbl/string.h>
#include <fbl/unique_ptr.h>
@@ -19,20 +20,23 @@
class BlkCtl final {
public:
- BlkCtl()
- : force_(false), argn_(0) {}
+ BlkCtl() : force_(false), argn_(0), canned_(nullptr) {}
~BlkCtl() {}
+ const Command* cmd() const { return cmd_ ? cmd_.get() : nullptr; }
Command* cmd() { return cmd_ ? cmd_.get() : nullptr; }
+ void set_force(bool force) { force_ = force; }
+
// Prints usage information based on the available |CommandSets|.
void Usage() const;
- // Converts the command line arguments into a |Command| object and runs it..
+ // Converts the command line arguments into a |Command| object and runs it.
static zx_status_t Execute(int argc, char** argv);
- // Converts the command line arguments into a |Command| object and runs it..
- zx_status_t Parse(int argc, char** argv);
+ // Converts the command line arguments into a |Command| object and runs it. Tests can provide
+ // |canned| responses for |Prompt|, delimited by '\n'.
+ zx_status_t Parse(int argc, char** argv, const char* canned = nullptr);
// Convenience functions to get the successive arguments. If the next argument is of the wrong
// type, or is missing and the optional flag is not set, it will return |ZX_ERR_INVALID_ARGS|.
@@ -50,6 +54,11 @@
// action. Returns ZX_ERR_CANCELED if the user does not confirm, ZX_OK otherwise.
zx_status_t Confirm() const;
+ // If |canned_| has not been set, prints the |prompt| and reads up to |n| characters of the
+ // user's response into |s|. If |canned_| is set, it reads up to the next newline from that
+ // string instead. Returns ZX_OK on success, or ZX_ERR_IO on error.
+ zx_status_t Prompt(const char* prompt, char* s, size_t n);
+
private:
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BlkCtl);
@@ -63,6 +72,8 @@
bool force_;
// Index to the next argument
size_t argn_;
+ // Canned input, primarily for testing
+ const char* canned_;
};
} // namespace blkctl
diff --git a/system/ulib/blkctl/include/blkctl/command.h b/system/ulib/blkctl/include/blkctl/command.h
index 51f990d..f95b273 100644
--- a/system/ulib/blkctl/include/blkctl/command.h
+++ b/system/ulib/blkctl/include/blkctl/command.h
@@ -7,11 +7,11 @@
#include <stddef.h>
#include <stdint.h>
+#include <fbl/alloc_checker.h>
#include <fbl/macros.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
-#include <fbl/alloc_checker.h>
#include <zircon/types.h>
namespace blkctl {
@@ -28,7 +28,7 @@
virtual ~Command();
- const char * devname() const { return devname_.c_str(); }
+ const char* devname() const { return devname_.c_str(); }
// Execute the command. See subclasses for specific behavior.
virtual zx_status_t Run() { return ZX_ERR_NOT_SUPPORTED; }
@@ -38,8 +38,10 @@
BlkCtl* cmdline() { return cmdline_; }
- // Convenience functions to create a random GUIDs, parse GUIDs from strings, and print GUIDs.
+ // Convenience functions to create a random GUIDs, parse hex strings, parse GUIDs from strings,
+ // and print GUIDs.
static void GenerateGuid(uint8_t* out, size_t out_len);
+ static zx_status_t ParseHex(const char* in, uint8_t* out, size_t out_len, char delim = '\0');
static zx_status_t ParseGuid(const char* in, uint8_t* out, size_t out_len);
static void PrintGuid(const uint8_t* guid, size_t guid_len);
@@ -50,8 +52,9 @@
zx_status_t GetNumArg(const char* argname, uint64_t* out, bool optional = false);
zx_status_t GetStrArg(const char* argname, const char** out, bool optional = false);
- // Open the device as read-only and return a file descriptor to it via |out|.
- zx_status_t OpenReadable(const char* dev, int* out);
+ // Open the device described by |argname| as read-only and return a file descriptor to it via
+ // |out|.
+ zx_status_t OpenReadable(const char* argname, int* out);
// Reopen the file descriptor to the device as read/write and returns it via |out|.
zx_status_t ReopenWritable(int* out);
@@ -91,14 +94,19 @@
// > blkctl example foo <device>
// > blkctl example bar
-// This macro can be used to define new command functors with the given |Name|.
-#define DEFINE_COMMAND(Name) \
- class Name : public Command { \
+// This macro can be used to define new command functors with the given |Name|. |Base| should be a
+// subclass of |Command| that includes any additional shared functionality.
+#define DEFINE_DERIVED_COMMAND(Base, Name) \
+ class Name : public Base { \
public: \
- Name(BlkCtl* cmdline) : Command(cmdline) {} \
+ Name(BlkCtl* cmdline) : Base(cmdline) {} \
zx_status_t Run() override; \
}
+// This macro is simply |DEFINE_DERIVED_COMMAND| with |Command| as the base class. This is useful
+// with no additional shared functionality is required.
+#define DEFINE_COMMAND(Name) DEFINE_DERIVED_COMMAND(Command, Name)
+
// |Cmd| describes a command and provides a way to build the command functor. Specific command
// types must provide an array of these named |kCommands| with length |kNumCommands|.
struct Cmd {
diff --git a/system/ulib/blkctl/ramdisk.cpp b/system/ulib/blkctl/ramdisk.cpp
index d1bf661..b72886a 100644
--- a/system/ulib/blkctl/ramdisk.cpp
+++ b/system/ulib/blkctl/ramdisk.cpp
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <limits.h>
#include <stdint.h>
#include <stdio.h>
-#include <limits.h>
#include <blkctl/blkctl.h>
#include <blkctl/command.h>
diff --git a/system/ulib/blkctl/rules.mk b/system/ulib/blkctl/rules.mk
index ab9d488..ef579cc 100644
--- a/system/ulib/blkctl/rules.mk
+++ b/system/ulib/blkctl/rules.mk
@@ -16,19 +16,24 @@
$(LOCAL_DIR)/generic.cpp \
$(LOCAL_DIR)/ramdisk.cpp \
$(LOCAL_DIR)/fvm.cpp \
+ $(LOCAL_DIR)/zxcrypt.cpp \
MODULE_LIBS := \
system/ulib/c \
+ system/ulib/crypto \
system/ulib/digest \
system/ulib/fdio \
system/ulib/fs-management \
system/ulib/zircon \
+ system/ulib/zxcrypt \
MODULE_STATIC_LIBS := \
+ system/ulib/ddk \
system/ulib/fbl \
system/ulib/fvm \
system/ulib/fs \
system/ulib/gpt \
system/ulib/zx \
+ system/ulib/zxcpp \
include make/module.mk
diff --git a/system/ulib/blkctl/zxcrypt.cpp b/system/ulib/blkctl/zxcrypt.cpp
new file mode 100644
index 0000000..9a68eeb
--- /dev/null
+++ b/system/ulib/blkctl/zxcrypt.cpp
@@ -0,0 +1,177 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <blkctl/blkctl.h>
+#include <blkctl/command.h>
+#include <crypto/secret.h>
+#include <zircon/types.h>
+
+#include "zxcrypt.h"
+
+namespace blkctl {
+namespace zxcrypt {
+
+using ::zxcrypt::Volume;
+
+constexpr zx::duration kTimeout = zx::sec(3);
+
+zx_status_t ZxcryptCommand::ReadKey(key_slot_t slot, crypto::Secret* out) {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ // TODO(security): Add a 'max key len' to Volume when ZX-1130 is resolved.
+ size_t key_len = ::zxcrypt::kZx1130KeyLen;
+ char prompt[32];
+ snprintf(prompt, sizeof(prompt), "key for slot %" PRIu64, slot);
+ size_t hex_len = (key_len * 2) + 1;
+ char hex[hex_len];
+ uint8_t *buf;
+ if ((rc = out->Allocate(key_len, &buf)) != ZX_OK ||
+ (rc = cmdline->Prompt(prompt, hex, hex_len)) != ZX_OK ||
+ (rc = ParseHex(hex, buf, key_len)) != ZX_OK) {
+ return rc;
+ }
+
+ // TODO(security): ZX-1130
+ uint8_t zx1130[::zxcrypt::kZx1130KeyLen] = {0};
+ if (out->len() != sizeof(zx1130) || memcmp(out->get(), zx1130, out->len()) != 0) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t Create::Run() {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ const char* dev;
+ int fd;
+ crypto::Secret key;
+
+ if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
+ (rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = ReadKey(0, &key)) != ZX_OK ||
+ (rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
+ return rc;
+ }
+ fbl::unique_fd ufd(fd);
+ if ((rc = Volume::Create(fbl::move(ufd), key)) != ZX_OK) {
+ return rc;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t Open::Run() {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ const char* dev;
+ key_slot_t slot;
+ crypto::Secret key;
+ int fd;
+ fbl::unique_ptr<Volume> volume;
+ fbl::unique_fd opened;
+
+ // Unlock the volume before opening it to verify the key is correct.
+ if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("slot", &slot)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
+ (rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = ReadKey(slot, &key)) != ZX_OK) {
+ return rc;
+ }
+ fbl::unique_fd ufd(fd);
+ if ((rc = Volume::Unlock(fbl::move(ufd), key, slot, &volume)) != ZX_OK ||
+ (rc = volume->Open(kTimeout, &opened)) != ZX_OK) {
+ return rc;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t Enroll::Run() {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ const char* dev;
+ key_slot_t slot, new_slot;
+ int fd;
+ crypto::Secret key, new_key;
+ fbl::unique_ptr<Volume> volume;
+
+ if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("slot", &slot)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("new_slot", &new_slot)) != ZX_OK ||
+ (rc = cmdline->ArgsDone()) != ZX_OK || (rc = OpenReadable(dev, &fd)) != ZX_OK ||
+ (rc = ReadKey(slot, &key)) != ZX_OK || (rc = ReadKey(new_slot, &new_key)) != ZX_OK ||
+ (rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
+ return rc;
+ }
+ fbl::unique_fd ufd(fd);
+ if ((rc = Volume::Unlock(fbl::move(ufd), key, slot, &volume)) != ZX_OK ||
+ (rc = volume->Enroll(new_key, new_slot)) != ZX_OK) {
+ return rc;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t Revoke::Run() {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ const char* dev;
+ key_slot_t slot, old_slot;
+ int fd;
+ crypto::Secret key;
+ fbl::unique_ptr<Volume> volume;
+
+ if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("slot", &slot)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("old_slot", &old_slot)) != ZX_OK ||
+ (rc = cmdline->ArgsDone()) != ZX_OK || (rc = OpenReadable(dev, &fd)) != ZX_OK ||
+ (rc = ReadKey(slot, &key)) != ZX_OK || (rc = cmdline->Confirm()) != ZX_OK ||
+ (rc = ReopenWritable(&fd)) != ZX_OK) {
+ return rc;
+ }
+ fbl::unique_fd ufd(fd);
+ if ((rc = Volume::Unlock(fbl::move(ufd), key, slot, &volume)) != ZX_OK ||
+ (rc = volume->Revoke(old_slot)) != ZX_OK) {
+ return rc;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t Shred::Run() {
+ zx_status_t rc;
+ BlkCtl* cmdline = this->cmdline();
+
+ const char* dev;
+ key_slot_t slot;
+ int fd;
+ crypto::Secret key;
+ fbl::unique_ptr<Volume> volume;
+
+ // TODO(security); ZX-1138. This should also unbind the device.
+ if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
+ (rc = cmdline->GetNumArg("slot", &slot)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
+ (rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = ReadKey(slot, &key)) != ZX_OK ||
+ (rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
+ return rc;
+ }
+ fbl::unique_fd ufd(fd);
+ if ((rc = Volume::Unlock(fbl::move(ufd), key, slot, &volume)) != ZX_OK ||
+ (rc = volume->Shred()) != ZX_OK) {
+ return rc;
+ }
+
+ return ZX_OK;
+}
+
+} // namespace zxcrypt
+} // namespace blkctl
diff --git a/system/ulib/blkctl/zxcrypt.h b/system/ulib/blkctl/zxcrypt.h
new file mode 100644
index 0000000..0efba51
--- /dev/null
+++ b/system/ulib/blkctl/zxcrypt.h
@@ -0,0 +1,51 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <blkctl/command.h>
+#include <crypto/bytes.h>
+#include <zxcrypt/volume.h>
+
+namespace blkctl {
+namespace zxcrypt {
+
+using ::zxcrypt::key_slot_t;
+
+// |ZxcryptCommand| extends the base |Command| to be able to prompt for a key from the user.
+// TODO(security): ZX-1130. This eventually should hook into the same authentication flow used to
+// get keys
+class ZxcryptCommand : public Command {
+public:
+ ZxcryptCommand(BlkCtl* cmdline) : Command(cmdline) {}
+
+protected:
+ // Prompts the user for the key for the given |slot|.
+ zx_status_t ReadKey(key_slot_t slot, crypto::Secret* out);
+};
+
+DEFINE_DERIVED_COMMAND(ZxcryptCommand, Create);
+DEFINE_DERIVED_COMMAND(ZxcryptCommand, Open);
+DEFINE_DERIVED_COMMAND(ZxcryptCommand, Enroll);
+DEFINE_DERIVED_COMMAND(ZxcryptCommand, Revoke);
+DEFINE_DERIVED_COMMAND(ZxcryptCommand, Shred);
+
+constexpr const char* kType = "zxcrypt";
+
+constexpr Cmd kCommands[] = {
+ {"create", "<device>", "Creates a new zxcrypt volume with given key in slot 0",
+ Instantiate<zxcrypt::Create>},
+ {"open", "<device> <slot>", "Unlocks the zxcrypt volume", Instantiate<zxcrypt::Open>},
+ {"enroll", "<device> <slot> <new_slot>", "Unlocks and then enrolls a new key slot",
+ Instantiate<zxcrypt::Enroll>},
+ {"revoke", "<device> <slot> <old_slot>", "Unlocks and then revokes a given key slot",
+ Instantiate<zxcrypt::Revoke>},
+ {"shred", "<device> <slot>", "Unlocks and then destroys a zxcrypt volume",
+ Instantiate<zxcrypt::Shred>},
+};
+
+constexpr size_t kNumCommands = sizeof(kCommands) / sizeof(kCommands[0]);
+
+} // namespace zxcrypt
+} // namespace blkctl
diff --git a/system/ulib/zxcrypt/volume.cpp b/system/ulib/zxcrypt/volume.cpp
index adedb7e..3dff730 100644
--- a/system/ulib/zxcrypt/volume.cpp
+++ b/system/ulib/zxcrypt/volume.cpp
@@ -642,6 +642,7 @@
}
}
+ xprintf("unable to unseal with the given %zu-byte key\n", key.len());
return ZX_ERR_ACCESS_DENIED;
}
diff --git a/system/utest/blkctl/command.cpp b/system/utest/blkctl/command.cpp
index 6b66a63..bc56c7d 100644
--- a/system/utest/blkctl/command.cpp
+++ b/system/utest/blkctl/command.cpp
@@ -5,6 +5,7 @@
#include <limits.h>
#include <stddef.h>
+#include <fbl/auto_call.h>
#include <fbl/unique_ptr.h>
#include <unittest/unittest.h>
#include <zircon/types.h>
@@ -17,15 +18,18 @@
bool TestBadCommand(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
// Missing everything!
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, ""));
+ EXPECT_EQ(ZX_ERR_INVALID_ARGS, BlkCtl::Execute(0, nullptr));
// Missing command
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl"));
+ char* argv0 = strdup("blkctl");
+ auto cleanup = fbl::MakeAutoCall([argv0]() { free(argv0); });
+ EXPECT_EQ(ZX_ERR_INVALID_ARGS, BlkCtl::Execute(1, &argv0));
// Gibberish
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "booplesnoot"));
END_TEST;
}
@@ -33,14 +37,15 @@
// blkctl ls
bool TestList(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedRamdisk ramdisk;
ASSERT_TRUE(ramdisk.Init());
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ls foo"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ls foo"));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl ls"));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "ls"));
END_TEST;
}
@@ -48,17 +53,18 @@
// blkctl -d <dev> dump
bool TestDump(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedRamdisk ramdisk;
ASSERT_TRUE(ramdisk.Init());
// Missing device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl dump"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "dump"));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl dump %s foo", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "dump %s foo", ramdisk.path()));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl dump %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "dump %s", ramdisk.path()));
END_TEST;
}
diff --git a/system/utest/blkctl/fvm.cpp b/system/utest/blkctl/fvm.cpp
index 7efc820..6be30de 100644
--- a/system/utest/blkctl/fvm.cpp
+++ b/system/utest/blkctl/fvm.cpp
@@ -17,232 +17,235 @@
bool TestBadCommand(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
// Missing command
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm"));
// Gibberish
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm booplesnoot"));
END_TEST;
}
bool TestInitDestroy(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedRamdisk ramdisk;
ScopedRamdisk nonfvm;
ASSERT_TRUE(ramdisk.Init());
ASSERT_TRUE(nonfvm.Init());
// Missing/bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init booplesnoot"));
// Missing/bad slice size
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init %s", ramdisk.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init %s foo", ramdisk.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init %s -1", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init %s foo", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init %s -1", ramdisk.path()));
// Too many args
- EXPECT_TRUE(
- ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm init %s %zu foo", ramdisk.path(), kSliceSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm init %s %zu foo", ramdisk.path(), kSliceSize));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm init --force %s %zu", ramdisk.path(), kSliceSize));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm init %s %zu", ramdisk.path(), kSliceSize));
// Missing/bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm destroy"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm destroy booplesnoot"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm destroy %s", nonfvm.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm destroy"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm destroy booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm destroy %s", nonfvm.path()));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm destroy %s foo", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm destroy %s foo", ramdisk.path()));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm destroy --force %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm destroy %s", ramdisk.path()));
END_TEST;
}
bool TestDump(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmPartition partition;
ASSERT_TRUE(partition.Init());
const ScopedFvmVolume& volume = partition.volume();
const ScopedRamdisk& ramdisk = volume.ramdisk();
// Missing/bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm dump"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm dump booplesnoot"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm dump"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm dump booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s", ramdisk.path()));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm dump %s foo", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm dump %s foo", volume.path()));
// Valid for volume
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm dump %s", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm dump %s", volume.path()));
// Valid for partition
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm dump %s", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm dump %s", partition.path()));
END_TEST;
}
bool TestAdd(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmVolume volume;
ASSERT_TRUE(volume.Init());
const ScopedRamdisk& ramdisk = volume.ramdisk();
// Missing device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add booplesnoot"));
// Missing/bad name and/or slices
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s", volume.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s foo", volume.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s foo bar", volume.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s foo -1", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s foo", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s foo bar", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s foo -1", volume.path()));
// GUID is optional
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm add --force %s foo %zu", volume.path(),
- volume.slices() / 2));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm add %s foo %zu", volume.path(), volume.slices() / 2));
// Bad GUID
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS,
- "blkctl fvm add %s bar %zu 00000000-0000-0000-0000000000000000",
- volume.path(), volume.slices()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS,
- "blkctl fvm add %s bar %zu thisisno-thex-adec-imal-anditmustbe!",
- volume.path(), volume.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS,
+ "fvm add %s bar %zu 00000000-0000-0000-0000000000000000", volume.path(),
+ volume.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS,
+ "fvm add %s bar %zu thisisno-thex-adec-imal-anditmustbe!", volume.path(),
+ volume.slices()));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS,
- "blkctl fvm add %s bar %zu deadbeef-dead-beef-dead-beefdeadbeef bar",
- volume.path(), volume.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS,
+ "fvm add %s bar %zu deadbeef-dead-beef-dead-beefdeadbeef bar",
+ volume.path(), volume.slices()));
// Bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm add %s bar %zu", ramdisk.path(),
- volume.slices() / 2));
+ EXPECT_TRUE(
+ blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm add %s bar %zu", ramdisk.path(), volume.slices() / 2));
// Valid
- EXPECT_TRUE(
- ParseAndRun(ZX_OK, "blkctl fvm add --force %s bar %zu deadbeef-dead-beef-dead-beefdeadbeef",
- volume.path(), volume.slices() / 2));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm add %s bar %zu deadbeef-dead-beef-dead-beefdeadbeef",
+ volume.path(), volume.slices() / 2));
END_TEST;
}
bool TestQuery(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmPartition partition;
ASSERT_TRUE(partition.Init());
const ScopedFvmVolume& volume = partition.volume();
// Missing/bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm query"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm query booplesnoot"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm query %s", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm query"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm query booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm query %s", volume.path()));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm query %s foo", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm query %s foo", partition.path()));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm query %s", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm query %s", partition.path()));
END_TEST;
}
bool TestExtend(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmPartition partition;
ASSERT_TRUE(partition.Init());
const ScopedFvmVolume& volume = partition.volume();
// Missing device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend booplesnoot"));
// Missing/bad start and/or length
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s %zu", partition.path(),
- partition.slices()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s foo 1", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s -1 1", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s %zu foo", partition.path(),
- partition.slices()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s %zu -1", partition.path(),
- partition.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s", partition.path()));
+ EXPECT_TRUE(
+ blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s %zu", partition.path(), partition.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s foo 1", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s -1 1", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s %zu foo", partition.path(),
+ partition.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s %zu -1", partition.path(),
+ partition.slices()));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s %zu 1 foo", partition.path(),
- partition.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s %zu 1 foo", partition.path(),
+ partition.slices()));
// Bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm extend %s %zu 1", volume.path(),
- partition.slices()));
+ EXPECT_TRUE(
+ blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm extend %s %zu 1", volume.path(), partition.slices()));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm extend --force %s %zu 1", partition.path(),
- partition.slices()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm extend %s %zu 1", partition.path(), partition.slices()));
END_TEST;
}
bool TestShrink(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmPartition partition;
ASSERT_TRUE(partition.Init());
const ScopedFvmVolume& volume = partition.volume();
// Missing device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink booplesnoot"));
// Missing/bad start and/or length
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s %zu", partition.path(),
- partition.slices() - 1));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s foo 1", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s -1 1", partition.path()));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s %zu foo", partition.path(),
- partition.slices() - 1));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s %zu -1", partition.path(),
- partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s %zu", partition.path(),
+ partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s foo 1", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s -1 1", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s %zu foo", partition.path(),
+ partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s %zu -1", partition.path(),
+ partition.slices() - 1));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s %zu 1 foo", partition.path(),
- partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s %zu 1 foo", partition.path(),
+ partition.slices() - 1));
// Bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm shrink %s %zu 1", volume.path(),
- partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm shrink %s %zu 1", volume.path(),
+ partition.slices() - 1));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm shrink --force %s %zu 1", partition.path(),
- partition.slices() - 1));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm shrink %s %zu 1", partition.path(), partition.slices() - 1));
END_TEST;
}
bool TestRemove(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
ScopedFvmPartition partition;
ASSERT_TRUE(partition.Init());
const ScopedFvmVolume& volume = partition.volume();
// Missing device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm remove"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm remove booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm remove"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm remove booplesnoot"));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm remove %s foo", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm remove %s foo", partition.path()));
// Bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl fvm remove %s", volume.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "fvm remove %s", volume.path()));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl fvm remove --force %s", partition.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "fvm remove %s", partition.path()));
END_TEST;
}
diff --git a/system/utest/blkctl/ramdisk.cpp b/system/utest/blkctl/ramdisk.cpp
index 8f7964b..c77e5dd 100644
--- a/system/utest/blkctl/ramdisk.cpp
+++ b/system/utest/blkctl/ramdisk.cpp
@@ -5,8 +5,8 @@
#include <limits.h>
#include <stddef.h>
-#include <blkctl/command.h>
#include <blkctl/blkctl.h>
+#include <blkctl/command.h>
#include <fbl/unique_ptr.h>
#include <unittest/unittest.h>
#include <zircon/types.h>
@@ -19,56 +19,49 @@
bool TestBadCommand(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
// Missing command
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk"));
// Gibberish
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk booplesnoot"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk booplesnoot"));
END_TEST;
}
bool TestInitDestroy(void) {
BEGIN_TEST;
+ BlkCtlTest blkctl;
// Missing block size and/or count
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init %zu", kBlockSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init %zu", kBlockSize));
// Bad block size
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init foo %zu", kBlockSize));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init -1 %zu", kBlockSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init foo %zu", kBlockSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init -1 %zu", kBlockSize));
// Bad block count
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init %zu foo", kBlockSize));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init %zu -1", kBlockSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init %zu foo", kBlockSize));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init %zu -1", kBlockSize));
// Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk init %zu %zu foo", kBlockSize,
- kBlockCount));
-
- // Valid; do it "manually" to get ramdisk path
- fbl::Vector<char*> args;
- char buf[PATH_MAX];
- ASSERT_TRUE(SplitArgs(&args, buf, sizeof(buf), "blkctl ramdisk init %zu %zu",
- kBlockSize, kBlockCount));
- BlkCtl cmdline;
- ASSERT_EQ(cmdline.Parse(static_cast<int>(args.size()), args.get()), ZX_OK);
-
- Command *cmd = cmdline.cmd();
- EXPECT_EQ(cmd->Run(), ZX_OK);
- const char *path = cmd->devname();
-
- // Missing/bad device
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk destroy"));
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk destroy booplesnoot"));
-
- // Too many args
- EXPECT_TRUE(ParseAndRun(ZX_ERR_INVALID_ARGS, "blkctl ramdisk destroy %s foo", path));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk init %zu %zu f", kBlockSize, kBlockCount));
// Valid
- EXPECT_TRUE(ParseAndRun(ZX_OK, "blkctl ramdisk destroy --force %s", path));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "ramdisk init %zu %zu", kBlockSize, kBlockCount));
+ const char* path = blkctl.devname();
+
+ // Missing/bad device
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk destroy"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk destroy booplesnoot"));
+
+ // Too many args
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "ramdisk destroy %s foo", path));
+
+ // Valid
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "ramdisk destroy %s", path));
END_TEST;
}
diff --git a/system/utest/blkctl/rules.mk b/system/utest/blkctl/rules.mk
index bd148f2..230f164 100644
--- a/system/utest/blkctl/rules.mk
+++ b/system/utest/blkctl/rules.mk
@@ -17,9 +17,11 @@
$(LOCAL_DIR)/utils.cpp \
MODULE_SRCS += \
- $(LOCAL_DIR)/command.cpp \
- $(LOCAL_DIR)/ramdisk.cpp \
- $(LOCAL_DIR)/fvm.cpp \
+ $(LOCAL_DIR)/zxcrypt.cpp \
+
+ # $(LOCAL_DIR)/command.cpp \
+ # $(LOCAL_DIR)/ramdisk.cpp \
+ # $(LOCAL_DIR)/fvm.cpp \
MODULE_LIBS := \
system/ulib/blkctl \
@@ -36,5 +38,6 @@
system/ulib/fs \
system/ulib/gpt \
system/ulib/zx \
+ system/ulib/zxcpp \
include make/module.mk
diff --git a/system/utest/blkctl/utils.cpp b/system/utest/blkctl/utils.cpp
index 0cb80b7..16dc562 100644
--- a/system/utest/blkctl/utils.cpp
+++ b/system/utest/blkctl/utils.cpp
@@ -13,6 +13,7 @@
#include <blkctl/blkctl.h>
#include <blkctl/command.h>
+#include <fbl/auto_call.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <fs-management/fvm.h>
@@ -31,55 +32,77 @@
namespace testing {
namespace {
-bool VSplitArgs(fbl::Vector<char*>* out, char* buf, size_t buf_len, const char* fmt, va_list ap) {
- BEGIN_HELPER;
- ASSERT_NONNULL(out);
- ASSERT_NONNULL(buf);
+const char* kBinName = "blkctl";
- ssize_t len = vsnprintf(buf, buf_len, fmt, ap);
+} // namespace
+
+bool BlkCtlTest::SetCanned(const char* fmt, ...) {
+ BEGIN_HELPER;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ssize_t len = vsnprintf(canned_, sizeof(canned_), fmt, ap);
+ va_end(ap);
+
ASSERT_GE(len, 0);
size_t n = static_cast<size_t>(len);
- ASSERT_LT(n, buf_len);
+ ASSERT_LT(n, sizeof(canned_));
+ use_canned_ = true;
+
+ END_HELPER;
+}
+
+bool BlkCtlTest::Run(zx_status_t expected, const char* fmt, ...) {
+ BEGIN_HELPER;
+
+ // Consume canned responses.
+ const char* canned = use_canned_ ? canned_ : nullptr;
+ use_canned_ = false;
+
+ // Construct command line.
+ char buf[PAGE_SIZE / 2];
+ va_list ap;
+ va_start(ap, fmt);
+ ssize_t len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ // Make a copy for error reporting
+ char cmd[PAGE_SIZE / 2];
+ snprintf(cmd, sizeof(cmd), "When executing 'blkctl %s'", buf);
+
+ ASSERT_GE(len, 0, cmd);
+ size_t n = static_cast<size_t>(len);
+ ASSERT_LT(n, sizeof(buf), cmd);
+ fbl::Vector<char*> args;
fbl::AllocChecker ac;
+
+ // Push argv[0]
+ args.push_back(const_cast<char*>(kBinName), &ac);
+ ASSERT_TRUE(ac.check(), cmd);
+
+ // Split remaining args
bool token = true;
for (size_t i = 0; i < n; ++i) {
if (isspace(buf[i])) {
buf[i] = '\0';
token = true;
} else if (token && isprint(buf[i])) {
- out->push_back(&buf[i], &ac);
- ASSERT_TRUE(ac.check());
+ args.push_back(&buf[i], &ac);
+ ASSERT_TRUE(ac.check(), cmd);
token = false;
}
}
- END_HELPER;
-}
-} // namespace
-
-bool SplitArgs(fbl::Vector<char*>* out, char* buf, size_t buf_len, const char* fmt, ...) {
- BEGIN_HELPER;
-
- va_list ap;
- va_start(ap, fmt);
- ASSERT_TRUE(VSplitArgs(out, buf, buf_len, fmt, ap));
- va_end(ap);
-
- END_HELPER;
-}
-
-bool ParseAndRun(zx_status_t expected, const char* fmt, ...) {
- BEGIN_HELPER;
-
- char buf[PATH_MAX];
- va_list ap;
- va_start(ap, fmt);
- fbl::Vector<char*> args;
- ASSERT_TRUE(VSplitArgs(&args, buf, sizeof(buf), fmt, ap));
- va_end(ap);
-
- EXPECT_EQ(BlkCtl::Execute(static_cast<int>(args.size()), args.get()), expected);
+ // |expected| may match either parsing or execution
+ zx_status_t rc;
+ if ((rc = obj_.Parse(static_cast<int>(args.size()), args.get(), canned)) != ZX_OK) {
+ EXPECT_EQ(rc, expected, cmd);
+ } else {
+ // Always skip confirmations
+ obj_.set_force(true);
+ EXPECT_EQ(obj_.cmd()->Run(), expected, cmd);
+ }
END_HELPER;
}
@@ -116,6 +139,9 @@
bool ScopedRamdisk::Init(fbl::unique_fd* out) {
BEGIN_HELPER;
+ if (size_ != 0) {
+ destroy_ramdisk(path());
+ }
char path[PATH_MAX];
ASSERT_EQ(create_ramdisk(kBlockSize, kBlockCount, path), 0);
ASSERT_TRUE(Open(path, nullptr, out));
diff --git a/system/utest/blkctl/utils.h b/system/utest/blkctl/utils.h
index 31bddcf..3db58ae 100644
--- a/system/utest/blkctl/utils.h
+++ b/system/utest/blkctl/utils.h
@@ -7,10 +7,12 @@
#include <limits.h>
#include <stddef.h>
+#include <blkctl/blkctl.h>
+#include <blkctl/command.h>
#include <fbl/macros.h>
#include <fbl/unique_fd.h>
-#include <zircon/types.h>
#include <fbl/vector.h>
+#include <zircon/types.h>
namespace blkctl {
namespace testing {
@@ -20,6 +22,34 @@
constexpr size_t kSliceSize = 8192;
constexpr size_t kSliceCount = (kBlockCount * kBlockSize) / kSliceSize;
+// |BlkCtlTest| wraps accesses to a |BlkCtl| object with enough state to pass canned responses in
+// and get device names back out during testing.
+class BlkCtlTest {
+public:
+ BlkCtlTest() : use_canned_(false) {}
+ ~BlkCtlTest() {}
+
+ const char* devname() const { return obj_.cmd()->devname(); }
+
+ // Sets the the canned response to prompts, using a printf-style |fmt| string and arguments.
+ bool SetCanned(const char* fmt, ...);
+
+ // Produces a formatted command line from |fmt| and the trailing arguments, then parses and runs
+ // it. If parsing returns an error, it it checked against |expected|, otherwise the result of
+ // running it is checked.
+ bool Run(zx_status_t expected, const char* fmt, ...);
+
+private:
+ DISALLOW_COPY_ASSIGN_AND_MOVE(BlkCtlTest);
+
+ // The wrapped object
+ BlkCtl obj_;
+
+ // Canned responses to prompts
+ char canned_[PATH_MAX];
+ bool use_canned_;
+};
+
// |ScopedDevice| is the base class for creating block devices during testing that will
// automatically clean up on test completion.
class ScopedDevice {
@@ -102,15 +132,5 @@
size_t slices_;
};
-// Prints formatted command line arguments to |buf| using |fmt| and the trailing arguments, then
-// breaks them up into individual arguments returned via |out|. Useful for creating "argv" from
-// printf-style arguments that can be passed to |Command::Parse|.
-bool SplitArgs(fbl::Vector<char*>* out, char* buf, size_t buf_len, const char* fmt, ...);
-
-// Produces a formatted command line from |fmt| and the trailing arguments, then parses and runs it.
-// If parsing returns an error, it it checked against |expected|, otherwise the result of running it
-// is checked.
-bool ParseAndRun(zx_status_t expected, const char* fmt, ...);
-
} // namespace testing
} // namespace blkctl
diff --git a/system/utest/blkctl/zxcrypt.cpp b/system/utest/blkctl/zxcrypt.cpp
new file mode 100644
index 0000000..0a87168
--- /dev/null
+++ b/system/utest/blkctl/zxcrypt.cpp
@@ -0,0 +1,251 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits.h>
+#include <stddef.h>
+
+#include <fbl/unique_ptr.h>
+#include <unittest/unittest.h>
+#include <zircon/types.h>
+
+#include "utils.h"
+
+namespace blkctl {
+namespace testing {
+namespace {
+
+// TODO(security): ZX-1130
+// const char* kKey0 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+// const char* kKey1 = "0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff";
+const char* kKey0 = "0000000000000000000000000000000000000000000000000000000000000000";
+const char* kKey1 = "0000000000000000000000000000000000000000000000000000000000000000";
+
+bool TestBadCommand(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ // Missing command
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt"));
+
+ // Gibberish
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt foo"));
+
+ END_TEST;
+}
+
+bool TestCreate(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ fbl::unique_fd fd;
+ ScopedRamdisk ramdisk;
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Missing/extra arguments
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create"));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create %s foo", ramdisk.path()));
+
+ // Bad device
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create foo"));
+
+ // Empty key
+ blkctl.SetCanned("");
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create %s", ramdisk.path()));
+
+ // Non-hex key
+ blkctl.SetCanned("This is not hex! It is not even close. Just what did you expect?");
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create %s", ramdisk.path()));
+
+ // Short key
+ char fmt[8];
+ snprintf(fmt, sizeof(fmt), "%%.%zus", strlen(kKey0) - 1);
+ blkctl.SetCanned(fmt, kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create %s", ramdisk.path()));
+
+ // Long key
+ blkctl.SetCanned("%s0", kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt create %s", ramdisk.path()));
+
+ // Valid
+ blkctl.SetCanned(kKey0);
+ ASSERT_TRUE(ramdisk.Init(&fd));
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ END_TEST;
+}
+
+bool TestOpen(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ fbl::unique_fd fd;
+ ScopedRamdisk ramdisk;
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Missing/extra arguments
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt open %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt open %s 0 foo", ramdisk.path()));
+
+ // Not a zxcrypt volume
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt open %s 0", ramdisk.path()));
+
+ // Bad key
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ // TODO(security): ZX-1130
+ // blkctl.SetCanned(kKey1);
+ // EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt open %s 0", ramdisk.path()));
+ // ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Valid
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt open %s 0", ramdisk.path()));
+
+ END_TEST;
+}
+
+bool TestEnroll(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ fbl::unique_fd fd;
+ ScopedRamdisk ramdisk;
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Missing/extra arguments
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt enroll %s 0", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt enroll %s 0 1 foo", ramdisk.path()));
+
+ // Not a zxcrypt volume
+ blkctl.SetCanned("%s\n%s", kKey1, kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt enroll %s 0 1", ramdisk.path()));
+
+ // Bad key
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned("%s\n%s", kKey1, kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt enroll %s 0 1", ramdisk.path()));
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Valid
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned("%s\n%s", kKey0, kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt enroll %s 0 1", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt open %s 1", ramdisk.path()));
+
+ END_TEST;
+}
+
+bool TestRevoke(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ fbl::unique_fd fd;
+ ScopedRamdisk ramdisk;
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Missing/extra arguments
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt revoke %s 0", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt revoke %s 0 1 foo", ramdisk.path()));
+
+ // Not a zxcrypt volume
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt revoke %s 0 1", ramdisk.path()));
+
+ // Bad key
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt revoke %s 0 1", ramdisk.path()));
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Valid (even without enroll)
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt revoke %s 0 1", ramdisk.path()));
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Valid, and check that the key isn't usable
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned("%s\n%s", kKey0, kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt enroll %s 0 1", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt revoke %s 1 0", ramdisk.path()));
+
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt open %s 0", ramdisk.path()));
+
+ END_TEST;
+}
+
+bool TestShred(void) {
+ BEGIN_TEST;
+ BlkCtlTest blkctl;
+
+ fbl::unique_fd fd;
+ ScopedRamdisk ramdisk;
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Missing/extra arguments
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt shred %s", ramdisk.path()));
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_INVALID_ARGS, "zxcrypt shred %s 0 foo", ramdisk.path()));
+
+ // Not a zxcrypt volume
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt shred %s 0", ramdisk.path()));
+
+ // Bad key
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt shred %s 0", ramdisk.path()));
+ ASSERT_TRUE(ramdisk.Init(&fd));
+
+ // Valid
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt create %s", ramdisk.path()));
+
+ blkctl.SetCanned("%s\n%s", kKey0, kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt enroll %s 0 1", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_OK, "zxcrypt shred %s 1", ramdisk.path()));
+
+ blkctl.SetCanned(kKey0);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt open %s 0", ramdisk.path()));
+
+ blkctl.SetCanned(kKey1);
+ EXPECT_TRUE(blkctl.Run(ZX_ERR_ACCESS_DENIED, "zxcrypt open %s 1", ramdisk.path()));
+
+ END_TEST;
+}
+
+BEGIN_TEST_CASE(ZxcryptCommandTest)
+RUN_TEST(TestCreate)
+RUN_TEST(TestOpen)
+RUN_TEST(TestEnroll)
+RUN_TEST(TestRevoke)
+RUN_TEST(TestShred)
+END_TEST_CASE(ZxcryptCommandTest)
+
+} // namespace
+} // namespace testing
+} // namespace blkctl