From 7787db33b1ac4850f4fc1266ee823301a7a5b19e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 01/10] Update kernel.org links Link to docs.kernel.org/ instead of www.kernel.org/doc/html/latest/. It makes the links shorter, and they point to the latest version. --- NEWS.md | 2 +- README.md | 10 +++++----- fscryptctl.1.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0516f94..eed1ce7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -72,4 +72,4 @@ Note: this release of `fscryptctl` only includes support for v1 policies. For v2 policies, users will need to use v1.0.0 or later. For more information about v1 and v2 encryption policies, see [the Linux kernel -documentation](https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html). +documentation](https://docs.kernel.org/filesystems/fscrypt.html). diff --git a/README.md b/README.md index 5197354..335a61a 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ `fscryptctl` is a low-level tool written in C that handles raw keys and manages policies for [Linux filesystem -encryption](https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html), -specifically the "fscrypt" kernel interface which is supported by the ext4, -f2fs, UBIFS, and CephFS filesystems. +encryption](https://docs.kernel.org/filesystems/fscrypt.html), specifically the +"fscrypt" kernel interface which is supported by the ext4, f2fs, UBIFS, and +CephFS filesystems. `fscryptctl` is mainly intended for embedded systems which can't use the full-featured [`fscrypt` tool](https://github.com/google/fscrypt), or for @@ -18,8 +18,8 @@ which supports these features and generally is much easier to use. As `fscryptctl` is intended for advanced users, you should read the [kernel documentation for filesystem -encryption](https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html) -before using `fscryptctl`. +encryption](https://docs.kernel.org/filesystems/fscrypt.html) before using +`fscryptctl`. For the release notes, see the [NEWS file](NEWS.md). diff --git a/fscryptctl.1.md b/fscryptctl.1.md index eb96036..913e5f6 100644 --- a/fscryptctl.1.md +++ b/fscryptctl.1.md @@ -149,7 +149,7 @@ Options accepted by **fscryptctl set_policy**: file](https://github.com/google/fscryptctl/blob/master/README.md) * [Linux kernel documentation for filesystem - encryption](https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html) + encryption](https://docs.kernel.org/filesystems/fscrypt.html) * [**fscrypt** tool, recommended for most users over fscryptctl](https://github.com/google/fscrypt) From 2f081f2853ed9ecd99985adf75123cc19e3e596d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 02/10] Fix a typo and a format inconsistency in the Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 994de41..28a1ed4 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ PREFIX ?= /usr/local # Directory where the binary gets installed BINDIR ?= $(PREFIX)/bin -# Direectory where the man page gets installed +# Directory where the man page gets installed MANDIR ?= $(PREFIX)/share/man # C compiler flags @@ -108,7 +108,7 @@ test: fscryptctl python3 -m pytest test.py -s -q # Depend on test-teardown so that anything already present is cleaned up first. -test-setup:test-teardown +test-setup: test-teardown dd if=/dev/zero of="$(TEST_IMAGE)" bs=1M count=32 mkfs.ext4 -b 4096 -O encrypt -F "$(TEST_IMAGE)" mkdir -p "$(TEST_DIR)" From b1b9917512a068f67e0c4cd65b3d4c1761f67d43 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 03/10] Fix the usage text for --version It should say fscryptctl, not fscrypt. --- fscryptctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fscryptctl.c b/fscryptctl.c index 80f9ecc..bae1872 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -116,7 +116,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " -h, --help\n" " print this help screen\n" " -v, --version\n" - " print the version of fscrypt\n" + " print the version of fscryptctl\n" " remove_key\n" " --all-users\n" " force-remove all users' claims to the key (requires root)\n" From 69258d7bb00a1e315bd59344f0a7006475f4aab2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 04/10] Add xzalloc() function Add a xzalloc() helper function to simplify error-checked allocations. --- fscryptctl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fscryptctl.c b/fscryptctl.c index bae1872..c386a67 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -43,6 +43,15 @@ #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +static void *xzalloc(size_t size) { + void *ptr = calloc(1, size); + if (!ptr) { + fprintf(stderr, "error: out of memory\n"); + exit(EXIT_FAILURE); + } + return ptr; +} + static void secure_wipe(void *v, size_t n) { #ifdef explicit_bzero explicit_bzero(v, n); @@ -399,12 +408,7 @@ static int cmd_add_key(int argc, char *const argv[]) { const char *mountpoint = argv[0]; struct fscrypt_add_key_arg *arg = - calloc(sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE, 1); - if (!arg) { - fputs("error: failed to allocate memory\n", stderr); - return EXIT_FAILURE; - } - + xzalloc(sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE); int status = EXIT_FAILURE; arg->raw_size = read_key(arg->raw); if (arg->raw_size == 0) { From d8e1d50077a960219c4201fe7b28a9e5069907d4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 05/10] Make read_key() take a max_size parameter In preparation for supporting both raw keys (max size 64) and wrapped keys (max size 128), make read_key() take a max_size parameter. Also introduce a wipe_and_free() helper function. --- fscryptctl.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fscryptctl.c b/fscryptctl.c index c386a67..66a8389 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -63,6 +63,13 @@ static void secure_wipe(void *v, size_t n) { #endif } +static void wipe_and_free(void *v, size_t n) { + if (v) { + secure_wipe(v, n); + free(v); + } +} + // Although the kernel always allows 64-byte keys, it may allow shorter keys // too, depending on the encryption mode(s) used. The shortest key the kernel // can ever accept is 16 bytes, which occurs when AES-128-CBC contents @@ -324,15 +331,15 @@ static ssize_t read_until_limit_or_eof(int fd, uint8_t *buf, size_t limit) { return pos; } -// Reads a raw key, of size at least FSCRYPT_MIN_KEY_SIZE bytes and at most -// FSCRYPT_MAX_KEY_SIZE bytes, from standard input into the provided buffer. -// On success, returns the key size in bytes. On failure, returns 0. +// Reads a key, of size at least FSCRYPT_MIN_KEY_SIZE bytes and at most max_size +// bytes, from standard input into the provided buffer. On success, returns the +// key size in bytes. On failure, returns 0. // // Note that we use read(STDIN_FILENO) directly rather than fread(stdin), to // prevent the key from being copied into the internal buffer of the 'FILE *'. -static size_t read_key(uint8_t raw_key[FSCRYPT_MAX_KEY_SIZE]) { - uint8_t buf[FSCRYPT_MAX_KEY_SIZE + 1]; - ssize_t ret = read_until_limit_or_eof(STDIN_FILENO, buf, sizeof(buf)); +static size_t read_key(uint8_t *raw_key, size_t max_size) { + uint8_t *buf = xzalloc(max_size + 1); + ssize_t ret = read_until_limit_or_eof(STDIN_FILENO, buf, max_size + 1); if (ret < 0) { fprintf(stderr, "error: reading from stdin: %s\n", strerror(errno)); ret = 0; @@ -344,15 +351,15 @@ static size_t read_key(uint8_t raw_key[FSCRYPT_MAX_KEY_SIZE]) { ret = 0; goto cleanup; } - if (ret > FSCRYPT_MAX_KEY_SIZE) { - fprintf(stderr, "error: key was too long; it can be at most %d bytes\n", - FSCRYPT_MAX_KEY_SIZE); + if ((size_t)ret > max_size) { + fprintf(stderr, "error: key was too long; it can be at most %zu bytes\n", + max_size); ret = 0; goto cleanup; } memcpy(raw_key, buf, ret); cleanup: - secure_wipe(buf, sizeof(buf)); + wipe_and_free(buf, max_size + 1); return ret; } @@ -410,7 +417,7 @@ static int cmd_add_key(int argc, char *const argv[]) { struct fscrypt_add_key_arg *arg = xzalloc(sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE); int status = EXIT_FAILURE; - arg->raw_size = read_key(arg->raw); + arg->raw_size = read_key(arg->raw, FSCRYPT_MAX_KEY_SIZE); if (arg->raw_size == 0) { goto cleanup; } From 7852250d0cc1af3979a42287326ed4374ae0cc06 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 06/10] Add full_write() function The upcoming commands import_hw_wrapped_key, generate_hw_wrapped_key, and prepare_hw_wrapped_key will write the wrapped keys they produce to standard output. Add a full_write() helper function for them to use. --- fscryptctl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fscryptctl.c b/fscryptctl.c index 66a8389..f250384 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -363,6 +363,19 @@ static size_t read_key(uint8_t *raw_key, size_t max_size) { return ret; } +static bool full_write(int fd, const uint8_t *buf, size_t size) { + while (size) { + ssize_t ret = write(fd, buf, size); + if (ret < 0) { + fprintf(stderr, "error: writing output: %s\n", strerror(errno)); + return false; + } + buf += ret; + size -= ret; + } + return true; +} + static bool get_policy(const char *path, struct fscrypt_get_policy_ex_arg *arg) { int fd = open(path, O_RDONLY | O_CLOEXEC); From a0f4042b45e1f5c9296eaac54746fae12ddca798 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 07/10] Import the latest Import from Linux v6.16. This is needed to support adding hardware-wrapped keys. --- fscrypt_uapi.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fscrypt_uapi.h b/fscrypt_uapi.h index 7a8f4c2..3aff99f 100644 --- a/fscrypt_uapi.h +++ b/fscrypt_uapi.h @@ -119,7 +119,7 @@ struct fscrypt_key_specifier { */ struct fscrypt_provisioning_key_payload { __u32 type; - __u32 __reserved; + __u32 flags; __u8 raw[]; }; @@ -128,7 +128,9 @@ struct fscrypt_add_key_arg { struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; - __u32 __reserved[8]; +#define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001 + __u32 flags; + __u32 __reserved[7]; __u8 raw[]; }; From d6f2db5600b6fbfaeac53bd3ce867a9831a23f5a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 08/10] Import Import from Linux v6.16. Needed for the definition of the blk-crypto ioctls. --- Makefile | 4 ++-- blk-crypto_uapi.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 blk-crypto_uapi.h diff --git a/Makefile b/Makefile index 28a1ed4..9b28c8c 100644 --- a/Makefile +++ b/Makefile @@ -67,8 +67,8 @@ fscryptctl.1: fscryptctl.1.md ############################################################################## -# Don't format fscrypt_uapi.h, so that it stays identical to the kernel version. -FILES_TO_FORMAT := $(filter-out fscrypt_uapi.h, $(SRC) $(HDRS)) +# Don't format UAPI files. They should stay identical to the kernel's copies. +FILES_TO_FORMAT := $(filter-out %_uapi.h, $(SRC) $(HDRS)) .PHONY: format format-check format: diff --git a/blk-crypto_uapi.h b/blk-crypto_uapi.h new file mode 100644 index 0000000..97302c6 --- /dev/null +++ b/blk-crypto_uapi.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_BLK_CRYPTO_H +#define _UAPI_LINUX_BLK_CRYPTO_H + +#include +#include + +struct blk_crypto_import_key_arg { + /* Raw key (input) */ + __u64 raw_key_ptr; + __u64 raw_key_size; + /* Long-term wrapped key blob (output) */ + __u64 lt_key_ptr; + __u64 lt_key_size; + __u64 reserved[4]; +}; + +struct blk_crypto_generate_key_arg { + /* Long-term wrapped key blob (output) */ + __u64 lt_key_ptr; + __u64 lt_key_size; + __u64 reserved[4]; +}; + +struct blk_crypto_prepare_key_arg { + /* Long-term wrapped key blob (input) */ + __u64 lt_key_ptr; + __u64 lt_key_size; + /* Ephemerally-wrapped key blob (output) */ + __u64 eph_key_ptr; + __u64 eph_key_size; + __u64 reserved[4]; +}; + +/* + * These ioctls share the block device ioctl space; see uapi/linux/fs.h. + * 140-141 are reserved for future blk-crypto ioctls; any more than that would + * require an additional allocation from the block device ioctl space. + */ +#define BLKCRYPTOIMPORTKEY _IOWR(0x12, 137, struct blk_crypto_import_key_arg) +#define BLKCRYPTOGENERATEKEY _IOWR(0x12, 138, struct blk_crypto_generate_key_arg) +#define BLKCRYPTOPREPAREKEY _IOWR(0x12, 139, struct blk_crypto_prepare_key_arg) + +#endif /* _UAPI_LINUX_BLK_CRYPTO_H */ From e9ef4406303c25ec5246ed74adb55349e126a2b0 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 09/10] Add support for adding hardware-wrapped keys Update the 'fscryptctl add_key' command to accept hardware-wrapped keys. Previously, it only accepted raw keys. This relies on the support for FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED which is available in Linux 6.16 and later. For more details, see the Linux kernel commit https://git.kernel.org/linus/c07d3aede2b26830 --- NEWS.md | 4 ++++ fscryptctl.1.md | 8 ++++++-- fscryptctl.c | 40 +++++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/NEWS.md b/NEWS.md index eed1ce7..4eef264 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # fscryptctl release notes +## Version 1.3.0 + +* `fscryptctl add_key` now supports the `--hw-wrapped-key` option. + ## Version 1.2.0 * `fscryptctl set_policy` now accepts the `--data-unit-size` option. diff --git a/fscryptctl.1.md b/fscryptctl.1.md index 913e5f6..2462521 100644 --- a/fscryptctl.1.md +++ b/fscryptctl.1.md @@ -40,7 +40,7 @@ feature, see the references at the end of this page. # SUBCOMMANDS -## **fscryptctl add_key** *MOUNTPOINT* +## **fscryptctl add_key** [*OPTION*...] *MOUNTPOINT* Add an encryption key to the given mounted filesystem. This will "unlock" any files and directories that are protected by the given key on the given @@ -54,7 +54,11 @@ If successful, **fscryptctl add_key** will print the key identifier of the newly added key; this will be a 32-character hex string which can be passed to other **fscryptctl** commands. -**fscryptctl add_key** does not accept any options. +Options accepted by **fscryptctl add_key**: + +**\-\-hw\-wrapped\-key** +: Add a hardware-wrapped key. If this option is given, the key must be a + hardware-wrapped key in ephemerally-wrapped form, rather than a raw key. ## **fscryptctl remove_key** [*OPTION*...] *KEY_IDENTIFIER* *MOUNTPOINT* diff --git a/fscryptctl.c b/fscryptctl.c index f250384..18d8fc9 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -41,6 +41,11 @@ #define VERSION "v1.2.0" #endif +// This matches the limit used by the kernel internally as of Linux 6.16. There +// isn't much reason to think that a wrapped key would ever be larger than this, +// but it can be increased if ever needed. +#define MAX_WRAPPED_KEY_SIZE 128 + #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) static void *xzalloc(size_t size) { @@ -103,6 +108,7 @@ enum { OPT_DATA_UNIT_SIZE, OPT_DIRECT_KEY, OPT_FILENAMES, + OPT_HW_WRAPPED_KEY, OPT_IV_INO_LBLK_32, OPT_IV_INO_LBLK_64, OPT_PADDING, @@ -133,6 +139,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " print this help screen\n" " -v, --version\n" " print the version of fscryptctl\n" + " add_key\n" + " --hw-wrapped-key\n" + " add a hardware-wrapped key (rather than a raw key)\n" " remove_key\n" " --all-users\n" " force-remove all users' claims to the key (requires root)\n" @@ -154,7 +163,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { "\nNotes:\n" " Keys are identified by 32-character hex strings (key identifiers).\n" "\n" - " Raw keys are given on stdin in binary and usually must be 64 bytes.\n" + " Keys are given on stdin in raw binary.\n" "\n" " For more information, run `man fscryptctl`.\n", out); @@ -420,20 +429,38 @@ static bool set_policy(const char *path, // ----------------------------------------------------------------------------- static int cmd_add_key(int argc, char *const argv[]) { - handle_no_options(&argc, &argv); + static const struct option add_key_options[] = { + {"hw-wrapped-key", no_argument, NULL, OPT_HW_WRAPPED_KEY}, + {NULL, 0, NULL, 0}}; + int max_size = FSCRYPT_MAX_KEY_SIZE; + __u32 flags = 0; + + int ch; + while ((ch = getopt_long(argc, argv, "", add_key_options, NULL)) != -1) { + switch (ch) { + case OPT_HW_WRAPPED_KEY: + flags |= FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED; + max_size = MAX_WRAPPED_KEY_SIZE; + break; + default: + usage(stderr); + } + } + argc -= optind; + argv += optind; if (argc != 1) { fputs("error: must specify a single mountpoint\n", stderr); return EXIT_FAILURE; } const char *mountpoint = argv[0]; - struct fscrypt_add_key_arg *arg = - xzalloc(sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE); + struct fscrypt_add_key_arg *arg = xzalloc(sizeof(*arg) + max_size); int status = EXIT_FAILURE; - arg->raw_size = read_key(arg->raw, FSCRYPT_MAX_KEY_SIZE); + arg->raw_size = read_key(arg->raw, max_size); if (arg->raw_size == 0) { goto cleanup; } + arg->flags = flags; arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; int fd = open(mountpoint, O_RDONLY | O_CLOEXEC); @@ -455,8 +482,7 @@ static int cmd_add_key(int argc, char *const argv[]) { puts(identifier_hex); status = EXIT_SUCCESS; cleanup: - secure_wipe(arg->raw, arg->raw_size); - free(arg); + wipe_and_free(arg, sizeof(*arg) + max_size); return status; } From 9b84dc56739e1630e47bb1de194daa1fd29039ee Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Oct 2025 15:48:18 -0700 Subject: [PATCH 10/10] Add support for creating and preparing hardware-wrapped keys Add fscryptctl commands that wrap the BLKCRYPTOIMPORTKEY, BLKCRYPTOGENERATEKEY, and BLKCRYPTOPREPAREKEY ioctls that were added in Linux 6.15. These are needed to use hardware-wrapped keys. --- NEWS.md | 3 ++ README.md | 22 ++++++++ fscryptctl.1.md | 42 ++++++++++++++- fscryptctl.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 200 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4eef264..0f0f66d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,9 @@ * `fscryptctl add_key` now supports the `--hw-wrapped-key` option. +* Added new commands `fscryptctl import_hw_wrapped_key`, + `fscryptctl generate_hw_wrapped_key`, and `fscryptctl prepare_hw_wrapped_key`. + ## Version 1.2.0 * `fscryptctl set_policy` now accepts the `--data-unit-size` option. diff --git a/README.md b/README.md index 335a61a..2adb21d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ For the release notes, see the [NEWS file](NEWS.md). - [Runtime Dependencies](#runtime-dependencies) - [Features](#features) - [Example Usage](#example-usage) +- [Example Usage with Hardware-Wrapped Key](#example-usage-with-hardware-wrapped-key) - [Contributing](#contributing) - [Legal](#legal) @@ -72,6 +73,9 @@ tips](https://github.com/google/fscrypt#getting-encryption-not-enabled-on-an-ext * `fscryptctl key_status` - get the status of an encryption key on a filesystem * `fscryptctl get_policy` - get the encryption policy of a file or directory * `fscryptctl set_policy` - set the encryption policy of an empty directory +* `fscryptctl import_hw_wrapped_key` - import a hardware-wrapped key +* `fscryptctl generate_hw_wrapped_key` - generate a hardware-wrapped key +* `fscryptctl prepare_hw_wrapped_key` - prepare a hardware-wrapped key For full usage details, see the manual page (`man fscryptctl`), or alternatively run `fscryptctl --help`. @@ -154,6 +158,24 @@ bar foo foo ``` +## Example Usage with Hardware-Wrapped Key + +On systems that support hardware-wrapped inline encryption keys, +hardware-wrapped keys can be used instead of raw keys: + +```shell +> mkfs.ext4 -O encrypt,stable_inodes /dev/vdb +> mount /dev/vdb -o inlinecrypt /mnt +> head -c 32 /dev/urandom | fscryptctl import_hw_wrapped_key /dev/vdb > /tmp/lt_key +> fscryptctl prepare_hw_wrapped_key /dev/vdb < /tmp/lt_key | fscryptctl add_key --hw-wrapped-key /mnt +f12fccad977328d20a16c79627787a1c +> mkdir /mnt/dir +> fscryptctl set_policy --iv-ino-lblk-64 f12fccad977328d20a16c79627787a1c /mnt/dir +``` + +Hardware-wrapped inline encryption keys require Linux 6.16 or later as well as +hardware that supports this feature, for example the Qualcomm SM8650 HDK. + ## Contributing We would love to accept your contributions to `fscryptctl`. See the diff --git a/fscryptctl.1.md b/fscryptctl.1.md index 2462521..ded9202 100644 --- a/fscryptctl.1.md +++ b/fscryptctl.1.md @@ -9,7 +9,10 @@ fscryptctl - low-level userspace tool for Linux filesystem encryption **fscryptctl remove_key** [*OPTION*...] *KEY_IDENTIFIER* *MOUNTPOINT* \ **fscryptctl key_status** *KEY_IDENTIFIER* *MOUNTPOINT* \ **fscryptctl get_policy** *PATH* \ -**fscryptctl set_policy** [*OPTION*...] *KEY_IDENTIFIER* *DIRECTORY* +**fscryptctl set_policy** [*OPTION*...] *KEY_IDENTIFIER* *DIRECTORY* \ +**fscryptctl import_hw_wrapped_key** *BLOCK_DEVICE* \ +**fscryptctl generate_hw_wrapped_key** *BLOCK_DEVICE* \ +**fscryptctl prepare_hw_wrapped_key** *BLOCK_DEVICE* # DESCRIPTION @@ -147,6 +150,40 @@ Options accepted by **fscryptctl set_policy**: : Select the crypto data unit size, i.e. the granularity of file contents encryption, in bytes. +## **fscryptctl import_hw_wrapped_key** *BLOCK_DEVICE* + +Create a hardware-wrapped inline encryption key by importing a raw key, turning +it into a long-term wrapped key. The raw key is read from standard input and +the long-term wrapped key blob is written to standard output, both in binary. + +This subcommand is a thin wrapper around the **BLKCRYPTOIMPORTKEY** ioctl. For +more information, see the kernel documentation. + +**fscryptctl import_hw_wrapped_key** does not accept any options. + +## **fscryptctl generate_hw_wrapped_key** *BLOCK_DEVICE* + +Create a hardware-wrapped inline encryption key by having the hardware generate +one. The new long-term wrapped key blob is written to standard output in +binary. + +This subcommand is a thin wrapper around the **BLKCRYPTOGENERATEKEY** ioctl. +For more information, see the kernel documentation. + +**fscryptctl generate_hw_wrapped_key** does not accept any options. + +## **fscryptctl prepare_hw_wrapped_key** *BLOCK_DEVICE* + +Prepares a hardware-wrapped inline encryption key to be used by converting it +from long-term wrapped form to ephemerally-wrapped form. The long-term wrapped +key blob is read from standard input and the ephemerally-wrapped key blob is +written to standard output, both in binary. + +This subcommand is a thin wrapper around the **BLKCRYPTOPREPAREKEY** ioctl. For +more information, see the kernel documentation. + +**fscryptctl prepare_hw_wrapped_key** does not accept any options. + # SEE ALSO * [**fscryptctl** README @@ -157,3 +194,6 @@ Options accepted by **fscryptctl set_policy**: * [**fscrypt** tool, recommended for most users over fscryptctl](https://github.com/google/fscrypt) + +* [Linux kernel documentation for hardware-wrapped + keys](https://docs.kernel.org/block/inline-encryption.html#hardware-wrapped-keys) diff --git a/fscryptctl.c b/fscryptctl.c index 18d8fc9..9a5d014 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -34,6 +34,7 @@ #include #include +#include "blk-crypto_uapi.h" #include "fscrypt_uapi.h" #ifndef VERSION @@ -134,6 +135,13 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " fscryptctl set_policy \n" " Set up an encryption policy on the specified directory with the\n" " specified key identifier.\n" + " fscryptctl import_hw_wrapped_key \n" + " Create a hardware-wrapped key by importing a raw key.\n" + " fscryptctl generate_hw_wrapped_key \n" + " Create a hardware-wrapped key by generating one in hardware.\n" + " fscryptctl prepare_hw_wrapped_key \n" + " Prepare a hardware-wrapped key to be used by converting it from\n" + " long-term wrapped form to ephemerally-wrapped form.\n" "\nOptions:\n" " -h, --help\n" " print this help screen\n" @@ -778,6 +786,125 @@ static int cmd_set_policy(int argc, char *const argv[]) { return EXIT_SUCCESS; } +static int cmd_import_hw_wrapped_key(int argc, char *const argv[]) { + handle_no_options(&argc, &argv); + if (argc != 1) { + fputs("error: must specify a single block device\n", stderr); + return EXIT_FAILURE; + } + const char *blkdev = argv[0]; + int status = EXIT_FAILURE; + + struct blk_crypto_import_key_arg arg = {0}; + uint8_t *raw_key = xzalloc(FSCRYPT_MAX_KEY_SIZE); + uint8_t *lt_key = xzalloc(MAX_WRAPPED_KEY_SIZE); + arg.raw_key_ptr = (uintptr_t)raw_key; + arg.raw_key_size = read_key(raw_key, FSCRYPT_MAX_KEY_SIZE); + if (arg.raw_key_size == 0) { + goto cleanup; + } + arg.lt_key_ptr = (uintptr_t)lt_key; + arg.lt_key_size = MAX_WRAPPED_KEY_SIZE; + + int fd = open(blkdev, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "error: opening %s: %s\n", blkdev, strerror(errno)); + goto cleanup; + } + if (ioctl(fd, BLKCRYPTOIMPORTKEY, &arg) != 0) { + fprintf(stderr, "error: importing hardware-wrapped key: %s\n", + strerror(errno)); + close(fd); + goto cleanup; + } + close(fd); + if (!full_write(STDOUT_FILENO, lt_key, arg.lt_key_size)) { + goto cleanup; + } + status = EXIT_SUCCESS; +cleanup: + wipe_and_free(raw_key, FSCRYPT_MAX_KEY_SIZE); + wipe_and_free(lt_key, MAX_WRAPPED_KEY_SIZE); + return status; +} + +static int cmd_generate_hw_wrapped_key(int argc, char *const argv[]) { + handle_no_options(&argc, &argv); + if (argc != 1) { + fputs("error: must specify a single block device\n", stderr); + return EXIT_FAILURE; + } + const char *blkdev = argv[0]; + int status = EXIT_FAILURE; + + struct blk_crypto_generate_key_arg arg = {0}; + uint8_t *lt_key = xzalloc(MAX_WRAPPED_KEY_SIZE); + arg.lt_key_ptr = (uintptr_t)lt_key; + arg.lt_key_size = MAX_WRAPPED_KEY_SIZE; + + int fd = open(blkdev, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "error: opening %s: %s\n", blkdev, strerror(errno)); + goto cleanup; + } + if (ioctl(fd, BLKCRYPTOGENERATEKEY, &arg) != 0) { + fprintf(stderr, "error: generating hardware-wrapped key: %s\n", + strerror(errno)); + close(fd); + goto cleanup; + } + close(fd); + if (!full_write(STDOUT_FILENO, lt_key, arg.lt_key_size)) { + goto cleanup; + } + status = EXIT_SUCCESS; +cleanup: + wipe_and_free(lt_key, MAX_WRAPPED_KEY_SIZE); + return status; +} + +static int cmd_prepare_hw_wrapped_key(int argc, char *const argv[]) { + handle_no_options(&argc, &argv); + if (argc != 1) { + fputs("error: must specify a single block device\n", stderr); + return EXIT_FAILURE; + } + const char *blkdev = argv[0]; + int status = EXIT_FAILURE; + + struct blk_crypto_prepare_key_arg arg = {0}; + uint8_t *lt_key = xzalloc(MAX_WRAPPED_KEY_SIZE); + uint8_t *eph_key = xzalloc(MAX_WRAPPED_KEY_SIZE); + arg.lt_key_ptr = (uintptr_t)lt_key; + arg.lt_key_size = read_key(lt_key, MAX_WRAPPED_KEY_SIZE); + if (arg.lt_key_size == 0) { + goto cleanup; + } + arg.eph_key_ptr = (uintptr_t)eph_key; + arg.eph_key_size = MAX_WRAPPED_KEY_SIZE; + + int fd = open(blkdev, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "error: opening %s: %s\n", blkdev, strerror(errno)); + goto cleanup; + } + if (ioctl(fd, BLKCRYPTOPREPAREKEY, &arg) != 0) { + fprintf(stderr, "error: preparing hardware-wrapped key: %s\n", + strerror(errno)); + close(fd); + goto cleanup; + } + close(fd); + if (!full_write(STDOUT_FILENO, eph_key, arg.eph_key_size)) { + goto cleanup; + } + status = EXIT_SUCCESS; +cleanup: + wipe_and_free(lt_key, MAX_WRAPPED_KEY_SIZE); + wipe_and_free(eph_key, MAX_WRAPPED_KEY_SIZE); + return status; +} + // ----------------------------------------------------------------------------- // The main() function // ----------------------------------------------------------------------------- @@ -786,9 +913,14 @@ static const struct { const char *name; int (*func)(int argc, char *const argv[]); } commands[] = { - {"add_key", cmd_add_key}, {"remove_key", cmd_remove_key}, - {"key_status", cmd_key_status}, {"get_policy", cmd_get_policy}, + {"add_key", cmd_add_key}, + {"remove_key", cmd_remove_key}, + {"key_status", cmd_key_status}, + {"get_policy", cmd_get_policy}, {"set_policy", cmd_set_policy}, + {"import_hw_wrapped_key", cmd_import_hw_wrapped_key}, + {"generate_hw_wrapped_key", cmd_generate_hw_wrapped_key}, + {"prepare_hw_wrapped_key", cmd_prepare_hw_wrapped_key}, }; int main(int argc, char *const argv[]) {