Path: blob/main/sys/contrib/openzfs/module/icp/algs/aes/aes_impl.c
48775 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.23*/2425#include <sys/zfs_context.h>26#include <sys/crypto/icp.h>27#include <sys/crypto/spi.h>28#include <sys/simd.h>29#include <modes/modes.h>30#include <aes/aes_impl.h>3132/*33* Initialize AES encryption and decryption key schedules.34*35* Parameters:36* cipherKey User key37* keyBits AES key size (128, 192, or 256 bits)38* keysched AES key schedule to be initialized, of type aes_key_t.39* Allocated by aes_alloc_keysched().40*/41void42aes_init_keysched(const uint8_t *cipherKey, uint_t keyBits, void *keysched)43{44const aes_impl_ops_t *ops = aes_impl_get_ops();45aes_key_t *newbie = keysched;46uint_t keysize, i, j;47union {48uint64_t ka64[4];49uint32_t ka32[8];50} keyarr;5152switch (keyBits) {53case 128:54newbie->nr = 10;55break;5657case 192:58newbie->nr = 12;59break;6061case 256:62newbie->nr = 14;63break;6465default:66/* should never get here */67return;68}69keysize = CRYPTO_BITS2BYTES(keyBits);7071/*72* Generic C implementation requires byteswap for little endian73* machines, various accelerated implementations for various74* architectures may not.75*/76if (!ops->needs_byteswap) {77/* no byteswap needed */78if (IS_P2ALIGNED(cipherKey, sizeof (uint64_t))) {79for (i = 0, j = 0; j < keysize; i++, j += 8) {80/* LINTED: pointer alignment */81keyarr.ka64[i] = *((uint64_t *)&cipherKey[j]);82}83} else {84memcpy(keyarr.ka32, cipherKey, keysize);85}86} else {87/* byte swap */88for (i = 0, j = 0; j < keysize; i++, j += 4) {89keyarr.ka32[i] =90htonl(*(uint32_t *)(void *)&cipherKey[j]);91}92}9394ops->generate(newbie, keyarr.ka32, keyBits);95newbie->ops = ops;9697/*98* Note: if there are systems that need the AES_64BIT_KS type in the99* future, move setting key schedule type to individual implementations100*/101newbie->type = AES_32BIT_KS;102}103104105/*106* Encrypt one block using AES.107* Align if needed and (for x86 32-bit only) byte-swap.108*109* Parameters:110* ks Key schedule, of type aes_key_t111* pt Input block (plain text)112* ct Output block (crypto text). Can overlap with pt113*/114int115aes_encrypt_block(const void *ks, const uint8_t *pt, uint8_t *ct)116{117aes_key_t *ksch = (aes_key_t *)ks;118const aes_impl_ops_t *ops = ksch->ops;119120if (IS_P2ALIGNED2(pt, ct, sizeof (uint32_t)) && !ops->needs_byteswap) {121/* LINTED: pointer alignment */122ops->encrypt(&ksch->encr_ks.ks32[0], ksch->nr,123/* LINTED: pointer alignment */124(uint32_t *)pt, (uint32_t *)ct);125} else {126uint32_t buffer[AES_BLOCK_LEN / sizeof (uint32_t)];127128/* Copy input block into buffer */129if (ops->needs_byteswap) {130buffer[0] = htonl(*(uint32_t *)(void *)&pt[0]);131buffer[1] = htonl(*(uint32_t *)(void *)&pt[4]);132buffer[2] = htonl(*(uint32_t *)(void *)&pt[8]);133buffer[3] = htonl(*(uint32_t *)(void *)&pt[12]);134} else135memcpy(&buffer, pt, AES_BLOCK_LEN);136137ops->encrypt(&ksch->encr_ks.ks32[0], ksch->nr, buffer, buffer);138139/* Copy result from buffer to output block */140if (ops->needs_byteswap) {141*(uint32_t *)(void *)&ct[0] = htonl(buffer[0]);142*(uint32_t *)(void *)&ct[4] = htonl(buffer[1]);143*(uint32_t *)(void *)&ct[8] = htonl(buffer[2]);144*(uint32_t *)(void *)&ct[12] = htonl(buffer[3]);145} else146memcpy(ct, &buffer, AES_BLOCK_LEN);147}148return (CRYPTO_SUCCESS);149}150151152/*153* Decrypt one block using AES.154* Align and byte-swap if needed.155*156* Parameters:157* ks Key schedule, of type aes_key_t158* ct Input block (crypto text)159* pt Output block (plain text). Can overlap with pt160*/161int162aes_decrypt_block(const void *ks, const uint8_t *ct, uint8_t *pt)163{164aes_key_t *ksch = (aes_key_t *)ks;165const aes_impl_ops_t *ops = ksch->ops;166167if (IS_P2ALIGNED2(ct, pt, sizeof (uint32_t)) && !ops->needs_byteswap) {168/* LINTED: pointer alignment */169ops->decrypt(&ksch->decr_ks.ks32[0], ksch->nr,170/* LINTED: pointer alignment */171(uint32_t *)ct, (uint32_t *)pt);172} else {173uint32_t buffer[AES_BLOCK_LEN / sizeof (uint32_t)];174175/* Copy input block into buffer */176if (ops->needs_byteswap) {177buffer[0] = htonl(*(uint32_t *)(void *)&ct[0]);178buffer[1] = htonl(*(uint32_t *)(void *)&ct[4]);179buffer[2] = htonl(*(uint32_t *)(void *)&ct[8]);180buffer[3] = htonl(*(uint32_t *)(void *)&ct[12]);181} else182memcpy(&buffer, ct, AES_BLOCK_LEN);183184ops->decrypt(&ksch->decr_ks.ks32[0], ksch->nr, buffer, buffer);185186/* Copy result from buffer to output block */187if (ops->needs_byteswap) {188*(uint32_t *)(void *)&pt[0] = htonl(buffer[0]);189*(uint32_t *)(void *)&pt[4] = htonl(buffer[1]);190*(uint32_t *)(void *)&pt[8] = htonl(buffer[2]);191*(uint32_t *)(void *)&pt[12] = htonl(buffer[3]);192} else193memcpy(pt, &buffer, AES_BLOCK_LEN);194}195return (CRYPTO_SUCCESS);196}197198199/*200* Allocate key schedule for AES.201*202* Return the pointer and set size to the number of bytes allocated.203* Memory allocated must be freed by the caller when done.204*205* Parameters:206* size Size of key schedule allocated, in bytes207* kmflag Flag passed to kmem_alloc(9F); ignored in userland.208*/209void *210aes_alloc_keysched(size_t *size, int kmflag)211{212aes_key_t *keysched;213214keysched = kmem_alloc(sizeof (aes_key_t), kmflag);215if (keysched != NULL) {216*size = sizeof (aes_key_t);217return (keysched);218}219return (NULL);220}221222/* AES implementation that contains the fastest methods */223static aes_impl_ops_t aes_fastest_impl = {224.name = "fastest"225};226227/* All compiled in implementations */228static const aes_impl_ops_t *aes_all_impl[] = {229&aes_generic_impl,230#if defined(__x86_64)231&aes_x86_64_impl,232#endif233#if defined(__x86_64) && defined(HAVE_AES)234&aes_aesni_impl,235#endif236};237238/* Indicate that benchmark has been completed */239static boolean_t aes_impl_initialized = B_FALSE;240241/* Select aes implementation */242#define IMPL_FASTEST (UINT32_MAX)243#define IMPL_CYCLE (UINT32_MAX-1)244245#define AES_IMPL_READ(i) (*(volatile uint32_t *) &(i))246247static uint32_t icp_aes_impl = IMPL_FASTEST;248static uint32_t user_sel_impl = IMPL_FASTEST;249250/* Hold all supported implementations */251static size_t aes_supp_impl_cnt = 0;252static aes_impl_ops_t *aes_supp_impl[ARRAY_SIZE(aes_all_impl)];253254/*255* Returns the AES operations for encrypt/decrypt/key setup. When a256* SIMD implementation is not allowed in the current context, then257* fallback to the fastest generic implementation.258*/259const aes_impl_ops_t *260aes_impl_get_ops(void)261{262if (!kfpu_allowed())263return (&aes_generic_impl);264265const aes_impl_ops_t *ops = NULL;266const uint32_t impl = AES_IMPL_READ(icp_aes_impl);267268switch (impl) {269case IMPL_FASTEST:270ASSERT(aes_impl_initialized);271ops = &aes_fastest_impl;272break;273case IMPL_CYCLE:274/* Cycle through supported implementations */275ASSERT(aes_impl_initialized);276ASSERT3U(aes_supp_impl_cnt, >, 0);277static size_t cycle_impl_idx = 0;278size_t idx = (++cycle_impl_idx) % aes_supp_impl_cnt;279ops = aes_supp_impl[idx];280break;281default:282ASSERT3U(impl, <, aes_supp_impl_cnt);283ASSERT3U(aes_supp_impl_cnt, >, 0);284if (impl < ARRAY_SIZE(aes_all_impl))285ops = aes_supp_impl[impl];286break;287}288289ASSERT3P(ops, !=, NULL);290291return (ops);292}293294/*295* Initialize all supported implementations.296*/297void298aes_impl_init(void)299{300aes_impl_ops_t *curr_impl;301int i, c;302303/* Move supported implementations into aes_supp_impls */304for (i = 0, c = 0; i < ARRAY_SIZE(aes_all_impl); i++) {305curr_impl = (aes_impl_ops_t *)aes_all_impl[i];306307if (curr_impl->is_supported())308aes_supp_impl[c++] = (aes_impl_ops_t *)curr_impl;309}310aes_supp_impl_cnt = c;311312/*313* Set the fastest implementation given the assumption that the314* hardware accelerated version is the fastest.315*/316#if defined(__x86_64)317#if defined(HAVE_AES)318if (aes_aesni_impl.is_supported()) {319memcpy(&aes_fastest_impl, &aes_aesni_impl,320sizeof (aes_fastest_impl));321} else322#endif323{324memcpy(&aes_fastest_impl, &aes_x86_64_impl,325sizeof (aes_fastest_impl));326}327#else328memcpy(&aes_fastest_impl, &aes_generic_impl,329sizeof (aes_fastest_impl));330#endif331332strlcpy(aes_fastest_impl.name, "fastest", AES_IMPL_NAME_MAX);333334/* Finish initialization */335atomic_swap_32(&icp_aes_impl, user_sel_impl);336aes_impl_initialized = B_TRUE;337}338339static const struct {340const char *name;341uint32_t sel;342} aes_impl_opts[] = {343{ "cycle", IMPL_CYCLE },344{ "fastest", IMPL_FASTEST },345};346347/*348* Function sets desired aes implementation.349*350* If we are called before init(), user preference will be saved in351* user_sel_impl, and applied in later init() call. This occurs when module352* parameter is specified on module load. Otherwise, directly update353* icp_aes_impl.354*355* @val Name of aes implementation to use356* @param Unused.357*/358int359aes_impl_set(const char *val)360{361int err = -EINVAL;362char req_name[AES_IMPL_NAME_MAX];363uint32_t impl = AES_IMPL_READ(user_sel_impl);364size_t i;365366/* sanitize input */367i = strnlen(val, AES_IMPL_NAME_MAX);368if (i == 0 || i >= AES_IMPL_NAME_MAX)369return (err);370371strlcpy(req_name, val, AES_IMPL_NAME_MAX);372while (i > 0 && isspace(req_name[i-1]))373i--;374req_name[i] = '\0';375376/* Check mandatory options */377for (i = 0; i < ARRAY_SIZE(aes_impl_opts); i++) {378if (strcmp(req_name, aes_impl_opts[i].name) == 0) {379impl = aes_impl_opts[i].sel;380err = 0;381break;382}383}384385/* check all supported impl if init() was already called */386if (err != 0 && aes_impl_initialized) {387/* check all supported implementations */388for (i = 0; i < aes_supp_impl_cnt; i++) {389if (strcmp(req_name, aes_supp_impl[i]->name) == 0) {390impl = i;391err = 0;392break;393}394}395}396397if (err == 0) {398if (aes_impl_initialized)399atomic_swap_32(&icp_aes_impl, impl);400else401atomic_swap_32(&user_sel_impl, impl);402}403404return (err);405}406407#if defined(_KERNEL) && defined(__linux__)408409static int410icp_aes_impl_set(const char *val, zfs_kernel_param_t *kp)411{412return (aes_impl_set(val));413}414415static int416icp_aes_impl_get(char *buffer, zfs_kernel_param_t *kp)417{418int i, cnt = 0;419char *fmt;420const uint32_t impl = AES_IMPL_READ(icp_aes_impl);421422/* list mandatory options */423for (i = 0; i < ARRAY_SIZE(aes_impl_opts); i++) {424fmt = (impl == aes_impl_opts[i].sel) ? "[%s] " : "%s ";425cnt += kmem_scnprintf(buffer + cnt, PAGE_SIZE - cnt, fmt,426aes_impl_opts[i].name);427}428429/* list all supported implementations */430for (i = 0; i < aes_supp_impl_cnt; i++) {431fmt = (i == impl) ? "[%s] " : "%s ";432cnt += kmem_scnprintf(buffer + cnt, PAGE_SIZE - cnt, fmt,433aes_supp_impl[i]->name);434}435436return (cnt);437}438439module_param_call(icp_aes_impl, icp_aes_impl_set, icp_aes_impl_get,440NULL, 0644);441MODULE_PARM_DESC(icp_aes_impl, "Select aes implementation.");442#endif443444445