Generic callers which supply an AES key need a provider-owned slot policy, since hardware may reserve or preload particular key slots. Selecting only UCLASS_AES device zero also prevents a later provider from handling an operation unsupported by the first device.
Add a callback through which each provider exposes a slot for software-provided keys. Add a CBC decrypt helper which probes providers in order, continuing only when a provider explicitly reports an unsupported operation and preserving hard probe, setup and decrypt failures. Use slot zero for the software provider and avoid Tegra's preloaded SBK slot. Add sandbox coverage for provider fallback and hard-error propagation. Signed-off-by: James Hilliard <[email protected]> --- Changes v3 -> v4: - New patch --- drivers/crypto/aes/aes-sw.c | 6 ++ drivers/crypto/aes/aes-uclass.c | 73 +++++++++++++++++++ drivers/crypto/tegra/tegra_aes.c | 7 ++ include/uboot_aes.h | 66 +++++++++++++++-- test/dm/aes.c | 149 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 297 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/aes/aes-sw.c b/drivers/crypto/aes/aes-sw.c index 6bad343ea7d..fe06fd2d5ba 100644 --- a/drivers/crypto/aes/aes-sw.c +++ b/drivers/crypto/aes/aes-sw.c @@ -50,6 +50,11 @@ static int sw_aes_ops_available_key_slots(struct udevice *dev) return SW_KEY_SLOTS; } +static int sw_aes_ops_get_software_key_slot(struct udevice *dev) +{ + return 0; +} + static int sw_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot) { struct sw_aes_priv *priv = dev_get_priv(dev); @@ -164,6 +169,7 @@ static int sw_aes_ops_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, static const struct aes_ops aes_ops_sw = { .available_key_slots = sw_aes_ops_available_key_slots, + .get_software_key_slot = sw_aes_ops_get_software_key_slot, .select_key_slot = sw_aes_ops_select_key_slot, .set_key_for_key_slot = sw_aes_ops_set_key_for_key_slot, .aes_ecb_encrypt = sw_aes_ops_aes_ecb_encrypt, diff --git a/drivers/crypto/aes/aes-uclass.c b/drivers/crypto/aes/aes-uclass.c index 5bdd3d736c4..ec1ea075367 100644 --- a/drivers/crypto/aes/aes-uclass.c +++ b/drivers/crypto/aes/aes-uclass.c @@ -23,6 +23,21 @@ int dm_aes_get_available_key_slots(struct udevice *dev) return ops->available_key_slots(dev); } +int dm_aes_get_software_key_slot(struct udevice *dev) +{ + const struct aes_ops *ops; + + if (!dev) + return -ENODEV; + + ops = aes_get_ops(dev); + + if (!ops->get_software_key_slot) + return -ENOSYS; + + return ops->get_software_key_slot(dev); +} + int dm_aes_select_key_slot(struct udevice *dev, u32 key_size, u8 slot) { const struct aes_ops *ops; @@ -113,6 +128,64 @@ int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_ae return ops->aes_cbc_decrypt(dev, iv, src, dst, num_aes_blocks); } +static bool aes_op_unsupported(int ret) +{ + return ret == -ENOSYS || ret == -EOPNOTSUPP; +} + +int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv, u8 *src, + u8 *dst, u32 num_aes_blocks) +{ + struct udevice *dev; + int first_probe_err = 0; + bool found = false; + int ret; + + for (ret = uclass_first_device_check(UCLASS_AES, &dev); dev; + ret = uclass_next_device_check(&dev)) { + u8 slot; + + found = true; + if (ret) { + if (!first_probe_err) + first_probe_err = ret; + continue; + } + + ret = dm_aes_get_software_key_slot(dev); + if (aes_op_unsupported(ret)) + continue; + if (ret < 0) + return ret; + if (ret > U8_MAX) + return -ERANGE; + slot = ret; + + ret = dm_aes_set_key_for_key_slot(dev, key_size, key, slot); + if (aes_op_unsupported(ret)) + continue; + if (ret) + return ret; + + ret = dm_aes_select_key_slot(dev, key_size, slot); + if (aes_op_unsupported(ret)) + continue; + if (ret) + return ret; + + ret = dm_aes_cbc_decrypt(dev, iv, src, dst, num_aes_blocks); + if (!ret) + return 0; + if (!aes_op_unsupported(ret)) + return ret; + } + + if (first_probe_err) + return first_probe_err; + + return found ? -EOPNOTSUPP : -ENODEV; +} + static void left_shift_vector(u8 *in, u8 *out, int size) { int carry = 0; diff --git a/drivers/crypto/tegra/tegra_aes.c b/drivers/crypto/tegra/tegra_aes.c index 55a4cec525b..348115ac5a7 100644 --- a/drivers/crypto/tegra/tegra_aes.c +++ b/drivers/crypto/tegra/tegra_aes.c @@ -321,6 +321,12 @@ static int tegra_aes_ops_available_key_slots(struct udevice *dev) return 4; /* 4 slots in Tegra20 and Tegra30 */ } +static int tegra_aes_ops_get_software_key_slot(struct udevice *dev) +{ + /* Avoid SBK slot 0, which may hold a preloaded key. */ + return TEGRA_AES_SLOT_SBK + 1; +} + static int tegra_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot) { struct tegra_aes_priv *priv = dev_get_priv(dev); @@ -565,6 +571,7 @@ static int tegra_aes_probe(struct udevice *dev) static const struct aes_ops tegra_aes_ops = { .available_key_slots = tegra_aes_ops_available_key_slots, + .get_software_key_slot = tegra_aes_ops_get_software_key_slot, .select_key_slot = tegra_aes_ops_select_key_slot, .set_key_for_key_slot = tegra_aes_ops_set_key_for_key_slot, .aes_ecb_encrypt = tegra_aes_ops_aes_ecb_encrypt, diff --git a/include/uboot_aes.h b/include/uboot_aes.h index 65a9b382843..a2c35aa30ce 100644 --- a/include/uboot_aes.h +++ b/include/uboot_aes.h @@ -128,11 +128,17 @@ struct udevice; * Note that some devices like Tegra AES engine may contain preloaded keys by bootrom, * thus in those cases the set_key_for_key_slot() may be skipped. * - * Sequence for a series of AES CBC encryption, one decryption and a CMAC hash example - * with 128bits key at slot 0 would be as follow: + * Generic callers which load a software-provided key should first ask the + * driver for a suitable software key slot. This lets hardware drivers avoid + * reserved or preloaded slots while keeping the slot policy local to the + * provider. * - * set_key_for_key_slot(DEV, 128, KEY, 0); - * select_key_slot(DEV, 128, 0); + * Sequence for a series of AES CBC encryption, one decryption and a CMAC hash + * example with a 128-bit software-provided key would be as follows: + * + * slot = get_software_key_slot(DEV); + * set_key_for_key_slot(DEV, 128, KEY, slot); + * select_key_slot(DEV, 128, slot); * aes_cbc_encrypt(DEV, IV1, SRC1, DST1, LEN1); * aes_cbc_encrypt(DEV, IV2, SRC2, DST2, LEN2); * aes_cbc_decrypt(DEV, IV3, SRC3, DST3, LEN3); @@ -146,6 +152,16 @@ struct aes_ops { */ int (*available_key_slots)(struct udevice *dev); + /** + * get_software_key_slot() - Get a slot for software-provided keys + * + * @dev The AES udevice + * @return Key slot to use for a software-provided key, + * 0 or positive on success, negative value on + * failure + */ + int (*get_software_key_slot)(struct udevice *dev); + /** * select_key_slot() - Selects the AES key slot to use for following operations * @@ -210,6 +226,7 @@ struct aes_ops { * @iv Initialization vector * @src Source data of length 'num_aes_blocks' blocks * @dst Destination data of length 'num_aes_blocks' blocks + * Must support dst == src for in-place decrypt * @num_aes_blocks Number of AES blocks to encrypt/decrypt * @return 0 on success, negative value on failure */ @@ -229,6 +246,15 @@ struct aes_ops { */ int dm_aes_get_available_key_slots(struct udevice *dev); +/** + * dm_aes_get_software_key_slot - Get a slot for software-provided keys + * + * @dev The AES udevice + * Return: Key slot to use for a software-provided key, + * 0 or positive on success, -ve on failure + */ +int dm_aes_get_software_key_slot(struct udevice *dev); + /** * dm_aes_select_key_slot - Selects the AES key slot to use for following operations * @@ -291,11 +317,31 @@ int dm_aes_cbc_encrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_ae * @iv Initialization vector * @src Source data of length 'num_aes_blocks' blocks * @dst Destination data of length 'num_aes_blocks' blocks + * Must support dst == src for in-place decrypt * @num_aes_blocks Number of AES blocks to encrypt/decrypt * Return: 0 on success, negative value on failure */ int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_aes_blocks); +/** + * dm_aes_cbc_decrypt_with_key() - Decrypt using a software-provided key + * + * Probe AES providers in order and use the first one which accepts the key + * and CBC operation. Hard failures from an accepting provider are returned + * without trying another provider. + * + * @key_size: AES key size in bits + * @key: AES key + * @iv: Initialization vector + * @src: Ciphertext input + * @dst: Plaintext output, which may be the same buffer as @src + * @num_aes_blocks: Number of AES blocks to decrypt + * Return: 0 on success, -ENODEV if there are no providers, -EOPNOTSUPP if no + * provider supports the operation, or another error from a provider + */ +int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv, u8 *src, + u8 *dst, u32 num_aes_blocks); + /** * dm_aes_cmac - Hashes the input data with AES-CMAC, putting the result into dst. * The key slot must be selected already. @@ -316,6 +362,11 @@ static inline int dm_aes_get_available_key_slots(struct udevice *dev) return -ENOSYS; } +static inline int dm_aes_get_software_key_slot(struct udevice *dev) +{ + return -ENOSYS; +} + static inline int dm_aes_select_key_slot(struct udevice *dev, u32 key_size, u8 slot) { return -ENOSYS; @@ -351,6 +402,13 @@ static inline int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, return -ENOSYS; } +static inline int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv, + u8 *src, u8 *dst, + u32 num_aes_blocks) +{ + return -ENOSYS; +} + static inline int dm_aes_cmac(struct udevice *dev, u8 *src, u8 *dst, u32 num_aes_blocks) { return -ENOSYS; diff --git a/test/dm/aes.c b/test/dm/aes.c index 9c85fb1dac9..9f4ce42ab2a 100644 --- a/test/dm/aes.c +++ b/test/dm/aes.c @@ -6,7 +6,10 @@ */ #include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> #include <dm/test.h> +#include <dm/uclass-internal.h> #include <uboot_aes.h> #include <test/test.h> #include <test/ut.h> @@ -162,3 +165,149 @@ static int dm_test_aes_key_sizes(struct unit_test_state *uts) } DM_TEST(dm_test_aes_key_sizes, UTF_SCAN_FDT); + +static int unsupported_calls; +static int success_calls; +static int hard_error_calls; + +static int aes_test_get_slot(struct udevice *dev) +{ + return 0; +} + +static int aes_test_set_key(struct udevice *dev, u32 key_size, u8 *key, + u8 slot) +{ + return 0; +} + +static int aes_test_select_key(struct udevice *dev, u32 key_size, u8 slot) +{ + return 0; +} + +static int aes_test_unsupported_slot(struct udevice *dev) +{ + unsupported_calls++; + + return -ENOSYS; +} + +static int aes_test_success_decrypt(struct udevice *dev, u8 *iv, u8 *src, + u8 *dst, u32 num_aes_blocks) +{ + success_calls++; + memcpy(dst, src, num_aes_blocks * AES_BLOCK_LENGTH); + + return 0; +} + +static int aes_test_hard_error_decrypt(struct udevice *dev, u8 *iv, u8 *src, + u8 *dst, u32 num_aes_blocks) +{ + hard_error_calls++; + + return -EINVAL; +} + +static const struct aes_ops aes_test_unsupported_ops = { + .get_software_key_slot = aes_test_unsupported_slot, +}; + +static const struct aes_ops aes_test_success_ops = { + .get_software_key_slot = aes_test_get_slot, + .set_key_for_key_slot = aes_test_set_key, + .select_key_slot = aes_test_select_key, + .aes_cbc_decrypt = aes_test_success_decrypt, +}; + +static const struct aes_ops aes_test_hard_error_ops = { + .get_software_key_slot = aes_test_get_slot, + .set_key_for_key_slot = aes_test_set_key, + .select_key_slot = aes_test_select_key, + .aes_cbc_decrypt = aes_test_hard_error_decrypt, +}; + +U_BOOT_DRIVER(aes_test_unsupported_drv) = { + .name = "aes_test_unsupported", + .id = UCLASS_AES, + .ops = &aes_test_unsupported_ops, +}; + +U_BOOT_DRIVER(aes_test_success_drv) = { + .name = "aes_test_success", + .id = UCLASS_AES, + .ops = &aes_test_success_ops, +}; + +U_BOOT_DRIVER(aes_test_hard_error_drv) = { + .name = "aes_test_hard_error", + .id = UCLASS_AES, + .ops = &aes_test_hard_error_ops, +}; + +static int aes_test_unbind_all(void) +{ + struct udevice *dev; + int ret; + + for (;;) { + ret = uclass_find_first_device(UCLASS_AES, &dev); + if (ret || !dev) + return ret; + if (device_active(dev)) { + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) + return ret; + } + ret = device_unbind(dev); + if (ret) + return ret; + } +} + +static int aes_test_bind(const struct driver *drv, const char *name) +{ + struct udevice *dev; + + return device_bind(dm_root(), drv, name, 0, ofnode_null(), &dev); +} + +static int dm_test_aes_provider_selection(struct unit_test_state *uts) +{ + u8 key[AES128_KEY_LENGTH] = { }; + u8 iv[AES_BLOCK_LENGTH] = { }; + u8 src[AES_BLOCK_LENGTH] = { 0x5a }; + u8 dst[AES_BLOCK_LENGTH] = { }; + int ret; + + ut_assertok(aes_test_unbind_all()); + ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_unsupported_drv), + "aes-unsupported")); + ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_success_drv), + "aes-success")); + + unsupported_calls = 0; + success_calls = 0; + ut_assertok(dm_aes_cbc_decrypt_with_key(128, key, iv, src, dst, 1)); + ut_asserteq(1, unsupported_calls); + ut_asserteq(1, success_calls); + ut_asserteq_mem(src, dst, sizeof(src)); + + ut_assertok(aes_test_unbind_all()); + ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_hard_error_drv), + "aes-hard-error")); + ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_success_drv), + "aes-success")); + + hard_error_calls = 0; + success_calls = 0; + ret = dm_aes_cbc_decrypt_with_key(128, key, iv, src, dst, 1); + ut_asserteq(-EINVAL, ret); + ut_asserteq(1, hard_error_calls); + ut_asserteq(0, success_calls); + + return 0; +} + +DM_TEST(dm_test_aes_provider_selection, UTF_SCAN_FDT); -- 2.53.0

