Skip to content

Commit 1ebd4a3

Browse files
ebiggersaxboe
authored andcommitted
blk-crypto: add ioctls to create and prepare hardware-wrapped keys
Until this point, the kernel can use hardware-wrapped keys to do encryption if userspace provides one -- specifically a key in ephemerally-wrapped form. However, no generic way has been provided for userspace to get such a key in the first place. Getting such a key is a two-step process. First, the key needs to be imported from a raw key or generated by the hardware, producing a key in long-term wrapped form. This happens once in the whole lifetime of the key. Second, the long-term wrapped key needs to be converted into ephemerally-wrapped form. This happens each time the key is "unlocked". In Android, these operations are supported in a generic way through KeyMint, a userspace abstraction layer. However, that method is Android-specific and can't be used on other Linux systems, may rely on proprietary libraries, and also misleads people into supporting KeyMint features like rollback resistance that make sense for other KeyMint keys but don't make sense for hardware-wrapped inline encryption keys. Therefore, this patch provides a generic kernel interface for these operations by introducing new block device ioctls: - BLKCRYPTOIMPORTKEY: convert a raw key to long-term wrapped form. - BLKCRYPTOGENERATEKEY: have the hardware generate a new key, then return it in long-term wrapped form. - BLKCRYPTOPREPAREKEY: convert a key from long-term wrapped form to ephemerally-wrapped form. These ioctls are implemented using new operations in blk_crypto_ll_ops. Signed-off-by: Eric Biggers <[email protected]> Tested-by: Bartosz Golaszewski <[email protected]> # sm8650 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent e35fde4 commit 1ebd4a3

File tree

10 files changed

+350
-4
lines changed

10 files changed

+350
-4
lines changed

Documentation/block/inline-encryption.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,42 @@ when hardware support is available. This works in the following way:
492492
blk-crypto-fallback doesn't support hardware-wrapped keys. Therefore,
493493
hardware-wrapped keys can only be used with actual inline encryption hardware.
494494

495+
All the above deals with hardware-wrapped keys in ephemerally-wrapped form only.
496+
To get such keys in the first place, new block device ioctls have been added to
497+
provide a generic interface to creating and preparing such keys:
498+
499+
- ``BLKCRYPTOIMPORTKEY`` converts a raw key to long-term wrapped form. It takes
500+
in a pointer to a ``struct blk_crypto_import_key_arg``. The caller must set
501+
``raw_key_ptr`` and ``raw_key_size`` to the pointer and size (in bytes) of the
502+
raw key to import. On success, ``BLKCRYPTOIMPORTKEY`` returns 0 and writes
503+
the resulting long-term wrapped key blob to the buffer pointed to by
504+
``lt_key_ptr``, which is of maximum size ``lt_key_size``. It also updates
505+
``lt_key_size`` to be the actual size of the key. On failure, it returns -1
506+
and sets errno. An errno of ``EOPNOTSUPP`` indicates that the block device
507+
does not support hardware-wrapped keys. An errno of ``EOVERFLOW`` indicates
508+
that the output buffer did not have enough space for the key blob.
509+
510+
- ``BLKCRYPTOGENERATEKEY`` is like ``BLKCRYPTOIMPORTKEY``, but it has the
511+
hardware generate the key instead of importing one. It takes in a pointer to
512+
a ``struct blk_crypto_generate_key_arg``.
513+
514+
- ``BLKCRYPTOPREPAREKEY`` converts a key from long-term wrapped form to
515+
ephemerally-wrapped form. It takes in a pointer to a ``struct
516+
blk_crypto_prepare_key_arg``. The caller must set ``lt_key_ptr`` and
517+
``lt_key_size`` to the pointer and size (in bytes) of the long-term wrapped
518+
key blob to convert. On success, ``BLKCRYPTOPREPAREKEY`` returns 0 and writes
519+
the resulting ephemerally-wrapped key blob to the buffer pointed to by
520+
``eph_key_ptr``, which is of maximum size ``eph_key_size``. It also updates
521+
``eph_key_size`` to be the actual size of the key. On failure, it returns -1
522+
and sets errno. Errno values of ``EOPNOTSUPP`` and ``EOVERFLOW`` mean the
523+
same as they do for ``BLKCRYPTOIMPORTKEY``. An errno of ``EBADMSG`` indicates
524+
that the long-term wrapped key is invalid.
525+
526+
Userspace needs to use either ``BLKCRYPTOIMPORTKEY`` or ``BLKCRYPTOGENERATEKEY``
527+
once to create a key, and then ``BLKCRYPTOPREPAREKEY`` each time the key is
528+
unlocked and added to the kernel. Note that these ioctls have no relevance for
529+
raw keys; they are only for hardware-wrapped keys.
530+
495531
Testability
496532
-----------
497533

Documentation/userspace-api/ioctl/ioctl-number.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Code Seq# Include File Comments
8585
0x10 20-2F arch/s390/include/uapi/asm/hypfs.h
8686
0x12 all linux/fs.h BLK* ioctls
8787
linux/blkpg.h
88+
linux/blkzoned.h
89+
linux/blk-crypto.h
8890
0x15 all linux/fs.h FS_IOC_* ioctls
8991
0x1b all InfiniBand Subsystem
9092
<http://infiniband.sourceforge.net/>

block/blk-crypto-internal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ int __blk_crypto_evict_key(struct blk_crypto_profile *profile,
8383
bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile,
8484
const struct blk_crypto_config *cfg);
8585

86+
int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,
87+
void __user *argp);
88+
8689
#else /* CONFIG_BLK_INLINE_ENCRYPTION */
8790

8891
static inline int blk_crypto_sysfs_register(struct gendisk *disk)
@@ -130,6 +133,12 @@ static inline bool blk_crypto_rq_has_keyslot(struct request *rq)
130133
return false;
131134
}
132135

136+
static inline int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,
137+
void __user *argp)
138+
{
139+
return -ENOTTY;
140+
}
141+
133142
#endif /* CONFIG_BLK_INLINE_ENCRYPTION */
134143

135144
void __bio_crypt_advance(struct bio *bio, unsigned int bytes);

block/blk-crypto-profile.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,61 @@ int blk_crypto_derive_sw_secret(struct block_device *bdev,
502502
return err;
503503
}
504504

505+
int blk_crypto_import_key(struct blk_crypto_profile *profile,
506+
const u8 *raw_key, size_t raw_key_size,
507+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
508+
{
509+
int ret;
510+
511+
if (!profile)
512+
return -EOPNOTSUPP;
513+
if (!(profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED))
514+
return -EOPNOTSUPP;
515+
if (!profile->ll_ops.import_key)
516+
return -EOPNOTSUPP;
517+
blk_crypto_hw_enter(profile);
518+
ret = profile->ll_ops.import_key(profile, raw_key, raw_key_size,
519+
lt_key);
520+
blk_crypto_hw_exit(profile);
521+
return ret;
522+
}
523+
524+
int blk_crypto_generate_key(struct blk_crypto_profile *profile,
525+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
526+
{
527+
int ret;
528+
529+
if (!profile)
530+
return -EOPNOTSUPP;
531+
if (!(profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED))
532+
return -EOPNOTSUPP;
533+
if (!profile->ll_ops.generate_key)
534+
return -EOPNOTSUPP;
535+
blk_crypto_hw_enter(profile);
536+
ret = profile->ll_ops.generate_key(profile, lt_key);
537+
blk_crypto_hw_exit(profile);
538+
return ret;
539+
}
540+
541+
int blk_crypto_prepare_key(struct blk_crypto_profile *profile,
542+
const u8 *lt_key, size_t lt_key_size,
543+
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
544+
{
545+
int ret;
546+
547+
if (!profile)
548+
return -EOPNOTSUPP;
549+
if (!(profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED))
550+
return -EOPNOTSUPP;
551+
if (!profile->ll_ops.prepare_key)
552+
return -EOPNOTSUPP;
553+
blk_crypto_hw_enter(profile);
554+
ret = profile->ll_ops.prepare_key(profile, lt_key, lt_key_size,
555+
eph_key);
556+
blk_crypto_hw_exit(profile);
557+
return ret;
558+
}
559+
505560
/**
506561
* blk_crypto_intersect_capabilities() - restrict supported crypto capabilities
507562
* by child device

block/blk-crypto.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,146 @@ void blk_crypto_evict_key(struct block_device *bdev,
469469
pr_warn_ratelimited("%pg: error %d evicting key\n", bdev, err);
470470
}
471471
EXPORT_SYMBOL_GPL(blk_crypto_evict_key);
472+
473+
static int blk_crypto_ioctl_import_key(struct blk_crypto_profile *profile,
474+
void __user *argp)
475+
{
476+
struct blk_crypto_import_key_arg arg;
477+
u8 raw_key[BLK_CRYPTO_MAX_RAW_KEY_SIZE];
478+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];
479+
int ret;
480+
481+
if (copy_from_user(&arg, argp, sizeof(arg)))
482+
return -EFAULT;
483+
484+
if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))
485+
return -EINVAL;
486+
487+
if (arg.raw_key_size < 16 || arg.raw_key_size > sizeof(raw_key))
488+
return -EINVAL;
489+
490+
if (copy_from_user(raw_key, u64_to_user_ptr(arg.raw_key_ptr),
491+
arg.raw_key_size)) {
492+
ret = -EFAULT;
493+
goto out;
494+
}
495+
ret = blk_crypto_import_key(profile, raw_key, arg.raw_key_size, lt_key);
496+
if (ret < 0)
497+
goto out;
498+
if (ret > arg.lt_key_size) {
499+
ret = -EOVERFLOW;
500+
goto out;
501+
}
502+
arg.lt_key_size = ret;
503+
if (copy_to_user(u64_to_user_ptr(arg.lt_key_ptr), lt_key,
504+
arg.lt_key_size) ||
505+
copy_to_user(argp, &arg, sizeof(arg))) {
506+
ret = -EFAULT;
507+
goto out;
508+
}
509+
ret = 0;
510+
511+
out:
512+
memzero_explicit(raw_key, sizeof(raw_key));
513+
memzero_explicit(lt_key, sizeof(lt_key));
514+
return ret;
515+
}
516+
517+
static int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,
518+
void __user *argp)
519+
{
520+
struct blk_crypto_generate_key_arg arg;
521+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];
522+
int ret;
523+
524+
if (copy_from_user(&arg, argp, sizeof(arg)))
525+
return -EFAULT;
526+
527+
if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))
528+
return -EINVAL;
529+
530+
ret = blk_crypto_generate_key(profile, lt_key);
531+
if (ret < 0)
532+
goto out;
533+
if (ret > arg.lt_key_size) {
534+
ret = -EOVERFLOW;
535+
goto out;
536+
}
537+
arg.lt_key_size = ret;
538+
if (copy_to_user(u64_to_user_ptr(arg.lt_key_ptr), lt_key,
539+
arg.lt_key_size) ||
540+
copy_to_user(argp, &arg, sizeof(arg))) {
541+
ret = -EFAULT;
542+
goto out;
543+
}
544+
ret = 0;
545+
546+
out:
547+
memzero_explicit(lt_key, sizeof(lt_key));
548+
return ret;
549+
}
550+
551+
static int blk_crypto_ioctl_prepare_key(struct blk_crypto_profile *profile,
552+
void __user *argp)
553+
{
554+
struct blk_crypto_prepare_key_arg arg;
555+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];
556+
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];
557+
int ret;
558+
559+
if (copy_from_user(&arg, argp, sizeof(arg)))
560+
return -EFAULT;
561+
562+
if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))
563+
return -EINVAL;
564+
565+
if (arg.lt_key_size > sizeof(lt_key))
566+
return -EINVAL;
567+
568+
if (copy_from_user(lt_key, u64_to_user_ptr(arg.lt_key_ptr),
569+
arg.lt_key_size)) {
570+
ret = -EFAULT;
571+
goto out;
572+
}
573+
ret = blk_crypto_prepare_key(profile, lt_key, arg.lt_key_size, eph_key);
574+
if (ret < 0)
575+
goto out;
576+
if (ret > arg.eph_key_size) {
577+
ret = -EOVERFLOW;
578+
goto out;
579+
}
580+
arg.eph_key_size = ret;
581+
if (copy_to_user(u64_to_user_ptr(arg.eph_key_ptr), eph_key,
582+
arg.eph_key_size) ||
583+
copy_to_user(argp, &arg, sizeof(arg))) {
584+
ret = -EFAULT;
585+
goto out;
586+
}
587+
ret = 0;
588+
589+
out:
590+
memzero_explicit(lt_key, sizeof(lt_key));
591+
memzero_explicit(eph_key, sizeof(eph_key));
592+
return ret;
593+
}
594+
595+
int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,
596+
void __user *argp)
597+
{
598+
struct blk_crypto_profile *profile =
599+
bdev_get_queue(bdev)->crypto_profile;
600+
601+
if (!profile)
602+
return -EOPNOTSUPP;
603+
604+
switch (cmd) {
605+
case BLKCRYPTOIMPORTKEY:
606+
return blk_crypto_ioctl_import_key(profile, argp);
607+
case BLKCRYPTOGENERATEKEY:
608+
return blk_crypto_ioctl_generate_key(profile, argp);
609+
case BLKCRYPTOPREPAREKEY:
610+
return blk_crypto_ioctl_prepare_key(profile, argp);
611+
default:
612+
return -ENOTTY;
613+
}
614+
}

block/ioctl.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/io_uring/cmd.h>
1616
#include <uapi/linux/blkdev.h>
1717
#include "blk.h"
18+
#include "blk-crypto-internal.h"
1819

1920
static int blkpg_do_ioctl(struct block_device *bdev,
2021
struct blkpg_partition __user *upart, int op)
@@ -620,6 +621,10 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,
620621
case BLKTRACESTOP:
621622
case BLKTRACETEARDOWN:
622623
return blk_trace_ioctl(bdev, cmd, argp);
624+
case BLKCRYPTOIMPORTKEY:
625+
case BLKCRYPTOGENERATEKEY:
626+
case BLKCRYPTOPREPAREKEY:
627+
return blk_crypto_ioctl(bdev, cmd, argp);
623628
case IOC_PR_REGISTER:
624629
return blkdev_pr_register(bdev, mode, argp);
625630
case IOC_PR_RESERVE:

include/linux/blk-crypto-profile.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,48 @@ struct blk_crypto_ll_ops {
7171
int (*derive_sw_secret)(struct blk_crypto_profile *profile,
7272
const u8 *eph_key, size_t eph_key_size,
7373
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);
74+
75+
/**
76+
* @import_key: Create a hardware-wrapped key by importing a raw key.
77+
*
78+
* This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
79+
* is supported.
80+
*
81+
* On success, must write the new key in long-term wrapped form to
82+
* @lt_key and return its size in bytes. On failure, must return a
83+
* -errno value.
84+
*/
85+
int (*import_key)(struct blk_crypto_profile *profile,
86+
const u8 *raw_key, size_t raw_key_size,
87+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
88+
89+
/**
90+
* @generate_key: Generate a hardware-wrapped key.
91+
*
92+
* This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
93+
* is supported.
94+
*
95+
* On success, must write the new key in long-term wrapped form to
96+
* @lt_key and return its size in bytes. On failure, must return a
97+
* -errno value.
98+
*/
99+
int (*generate_key)(struct blk_crypto_profile *profile,
100+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
101+
102+
/**
103+
* @prepare_key: Prepare a hardware-wrapped key to be used.
104+
*
105+
* Prepare a hardware-wrapped key to be used by converting it from
106+
* long-term wrapped form to ephemerally-wrapped form. This only needs
107+
* to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED is supported.
108+
*
109+
* On success, must write the key in ephemerally-wrapped form to
110+
* @eph_key and return its size in bytes. On failure, must return
111+
* -EBADMSG if the key is invalid, or another -errno on other error.
112+
*/
113+
int (*prepare_key)(struct blk_crypto_profile *profile,
114+
const u8 *lt_key, size_t lt_key_size,
115+
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
74116
};
75117

76118
/**
@@ -163,6 +205,17 @@ void blk_crypto_reprogram_all_keys(struct blk_crypto_profile *profile);
163205

164206
void blk_crypto_profile_destroy(struct blk_crypto_profile *profile);
165207

208+
int blk_crypto_import_key(struct blk_crypto_profile *profile,
209+
const u8 *raw_key, size_t raw_key_size,
210+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
211+
212+
int blk_crypto_generate_key(struct blk_crypto_profile *profile,
213+
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
214+
215+
int blk_crypto_prepare_key(struct blk_crypto_profile *profile,
216+
const u8 *lt_key, size_t lt_key_size,
217+
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
218+
166219
void blk_crypto_intersect_capabilities(struct blk_crypto_profile *parent,
167220
const struct blk_crypto_profile *child);
168221

include/linux/blk-crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <linux/minmax.h>
1010
#include <linux/types.h>
11+
#include <uapi/linux/blk-crypto.h>
1112

1213
enum blk_crypto_mode_num {
1314
BLK_ENCRYPTION_MODE_INVALID,

0 commit comments

Comments
 (0)