2016-05-12 17:51:24 +03:00
|
|
|
/*
|
|
|
|
* CDDL HEADER START
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the terms of the
|
|
|
|
* Common Development and Distribution License (the "License").
|
|
|
|
* You may not use this file except in compliance with the License.
|
|
|
|
*
|
|
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
2022-07-12 00:16:13 +03:00
|
|
|
* or https://opensource.org/licenses/CDDL-1.0.
|
2016-05-12 17:51:24 +03:00
|
|
|
* See the License for the specific language governing permissions
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
|
|
*
|
|
|
|
* CDDL HEADER END
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AES provider for the Kernel Cryptographic Framework (KCF)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/zfs_context.h>
|
|
|
|
#include <sys/crypto/common.h>
|
|
|
|
#include <sys/crypto/impl.h>
|
|
|
|
#include <sys/crypto/spi.h>
|
|
|
|
#include <sys/crypto/icp.h>
|
|
|
|
#include <modes/modes.h>
|
|
|
|
#define _AES_IMPL
|
|
|
|
#include <aes/aes_impl.h>
|
2018-08-02 21:59:24 +03:00
|
|
|
#include <modes/gcm_impl.h>
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mechanism info structure passed to KCF during registration.
|
|
|
|
*/
|
2022-01-15 02:37:55 +03:00
|
|
|
static const crypto_mech_info_t aes_mech_info_tab[] = {
|
2016-05-12 17:51:24 +03:00
|
|
|
/* AES_CCM */
|
|
|
|
{SUN_CKM_AES_CCM, AES_CCM_MECH_INFO_TYPE,
|
|
|
|
CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
|
2021-12-27 04:39:55 +03:00
|
|
|
CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC},
|
2016-05-12 17:51:24 +03:00
|
|
|
/* AES_GCM */
|
|
|
|
{SUN_CKM_AES_GCM, AES_GCM_MECH_INFO_TYPE,
|
|
|
|
CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
|
2021-12-27 04:39:55 +03:00
|
|
|
CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC},
|
2016-05-12 17:51:24 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int aes_encrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_key_t *, crypto_spi_ctx_template_t);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_decrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_key_t *, crypto_spi_ctx_template_t);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_common_init(crypto_ctx_t *, crypto_mechanism_t *,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_key_t *, crypto_spi_ctx_template_t, boolean_t);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_common_init_ctx(aes_ctx_t *, crypto_spi_ctx_template_t *,
|
|
|
|
crypto_mechanism_t *, crypto_key_t *, int, boolean_t);
|
2021-12-25 06:34:29 +03:00
|
|
|
static int aes_encrypt_final(crypto_ctx_t *, crypto_data_t *);
|
|
|
|
static int aes_decrypt_final(crypto_ctx_t *, crypto_data_t *);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
static int aes_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_encrypt_update(crypto_ctx_t *, crypto_data_t *,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *);
|
2021-12-27 04:53:32 +03:00
|
|
|
static int aes_encrypt_atomic(crypto_mechanism_t *, crypto_key_t *,
|
|
|
|
crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
static int aes_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_decrypt_update(crypto_ctx_t *, crypto_data_t *,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *);
|
2021-12-27 04:53:32 +03:00
|
|
|
static int aes_decrypt_atomic(crypto_mechanism_t *, crypto_key_t *,
|
|
|
|
crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2022-01-15 02:37:55 +03:00
|
|
|
static const crypto_cipher_ops_t aes_cipher_ops = {
|
2017-11-29 02:33:48 +03:00
|
|
|
.encrypt_init = aes_encrypt_init,
|
|
|
|
.encrypt = aes_encrypt,
|
|
|
|
.encrypt_update = aes_encrypt_update,
|
|
|
|
.encrypt_final = aes_encrypt_final,
|
|
|
|
.encrypt_atomic = aes_encrypt_atomic,
|
|
|
|
.decrypt_init = aes_decrypt_init,
|
|
|
|
.decrypt = aes_decrypt,
|
|
|
|
.decrypt_update = aes_decrypt_update,
|
|
|
|
.decrypt_final = aes_decrypt_final,
|
|
|
|
.decrypt_atomic = aes_decrypt_atomic
|
2016-05-12 17:51:24 +03:00
|
|
|
};
|
|
|
|
|
2021-12-27 04:32:37 +03:00
|
|
|
static int aes_create_ctx_template(crypto_mechanism_t *, crypto_key_t *,
|
|
|
|
crypto_spi_ctx_template_t *, size_t *);
|
2016-05-12 17:51:24 +03:00
|
|
|
static int aes_free_context(crypto_ctx_t *);
|
|
|
|
|
2022-01-15 02:37:55 +03:00
|
|
|
static const crypto_ctx_ops_t aes_ctx_ops = {
|
2017-11-29 02:33:48 +03:00
|
|
|
.create_ctx_template = aes_create_ctx_template,
|
|
|
|
.free_context = aes_free_context
|
2016-05-12 17:51:24 +03:00
|
|
|
};
|
|
|
|
|
2021-12-23 00:09:28 +03:00
|
|
|
static const crypto_ops_t aes_crypto_ops = {
|
2016-05-12 17:51:24 +03:00
|
|
|
NULL,
|
|
|
|
&aes_cipher_ops,
|
2024-05-18 14:57:36 +03:00
|
|
|
NULL,
|
2021-12-23 01:29:25 +03:00
|
|
|
&aes_ctx_ops,
|
2021-12-23 00:09:28 +03:00
|
|
|
};
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2021-12-23 00:09:28 +03:00
|
|
|
static const crypto_provider_info_t aes_prov_info = {
|
2016-05-12 17:51:24 +03:00
|
|
|
"AES Software Provider",
|
|
|
|
&aes_crypto_ops,
|
2022-01-15 02:37:55 +03:00
|
|
|
sizeof (aes_mech_info_tab) / sizeof (crypto_mech_info_t),
|
2016-05-12 17:51:24 +03:00
|
|
|
aes_mech_info_tab
|
2021-12-23 00:09:28 +03:00
|
|
|
};
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
static crypto_kcf_provider_handle_t aes_prov_handle = 0;
|
|
|
|
|
|
|
|
int
|
|
|
|
aes_mod_init(void)
|
|
|
|
{
|
2019-10-24 20:17:33 +03:00
|
|
|
/* Determine the fastest available implementation. */
|
|
|
|
aes_impl_init();
|
|
|
|
gcm_impl_init();
|
2018-08-02 21:59:24 +03:00
|
|
|
|
2016-05-12 17:51:24 +03:00
|
|
|
/* Register with KCF. If the registration fails, remove the module. */
|
2021-12-22 17:27:43 +03:00
|
|
|
if (crypto_register_provider(&aes_prov_info, &aes_prov_handle))
|
2016-05-12 17:51:24 +03:00
|
|
|
return (EACCES);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
aes_mod_fini(void)
|
|
|
|
{
|
|
|
|
/* Unregister from KCF if module is registered */
|
|
|
|
if (aes_prov_handle != 0) {
|
|
|
|
if (crypto_unregister_provider(aes_prov_handle))
|
|
|
|
return (EBUSY);
|
|
|
|
|
|
|
|
aes_prov_handle = 0;
|
|
|
|
}
|
|
|
|
|
2021-12-22 17:27:43 +03:00
|
|
|
return (0);
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-12-25 06:34:29 +03:00
|
|
|
aes_check_mech_param(crypto_mechanism_t *mechanism, aes_ctx_t **ctx)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
void *p = NULL;
|
|
|
|
boolean_t param_required = B_TRUE;
|
|
|
|
size_t param_len;
|
|
|
|
void *(*alloc_fun)(int);
|
|
|
|
int rv = CRYPTO_SUCCESS;
|
|
|
|
|
|
|
|
switch (mechanism->cm_type) {
|
|
|
|
case AES_CCM_MECH_INFO_TYPE:
|
|
|
|
param_len = sizeof (CK_AES_CCM_PARAMS);
|
|
|
|
alloc_fun = ccm_alloc_ctx;
|
|
|
|
break;
|
|
|
|
case AES_GCM_MECH_INFO_TYPE:
|
|
|
|
param_len = sizeof (CK_AES_GCM_PARAMS);
|
|
|
|
alloc_fun = gcm_alloc_ctx;
|
|
|
|
break;
|
|
|
|
default:
|
2024-05-18 14:57:36 +03:00
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
if (param_required && mechanism->cm_param != NULL &&
|
|
|
|
mechanism->cm_param_len != param_len) {
|
|
|
|
rv = CRYPTO_MECHANISM_PARAM_INVALID;
|
|
|
|
}
|
|
|
|
if (ctx != NULL) {
|
2021-12-25 06:34:29 +03:00
|
|
|
p = (alloc_fun)(KM_SLEEP);
|
2016-05-12 17:51:24 +03:00
|
|
|
*ctx = p;
|
|
|
|
}
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize key schedules for AES
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
init_keysched(crypto_key_t *key, void *newbie)
|
|
|
|
{
|
2021-12-25 05:23:07 +03:00
|
|
|
if (key->ck_length < AES_MINBITS ||
|
|
|
|
key->ck_length > AES_MAXBITS) {
|
|
|
|
return (CRYPTO_KEY_SIZE_RANGE);
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
2021-12-25 05:23:07 +03:00
|
|
|
/* key length must be either 128, 192, or 256 */
|
|
|
|
if ((key->ck_length & 63) != 0)
|
|
|
|
return (CRYPTO_KEY_SIZE_RANGE);
|
|
|
|
|
2016-05-12 17:51:24 +03:00
|
|
|
aes_init_keysched(key->ck_data, key->ck_length, newbie);
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_encrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_key_t *key, crypto_spi_ctx_template_t template)
|
2017-01-21 00:17:55 +03:00
|
|
|
{
|
2021-12-25 06:34:29 +03:00
|
|
|
return (aes_common_init(ctx, mechanism, key, template, B_TRUE));
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_decrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_key_t *key, crypto_spi_ctx_template_t template)
|
2017-01-21 00:17:55 +03:00
|
|
|
{
|
2021-12-25 06:34:29 +03:00
|
|
|
return (aes_common_init(ctx, mechanism, key, template, B_FALSE));
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* KCF software provider encrypt entry points.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
aes_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
|
|
|
|
crypto_key_t *key, crypto_spi_ctx_template_t template,
|
2021-12-25 06:34:29 +03:00
|
|
|
boolean_t is_encrypt_init)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
int rv;
|
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
if ((rv = aes_check_mech_param(mechanism, &aes_ctx))
|
2016-05-12 17:51:24 +03:00
|
|
|
!= CRYPTO_SUCCESS)
|
|
|
|
return (rv);
|
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
rv = aes_common_init_ctx(aes_ctx, template, mechanism, key, KM_SLEEP,
|
2016-05-12 17:51:24 +03:00
|
|
|
is_encrypt_init);
|
|
|
|
if (rv != CRYPTO_SUCCESS) {
|
|
|
|
crypto_free_mode_ctx(aes_ctx);
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->cc_provider_private = aes_ctx;
|
|
|
|
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *ciphertext)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
int ret = CRYPTO_FAILED;
|
|
|
|
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
size_t saved_length, saved_offset, length_needed;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(ciphertext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to just return the length needed to store the output.
|
|
|
|
* We should not destroy the context for the following case.
|
|
|
|
*/
|
2024-05-18 14:57:36 +03:00
|
|
|
switch (aes_ctx->ac_flags & (CCM_MODE|GCM_MODE)) {
|
2016-05-12 17:51:24 +03:00
|
|
|
case CCM_MODE:
|
|
|
|
length_needed = plaintext->cd_length + aes_ctx->ac_mac_len;
|
|
|
|
break;
|
|
|
|
case GCM_MODE:
|
|
|
|
length_needed = plaintext->cd_length + aes_ctx->ac_tag_len;
|
|
|
|
break;
|
|
|
|
default:
|
2024-05-18 14:57:36 +03:00
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ciphertext->cd_length < length_needed) {
|
|
|
|
ciphertext->cd_length = length_needed;
|
|
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_length = ciphertext->cd_length;
|
|
|
|
saved_offset = ciphertext->cd_offset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do an update on the specified input data.
|
|
|
|
*/
|
2021-12-25 06:34:29 +03:00
|
|
|
ret = aes_encrypt_update(ctx, plaintext, ciphertext);
|
2016-05-12 17:51:24 +03:00
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For CCM mode, aes_ccm_encrypt_final() will take care of any
|
|
|
|
* left-over unprocessed data, and compute the MAC
|
|
|
|
*/
|
|
|
|
if (aes_ctx->ac_flags & CCM_MODE) {
|
|
|
|
/*
|
|
|
|
* ccm_encrypt_final() will compute the MAC and append
|
|
|
|
* it to existing ciphertext. So, need to adjust the left over
|
|
|
|
* length value accordingly
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* order of following 2 lines MUST not be reversed */
|
|
|
|
ciphertext->cd_offset = ciphertext->cd_length;
|
|
|
|
ciphertext->cd_length = saved_length - ciphertext->cd_length;
|
|
|
|
ret = ccm_encrypt_final((ccm_ctx_t *)aes_ctx, ciphertext,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plaintext != ciphertext) {
|
|
|
|
ciphertext->cd_length =
|
|
|
|
ciphertext->cd_offset - saved_offset;
|
|
|
|
}
|
|
|
|
ciphertext->cd_offset = saved_offset;
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (aes_ctx->ac_flags & GCM_MODE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
/*
|
|
|
|
* gcm_encrypt_final() will compute the MAC and append
|
|
|
|
* it to existing ciphertext. So, need to adjust the left over
|
|
|
|
* length value accordingly
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* order of following 2 lines MUST not be reversed */
|
|
|
|
ciphertext->cd_offset = ciphertext->cd_length;
|
|
|
|
ciphertext->cd_length = saved_length - ciphertext->cd_length;
|
|
|
|
ret = gcm_encrypt_final((gcm_ctx_t *)aes_ctx, ciphertext,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block,
|
|
|
|
aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plaintext != ciphertext) {
|
|
|
|
ciphertext->cd_length =
|
|
|
|
ciphertext->cd_offset - saved_offset;
|
|
|
|
}
|
|
|
|
ciphertext->cd_offset = saved_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(aes_ctx->ac_remainder_len == 0);
|
|
|
|
(void) aes_free_context(ctx);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *plaintext)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
int ret = CRYPTO_FAILED;
|
|
|
|
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
off_t saved_offset;
|
|
|
|
size_t saved_length, length_needed;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(plaintext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return length needed to store the output.
|
|
|
|
* Do not destroy context when plaintext buffer is too small.
|
|
|
|
*
|
|
|
|
* CCM: plaintext is MAC len smaller than cipher text
|
|
|
|
* GCM: plaintext is TAG len smaller than cipher text
|
|
|
|
*/
|
2024-05-18 14:57:36 +03:00
|
|
|
switch (aes_ctx->ac_flags & (CCM_MODE|GCM_MODE)) {
|
2016-05-12 17:51:24 +03:00
|
|
|
case CCM_MODE:
|
|
|
|
length_needed = aes_ctx->ac_processed_data_len;
|
|
|
|
break;
|
|
|
|
case GCM_MODE:
|
|
|
|
length_needed = ciphertext->cd_length - aes_ctx->ac_tag_len;
|
|
|
|
break;
|
|
|
|
default:
|
2024-05-18 14:57:36 +03:00
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (plaintext->cd_length < length_needed) {
|
|
|
|
plaintext->cd_length = length_needed;
|
|
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_offset = plaintext->cd_offset;
|
|
|
|
saved_length = plaintext->cd_length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do an update on the specified input data.
|
|
|
|
*/
|
2021-12-25 06:34:29 +03:00
|
|
|
ret = aes_decrypt_update(ctx, ciphertext, plaintext);
|
2016-05-12 17:51:24 +03:00
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aes_ctx->ac_flags & CCM_MODE) {
|
|
|
|
ASSERT(aes_ctx->ac_processed_data_len == aes_ctx->ac_data_len);
|
|
|
|
ASSERT(aes_ctx->ac_processed_mac_len == aes_ctx->ac_mac_len);
|
|
|
|
|
|
|
|
/* order of following 2 lines MUST not be reversed */
|
|
|
|
plaintext->cd_offset = plaintext->cd_length;
|
|
|
|
plaintext->cd_length = saved_length - plaintext->cd_length;
|
|
|
|
|
|
|
|
ret = ccm_decrypt_final((ccm_ctx_t *)aes_ctx, plaintext,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block,
|
|
|
|
aes_xor_block);
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (plaintext != ciphertext) {
|
|
|
|
plaintext->cd_length =
|
|
|
|
plaintext->cd_offset - saved_offset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
plaintext->cd_offset = saved_offset;
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (aes_ctx->ac_flags & GCM_MODE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
/* order of following 2 lines MUST not be reversed */
|
|
|
|
plaintext->cd_offset = plaintext->cd_length;
|
|
|
|
plaintext->cd_length = saved_length - plaintext->cd_length;
|
|
|
|
|
|
|
|
ret = gcm_decrypt_final((gcm_ctx_t *)aes_ctx, plaintext,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block);
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (plaintext != ciphertext) {
|
|
|
|
plaintext->cd_length =
|
|
|
|
plaintext->cd_offset - saved_offset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
plaintext->cd_offset = saved_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(aes_ctx->ac_remainder_len == 0);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
(void) aes_free_context(ctx);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_encrypt_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *ciphertext)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
off_t saved_offset;
|
|
|
|
size_t saved_length, out_len;
|
|
|
|
int ret = CRYPTO_SUCCESS;
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(ciphertext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
/* compute number of bytes that will hold the ciphertext */
|
|
|
|
out_len = aes_ctx->ac_remainder_len;
|
|
|
|
out_len += plaintext->cd_length;
|
|
|
|
out_len &= ~(AES_BLOCK_LEN - 1);
|
|
|
|
|
|
|
|
/* return length needed to store the output */
|
|
|
|
if (ciphertext->cd_length < out_len) {
|
|
|
|
ciphertext->cd_length = out_len;
|
|
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_offset = ciphertext->cd_offset;
|
|
|
|
saved_length = ciphertext->cd_length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the AES update on the specified input data.
|
|
|
|
*/
|
|
|
|
switch (plaintext->cd_format) {
|
|
|
|
case CRYPTO_DATA_RAW:
|
|
|
|
ret = crypto_update_iov(ctx->cc_provider_private,
|
2021-12-25 05:33:19 +03:00
|
|
|
plaintext, ciphertext, aes_encrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
case CRYPTO_DATA_UIO:
|
|
|
|
ret = crypto_update_uio(ctx->cc_provider_private,
|
2021-12-25 05:33:19 +03:00
|
|
|
plaintext, ciphertext, aes_encrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = CRYPTO_ARGUMENTS_BAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (plaintext != ciphertext)
|
|
|
|
ciphertext->cd_length =
|
|
|
|
ciphertext->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
ciphertext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
ciphertext->cd_offset = saved_offset;
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_decrypt_update(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_data_t *plaintext)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
off_t saved_offset;
|
2024-05-18 14:57:36 +03:00
|
|
|
size_t saved_length;
|
2016-05-12 17:51:24 +03:00
|
|
|
int ret = CRYPTO_SUCCESS;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(plaintext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
saved_offset = plaintext->cd_offset;
|
|
|
|
saved_length = plaintext->cd_length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the AES update on the specified input data.
|
|
|
|
*/
|
|
|
|
switch (ciphertext->cd_format) {
|
|
|
|
case CRYPTO_DATA_RAW:
|
|
|
|
ret = crypto_update_iov(ctx->cc_provider_private,
|
2021-12-25 05:33:19 +03:00
|
|
|
ciphertext, plaintext, aes_decrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
case CRYPTO_DATA_UIO:
|
|
|
|
ret = crypto_update_uio(ctx->cc_provider_private,
|
2021-12-25 05:33:19 +03:00
|
|
|
ciphertext, plaintext, aes_decrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = CRYPTO_ARGUMENTS_BAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (ciphertext != plaintext)
|
|
|
|
plaintext->cd_length =
|
|
|
|
plaintext->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
plaintext->cd_offset = saved_offset;
|
|
|
|
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-12-25 06:34:29 +03:00
|
|
|
aes_encrypt_final(crypto_ctx_t *ctx, crypto_data_t *data)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
|
|
|
if (data->cd_format != CRYPTO_DATA_RAW &&
|
|
|
|
data->cd_format != CRYPTO_DATA_UIO) {
|
|
|
|
return (CRYPTO_ARGUMENTS_BAD);
|
|
|
|
}
|
|
|
|
|
2024-05-18 14:57:36 +03:00
|
|
|
if (aes_ctx->ac_flags & CCM_MODE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
ret = ccm_encrypt_final((ccm_ctx_t *)aes_ctx, data,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (aes_ctx->ac_flags & GCM_MODE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
size_t saved_offset = data->cd_offset;
|
|
|
|
|
|
|
|
ret = gcm_encrypt_final((gcm_ctx_t *)aes_ctx, data,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block,
|
|
|
|
aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
data->cd_length = data->cd_offset - saved_offset;
|
|
|
|
data->cd_offset = saved_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) aes_free_context(ctx);
|
|
|
|
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-12-25 06:34:29 +03:00
|
|
|
aes_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *data)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
aes_ctx_t *aes_ctx;
|
|
|
|
int ret;
|
|
|
|
off_t saved_offset;
|
|
|
|
size_t saved_length;
|
|
|
|
|
|
|
|
ASSERT(ctx->cc_provider_private != NULL);
|
|
|
|
aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
|
|
|
if (data->cd_format != CRYPTO_DATA_RAW &&
|
|
|
|
data->cd_format != CRYPTO_DATA_UIO) {
|
|
|
|
return (CRYPTO_ARGUMENTS_BAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There must be no unprocessed ciphertext.
|
|
|
|
* This happens if the length of the last ciphertext is
|
|
|
|
* not a multiple of the AES block length.
|
|
|
|
*/
|
2024-05-18 14:57:36 +03:00
|
|
|
if (aes_ctx->ac_remainder_len > 0)
|
|
|
|
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
if (aes_ctx->ac_flags & CCM_MODE) {
|
|
|
|
/*
|
|
|
|
* This is where all the plaintext is returned, make sure
|
|
|
|
* the plaintext buffer is big enough
|
|
|
|
*/
|
|
|
|
size_t pt_len = aes_ctx->ac_data_len;
|
|
|
|
if (data->cd_length < pt_len) {
|
|
|
|
data->cd_length = pt_len;
|
|
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(aes_ctx->ac_processed_data_len == pt_len);
|
|
|
|
ASSERT(aes_ctx->ac_processed_mac_len == aes_ctx->ac_mac_len);
|
|
|
|
saved_offset = data->cd_offset;
|
|
|
|
saved_length = data->cd_length;
|
|
|
|
ret = ccm_decrypt_final((ccm_ctx_t *)aes_ctx, data,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block,
|
|
|
|
aes_xor_block);
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
data->cd_length = data->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
data->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->cd_offset = saved_offset;
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (aes_ctx->ac_flags & GCM_MODE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
/*
|
|
|
|
* This is where all the plaintext is returned, make sure
|
|
|
|
* the plaintext buffer is big enough
|
|
|
|
*/
|
|
|
|
gcm_ctx_t *ctx = (gcm_ctx_t *)aes_ctx;
|
|
|
|
size_t pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len;
|
|
|
|
|
|
|
|
if (data->cd_length < pt_len) {
|
|
|
|
data->cd_length = pt_len;
|
|
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_offset = data->cd_offset;
|
|
|
|
saved_length = data->cd_length;
|
|
|
|
ret = gcm_decrypt_final((gcm_ctx_t *)aes_ctx, data,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block);
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
data->cd_length = data->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
data->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->cd_offset = saved_offset;
|
|
|
|
if (ret != CRYPTO_SUCCESS) {
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
(void) aes_free_context(ctx);
|
|
|
|
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-12-27 04:53:32 +03:00
|
|
|
aes_encrypt_atomic(crypto_mechanism_t *mechanism,
|
2016-05-12 17:51:24 +03:00
|
|
|
crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_spi_ctx_template_t template)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
Use memset to zero stack allocations containing unions
C99 6.7.8.17 says that when an undesignated initialiser is used, only
the first element of a union is initialised. If the first element is not
the largest within the union, how the remaining space is initialised is
up to the compiler.
GCC extends the initialiser to the entire union, while Clang treats the
remainder as padding, and so initialises according to whatever
automatic/implicit initialisation rules are currently active.
When Linux is compiled with CONFIG_INIT_STACK_ALL_PATTERN,
-ftrivial-auto-var-init=pattern is added to the kernel CFLAGS. This flag
sets the policy for automatic/implicit initialisation of variables on
the stack.
Taken together, this means that when compiling under
CONFIG_INIT_STACK_ALL_PATTERN on Clang, the "zero" initialiser will only
zero the first element in a union, and the rest will be filled with a
pattern. This is significant for aes_ctx_t, which in
aes_encrypt_atomic() and aes_decrypt_atomic() is initialised to zero,
but then used as a gcm_ctx_t, which is the fifth element in the union,
and thus gets pattern initialisation. Later, it's assumed to be zero,
resulting in a hang.
As confusing and undiscoverable as it is, by the spec, we are at fault
when we initialise a structure containing a union with the zero
initializer. As such, this commit replaces these uses with an explicit
memset(0).
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16135
Closes #16206
2024-05-25 05:00:29 +03:00
|
|
|
aes_ctx_t aes_ctx;
|
2016-05-12 17:51:24 +03:00
|
|
|
off_t saved_offset;
|
|
|
|
size_t saved_length;
|
|
|
|
size_t length_needed;
|
|
|
|
int ret;
|
|
|
|
|
Use memset to zero stack allocations containing unions
C99 6.7.8.17 says that when an undesignated initialiser is used, only
the first element of a union is initialised. If the first element is not
the largest within the union, how the remaining space is initialised is
up to the compiler.
GCC extends the initialiser to the entire union, while Clang treats the
remainder as padding, and so initialises according to whatever
automatic/implicit initialisation rules are currently active.
When Linux is compiled with CONFIG_INIT_STACK_ALL_PATTERN,
-ftrivial-auto-var-init=pattern is added to the kernel CFLAGS. This flag
sets the policy for automatic/implicit initialisation of variables on
the stack.
Taken together, this means that when compiling under
CONFIG_INIT_STACK_ALL_PATTERN on Clang, the "zero" initialiser will only
zero the first element in a union, and the rest will be filled with a
pattern. This is significant for aes_ctx_t, which in
aes_encrypt_atomic() and aes_decrypt_atomic() is initialised to zero,
but then used as a gcm_ctx_t, which is the fifth element in the union,
and thus gets pattern initialisation. Later, it's assumed to be zero,
resulting in a hang.
As confusing and undiscoverable as it is, by the spec, we are at fault
when we initialise a structure containing a union with the zero
initializer. As such, this commit replaces these uses with an explicit
memset(0).
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16135
Closes #16206
2024-05-25 05:00:29 +03:00
|
|
|
memset(&aes_ctx, 0, sizeof (aes_ctx_t));
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(ciphertext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
if ((ret = aes_check_mech_param(mechanism, NULL)) != CRYPTO_SUCCESS)
|
2016-05-12 17:51:24 +03:00
|
|
|
return (ret);
|
|
|
|
|
|
|
|
ret = aes_common_init_ctx(&aes_ctx, template, mechanism, key,
|
2021-12-25 06:34:29 +03:00
|
|
|
KM_SLEEP, B_TRUE);
|
2016-05-12 17:51:24 +03:00
|
|
|
if (ret != CRYPTO_SUCCESS)
|
|
|
|
return (ret);
|
|
|
|
|
|
|
|
switch (mechanism->cm_type) {
|
|
|
|
case AES_CCM_MECH_INFO_TYPE:
|
|
|
|
length_needed = plaintext->cd_length + aes_ctx.ac_mac_len;
|
|
|
|
break;
|
|
|
|
case AES_GCM_MECH_INFO_TYPE:
|
|
|
|
length_needed = plaintext->cd_length + aes_ctx.ac_tag_len;
|
|
|
|
break;
|
|
|
|
default:
|
2024-05-18 14:57:36 +03:00
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* return size of buffer needed to store output */
|
|
|
|
if (ciphertext->cd_length < length_needed) {
|
|
|
|
ciphertext->cd_length = length_needed;
|
|
|
|
ret = CRYPTO_BUFFER_TOO_SMALL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_offset = ciphertext->cd_offset;
|
|
|
|
saved_length = ciphertext->cd_length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do an update on the specified input data.
|
|
|
|
*/
|
|
|
|
switch (plaintext->cd_format) {
|
|
|
|
case CRYPTO_DATA_RAW:
|
|
|
|
ret = crypto_update_iov(&aes_ctx, plaintext, ciphertext,
|
2021-12-25 05:33:19 +03:00
|
|
|
aes_encrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
case CRYPTO_DATA_UIO:
|
|
|
|
ret = crypto_update_uio(&aes_ctx, plaintext, ciphertext,
|
2021-12-25 05:33:19 +03:00
|
|
|
aes_encrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = CRYPTO_ARGUMENTS_BAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (mechanism->cm_type == AES_CCM_MECH_INFO_TYPE) {
|
|
|
|
ret = ccm_encrypt_final((ccm_ctx_t *)&aes_ctx,
|
|
|
|
ciphertext, AES_BLOCK_LEN, aes_encrypt_block,
|
|
|
|
aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS)
|
|
|
|
goto out;
|
|
|
|
ASSERT(aes_ctx.ac_remainder_len == 0);
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (mechanism->cm_type == AES_GCM_MECH_INFO_TYPE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
ret = gcm_encrypt_final((gcm_ctx_t *)&aes_ctx,
|
|
|
|
ciphertext, AES_BLOCK_LEN, aes_encrypt_block,
|
|
|
|
aes_copy_block, aes_xor_block);
|
|
|
|
if (ret != CRYPTO_SUCCESS)
|
|
|
|
goto out;
|
|
|
|
ASSERT(aes_ctx.ac_remainder_len == 0);
|
|
|
|
} else {
|
|
|
|
ASSERT(aes_ctx.ac_remainder_len == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plaintext != ciphertext) {
|
|
|
|
ciphertext->cd_length =
|
|
|
|
ciphertext->cd_offset - saved_offset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ciphertext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
ciphertext->cd_offset = saved_offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (aes_ctx.ac_flags & PROVIDER_OWNS_KEY_SCHEDULE) {
|
2022-02-25 16:26:54 +03:00
|
|
|
memset(aes_ctx.ac_keysched, 0, aes_ctx.ac_keysched_len);
|
2016-05-12 17:51:24 +03:00
|
|
|
kmem_free(aes_ctx.ac_keysched, aes_ctx.ac_keysched_len);
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
if (aes_ctx.ac_flags & GCM_MODE) {
|
2023-02-28 01:38:12 +03:00
|
|
|
gcm_clear_ctx((gcm_ctx_t *)&aes_ctx);
|
2020-10-31 01:24:21 +03:00
|
|
|
}
|
2016-05-12 17:51:24 +03:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-12-27 04:53:32 +03:00
|
|
|
aes_decrypt_atomic(crypto_mechanism_t *mechanism,
|
2016-05-12 17:51:24 +03:00
|
|
|
crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_spi_ctx_template_t template)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
Use memset to zero stack allocations containing unions
C99 6.7.8.17 says that when an undesignated initialiser is used, only
the first element of a union is initialised. If the first element is not
the largest within the union, how the remaining space is initialised is
up to the compiler.
GCC extends the initialiser to the entire union, while Clang treats the
remainder as padding, and so initialises according to whatever
automatic/implicit initialisation rules are currently active.
When Linux is compiled with CONFIG_INIT_STACK_ALL_PATTERN,
-ftrivial-auto-var-init=pattern is added to the kernel CFLAGS. This flag
sets the policy for automatic/implicit initialisation of variables on
the stack.
Taken together, this means that when compiling under
CONFIG_INIT_STACK_ALL_PATTERN on Clang, the "zero" initialiser will only
zero the first element in a union, and the rest will be filled with a
pattern. This is significant for aes_ctx_t, which in
aes_encrypt_atomic() and aes_decrypt_atomic() is initialised to zero,
but then used as a gcm_ctx_t, which is the fifth element in the union,
and thus gets pattern initialisation. Later, it's assumed to be zero,
resulting in a hang.
As confusing and undiscoverable as it is, by the spec, we are at fault
when we initialise a structure containing a union with the zero
initializer. As such, this commit replaces these uses with an explicit
memset(0).
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16135
Closes #16206
2024-05-25 05:00:29 +03:00
|
|
|
aes_ctx_t aes_ctx;
|
2016-05-12 17:51:24 +03:00
|
|
|
off_t saved_offset;
|
|
|
|
size_t saved_length;
|
|
|
|
size_t length_needed;
|
|
|
|
int ret;
|
|
|
|
|
Use memset to zero stack allocations containing unions
C99 6.7.8.17 says that when an undesignated initialiser is used, only
the first element of a union is initialised. If the first element is not
the largest within the union, how the remaining space is initialised is
up to the compiler.
GCC extends the initialiser to the entire union, while Clang treats the
remainder as padding, and so initialises according to whatever
automatic/implicit initialisation rules are currently active.
When Linux is compiled with CONFIG_INIT_STACK_ALL_PATTERN,
-ftrivial-auto-var-init=pattern is added to the kernel CFLAGS. This flag
sets the policy for automatic/implicit initialisation of variables on
the stack.
Taken together, this means that when compiling under
CONFIG_INIT_STACK_ALL_PATTERN on Clang, the "zero" initialiser will only
zero the first element in a union, and the rest will be filled with a
pattern. This is significant for aes_ctx_t, which in
aes_encrypt_atomic() and aes_decrypt_atomic() is initialised to zero,
but then used as a gcm_ctx_t, which is the fifth element in the union,
and thus gets pattern initialisation. Later, it's assumed to be zero,
resulting in a hang.
As confusing and undiscoverable as it is, by the spec, we are at fault
when we initialise a structure containing a union with the zero
initializer. As such, this commit replaces these uses with an explicit
memset(0).
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16135
Closes #16206
2024-05-25 05:00:29 +03:00
|
|
|
memset(&aes_ctx, 0, sizeof (aes_ctx_t));
|
|
|
|
|
2020-03-26 20:41:57 +03:00
|
|
|
ASSERT(plaintext != NULL);
|
2016-05-12 17:51:24 +03:00
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
if ((ret = aes_check_mech_param(mechanism, NULL)) != CRYPTO_SUCCESS)
|
2016-05-12 17:51:24 +03:00
|
|
|
return (ret);
|
|
|
|
|
|
|
|
ret = aes_common_init_ctx(&aes_ctx, template, mechanism, key,
|
2021-12-25 06:34:29 +03:00
|
|
|
KM_SLEEP, B_FALSE);
|
2016-05-12 17:51:24 +03:00
|
|
|
if (ret != CRYPTO_SUCCESS)
|
|
|
|
return (ret);
|
|
|
|
|
|
|
|
switch (mechanism->cm_type) {
|
|
|
|
case AES_CCM_MECH_INFO_TYPE:
|
|
|
|
length_needed = aes_ctx.ac_data_len;
|
|
|
|
break;
|
|
|
|
case AES_GCM_MECH_INFO_TYPE:
|
|
|
|
length_needed = ciphertext->cd_length - aes_ctx.ac_tag_len;
|
|
|
|
break;
|
|
|
|
default:
|
2024-05-18 14:57:36 +03:00
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* return size of buffer needed to store output */
|
|
|
|
if (plaintext->cd_length < length_needed) {
|
|
|
|
plaintext->cd_length = length_needed;
|
|
|
|
ret = CRYPTO_BUFFER_TOO_SMALL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_offset = plaintext->cd_offset;
|
|
|
|
saved_length = plaintext->cd_length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do an update on the specified input data.
|
|
|
|
*/
|
|
|
|
switch (ciphertext->cd_format) {
|
|
|
|
case CRYPTO_DATA_RAW:
|
|
|
|
ret = crypto_update_iov(&aes_ctx, ciphertext, plaintext,
|
2021-12-25 05:33:19 +03:00
|
|
|
aes_decrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
case CRYPTO_DATA_UIO:
|
|
|
|
ret = crypto_update_uio(&aes_ctx, ciphertext, plaintext,
|
2021-12-25 05:33:19 +03:00
|
|
|
aes_decrypt_contiguous_blocks);
|
2016-05-12 17:51:24 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = CRYPTO_ARGUMENTS_BAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == CRYPTO_SUCCESS) {
|
|
|
|
if (mechanism->cm_type == AES_CCM_MECH_INFO_TYPE) {
|
|
|
|
ASSERT(aes_ctx.ac_processed_data_len
|
|
|
|
== aes_ctx.ac_data_len);
|
|
|
|
ASSERT(aes_ctx.ac_processed_mac_len
|
|
|
|
== aes_ctx.ac_mac_len);
|
|
|
|
ret = ccm_decrypt_final((ccm_ctx_t *)&aes_ctx,
|
|
|
|
plaintext, AES_BLOCK_LEN, aes_encrypt_block,
|
|
|
|
aes_copy_block, aes_xor_block);
|
|
|
|
ASSERT(aes_ctx.ac_remainder_len == 0);
|
|
|
|
if ((ret == CRYPTO_SUCCESS) &&
|
|
|
|
(ciphertext != plaintext)) {
|
|
|
|
plaintext->cd_length =
|
|
|
|
plaintext->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (mechanism->cm_type == AES_GCM_MECH_INFO_TYPE) {
|
2016-05-12 17:51:24 +03:00
|
|
|
ret = gcm_decrypt_final((gcm_ctx_t *)&aes_ctx,
|
|
|
|
plaintext, AES_BLOCK_LEN, aes_encrypt_block,
|
|
|
|
aes_xor_block);
|
|
|
|
ASSERT(aes_ctx.ac_remainder_len == 0);
|
|
|
|
if ((ret == CRYPTO_SUCCESS) &&
|
|
|
|
(ciphertext != plaintext)) {
|
|
|
|
plaintext->cd_length =
|
|
|
|
plaintext->cd_offset - saved_offset;
|
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
} else
|
|
|
|
__builtin_unreachable();
|
2016-05-12 17:51:24 +03:00
|
|
|
} else {
|
|
|
|
plaintext->cd_length = saved_length;
|
|
|
|
}
|
|
|
|
plaintext->cd_offset = saved_offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (aes_ctx.ac_flags & PROVIDER_OWNS_KEY_SCHEDULE) {
|
2022-02-25 16:26:54 +03:00
|
|
|
memset(aes_ctx.ac_keysched, 0, aes_ctx.ac_keysched_len);
|
2016-05-12 17:51:24 +03:00
|
|
|
kmem_free(aes_ctx.ac_keysched, aes_ctx.ac_keysched_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aes_ctx.ac_flags & CCM_MODE) {
|
|
|
|
if (aes_ctx.ac_pt_buf != NULL) {
|
|
|
|
vmem_free(aes_ctx.ac_pt_buf, aes_ctx.ac_data_len);
|
|
|
|
}
|
2024-05-18 14:57:36 +03:00
|
|
|
} else if (aes_ctx.ac_flags & GCM_MODE) {
|
2023-02-28 01:38:12 +03:00
|
|
|
gcm_clear_ctx((gcm_ctx_t *)&aes_ctx);
|
2016-05-12 17:51:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* KCF software provider context template entry points.
|
|
|
|
*/
|
|
|
|
static int
|
2021-12-27 04:32:37 +03:00
|
|
|
aes_create_ctx_template(crypto_mechanism_t *mechanism, crypto_key_t *key,
|
2021-12-25 06:34:29 +03:00
|
|
|
crypto_spi_ctx_template_t *tmpl, size_t *tmpl_size)
|
2016-05-12 17:51:24 +03:00
|
|
|
{
|
|
|
|
void *keysched;
|
|
|
|
size_t size;
|
|
|
|
int rv;
|
|
|
|
|
2024-05-18 14:57:36 +03:00
|
|
|
if (mechanism->cm_type != AES_CCM_MECH_INFO_TYPE &&
|
|
|
|
mechanism->cm_type != AES_GCM_MECH_INFO_TYPE)
|
2016-05-12 17:51:24 +03:00
|
|
|
return (CRYPTO_MECHANISM_INVALID);
|
|
|
|
|
2021-12-25 06:34:29 +03:00
|
|
|
if ((keysched = aes_alloc_keysched(&size, KM_SLEEP)) == NULL) {
|
2016-05-12 17:51:24 +03:00
|
|
|
return (CRYPTO_HOST_MEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize key schedule. Key length information is stored
|
|
|
|
* in the key.
|
|
|
|
*/
|
|
|
|
if ((rv = init_keysched(key, keysched)) != CRYPTO_SUCCESS) {
|
2022-02-25 16:26:54 +03:00
|
|
|
memset(keysched, 0, size);
|
2016-05-12 17:51:24 +03:00
|
|
|
kmem_free(keysched, size);
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
*tmpl = keysched;
|
|
|
|
*tmpl_size = size;
|
|
|
|
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_free_context(crypto_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
aes_ctx_t *aes_ctx = ctx->cc_provider_private;
|
|
|
|
|
|
|
|
if (aes_ctx != NULL) {
|
|
|
|
if (aes_ctx->ac_flags & PROVIDER_OWNS_KEY_SCHEDULE) {
|
|
|
|
ASSERT(aes_ctx->ac_keysched_len != 0);
|
2022-02-25 16:26:54 +03:00
|
|
|
memset(aes_ctx->ac_keysched, 0,
|
|
|
|
aes_ctx->ac_keysched_len);
|
2016-05-12 17:51:24 +03:00
|
|
|
kmem_free(aes_ctx->ac_keysched,
|
|
|
|
aes_ctx->ac_keysched_len);
|
|
|
|
}
|
|
|
|
crypto_free_mode_ctx(aes_ctx);
|
|
|
|
ctx->cc_provider_private = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
aes_common_init_ctx(aes_ctx_t *aes_ctx, crypto_spi_ctx_template_t *template,
|
|
|
|
crypto_mechanism_t *mechanism, crypto_key_t *key, int kmflag,
|
|
|
|
boolean_t is_encrypt_init)
|
|
|
|
{
|
|
|
|
int rv = CRYPTO_SUCCESS;
|
|
|
|
void *keysched;
|
2016-10-05 04:15:57 +03:00
|
|
|
size_t size = 0;
|
2016-05-12 17:51:24 +03:00
|
|
|
|
|
|
|
if (template == NULL) {
|
|
|
|
if ((keysched = aes_alloc_keysched(&size, kmflag)) == NULL)
|
|
|
|
return (CRYPTO_HOST_MEMORY);
|
|
|
|
/*
|
|
|
|
* Initialize key schedule.
|
|
|
|
* Key length is stored in the key.
|
|
|
|
*/
|
|
|
|
if ((rv = init_keysched(key, keysched)) != CRYPTO_SUCCESS) {
|
|
|
|
kmem_free(keysched, size);
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
aes_ctx->ac_flags |= PROVIDER_OWNS_KEY_SCHEDULE;
|
|
|
|
aes_ctx->ac_keysched_len = size;
|
|
|
|
} else {
|
|
|
|
keysched = template;
|
|
|
|
}
|
|
|
|
aes_ctx->ac_keysched = keysched;
|
|
|
|
|
|
|
|
switch (mechanism->cm_type) {
|
|
|
|
case AES_CCM_MECH_INFO_TYPE:
|
|
|
|
if (mechanism->cm_param == NULL ||
|
|
|
|
mechanism->cm_param_len != sizeof (CK_AES_CCM_PARAMS)) {
|
|
|
|
return (CRYPTO_MECHANISM_PARAM_INVALID);
|
|
|
|
}
|
|
|
|
rv = ccm_init_ctx((ccm_ctx_t *)aes_ctx, mechanism->cm_param,
|
|
|
|
kmflag, is_encrypt_init, AES_BLOCK_LEN, aes_encrypt_block,
|
|
|
|
aes_xor_block);
|
|
|
|
break;
|
|
|
|
case AES_GCM_MECH_INFO_TYPE:
|
|
|
|
if (mechanism->cm_param == NULL ||
|
|
|
|
mechanism->cm_param_len != sizeof (CK_AES_GCM_PARAMS)) {
|
|
|
|
return (CRYPTO_MECHANISM_PARAM_INVALID);
|
|
|
|
}
|
|
|
|
rv = gcm_init_ctx((gcm_ctx_t *)aes_ctx, mechanism->cm_param,
|
|
|
|
AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block,
|
|
|
|
aes_xor_block);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rv != CRYPTO_SUCCESS) {
|
|
|
|
if (aes_ctx->ac_flags & PROVIDER_OWNS_KEY_SCHEDULE) {
|
2022-02-25 16:26:54 +03:00
|
|
|
memset(keysched, 0, size);
|
2016-05-12 17:51:24 +03:00
|
|
|
kmem_free(keysched, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|