diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 5edebd83ef..9596b04520 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -752,6 +752,7 @@ WC_ASYNC_NO_SHA384 WC_ASYNC_NO_SHA512 WC_ASYNC_NO_X25519 WC_ASYNC_THREAD_BIND +WC_BENCH_AES_IV_SWEEP WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS WC_CACHE_RESISTANT_BASE64_TABLE WC_DISABLE_RADIX_ZERO_PAD @@ -841,6 +842,7 @@ WOLFSSL_ATECC_TNGTLS WOLFSSL_ATMEL WOLFSSL_ATMEL_TIME WOLFSSL_BEFORE_DATE_CLOCK_SKEW +WOLFSSL_BENCH_ECC_ALL WOLFSSL_BENCH_RSA_PAD WOLFSSL_BIGINT_TYPES WOLFSSL_BIO_NO_FLOW_STATS @@ -1121,6 +1123,14 @@ WOLFSSL_VAULTIC_DEBUG WOLFSSL_VERSAL_GEN2_ASU WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED +WOLFSSL_VERSAL_GEN2_ASU_DEBUG +WOLFSSL_VERSAL_GEN2_ASU_ECC_P521 +WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR +WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT +WOLFSSL_VERSAL_GEN2_ASU_NO_ECDH +WOLFSSL_VERSAL_GEN2_ASU_NO_ECIES +WOLFSSL_VERSAL_GEN2_ASU_NO_ED25519 +WOLFSSL_VERSAL_GEN2_ASU_NO_ED448 WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD WOLFSSL_VERSAL_GEN2_ASU_RTC WOLFSSL_VERSAL_GEN2_ASU_TRNG_DIRECT diff --git a/IDE/XilinxSDK/README.md b/IDE/XilinxSDK/README.md index 5f12012a04..0bbcf844ad 100644 --- a/IDE/XilinxSDK/README.md +++ b/IDE/XilinxSDK/README.md @@ -1,3 +1,9 @@ +# Vitis Unified IDE (2023.2+) + +For the Vitis Unified IDE (system device tree flow), wolfSSL can be added to +a BSP as an embedded software library — see `vitis_sdt/README.md` in this +directory. The instructions below apply to the classic Vitis/SDK IDE. + # Common Gotcha's - If compiling all code together (ie no separate wolfssl library) then the -fPIC compiler flag should be used. Without using -fPIC in this build setup there could be unexpected failures. diff --git a/IDE/XilinxSDK/vitis_sdt/README.md b/IDE/XilinxSDK/vitis_sdt/README.md new file mode 100644 index 0000000000..bcb44aa91b --- /dev/null +++ b/IDE/XilinxSDK/vitis_sdt/README.md @@ -0,0 +1,41 @@ +# wolfSSL library for the Vitis Unified IDE (SDT flow) + +Packages wolfSSL as an embedded software library for the Vitis Unified +IDE (2023.2 and later, system device tree flow). Once the repository is +added, wolfssl appears as a library checkbox in a standalone domain's +BSP settings. + +## Adding wolfSSL to a Vitis application + +1. Add `/IDE/XilinxSDK/vitis_sdt` as an embedded software + repository (Vitis > Embedded SW Repositories). Do this before + creating the platform. + +2. Open your platform, pick a standalone domain, open its BSP settings + > Libraries, and enable wolfssl. + +3. Build the platform. The first build creates a starter + `user_settings.h` at `//bsp/user_settings.h`, next + to `bsp.yaml`. Edit that file to configure wolfSSL, then rebuild the + platform; it is never overwritten. All wolfSSL feature choices + happen in this one file. + +4. Create the application: File > New Component > Application, same + platform and domain. It links the library on its own. Include + headers as usual: + + ```c + #include + #include + ``` + +5. Build and run. Give the application a large heap in `lscript.ld` + (8 MB or more for the wolfCrypt self-test). + +## Library options (BSP settings GUI) + +- `wolfssl_user_settings_path` (default empty). Absolute path to your + `user_settings.h`. Leave empty to use the one in the domain's BSP + directory. +- `wolfssl_source_path` (default empty). Path to the wolfSSL checkout. + Leave empty for auto-detect. diff --git a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/data/wolfssl.yaml b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/data/wolfssl.yaml new file mode 100644 index 0000000000..86fce27328 --- /dev/null +++ b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/data/wolfssl.yaml @@ -0,0 +1,16 @@ +%YAML 1.2 +--- +# Copyright (C) 2006-2026 wolfSSL Inc. All rights reserved. +# SPDX-License-Identifier: GPL-3.0-or-later +title: Bindings for the wolfSSL library. +maintainers: + - wolfSSL Inc. +type: library +description: wolfSSL embedded TLS and wolfCrypt cryptography library +supported_processors: + - condition: | + supported_processors = ["cortexa78", "psx_cortexa78", "psv_cortexa72", + "psu_cortexa53", "cortexr52", "psx_cortexr52", "psv_cortexr5", + "psu_cortexr5", "microblaze"] +supported_os: + - standalone diff --git a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/CMakeLists.txt b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/CMakeLists.txt new file mode 100644 index 0000000000..cf15dd4163 --- /dev/null +++ b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/CMakeLists.txt @@ -0,0 +1,286 @@ +# Copyright (C) 2006-2026 wolfSSL Inc. All rights reserved. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Builds the wolfssl library for the Vitis Unified IDE (SDT flow). The +# sources compile in place from the checkout; nothing is copied or +# listed by hand. See IDE/XilinxSDK/vitis_sdt/README.md. + +cmake_minimum_required(VERSION 3.15) +project(wolfssl) + +find_package(common) + +include(${CMAKE_CURRENT_SOURCE_DIR}/wolfssl.cmake NO_POLICY_SCOPE) + +# Find the wolfSSL checkout: the wolfssl_source_path option, then the +# path recorded in lib_list.yaml, then walking up from this directory, +# then WOLFSSL_ROOT. Fix Windows backslashes first: Vitis passes the +# path options with backslashes, which CMake reads as escapes like \f. +if(wolfssl_source_path) + file(TO_CMAKE_PATH "${wolfssl_source_path}" wolfssl_source_path) +endif() +if(wolfssl_user_settings_path) + file(TO_CMAKE_PATH "${wolfssl_user_settings_path}" wolfssl_user_settings_path) +endif() +if(wolfssl_source_path AND EXISTS ${wolfssl_source_path}/wolfcrypt/src) + set(WOLFSSL_REPO_ROOT ${wolfssl_source_path}) +endif() +if(NOT DEFINED WOLFSSL_REPO_ROOT AND EXISTS ${CMAKE_SOURCE_DIR}/lib_list.yaml) + file(READ ${CMAKE_SOURCE_DIR}/lib_list.yaml _wolfssl_lib_list) + string(REGEX MATCH "wolfssl:.*" _wolfssl_lib_entry "${_wolfssl_lib_list}") + string(REGEX MATCH "path:[ \t\r\n]+-[ \t]+([^\r\n]+)" + _wolfssl_path_match "${_wolfssl_lib_entry}") + if(CMAKE_MATCH_1) + file(TO_CMAKE_PATH "${CMAKE_MATCH_1}" _wolfssl_pkg_path) + # The package sits at IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl + get_filename_component(_wolfssl_candidate + "${_wolfssl_pkg_path}/../../../../../.." ABSOLUTE) + if(EXISTS ${_wolfssl_candidate}/wolfcrypt/src) + set(WOLFSSL_REPO_ROOT ${_wolfssl_candidate}) + endif() + endif() +endif() +if(NOT DEFINED WOLFSSL_REPO_ROOT) + get_filename_component(_wolfssl_candidate + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../.. ABSOLUTE) + if(EXISTS ${_wolfssl_candidate}/wolfcrypt/src) + set(WOLFSSL_REPO_ROOT ${_wolfssl_candidate}) + elseif(DEFINED ENV{WOLFSSL_ROOT} AND EXISTS $ENV{WOLFSSL_ROOT}/wolfcrypt/src) + file(TO_CMAKE_PATH "$ENV{WOLFSSL_ROOT}" WOLFSSL_REPO_ROOT) + endif() +endif() +# A user_settings.h is required; the default spot is the domain's BSP +# directory (next to bsp.yaml), and wolfssl_user_settings_path overrides +# it. A missing file must not fail configure (that would undo adding the +# library), so instead a stub fails the compile with instructions. +set(_wolfssl_user_settings "") +if(wolfssl_user_settings_path) + set(_wolfssl_user_settings ${wolfssl_user_settings_path}) +elseif(EXISTS ${CMAKE_SOURCE_DIR}/user_settings.h) + set(_wolfssl_user_settings ${CMAKE_SOURCE_DIR}/user_settings.h) +elseif(EXISTS ${CMAKE_SOURCE_DIR}/bsp.yaml) + # First build with no settings anywhere: create a starter file for + # the user to edit. Made once, never overwritten. The bsp.yaml check + # makes sure this only happens inside a real BSP. + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/user_settings_template.h + ${CMAKE_SOURCE_DIR}/user_settings.h COPYONLY) + message(STATUS + "wolfssl: created starter user_settings.h in the BSP directory; " + "edit it for your project and rebuild the platform") + set(_wolfssl_user_settings ${CMAKE_SOURCE_DIR}/user_settings.h) +endif() + +set(_wolfssl_setup_error "") +if(NOT DEFINED WOLFSSL_REPO_ROOT) + string(CONCAT _wolfssl_setup_error + "wolfssl: checkout not located. Set the wolfssl_source_path BSP " + "option to the wolfSSL repository path and regenerate the BSP.") +elseif(NOT _wolfssl_user_settings) + string(CONCAT _wolfssl_setup_error + "wolfssl: user_settings.h required. Place your configuration file " + "at /bsp/user_settings.h (next to bsp.yaml), or set the " + "wolfssl_user_settings_path BSP option to its absolute path, then " + "regenerate the BSP.") +elseif(NOT EXISTS ${_wolfssl_user_settings}) + string(CONCAT _wolfssl_setup_error + "wolfssl: user_settings.h not found at ${_wolfssl_user_settings}. " + "Fix the wolfssl_user_settings_path BSP option and regenerate the " + "BSP.") +endif() +if(_wolfssl_setup_error) + message(WARNING "${_wolfssl_setup_error}") + set(_wolfssl_stub_src ${CMAKE_BINARY_DIR}/wolfssl_setup_error.c) + file(WRITE ${_wolfssl_stub_src} + "#error \"${_wolfssl_setup_error}\"\n") + add_library(wolfssl STATIC ${_wolfssl_stub_src}) + set_target_properties(wolfssl PROPERTIES LINKER_LANGUAGE C) + return() +endif() +message(STATUS "wolfSSL sources: ${WOLFSSL_REPO_ROOT}") +message(STATUS "wolfSSL user_settings: ${_wolfssl_user_settings}") +configure_file(${_wolfssl_user_settings} + ${CMAKE_BINARY_DIR}/include/user_settings.h COPYONLY) + +# Most wolfSSL C files compile to nothing when their feature is off, so +# the source lists below are simple. Assembly files cannot do that, so +# run the preprocessor on user_settings.h to learn which macros are on. +set(_wolfssl_probe_src ${CMAKE_BINARY_DIR}/wolfssl_config_probe.c) +file(WRITE ${_wolfssl_probe_src} + "#include \"user_settings.h\"\n" + "#ifdef WOLFSSL_ARMASM\n\"@CFG_ARMASM@\"\n#endif\n" + "#ifdef WOLFSSL_ARMASM_INLINE\n\"@CFG_ARMASM_INLINE@\"\n#endif\n" + "#ifdef HAVE_POLY1305\n\"@CFG_POLY1305@\"\n#endif\n" + "#ifdef HAVE_CHACHA\n\"@CFG_CHACHA@\"\n#endif\n") +# Give the probe the same includes and defines as the library target below. +# A user_settings.h that pulls in BSP headers has to preprocess here too. +set(_wolfssl_probe_inc) +foreach(_wolfssl_dir + ${CMAKE_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_INCLUDE_PATH} + ${WOLFSSL_REPO_ROOT}) + list(APPEND _wolfssl_probe_inc -I${_wolfssl_dir}) +endforeach() +execute_process( + COMMAND ${CMAKE_C_COMPILER} -E -P + -DWOLFSSL_USER_SETTINGS + ${_wolfssl_probe_inc} + ${_wolfssl_probe_src} + OUTPUT_VARIABLE _wolfssl_probe + RESULT_VARIABLE _wolfssl_probe_rc + ERROR_VARIABLE _wolfssl_probe_err) +if(NOT _wolfssl_probe_rc EQUAL 0) + message(FATAL_ERROR + "Could not preprocess the selected user_settings.h. Check the file " + "named by wolfssl_user_settings_path. Compiler output:\n" + "${_wolfssl_probe_err}") +endif() +foreach(_wolfssl_flag ARMASM ARMASM_INLINE POLY1305 CHACHA) + string(FIND "${_wolfssl_probe}" "@CFG_${_wolfssl_flag}@" _wolfssl_pos) + if(_wolfssl_pos EQUAL -1) + set(_wolfssl_cfg_${_wolfssl_flag} FALSE) + else() + set(_wolfssl_cfg_${_wolfssl_flag} TRUE) + endif() +endforeach() + +# wolfCrypt: build everything except files meant to be #include'd. +file(GLOB WOLFCRYPT_SOURCES ${WOLFSSL_REPO_ROOT}/wolfcrypt/src/*.c) +list(REMOVE_ITEM WOLFCRYPT_SOURCES + ${WOLFSSL_REPO_ROOT}/wolfcrypt/src/misc.c + ${WOLFSSL_REPO_ROOT}/wolfcrypt/src/evp.c) +set(_sources ${WOLFCRYPT_SOURCES}) + +# TLS layer. Only these files build on their own; the rest of src/ is +# pulled in by ssl.c. With WOLFCRYPT_ONLY they compile to nothing. +foreach(_tls_file ssl.c internal.c tls.c tls13.c keys.c wolfio.c crl.c + ocsp.c dtls.c dtls13.c quic.c) + if(EXISTS ${WOLFSSL_REPO_ROOT}/src/${_tls_file}) + list(APPEND _sources ${WOLFSSL_REPO_ROOT}/src/${_tls_file}) + endif() +endforeach() + +# wolfCrypt self-test and benchmark. Define NO_CRYPT_TEST or +# NO_CRYPT_BENCHMARK in user_settings.h to leave them out. +list(APPEND _sources + ${WOLFSSL_REPO_ROOT}/wolfcrypt/test/test.c + ${WOLFSSL_REPO_ROOT}/wolfcrypt/benchmark/benchmark.c) + +# Versal Gen 2 ASU hardware offload. The port files compile to nothing +# unless user_settings.h defines WOLFSSL_VERSAL_GEN2_ASU. +file(GLOB WOLFSSL_ASU_SOURCES + ${WOLFSSL_REPO_ROOT}/wolfcrypt/src/port/xilinx/versal_gen2_asu/*.c) +list(APPEND _sources ${WOLFSSL_ASU_SOURCES}) + +# ARM assembly: built when the settings define WOLFSSL_ARMASM and the +# CPU is AArch64. With WOLFSSL_ARMASM_INLINE the armv8-*_c.c files hold +# the assembly as inline C; without it the .S files are used instead. +set(_wolfssl_arm_dir ${WOLFSSL_REPO_ROOT}/wolfcrypt/src/port/arm) +set(_wolfssl_build_armasm_a64 FALSE) +if(_wolfssl_cfg_ARMASM) + if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES + "cortexa78|cortexa72|cortexa53|aarch64") + set(_wolfssl_build_armasm_a64 TRUE) + else() + message(WARNING + "user_settings.h defines WOLFSSL_ARMASM but no assembly support is " + "wired for ${CMAKE_SYSTEM_PROCESSOR}; expect link errors. Remove " + "WOLFSSL_ARMASM from the settings for this domain.") + endif() +endif() +if(_wolfssl_build_armasm_a64) + file(GLOB WOLFSSL_ARMASM_SOURCES ${_wolfssl_arm_dir}/armv8-*.c) + file(GLOB _wolfssl_armasm_32 ${_wolfssl_arm_dir}/armv8-32-*.c) + if(_wolfssl_armasm_32) + list(REMOVE_ITEM WOLFSSL_ARMASM_SOURCES ${_wolfssl_armasm_32}) + endif() + # Without inline mode the armv8-*_c.c files carry no assembly, so drop + # them and build the matching .S files instead. + if(NOT _wolfssl_cfg_ARMASM_INLINE) + file(GLOB _wolfssl_armasm_inline_c ${_wolfssl_arm_dir}/armv8-*_c.c) + if(_wolfssl_armasm_inline_c) + list(REMOVE_ITEM WOLFSSL_ARMASM_SOURCES ${_wolfssl_armasm_inline_c}) + endif() + enable_language(ASM) + file(GLOB _wolfssl_armasm_s ${_wolfssl_arm_dir}/armv8-*.S) + file(GLOB _wolfssl_armasm_s32 ${_wolfssl_arm_dir}/armv8-32-*.S) + if(_wolfssl_armasm_s32) + list(REMOVE_ITEM _wolfssl_armasm_s ${_wolfssl_armasm_s32}) + endif() + list(APPEND WOLFSSL_ARMASM_SOURCES ${_wolfssl_armasm_s}) + endif() + + # ChaCha and Poly1305 assembly assume their algorithm is on; drop + # them when the settings leave it off. + if(NOT _wolfssl_cfg_POLY1305) + file(GLOB _wolfssl_armasm_poly + ${_wolfssl_arm_dir}/armv8-poly1305*.c + ${_wolfssl_arm_dir}/armv8-poly1305*.S) + if(_wolfssl_armasm_poly) + list(REMOVE_ITEM WOLFSSL_ARMASM_SOURCES ${_wolfssl_armasm_poly}) + endif() + endif() + if(NOT _wolfssl_cfg_CHACHA) + file(GLOB _wolfssl_armasm_chacha + ${_wolfssl_arm_dir}/armv8-chacha*.c + ${_wolfssl_arm_dir}/armv8-chacha*.S) + if(_wolfssl_armasm_chacha) + list(REMOVE_ITEM WOLFSSL_ARMASM_SOURCES ${_wolfssl_armasm_chacha}) + endif() + endif() + list(APPEND _sources ${WOLFSSL_ARMASM_SOURCES}) +endif() + +# Publish headers to the BSP include dir. The published settings.h is +# patched to always pick user_settings.h, because the BSP flow cannot +# add compile flags to applications. The checkout is never modified. +if(NOT ${YOCTO}) + file(COPY ${WOLFSSL_REPO_ROOT}/wolfssl DESTINATION ${CMAKE_INCLUDE_PATH}/) + file(COPY ${CMAKE_BINARY_DIR}/include/user_settings.h + DESTINATION ${CMAKE_INCLUDE_PATH}/) + file(COPY ${WOLFSSL_REPO_ROOT}/wolfcrypt/test/test.h + DESTINATION ${CMAKE_INCLUDE_PATH}/wolfcrypt/test/) + file(COPY ${WOLFSSL_REPO_ROOT}/wolfcrypt/benchmark/benchmark.h + DESTINATION ${CMAKE_INCLUDE_PATH}/wolfcrypt/benchmark/) + + set(_published_settings ${CMAKE_INCLUDE_PATH}/wolfssl/wolfcrypt/settings.h) + file(READ ${_published_settings} _settings_content) + string(FIND "${_settings_content}" "Vitis SDT BSP flow" _already_patched) + if(_already_patched EQUAL -1) + string(CONCAT _settings_patch + "/* Published for the Vitis SDT BSP flow: " + "user_settings.h is always used. */\n" + "#ifndef WOLFSSL_USER_SETTINGS\n" + "#define WOLFSSL_USER_SETTINGS\n" + "#endif\n") + file(WRITE ${_published_settings} + "${_settings_patch}${_settings_content}") + endif() +endif() + +add_library(wolfssl STATIC ${_sources}) + +target_compile_definitions(wolfssl PRIVATE WOLFSSL_USER_SETTINGS) + +if(_wolfssl_build_armasm_a64) + # Crypto extensions for the AArch64 assembly; placed after the + # toolchain flags so this -mcpu wins. + target_compile_options(wolfssl PRIVATE -mcpu=generic+crypto -mstrict-align) +endif() + +# The patched header tree must shadow the checkout's own headers; the +# checkout root stays for TLS relative includes like "src/bio.c". +target_include_directories(wolfssl PRIVATE + ${CMAKE_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_INCLUDE_PATH} + ${WOLFSSL_REPO_ROOT}) + +set_target_properties(wolfssl PROPERTIES LINKER_LANGUAGE C) +set_target_properties(wolfssl PROPERTIES + ADDITIONAL_CLEAN_FILES "${CMAKE_LIBRARY_PATH}/libwolfssl.a") + +# The BSP install step copies the archive into the BSP lib directory, +# where the platform export and the application linker find it. +install(TARGETS wolfssl LIBRARY DESTINATION ${CMAKE_LIBRARY_PATH} + ARCHIVE DESTINATION ${CMAKE_LIBRARY_PATH}) diff --git a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h new file mode 100644 index 0000000000..815fc8c760 --- /dev/null +++ b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h @@ -0,0 +1,88 @@ +/* user_settings.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Starter wolfSSL settings for this BSP, created on the first build. + * Edit it for your project and rebuild the platform; it is never + * overwritten. It applies to the library and every app on the domain. */ + +#ifndef USER_SETTINGS_H +#define USER_SETTINGS_H + +/* Bare metal: no OS, no filesystem, no sockets. For TLS, provide I/O + * callbacks with wolfSSL_SSLSetIORecv/wolfSSL_SSLSetIOSend. */ +#define SINGLE_THREADED +#define NO_FILESYSTEM +#define WOLFSSL_NO_SOCK +#define WOLFSSL_USER_IO +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define WOLFSSL_IGNORE_FILE_WARN + +/* No wall clock: certificate date checks are skipped. For production, + * remove this and provide XTIME and XGMTIME instead. */ +#define NO_ASN_TIME + +/* WARNING: test-only seed so builds work out of the box. For production + * use real entropy: the ASU offload below or CUSTOM_RAND_GENERATE_SEED. */ +#define WOLFSSL_GENSEED_FORTEST + +/* Math backend */ +#define WOLFSSL_SP_MATH_ALL + +/* Hardening */ +#define TFM_TIMING_RESISTANT +#define ECC_TIMING_RESISTANT +#define WC_RSA_BLINDING +#define WC_RSA_PSS /* required for TLS 1.3 with RSA */ + +/* Algorithms */ +#define HAVE_ECC +#define HAVE_AESGCM +#define WOLFSSL_SHA384 +#define WOLFSSL_SHA512 +#define HAVE_HKDF +#define HAVE_HASHDRBG + +/* TLS 1.3 */ +#define WOLFSSL_TLS13 +#define HAVE_TLS_EXTENSIONS +#define HAVE_SUPPORTED_CURVES +#define NO_DH /* ECDHE only; use HAVE_FFDHE_2048 instead for DH */ + +/* For the wolfCrypt self-test and benchmark. Define NO_CRYPT_TEST and + * NO_CRYPT_BENCHMARK to leave them out instead. */ +#define USE_CERT_BUFFERS_2048 +#define USE_CERT_BUFFERS_256 +#define BENCH_EMBEDDED + +/* Uncomment for a build with only wolfCrypt (no TLS layer). */ +/* #define WOLFCRYPT_ONLY */ + +/* Uncomment on Cortex-A domains for ARM assembly acceleration. */ +/* #define WOLFSSL_ARMASM */ +/* #define WOLFSSL_ARMASM_INLINE */ + +/* Uncomment for Versal Gen 2 ASU hardware crypto offload. Also enable the + * xilasu and xilmailbox libraries in this BSP. wolfCrypt_Init registers the + * device, so the app needs no ASU calls of its own. */ +/* #define WOLFSSL_VERSAL_GEN2_ASU */ + +#endif /* USER_SETTINGS_H */ diff --git a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/wolfssl.cmake b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/wolfssl.cmake new file mode 100644 index 0000000000..7d0f06a6fe --- /dev/null +++ b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/wolfssl.cmake @@ -0,0 +1,19 @@ +# Copyright (C) 2006-2026 wolfSSL Inc. All rights reserved. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# BSP options for the wolfssl library, shown in the Vitis BSP settings +# GUI. The wolfSSL feature set itself is not set here: it comes from +# user_settings.h. See IDE/XilinxSDK/vitis_sdt/README.md. + +string(CONCAT _wolfssl_desc + "Path to the wolfSSL source checkout. Leave empty to auto-detect " + "(works when this repository was added to Vitis from inside the " + "checkout).") +set(wolfssl_source_path "" CACHE STRING "${_wolfssl_desc}") + +string(CONCAT _wolfssl_desc + "Absolute path to your user_settings.h. Leave empty to use " + "user_settings.h from the domain's BSP directory (next to bsp.yaml). " + "One of the two must exist; it configures wolfSSL for the library " + "and every application on this domain.") +set(wolfssl_user_settings_path "" CACHE STRING "${_wolfssl_desc}") diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index 95ed354da4..692da2bc60 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1567,8 +1567,8 @@ int test_wc_ecc_ctx_set_info(void) /* * Testing the crypto-callback context accessors wc_ecc_ctx_get_algo, - * wc_ecc_ctx_get_kdf_salt and wc_ecc_ctx_get_info (built only when - * WOLF_CRYPTO_CB is enabled). + * wc_ecc_ctx_get_kdf_salt, wc_ecc_ctx_get_info, wc_ecc_ctx_get_mac_salt and + * wc_ecc_ctx_get_protocol (built only when WOLF_CRYPTO_CB is enabled). */ int test_wc_ecc_ctx_getters(void) { @@ -1635,6 +1635,66 @@ int test_wc_ecc_ctx_getters(void) ExpectIntEQ(wc_ecc_ctx_get_info(ctx, &got, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* Setting the KDF salt and info must leave the MAC salt empty. The ASU + * ECIES offload only runs when that salt is empty. */ + got = NULL; gotSz = 123; + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(ctx, &got, &gotSz), 0); + ExpectIntEQ(gotSz, 0); + ExpectNull(got); + + /* get_mac_salt: macSalt is only populated after the own-salt/peer-salt + * handshake, so drive that on a fresh context then read it back. */ + { + ecEncCtx* ctx2 = NULL; + const byte* macGot = NULL; + word32 macGotSz = 0; + + ExpectNotNull(ctx2 = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng)); + /* Before the salt exchange the MAC salt is empty, which is the state + * the ASU offload looks for. Both out-parameters are set to something + * else first, so the checks fail if the getter never writes. */ + macGot = salt; + macGotSz = 0xFFFFFFFFU; + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(ctx2, &macGot, &macGotSz), 0); + ExpectIntEQ(macGotSz, 0); + ExpectNull(macGot); + ExpectNotNull(wc_ecc_ctx_get_own_salt(ctx2)); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(ctx2, salt), 0); + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(ctx2, &macGot, &macGotSz), 0); + ExpectIntEQ(macGotSz, (word32)EXCHANGE_SALT_SZ); + ExpectNotNull(macGot); + /* The two salts differ in their second half, so a getter that returns + * the wrong one fails this check. */ + ExpectIntEQ(XMEMCMP(macGot + (EXCHANGE_SALT_SZ / 2), + salt + (EXCHANGE_SALT_SZ / 2), EXCHANGE_SALT_SZ / 2), 0); + /* bad args: NULL ctx / salt / size */ + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(NULL, &macGot, &macGotSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(ctx2, NULL, &macGotSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ecc_ctx_get_mac_salt(ctx2, &macGot, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + wc_ecc_ctx_free(ctx2); + } + + /* Read both roles back and check the NULL guards. The ASU offload picks + * different paths for each role, so they must not be mixed up. */ + { + ecEncCtx* sctx = NULL; + int proto = 0; + ExpectIntEQ(wc_ecc_ctx_get_protocol(ctx, &proto), 0); + ExpectIntEQ(proto, REQ_RESP_CLIENT); + ExpectNotNull(sctx = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng)); + proto = 0; + ExpectIntEQ(wc_ecc_ctx_get_protocol(sctx, &proto), 0); + ExpectIntEQ(proto, REQ_RESP_SERVER); + wc_ecc_ctx_free(sctx); + ExpectIntEQ(wc_ecc_ctx_get_protocol(NULL, &proto), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ecc_ctx_get_protocol(ctx, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + } + wc_ecc_ctx_free(ctx); DoExpectIntEQ(wc_FreeRng(&rng), 0); #endif diff --git a/wolfcrypt/benchmark/README.md b/wolfcrypt/benchmark/README.md index e2529f7157..f3084b99d2 100644 --- a/wolfcrypt/benchmark/README.md +++ b/wolfcrypt/benchmark/README.md @@ -30,6 +30,26 @@ It requires `WC_RSA_PSS`, one of `WC_RSA_DIRECT`/`WC_RSA_NO_PADDING`/`OPENSSL_EX OAEP support, and at least one `USE_CERT_BUFFERS_2048/3072/4096`; if any is absent the sweep silently produces no rows. +To make an argument-less `benchmark` run sweep every compiled-in ECC curve +(instead of only the default curve), compile with: + +`-DWOLFSSL_BENCH_ECC_ALL` + +`-p256`/`-p384`/`-p521` still select a single curve, and the option has no effect +in FIPS/SELFTEST builds. The `-ecc-all` runtime flag performs the same sweep +without the macro. + +To add an extra ECIES run keyed with a KDF salt and context instead of the +client and server salt exchange, compile with: + +`-DWC_BENCH_ECIES_KDF` + +The salt exchange also sets a MAC salt, which wolfSSL passes to the cipher as +extra authenticated data. A crypto callback backend that cannot take that data +declines the operation, so the normal ECIES rows measure software even when a +device id is given. The extra rows are tagged `-kdf`, use a context with no MAC +salt, and run as their own pass so the two can be compared side by side. + To track per-algorithm heap and stack usage in the output, configure wolfSSL with: ``` diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 9d96beb57c..f8eb5efdd6 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -160,6 +160,8 @@ #ifdef HAVE_ECC #include #endif +/* bench_ecc() needs the digest size limits from hash.h, so include it here. */ +#include #ifdef WOLFSSL_SM2 #include #endif @@ -634,6 +636,12 @@ static WC_INLINE void bench_append_memory_info(char* buffer, size_t size, #define WC_BENCH_TRACK_STATS #endif +/* The IV sweep builds its row labels into shared buffers at run time, which + * several benchmark threads would write at once. Refuse the pair for now. */ +#if defined(WC_BENCH_AES_IV_SWEEP) && defined(WC_ENABLE_BENCH_THREADING) + #error "WC_BENCH_AES_IV_SWEEP cannot be used with threaded benchmarks" +#endif + #ifdef GENERATE_MACHINE_PARSEABLE_REPORT static const char info_prefix[] = "###, "; static const char err_prefix[] = "!!!, "; @@ -2169,6 +2177,13 @@ static const char* bench_result_words2[][6] = { static volatile int g_threadCount; #endif +/* The software DRBG rows need a seed source that is not behind the device. */ +#if defined(WOLFSSL_GENSEED_FORTEST) || \ + defined(CUSTOM_RAND_GENERATE_SEED) || \ + defined(CUSTOM_RAND_GENERATE_BLOCK) || !defined(NO_DEV_RANDOM) + #define BENCH_HAVE_SW_SEED +#endif + #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_CAAM) || \ defined(WC_USE_DEVID) || \ defined(WOLFSSL_MICROCHIP_TA100) @@ -2186,6 +2201,18 @@ static const char* bench_result_words2[][6] = { #define BENCH_DEVID_GET_NAME(useDeviceID) "" #endif +/* HW/SW column for the CSV. Empty with no device, so the header and the rows + * always have the same fields. */ +#ifdef BENCH_DEVID + #define BENCH_DEVID_CSV_HEADER "HW/SW," + #define BENCH_DEVID_CSV_FMT "%s," + #define BENCH_DEVID_CSV_ARG(useDeviceID) BENCH_DEVID_GET_NAME(useDeviceID), +#else + #define BENCH_DEVID_CSV_HEADER + #define BENCH_DEVID_CSV_FMT + #define BENCH_DEVID_CSV_ARG(useDeviceID) +#endif + #ifdef WOLFSSL_ASYNC_CRYPT static WOLF_EVENT_QUEUE eventQueue; @@ -3504,11 +3531,12 @@ static void bench_stats_asym_finish_ex(const char* algo, int strength, #else printf("\n%sAsymmetric Ciphers:\n\n", info_prefix); #ifdef HAVE_GET_CYCLES - printf("%sAlgorithm,key size,operation,ops/" - WOLFSSL_FIXED_TIME_UNIT "ec,cycles/op,", info_prefix); + printf("%sAlgorithm,key size,operation," BENCH_DEVID_CSV_HEADER + "ops/" WOLFSSL_FIXED_TIME_UNIT "ec,cycles/op,", + info_prefix); #else - printf("%sAlgorithm,key size,operation,ops/" - WOLFSSL_FIXED_TIME_UNIT "ec,", info_prefix); + printf("%sAlgorithm,key size,operation," BENCH_DEVID_CSV_HEADER + "ops/" WOLFSSL_FIXED_TIME_UNIT "ec,", info_prefix); #endif printf("%s", #ifdef WC_BENCH_HEAP_TRACKING @@ -3542,17 +3570,19 @@ static void bench_stats_asym_finish_ex(const char* algo, int strength, #endif #else #ifdef HAVE_GET_CYCLES - (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," + (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," BENCH_DEVID_CSV_FMT FLT_FMT_PREC "," FLT_FMT_PREC "," STATS_CLAUSE_SEPARATOR, algo, strength, desc, desc_extra, + BENCH_DEVID_CSV_ARG(useDeviceID) FLT_FMT_PREC_ARGS(digits, opsSec), FLT_FMT_PREC_ARGS(2, (double)total_cycles / (double)count)); #else - (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," + (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," BENCH_DEVID_CSV_FMT FLT_FMT_PREC "," STATS_CLAUSE_SEPARATOR, algo, strength, desc, desc_extra, + BENCH_DEVID_CSV_ARG(useDeviceID) FLT_FMT_PREC_ARGS(digits, opsSec)); #endif #endif @@ -3632,8 +3662,8 @@ static void bench_stats_asym_finish_ex(const char* algo, int strength, #endif #else printf("\n%sAsymmetric Ciphers:\n\n", info_prefix); - printf("%sAlgorithm,key size,operation,avg ms,ops/" - WOLFSSL_FIXED_TIME_UNIT "ec,", info_prefix); + printf("%sAlgorithm,key size,operation," BENCH_DEVID_CSV_HEADER + "avg ms,ops/" WOLFSSL_FIXED_TIME_UNIT "ec,", info_prefix); printf("%s", #ifdef WC_BENCH_HEAP_TRACKING "heap_bytes,heap_allocs," @@ -3667,9 +3697,11 @@ static void bench_stats_asym_finish_ex(const char* algo, int strength, count, FLT_FMT_ARGS(total)); #endif #else - (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," FLT_FMT_PREC "," - FLT_FMT_PREC "," STATS_CLAUSE_SEPARATOR, + (void)XSNPRINTF(msg, sizeof(msg), "%s,%d,%s%s," BENCH_DEVID_CSV_FMT + FLT_FMT_PREC "," FLT_FMT_PREC "," + STATS_CLAUSE_SEPARATOR, algo, strength, desc, desc_extra, + BENCH_DEVID_CSV_ARG(useDeviceID) FLT_FMT_PREC_ARGS(3, milliEach), FLT_FMT_PREC_ARGS(digits, opsSec)); #endif @@ -4030,14 +4062,29 @@ static void* benchmarks_do(void* args) #endif #ifndef WC_NO_RNG - if (bench_all || (bench_other_algs & BENCH_RNG)) - bench_rng(); + if (bench_all || (bench_other_algs & BENCH_RNG)) { + #if !defined(NO_SW_BENCH) && defined(BENCH_HAVE_SW_SEED) + bench_rng(0); + #endif + /* A FIPS build takes no device id when it starts the RNG, so a second row + * would repeat the first one under a hardware label. */ + #if defined(BENCH_DEVID) && !defined(HAVE_FIPS) + bench_rng(1); + #endif + } #endif /* WC_NO_RNG */ #if defined(WOLFSSL_DRBG_SHA512) && !defined(WC_NO_RNG) && \ !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) - if (bench_all || (bench_other_algs & BENCH_RNG_SHA512)) - bench_rng_sha512(); + if (bench_all || (bench_other_algs & BENCH_RNG_SHA512)) { + #if !defined(NO_SW_BENCH) && defined(BENCH_HAVE_SW_SEED) + bench_rng_sha512(0); + #endif + /* Same as above: no device id reaches the RNG in a FIPS build. */ + #if defined(BENCH_DEVID) && !defined(HAVE_FIPS) + bench_rng_sha512(1); + #endif + } #endif #ifndef NO_AES #ifdef HAVE_AES_CBC @@ -4114,6 +4161,25 @@ static void* benchmarks_do(void* args) #endif } #endif +/* Key wrap rides on the AES engine, so it follows the AES-ECB selection. */ +#if defined(HAVE_AES_KEYWRAP) && !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + if (bench_all || (bench_cipher_algs & BENCH_AES_ECB)) { + #ifndef NO_SW_BENCH + bench_aeskeywrap(0); + #endif + #ifdef BENCH_DEVID + bench_aeskeywrap(1); + #endif + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + #ifndef NO_SW_BENCH + bench_aeskeywrap_pad(0); + #endif + #ifdef BENCH_DEVID + bench_aeskeywrap_pad(1); + #endif + #endif + } +#endif #ifdef WOLFSSL_AES_SIV if (bench_all || (bench_cipher_algs & BENCH_AES_SIV)) bench_aessiv(); @@ -4719,7 +4785,16 @@ static void* benchmarks_do(void* args) (bench_asym_algs & BENCH_ECC_ALL) || (bench_asym_algs & BENCH_ECC_ENCRYPT)) { + /* Let a plain bench all run cover every curve in the build, not just + * P-256. FIPS builds stay on the default curve. */ +#if defined(WOLFSSL_BENCH_ECC_ALL) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) + if ((bench_asym_algs & BENCH_ECC_ALL) || + (bench_all && !(bench_asym_algs & + (BENCH_ECC_P256 | BENCH_ECC_P384 | BENCH_ECC_P521)))) { +#else if (bench_asym_algs & BENCH_ECC_ALL) { +#endif #if defined(HAVE_FIPS) || defined(HAVE_SELFTEST) printf("%snot supported in FIPS mode (no ending enum value)\n", err_prefix); @@ -4831,10 +4906,19 @@ static void* benchmarks_do(void* args) #endif #ifdef HAVE_ED448 - if (bench_all || (bench_asym_algs & BENCH_ED448_KEYGEN)) + if (bench_all || (bench_asym_algs & BENCH_ED448_KEYGEN)) { + #ifndef NO_SW_BENCH bench_ed448KeyGen(); - if (bench_all || (bench_asym_algs & BENCH_ED448_SIGN)) - bench_ed448KeySign(); + #endif + } + if (bench_all || (bench_asym_algs & BENCH_ED448_SIGN)) { + #ifndef NO_SW_BENCH + bench_ed448KeySign(0); + #endif + #ifdef BENCH_DEVID + bench_ed448KeySign(1); + #endif + } #endif #ifdef WOLFCRYPT_HAVE_ECCSI @@ -5284,7 +5368,7 @@ int benchmark_test(void *args) #ifndef WC_NO_RNG -void bench_rng(void) +void bench_rng(int useDeviceID) { int ret, i, count; double start; @@ -5310,7 +5394,8 @@ void bench_rng(void) bench_stats_prepare(); #ifndef HAVE_FIPS - ret = wc_InitRng_ex(&myrng, HEAP_HINT, devId); + ret = wc_InitRng_ex(&myrng, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); #else ret = wc_InitRng(&myrng); #endif @@ -5350,8 +5435,8 @@ void bench_rng(void) #endif ); exit_rng: - bench_stats_sym_finish("RNG SHA-256 DRBG", 0, count, bench_size, start, - ret); + bench_stats_sym_finish("RNG SHA-256 DRBG", useDeviceID, count, bench_size, + start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif @@ -5369,7 +5454,7 @@ void bench_rng(void) #if defined(WOLFSSL_DRBG_SHA512) && !defined(WC_NO_RNG) && \ !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) -void bench_rng_sha512(void) +void bench_rng_sha512(int useDeviceID) { int ret, i, count; double start; @@ -5389,7 +5474,8 @@ void bench_rng_sha512(void) bench_stats_prepare(); #ifndef HAVE_FIPS - ret = wc_InitRng_ex(&myrng, HEAP_HINT, devId); + ret = wc_InitRng_ex(&myrng, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); #else ret = wc_InitRng(&myrng); #endif @@ -5428,8 +5514,8 @@ void bench_rng_sha512(void) #endif ); exit_rng_sha512: - bench_stats_sym_finish("RNG SHA-512 DRBG", 0, count, bench_size, start, - ret); + bench_stats_sym_finish("RNG SHA-512 DRBG", useDeviceID, count, bench_size, + start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif @@ -6280,6 +6366,19 @@ void bench_aesgcm(int useDeviceID) AES_GCM_STRING(256, enc), AES_GCM_STRING(256, dec)); #endif #endif +#ifdef WC_BENCH_AES_IV_SWEEP + /* Extra rows using a 16-byte IV alongside the 12-byte default above. */ +#ifdef WOLFSSL_AES_128 + bench_aesgcm_internal(useDeviceID, bench_key, 16, bench_iv, 16, + AES_AAD_STRING("AES-128-GCM-iv16-enc"), + AES_AAD_STRING("AES-128-GCM-iv16-dec")); +#endif +#ifdef WOLFSSL_AES_256 + bench_aesgcm_internal(useDeviceID, bench_key, 32, bench_iv, 16, + AES_AAD_STRING("AES-256-GCM-iv16-enc"), + AES_AAD_STRING("AES-256-GCM-iv16-dec")); +#endif +#endif /* WC_BENCH_AES_IV_SWEEP */ #ifdef WOLFSSL_AESGCM_STREAM #undef AES_GCM_STRING #define AES_GCM_STRING(n, dir) AES_AAD_STRING("AES-" #n "-GCM-STREAM-" #dir) @@ -6303,7 +6402,8 @@ void bench_aesgcm(int useDeviceID) } /* GMAC */ -void bench_gmac(int useDeviceID) +static void bench_gmac_internal(int useDeviceID, word32 ivSz, + const char* gmacStr) { int ret = 0, times, count = 0; Gmac gmac; @@ -6311,21 +6411,6 @@ void bench_gmac(int useDeviceID) byte tag[AES_AUTH_TAG_SZ]; DECLARE_MULTI_VALUE_STATS_VARS() - /* determine GCM GHASH method */ -#if defined(WOLFSSL_ARMASM) - const char* gmacStr = "GMAC ARM ASM"; -#elif defined(GCM_SMALL) - const char* gmacStr = "GMAC Small"; -#elif defined(GCM_TABLE) - const char* gmacStr = "GMAC Table"; -#elif defined(GCM_TABLE_4BIT) - const char* gmacStr = "GMAC Table 4-bit"; -#elif defined(GCM_WORD32) - const char* gmacStr = "GMAC Word32"; -#else - const char* gmacStr = "GMAC Default"; -#endif - bench_stats_prepare(); /* Implementations of /Dev/Crypto will error out if the size of Auth in is */ @@ -6350,7 +6435,7 @@ void bench_gmac(int useDeviceID) bench_stats_start(&count, &start); do { for (times = 0; times < numBlocks; times++) { - ret = wc_GmacUpdate(&gmac, bench_iv, 12, bench_plain, bench_size, + ret = wc_GmacUpdate(&gmac, bench_iv, ivSz, bench_plain, bench_size, tag, sizeof(tag)); } /* for times */ @@ -6377,6 +6462,36 @@ void bench_gmac(int useDeviceID) #endif } +void bench_gmac(int useDeviceID) +{ + /* determine GCM GHASH method */ +#if defined(WOLFSSL_ARMASM) + const char* gmacStr = "GMAC ARM ASM"; +#elif defined(GCM_SMALL) + const char* gmacStr = "GMAC Small"; +#elif defined(GCM_TABLE) + const char* gmacStr = "GMAC Table"; +#elif defined(GCM_TABLE_4BIT) + const char* gmacStr = "GMAC Table 4-bit"; +#elif defined(GCM_WORD32) + const char* gmacStr = "GMAC Word32"; +#else + const char* gmacStr = "GMAC Default"; +#endif + + bench_gmac_internal(useDeviceID, 12, gmacStr); + +#ifdef WC_BENCH_AES_IV_SWEEP + /* Extra row using a 16-byte IV alongside the 12-byte default above. The + * stats list keeps the label by pointer, so it must outlive this call. */ + { + static char gmacIvStr[40]; + (void)XSNPRINTF(gmacIvStr, sizeof(gmacIvStr), "%s-iv16", gmacStr); + bench_gmac_internal(useDeviceID, 16, gmacIvStr); + } +#endif /* WC_BENCH_AES_IV_SWEEP */ +} + #endif /* HAVE_AESGCM */ @@ -6924,7 +7039,8 @@ void bench_aesctr(int useDeviceID) #ifdef HAVE_AESCCM -void bench_aesccm(int useDeviceID) +static void bench_aesccm_internal(int useDeviceID, word32 nonceSz, + const char* encLabel, const char* decLabel) { Aes enc; int enc_inited = 0; @@ -6960,7 +7076,7 @@ void bench_aesccm(int useDeviceID) do { for (i = 0; i < numBlocks; i++) { ret |= wc_AesCcmEncrypt(&enc, bench_cipher, bench_plain, bench_size, - bench_iv, 12, bench_tag, AES_AUTH_TAG_SZ, + bench_iv, nonceSz, bench_tag, AES_AUTH_TAG_SZ, bench_additional, 0); RECORD_MULTI_VALUE_STATS(); } @@ -6971,7 +7087,7 @@ void bench_aesccm(int useDeviceID) #endif ); - bench_stats_sym_finish(AES_AAD_STRING("AES-CCM-enc"), useDeviceID, count, + bench_stats_sym_finish(encLabel, useDeviceID, count, bench_size, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); @@ -6988,7 +7104,7 @@ void bench_aesccm(int useDeviceID) do { for (i = 0; i < numBlocks; i++) { ret |= wc_AesCcmDecrypt(&enc, bench_plain, bench_cipher, bench_size, - bench_iv, 12, bench_tag, AES_AUTH_TAG_SZ, + bench_iv, nonceSz, bench_tag, AES_AUTH_TAG_SZ, bench_additional, 0); RECORD_MULTI_VALUE_STATS(); } @@ -6999,7 +7115,7 @@ void bench_aesccm(int useDeviceID) #endif ); - bench_stats_sym_finish(AES_AAD_STRING("AES-CCM-dec"), useDeviceID, count, + bench_stats_sym_finish(decLabel, useDeviceID, count, bench_size, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); @@ -7018,6 +7134,37 @@ void bench_aesccm(int useDeviceID) WC_FREE_VAR(bench_additional, HEAP_HINT); WC_FREE_VAR(bench_tag, HEAP_HINT); } + +void bench_aesccm(int useDeviceID) +{ +#ifdef WC_BENCH_AES_IV_SWEEP + /* One enc/dec row per nonce length from 7 to 13 bytes. The stats list + * keeps these labels by pointer, so they must outlive this call. */ + word32 nsz; + static char encLabel[7][28], decLabel[7][28]; + + for (nsz = 7; nsz <= 13; nsz++) { + /* CCM stores the message length in 15 - nonce bytes, so a long nonce + * cannot describe a large block. Skip those pairs, not the nonce. */ + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - nsz; + if ((lenSz < sizeof(bench_size)) && + (bench_size >= ((word32)1 << (lenSz * 8)))) { + printf("AES-CCM-n%u (Skipped: block size needs a shorter nonce)\n", + (unsigned)nsz); + continue; + } + (void)XSNPRINTF(encLabel[nsz - 7], sizeof(encLabel[0]), + "AES-CCM-n%u-enc", (unsigned)nsz); + (void)XSNPRINTF(decLabel[nsz - 7], sizeof(decLabel[0]), + "AES-CCM-n%u-dec", (unsigned)nsz); + bench_aesccm_internal(useDeviceID, nsz, encLabel[nsz - 7], + decLabel[nsz - 7]); + } +#else + bench_aesccm_internal(useDeviceID, 12, + AES_AAD_STRING("AES-CCM-enc"), AES_AAD_STRING("AES-CCM-dec")); +#endif /* WC_BENCH_AES_IV_SWEEP */ +} #endif /* HAVE_AESCCM */ @@ -7087,6 +7234,7 @@ static void bench_aessiv_internal(const byte* key, word32 keySz, const char* #endif } + void bench_aessiv(void) { bench_aessiv_internal(bench_key, 32, "AES-256-SIV-enc", "AES-256-SIV-dec"); @@ -7095,6 +7243,164 @@ void bench_aessiv(void) } #endif /* WOLFSSL_AES_SIV */ +/* The _ex calls take a keyed Aes, so they carry the device id. They are not in + * the FIPS or selftest headers, so the whole benchmark follows them. */ +#if defined(HAVE_AES_KEYWRAP) && !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) +/* Wrap and unwrap one payload per operation. RFC 3394 needs a multiple of 8 + * and at least 16 bytes, so the payload is bench_size trimmed to fit. */ +static void bench_aeskeywrap_internal(int useDeviceID, const byte* key, + word32 keySz, int pad, + const char* wrapLabel, + const char* unwrapLabel) +{ + Aes aes; + word32 inSz = (word32)((bench_size - 8) & ~7U); + word32 outSz = inSz + 8; + double start = 0; + int ret = 0, count = 0; + DECLARE_MULTI_VALUE_STATS_VARS() + + if (bench_size < 24) { + printf("%s (Skipped: block size too small)\n", wrapLabel); + return; + } + + bench_stats_prepare(); + + /* Wrap: the key encryption key is set for encrypt. */ + ret = wc_AesInit(&aes, HEAP_HINT, useDeviceID ? devId : INVALID_DEVID); + if (ret != 0) { + printf("AesInit failed, ret = %d\n", ret); + return; + } + ret = wc_AesSetKey(&aes, key, keySz, NULL, AES_ENCRYPTION); + if (ret != 0) { + printf("AesSetKey failed, ret = %d\n", ret); + goto exit_wrap; + } + + /* One wrap per pass so the timer stops this, not a block count. Key wrap + * makes six AES passes, so a byte scaled count runs far too long. */ + bench_stats_start(&count, &start); + do { + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + if (pad) { + ret = wc_AesKeyWrap_Pad_ex(&aes, bench_plain, inSz, + bench_cipher, outSz, NULL); + } + else + #endif + { + ret = wc_AesKeyWrap_ex(&aes, bench_plain, inSz, + bench_cipher, outSz, NULL); + } + if (ret < 0) { + printf("%s failed, ret = %d\n", wrapLabel, ret); + goto exit_wrap; + } + RECORD_MULTI_VALUE_STATS(); + count++; + } while (bench_stats_check(start) +#ifdef MULTI_VALUE_STATISTICS + || runs < minimum_runs +#endif + ); + ret = 0; + +exit_wrap: + bench_stats_sym_finish(wrapLabel, useDeviceID, count, inSz, start, ret); +#ifdef MULTI_VALUE_STATISTICS + bench_multi_value_stats(max, min, sum, squareSum, runs); +#endif + wc_AesFree(&aes); + if (ret != 0) { + return; + } + + /* Unwrap: the same key encryption key, this time set for decrypt. */ + RESET_MULTI_VALUE_STATS_VARS(); + count = 0; + ret = wc_AesInit(&aes, HEAP_HINT, useDeviceID ? devId : INVALID_DEVID); + if (ret != 0) { + printf("AesInit failed, ret = %d\n", ret); + return; + } + ret = wc_AesSetKey(&aes, key, keySz, NULL, AES_DECRYPTION); + if (ret != 0) { + printf("AesSetKey failed, ret = %d\n", ret); + goto exit_unwrap; + } + + bench_stats_start(&count, &start); + do { + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + if (pad) { + ret = wc_AesKeyUnWrap_Pad_ex(&aes, bench_cipher, outSz, + bench_plain, inSz, NULL); + } + else + #endif + { + ret = wc_AesKeyUnWrap_ex(&aes, bench_cipher, outSz, + bench_plain, inSz, NULL); + } + if (ret < 0) { + printf("%s failed, ret = %d\n", unwrapLabel, ret); + goto exit_unwrap; + } + RECORD_MULTI_VALUE_STATS(); + count++; + } while (bench_stats_check(start) +#ifdef MULTI_VALUE_STATISTICS + || runs < minimum_runs +#endif + ); + ret = 0; + +exit_unwrap: + bench_stats_sym_finish(unwrapLabel, useDeviceID, count, inSz, start, ret); +#ifdef MULTI_VALUE_STATISTICS + bench_multi_value_stats(max, min, sum, squareSum, runs); +#endif + wc_AesFree(&aes); + (void)pad; +} + +void bench_aeskeywrap(int useDeviceID) +{ +#ifdef WOLFSSL_AES_128 + bench_aeskeywrap_internal(useDeviceID, bench_key, 16, 0, + "AES-128-KW-wrap", "AES-128-KW-unwrap"); +#endif +#ifdef WOLFSSL_AES_192 + bench_aeskeywrap_internal(useDeviceID, bench_key, 24, 0, + "AES-192-KW-wrap", "AES-192-KW-unwrap"); +#endif +#ifdef WOLFSSL_AES_256 + bench_aeskeywrap_internal(useDeviceID, bench_key, 32, 0, + "AES-256-KW-wrap", "AES-256-KW-unwrap"); +#endif +} + +#ifdef WOLFSSL_AES_KEYWRAP_PADDING +void bench_aeskeywrap_pad(int useDeviceID) +{ +#ifdef WOLFSSL_AES_128 + bench_aeskeywrap_internal(useDeviceID, bench_key, 16, 1, + "AES-128-KWP-wrap", "AES-128-KWP-unwrap"); +#endif +#ifdef WOLFSSL_AES_192 + bench_aeskeywrap_internal(useDeviceID, bench_key, 24, 1, + "AES-192-KWP-wrap", "AES-192-KWP-unwrap"); +#endif +#ifdef WOLFSSL_AES_256 + bench_aeskeywrap_internal(useDeviceID, bench_key, 32, 1, + "AES-256-KWP-wrap", "AES-256-KWP-unwrap"); +#endif +} +#endif /* WOLFSSL_AES_KEYWRAP_PADDING */ +#endif /* HAVE_AES_KEYWRAP && !HAVE_FIPS && !HAVE_SELFTEST */ + #ifdef WOLFSSL_AESGCM_SIV static void bench_aesgcmsiv_internal(const byte* key, word32 keySz, const char* encLabel, const char* decLabel) @@ -14015,8 +14321,22 @@ void bench_ecc_curve(int curveId) #endif } #ifdef HAVE_ECC_ENCRYPT - if (bench_all || (bench_asym_algs & BENCH_ECC_ENCRYPT)) - bench_eccEncrypt(curveId); + if (bench_all || (bench_asym_algs & BENCH_ECC_ENCRYPT)) { + #ifndef NO_SW_BENCH + bench_eccEncrypt(0, curveId); + #endif + #if defined(BENCH_DEVID) + bench_eccEncrypt(1, curveId); + #endif + #ifdef WC_BENCH_ECIES_KDF + #ifndef NO_SW_BENCH + bench_eccEncryptKdf(0, curveId); + #endif + #if defined(BENCH_DEVID) + bench_eccEncryptKdf(1, curveId); + #endif + #endif + } #endif } @@ -14156,7 +14476,9 @@ void bench_ecc(int useDeviceID, int curveId) #if !defined(NO_ASN) && defined(HAVE_ECC_SIGN) WC_ALLOC_ARRAY(sig, byte, BENCH_MAX_PENDING, ECC_MAX_SIG_SIZE, HEAP_HINT); - WC_ALLOC_ARRAY(digest, byte, BENCH_MAX_PENDING, MAX_ECC_BYTES, HEAP_HINT); + /* digest[] is the largest size, so it still fits after the bump below. */ + WC_ALLOC_ARRAY(digest, byte, BENCH_MAX_PENDING, WC_MAX_DIGEST_SIZE, + HEAP_HINT); #endif deviceID = useDeviceID ? devId : INVALID_DEVID; @@ -14184,6 +14506,12 @@ void bench_ecc(int useDeviceID, int curveId) if (dgstSize > WC_MAX_DIGEST_SIZE) { dgstSize = WC_MAX_DIGEST_SIZE; } + /* Small curves give a digest below the sign minimum, so bump it up and + * keep it inside the buffer. */ + if ((WC_MIN_DIGEST_SIZE_FOR_SIGN <= WC_MAX_DIGEST_SIZE) && + (dgstSize < WC_MIN_DIGEST_SIZE_FOR_SIGN)) { + dgstSize = WC_MIN_DIGEST_SIZE_FOR_SIGN; + } /* init keys */ for (i = 0; i < BENCH_MAX_PENDING; i++) { @@ -14427,8 +14755,26 @@ void bench_ecc(int useDeviceID, int curveId) * per-cipher benchmark loops reset and re-prime the context before each op. * Fixed salts let the encrypt/decrypt directions agree (required for GCM's * authentication tag to verify). */ -static int bench_ecies_prep(ecEncCtx* ctx, byte encAlgo, const byte* ownSalt, - const byte* peerSalt) +/* The two ways to key an ECIES context. SALTX is the client/server salt + * exchange, which also sets a MAC salt. KDF sets the salt and context. */ +#define BENCH_ECIES_MODE_SALTX 0 +#define BENCH_ECIES_MODE_KDF 1 + +#ifdef WC_BENCH_ECIES_KDF +/* KDF salt and context for BENCH_ECIES_MODE_KDF. Both sides use the same + * values, so the derived keys match without the salt exchange. */ +static const byte bench_eciesKdfSalt[EXCHANGE_SALT_SZ] = { + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f +}; +static const byte bench_eciesKdfInfo[] = { + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f +}; +#endif + +static int bench_ecies_prep(ecEncCtx* ctx, byte encAlgo, int ctxMode, + const byte* ownSalt, const byte* peerSalt) { int ret; byte* own; @@ -14436,23 +14782,42 @@ static int bench_ecies_prep(ecEncCtx* ctx, byte encAlgo, const byte* ownSalt, ret = wc_ecc_ctx_reset(ctx, &gRng); if (ret == 0) ret = wc_ecc_ctx_set_algo(ctx, encAlgo, ecHKDF_SHA256, ecHMAC_SHA256); - if (ret == 0) { - own = (byte*)wc_ecc_ctx_get_own_salt(ctx); - if (own == NULL) - ret = BAD_FUNC_ARG; - else { - XMEMCPY(own, ownSalt, EXCHANGE_SALT_SZ); - ret = wc_ecc_ctx_set_peer_salt(ctx, peerSalt); - } + if (ret != 0) + return ret; + +#ifdef WC_BENCH_ECIES_KDF + if (ctxMode == BENCH_ECIES_MODE_KDF) { + ret = wc_ecc_ctx_set_kdf_salt(ctx, bench_eciesKdfSalt, + EXCHANGE_SALT_SZ); + if (ret == 0) + ret = wc_ecc_ctx_set_info(ctx, bench_eciesKdfInfo, + (int)sizeof(bench_eciesKdfInfo)); + return ret; } - return ret; +#else + (void)ctxMode; +#endif + + own = (byte*)wc_ecc_ctx_get_own_salt(ctx); + if (own == NULL) + return BAD_FUNC_ARG; + XMEMCPY(own, ownSalt, EXCHANGE_SALT_SZ); + return wc_ecc_ctx_set_peer_salt(ctx, peerSalt); } -void bench_eccEncrypt(int curveId) +static void bench_eccEncryptEx(int useDeviceID, int curveId, int ctxMode) { #define BENCH_ECCENCRYPT_MSG_SIZE 48 +#ifdef WOLFSSL_ECIES_GEN_IV + /* GEN_IV adds a nonce to the output, so leave room for one AES block or + * the call fails before the GCM rows run. */ + #define BENCH_ECCENCRYPT_IV_ROOM 16 +#else + #define BENCH_ECCENCRYPT_IV_ROOM 0 +#endif #define BENCH_ECCENCRYPT_OUT_SIZE (BENCH_ECCENCRYPT_MSG_SIZE + \ WC_SHA256_DIGEST_SIZE + \ + BENCH_ECCENCRYPT_IV_ROOM + \ (MAX_ECC_BITS+3)/4 + 2) word32 outSz = BENCH_ECCENCRYPT_OUT_SIZE; #ifdef WOLFSSL_SMALL_STACK @@ -14490,13 +14855,13 @@ void bench_eccEncrypt(int curveId) #endif keySize = wc_ecc_get_curve_size_from_id(curveId); - ret = wc_ecc_init_ex(userA, HEAP_HINT, devId); + ret = wc_ecc_init_ex(userA, HEAP_HINT, useDeviceID ? devId : INVALID_DEVID); if (ret != 0) { printf("wc_ecc_encrypt make key A failed: %d\n", ret); goto exit; } - ret = wc_ecc_init_ex(userB, HEAP_HINT, devId); + ret = wc_ecc_init_ex(userB, HEAP_HINT, useDeviceID ? devId : INVALID_DEVID); if (ret != 0) { printf("wc_ecc_encrypt make key B failed: %d\n", ret); goto exit; @@ -14561,8 +14926,12 @@ void bench_eccEncrypt(int curveId) { ecAES_256_CTR, "AES256CTR" }, #endif #endif + /* GCM works with any of the three nonce sources, so allow all of them + * here and an older build still benchmarks GCM. */ #if !defined(NO_AES) && defined(HAVE_AESGCM) && \ - defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) + (defined(WOLFSSL_ECIES_OLD) || \ + defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) || \ + defined(WOLFSSL_ECIES_GEN_IV)) #ifdef WOLFSSL_AES_128 { ecAES_128_GCM, "AES128GCM" }, #endif @@ -14599,18 +14968,21 @@ void bench_eccEncrypt(int curveId) for (c = 0; eciesCiphers[c].label != NULL; c++) { byte algo = eciesCiphers[c].algo; + /* Tag the KDF rows so they do not read as the default ones. */ + const char* modeTag = + (ctxMode == BENCH_ECIES_MODE_KDF) ? "-kdf" : ""; - (void)XSNPRINTF(encDesc, sizeof(encDesc), "%s-%s", desc[6], - eciesCiphers[c].label); - (void)XSNPRINTF(decDesc, sizeof(decDesc), "%s-%s", desc[7], - eciesCiphers[c].label); + (void)XSNPRINTF(encDesc, sizeof(encDesc), "%s-%s%s", desc[6], + eciesCiphers[c].label, modeTag); + (void)XSNPRINTF(decDesc, sizeof(decDesc), "%s-%s%s", desc[7], + eciesCiphers[c].label, modeTag); /* encrypt msg to B */ bench_stats_start(&count, &start); do { for (i = 0; i < ntimes; i++) { outSz = BENCH_ECCENCRYPT_OUT_SIZE; - ret = bench_ecies_prep(cliCtx, algo, fixedCliSalt, + ret = bench_ecies_prep(cliCtx, algo, ctxMode, fixedCliSalt, fixedSrvSalt); if (ret == 0) ret = wc_ecc_encrypt(userA, userB, msg, @@ -14627,8 +14999,8 @@ void bench_eccEncrypt(int curveId) || runs < minimum_runs #endif ); - bench_stats_asym_finish(name, keySize * 8, encDesc, 0, count, start, - ret); + bench_stats_asym_finish(name, keySize * 8, encDesc, useDeviceID, + count, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif @@ -14640,7 +15012,7 @@ void bench_eccEncrypt(int curveId) do { for (i = 0; i < ntimes; i++) { bench_plainSz = bench_size; - ret = bench_ecies_prep(srvCtx, algo, fixedSrvSalt, + ret = bench_ecies_prep(srvCtx, algo, ctxMode, fixedSrvSalt, fixedCliSalt); if (ret == 0) ret = wc_ecc_decrypt(userB, decPubKey, out, outSz, @@ -14657,8 +15029,8 @@ void bench_eccEncrypt(int curveId) || runs < minimum_runs #endif ); - bench_stats_asym_finish(name, keySize * 8, decDesc, 0, count, start, - ret); + bench_stats_asym_finish(name, keySize * 8, decDesc, useDeviceID, + count, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif @@ -14689,6 +15061,20 @@ void bench_eccEncrypt(int curveId) wc_ecc_free(userA); #endif } + +void bench_eccEncrypt(int useDeviceID, int curveId) +{ + bench_eccEncryptEx(useDeviceID, curveId, BENCH_ECIES_MODE_SALTX); +} + +#ifdef WC_BENCH_ECIES_KDF +/* Same ECIES run, keyed with a KDF salt and context instead of the client and + * server salt swap. That context carries no MAC salt, so hardware can take it. */ +void bench_eccEncryptKdf(int useDeviceID, int curveId) +{ + bench_eccEncryptEx(useDeviceID, curveId, BENCH_ECIES_MODE_KDF); +} +#endif #endif #ifdef WOLFSSL_SM2 @@ -15412,7 +15798,7 @@ void bench_ed448KeyGen(void) bench_stats_start(&count, &start); do { for (i = 0; i < genTimes; i++) { - wc_ed448_init(&genKey); + (void)wc_ed448_init_ex(&genKey, HEAP_HINT, INVALID_DEVID); (void)wc_ed448_make_key(&gRng, ED448_KEY_SIZE, &genKey); wc_ed448_free(&genKey); RECORD_MULTI_VALUE_STATS(); @@ -15430,7 +15816,7 @@ void bench_ed448KeyGen(void) #endif } -void bench_ed448KeySign(void) +void bench_ed448KeySign(int useDeviceID) { int ret; WC_DECLARE_VAR(genKey, ed448_key, 1, HEAP_HINT); @@ -15448,7 +15834,12 @@ void bench_ed448KeySign(void) WC_ALLOC_VAR(genKey, ed448_key, 1, HEAP_HINT); - wc_ed448_init(genKey); + ret = wc_ed448_init_ex(genKey, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); + if (ret != 0) { + printf("ed448_init_ex failed\n"); + goto exit; + } ret = wc_ed448_make_key(&gRng, ED448_KEY_SIZE, genKey); if (ret != 0) { @@ -15480,7 +15871,7 @@ void bench_ed448KeySign(void) #endif ); - bench_stats_asym_finish("ED", 448, desc[4], 0, count, start, ret); + bench_stats_asym_finish("ED", 448, desc[4], useDeviceID, count, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif @@ -15507,7 +15898,7 @@ void bench_ed448KeySign(void) #endif ); - bench_stats_asym_finish("ED", 448, desc[5], 0, count, start, ret); + bench_stats_asym_finish("ED", 448, desc[5], useDeviceID, count, start, ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif diff --git a/wolfcrypt/benchmark/benchmark.h b/wolfcrypt/benchmark/benchmark.h index b74bd5df0f..2a3722cc74 100644 --- a/wolfcrypt/benchmark/benchmark.h +++ b/wolfcrypt/benchmark/benchmark.h @@ -58,6 +58,8 @@ void bench_aesxts(void); void bench_aesctr(int useDeviceID); void bench_aescfb(void); void bench_aesofb(void); +void bench_aeskeywrap(int useDeviceID); +void bench_aeskeywrap_pad(int useDeviceID); void bench_aessiv(void); void bench_aesgcmsiv(void); void bench_poly1305(void); @@ -117,7 +119,10 @@ void bench_slhdsa(int param); void bench_ecc_curve(int curveId); void bench_eccMakeKey(int useDeviceID, int curveId); void bench_ecc(int useDeviceID, int curveId); -void bench_eccEncrypt(int curveId); +void bench_eccEncrypt(int useDeviceID, int curveId); +#ifdef WC_BENCH_ECIES_KDF +void bench_eccEncryptKdf(int useDeviceID, int curveId); +#endif void bench_sm2(int useDeviceID); void bench_curve25519KeyGen(int useDeviceID); void bench_curve25519KeyAgree(int useDeviceID); @@ -126,7 +131,7 @@ void bench_ed25519KeySign(int useDeviceID); void bench_curve448KeyGen(void); void bench_curve448KeyAgree(void); void bench_ed448KeyGen(void); -void bench_ed448KeySign(void); +void bench_ed448KeySign(int useDeviceID); void bench_eccsiKeyGen(void); void bench_eccsiPairGen(void); void bench_eccsiValidate(void); @@ -135,12 +140,12 @@ void bench_sakkeKeyGen(void); void bench_sakkeRskGen(void); void bench_sakkeValidate(void); void bench_sakke(void); -void bench_rng(void); +void bench_rng(int useDeviceID); void bench_rng_init(void); #if defined(WOLFSSL_DRBG_SHA512) && !defined(WC_NO_RNG) && \ !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) -void bench_rng_sha512(void); +void bench_rng_sha512(int useDeviceID); void bench_rng_sha512_init(void); #endif void bench_blake2b(void); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index ad6c34b071..32d006df13 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -15006,6 +15006,29 @@ int wc_ecc_ctx_get_info(ecEncCtx* ctx, const byte** info, word32* sz) return 0; } + +int wc_ecc_ctx_get_mac_salt(ecEncCtx* ctx, const byte** salt, word32* sz) +{ + if (ctx == NULL || salt == NULL || sz == NULL) + return BAD_FUNC_ARG; + + *salt = ctx->macSalt; + *sz = ctx->macSaltSz; + + return 0; +} + +/* Read the client or server role, which picks which half of the derived key + * this message uses. */ +int wc_ecc_ctx_get_protocol(ecEncCtx* ctx, int* protocol) +{ + if (ctx == NULL || protocol == NULL) + return BAD_FUNC_ARG; + + *protocol = ctx->protocol; + + return 0; +} #endif /* WOLF_CRYPTO_CB */ diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am index f0e7ca3c71..84bc01d75e 100644 --- a/wolfcrypt/src/include.am +++ b/wolfcrypt/src/include.am @@ -95,6 +95,18 @@ EXTRA_DIST += wolfcrypt/src/port/ti/ti-aes.c \ wolfcrypt/src/port/xilinx/xil-aesgcm.c \ wolfcrypt/src/port/xilinx/xil-versal-glue.c \ wolfcrypt/src/port/xilinx/xil-versal-trng.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cmac.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecc.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecdh.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecies.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hmac.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_rng.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_rsa.c \ + wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c \ wolfcrypt/src/port/caam/caam_aes.c \ wolfcrypt/src/port/caam/caam_driver.c \ wolfcrypt/src/port/caam/caam_error.c \ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md b/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md new file mode 100644 index 0000000000..b1830cdb5c --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md @@ -0,0 +1,160 @@ +# wolfSSL port for the Versal Gen 2 ASU + +Routes wolfCrypt operations to the ASU (Application Security Unit) on Versal +Gen 2 parts. The port registers a wolfSSL crypto callback device; anything the +ASU cannot do falls back to software, so a build with the port on still passes +the full wolfCrypt self-test. + +## Turning it on + +Define this in `user_settings.h`: + +```c +#define WOLFSSL_VERSAL_GEN2_ASU +``` + +That is the whole setup. `wolfCrypt_Init()` registers the device and brings the +ASU client up, so an application needs no ASU calls of its own: + +```c +ret = wolfCrypt_Init(); /* opens the mailbox, calls XAsu_ClientInit */ +``` + +The BSP must have the `xilasu` and `xilmailbox` libraries enabled. + +## What runs on hardware + +| Engine | Covered | +| --- | --- | +| Hash | SHA2-256/384/512, SHA3-256/384/512, SHAKE256 | +| HMAC | over SHA2-256/384/512 and SHA3-256/384/512 | +| AES | CBC, ECB, CTR, CFB, OFB, GCM, CCM | +| CMAC | AES-CMAC | +| RSA | raw modexp, plus PSS and OAEP padding when `WOLF_CRYPTO_CB_RSA_PAD` is set | +| ECDSA | NIST P-192/256/384, Brainpool P-256/320/384/512 | +| EdDSA | plain Ed25519 and Ed448 sign and verify | +| ECDH | the same curves as ECDSA | +| ECIES | AES-GCM with HKDF-SHA256 | +| TRNG | seed and random block | + +Anything outside this list is declined and wolfSSL runs it in software. That +includes AES-192, partial AES blocks, SHA-512/224 and 512/256, Keccak padding, +SHAKE128, deterministic ECDSA, and the older ECIES layouts. + +## Settings + +`WOLFSSL_VERSAL_GEN2_ASU` on its own offloads every engine above. Name one or +more of these instead and only those are offloaded: + +``` +WOLFSSL_VERSAL_GEN2_ASU_TRNG WOLFSSL_VERSAL_GEN2_ASU_CMAC +WOLFSSL_VERSAL_GEN2_ASU_HASH WOLFSSL_VERSAL_GEN2_ASU_RSA +WOLFSSL_VERSAL_GEN2_ASU_HMAC WOLFSSL_VERSAL_GEN2_ASU_ECC +WOLFSSL_VERSAL_GEN2_ASU_CIPHER +``` + +`_ECC` also covers ECDH, ECIES, Ed25519 and Ed448 when those features are +built. + +Ed25519 and Ed448 offload needs classic ECC turned on as well, because the +EdDSA handlers live in the same file as ECDSA and that file is built only when +`HAVE_ECC` is set. A build with EdDSA but no classic ECC still works, it just +runs EdDSA in software. + +Other switches: + +| Macro | Effect | +| --- | --- | +| `WOLFSSL_VERSAL_GEN2_ASU_DEVID` | device id for the callback, default `0x4153` | +| `WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR` | IPI channel, default `XPAR_XIPIPSU_0_BASEADDR` | +| `WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT` | the application calls `XAsu_ClientInit` itself | +| `WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD` | RSA on, padding in software | +| `WOLFSSL_VERSAL_GEN2_ASU_ECC_P521` | add P-521, off by default, see below | +| `WOLFSSL_VERSAL_GEN2_ASU_DEBUG` | print every ASU operation over the UART | +| `WOLFSSL_VERSAL_GEN2_ASU_RTC` | supply the benchmark time source from the port | +| `XASU_DISABLE_CACHE` | cache is off, so skip all buffer flush and reload work | + +The port sets `WOLF_CRYPTO_CB`, `WOLF_CRYPTO_CB_CMD`, `WOLF_CRYPTO_CB_COPY` and +`WOLF_CRYPTO_CB_FREE` for you, and points `WC_USE_DEVID` at the ASU device so +the unmodified wolfCrypt test and benchmark route through it. + +## Known limits + +**P-521 is off by default.** Stock ASU firmware pads the digest wrong and the +client caps it at 64 bytes, which is short of the 66 P-521 needs. Turn it on +only with firmware that front-pads. + +**ECIES needs the KDF context path.** See below. + +## ECIES on hardware + +The ASU runs ECIES as one command: ECDH, then HKDF, then AES-GCM. It has no +input for extra authenticated data, so the context has to be keyed in the way +that leaves the MAC salt empty. + +Use `wc_ecc_ctx_set_kdf_salt`, not `wc_ecc_ctx_set_peer_salt`: + +```c +ecEncCtx* ctx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng); + +wc_ecc_ctx_set_algo(ctx, ecAES_256_GCM, ecHKDF_SHA256, ecHMAC_SHA256); +wc_ecc_ctx_set_kdf_salt(ctx, salt, saltSz); +wc_ecc_ctx_set_info(ctx, info, infoSz); + +wc_ecc_encrypt(privKey, peerPubKey, msg, msgSz, out, &outSz, ctx); +``` + +The other side does the same with `REQ_RESP_SERVER` and the same salt and +context bytes, then calls `wc_ecc_decrypt`. + +The wolfCrypt benchmark keys ECIES the other way by default, so its ECIES rows +run in software. Build the benchmark with `WC_BENCH_ECIES_KDF` to add a second +set of rows, tagged `-kdf`, that use the context shown above and reach the ASU. + +What the offload requires: + +| Setting | Value | +| --- | --- | +| Scheme | `ecAES_128_GCM` or `ecAES_256_GCM` with `ecHKDF_SHA256` | +| KDF salt | `wc_ecc_ctx_set_kdf_salt`, passed through as given | +| KDF context | `wc_ecc_ctx_set_info`, must not be empty | +| MAC salt | must be empty, so no `wc_ecc_ctx_set_peer_salt` | +| Protocol | default, or `REQ_RESP_CLIENT` to encrypt and `REQ_RESP_SERVER` to decrypt | +| Curves | P-256, P-384, Brainpool P-256, Brainpool P-384 | + +`wc_ecc_ctx_set_peer_salt` is the usual wolfSSL way to key ECIES, and it sets a +MAC salt as a side effect. wolfSSL feeds that salt to AES-GCM as extra +authenticated data, which the ASU cannot accept, so the port declines and +wolfSSL runs ECIES in software. There is no way around this from the port. + +Declining is not the same as running with no hardware. The software ECIES path +still passes the device id to the AES and HMAC underneath, so those operations +go to the ASU one at a time. Only the single-command ECIES is lost. + +**The private key passed to encrypt is not used.** `wc_ecc_encrypt` takes a +private key, and software derives the shared secret from it and puts its public +point in the output. The ASU cannot be given that scalar: it generates its own +ephemeral key pair inside the single ECIES command and returns that public key +in the output instead. The peer decrypts against the returned key, so the +exchange works and matches software on the wire, but the key you passed in does +not appear in the result. Decrypt is not affected, and uses the private key you +supply. + +## Files + +``` +asu_cryptocb.c device registration and the callback dispatcher +asu_util.c client bring-up, waiting, cache handling, timer +asu_hash.c SHA2, SHA3, SHAKE256 +asu_hmac.c HMAC +asu_cipher.c AES +asu_cmac.c AES-CMAC +asu_rsa.c RSA +asu_ecc.c ECDSA, Ed25519, Ed448 +asu_ecdh.c ECDH +asu_ecies.c ECIES +asu_rng.c TRNG +``` + +Headers live in `wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/`, with the +build settings in `asu_settings.h`. diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c index 5119c451f1..30df68a7a0 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c @@ -54,8 +54,7 @@ typedef struct { XAsu_AesKeyObject keyObj; } AsuCipherReq; -/* Hands one AES request to the ASU queue. wc_AsuTransact calls this while it - * holds the submit lock, so this must only queue the request and return. */ +/* Queue one ASU AES operation. The lock is held here, so only queue it. */ static int wc_AsuCipherSubmit(XAsu_ClientParams* params, void* ctx) { AsuCipherReq* req = (AsuCipherReq*)ctx; @@ -138,8 +137,8 @@ static int wc_AsuCipherOneShot(Aes* aes, byte* out, const byte* in, word32 sz, WC_ASU_PRINTF("[ASU] cipher mode=%d enc=%d keyLen=%u sz=%u\r\n", (int)engineMode, enc, (unsigned int)aes->keylen, (unsigned int)sz); - /* The ASU reads the key object, key, IV and input straight from RAM, so - * flush our cached copies out first; drop the cached output after the op. */ + /* The ASU reads the key, IV and input from memory, so push them out first, + * then reload the output afterward. */ wc_AsuCacheFlush(aes->devKey, aes->keylen); wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); if (iv != NULL) { @@ -166,7 +165,7 @@ static int wc_AsuCipherCbc(wc_CryptoInfo* info) int ret; byte lastBlock[WC_AES_BLOCK_SIZE]; - /* Reference info->cipher.aescbc fields directly; no aliasing locals. */ + /* Read the info fields directly, no copies into locals. */ if (info == NULL || info->cipher.aescbc.aes == NULL || info->cipher.aescbc.out == NULL || info->cipher.aescbc.in == NULL) { return BAD_FUNC_ARG; @@ -203,6 +202,7 @@ static int wc_AsuCipherCbc(wc_CryptoInfo* info) return 0; } +#ifdef HAVE_AES_ECB /* AES-ECB. No IV and no chaining state. */ static int wc_AsuCipherEcb(wc_CryptoInfo* info) { @@ -215,6 +215,7 @@ static int wc_AsuCipherEcb(wc_CryptoInfo* info) info->cipher.aesecb.in, info->cipher.aesecb.sz, info->cipher.enc, (u8)XASU_AES_ECB_MODE, NULL); } +#endif /* HAVE_AES_ECB */ #ifdef WOLFSSL_AES_COUNTER /* Add n to the 16-byte counter, starting at the last byte and carrying toward @@ -478,7 +479,7 @@ static int wc_AsuCipherGcm(wc_CryptoInfo* info) (unsigned int)info->cipher.aesgcm_enc.authInSz, (unsigned int)info->cipher.aesgcm_enc.authTagSz); - /* The ASU DMAs key, IV, AAD, input (and the tag on decrypt) from memory. */ + /* The ASU reads the key, IV, AAD and input from memory. */ wc_AsuCacheFlush(info->cipher.aesgcm_enc.aes->devKey, info->cipher.aesgcm_enc.aes->keylen); wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); @@ -504,7 +505,7 @@ static int wc_AsuCipherGcm(wc_CryptoInfo* info) status = wc_AsuTransact(wc_AsuCipherSubmit, &req, &addl); - /* Invalidate the CPU's view of the ASU-written output (and the tag on encrypt). */ + /* Reload the output the ASU wrote. */ if (info->cipher.aesgcm_enc.sz != 0) { wc_AsuCacheInvalidate(info->cipher.aesgcm_enc.out, info->cipher.aesgcm_enc.sz); @@ -571,12 +572,12 @@ static int wc_AsuCipherCcm(wc_CryptoInfo* info) return BAD_FUNC_ARG; } - /* Tag-only (no data, no AAD) is not something the engine accepts; software. */ + /* The engine needs some data or AAD, so a tag on its own runs in software. */ if (info->cipher.aesccm_enc.sz == 0 && info->cipher.aesccm_enc.authInSz == 0) { return CRYPTOCB_UNAVAILABLE; } - /* Oversized transfers exceed the ASU DMA limit; software handles them. */ + /* Transfers too big for the ASU run in software. */ if (info->cipher.aesccm_enc.sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH || info->cipher.aesccm_enc.authInSz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) { return CRYPTOCB_UNAVAILABLE; @@ -635,7 +636,7 @@ static int wc_AsuCipherCcm(wc_CryptoInfo* info) (unsigned int)info->cipher.aesccm_enc.authInSz, (unsigned int)info->cipher.aesccm_enc.authTagSz); - /* The ASU DMAs key, nonce, AAD, input (and the tag on decrypt) from memory. */ + /* The ASU reads the key, nonce, AAD and input from memory. */ wc_AsuCacheFlush(info->cipher.aesccm_enc.aes->devKey, info->cipher.aesccm_enc.aes->keylen); wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); @@ -661,7 +662,7 @@ static int wc_AsuCipherCcm(wc_CryptoInfo* info) status = wc_AsuTransact(wc_AsuCipherSubmit, &req, &addl); - /* Invalidate the CPU's view of the ASU-written output (and the tag on encrypt). */ + /* Reload the output the ASU wrote. */ if (info->cipher.aesccm_enc.sz != 0) { wc_AsuCacheInvalidate(info->cipher.aesccm_enc.out, info->cipher.aesccm_enc.sz); @@ -692,8 +693,7 @@ static int wc_AsuCipherCcm(wc_CryptoInfo* info) } #endif /* HAVE_AESCCM */ -/* Single entry point for the cipher engine, reached through the crypto callback - * dispatcher. */ +/* Entry point for the AES engine. */ int wc_AsuCipher(wc_CryptoInfo* info) { if (info == NULL) { diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c index ef72f49b21..81ca0fda3c 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c @@ -49,14 +49,53 @@ #if defined(WOLFSSL_VERSAL_GEN2_ASU_RSA) && !defined(NO_RSA) #include #endif +#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && defined(HAVE_ECC) && \ + !defined(NO_ECC) + #include +#endif +#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECDH + #include +#endif +#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECIES + #include +#endif + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT + #include +#endif #ifndef WOLF_CRYPTO_CB #error "WOLFSSL_VERSAL_GEN2_ASU requires WOLF_CRYPTO_CB" #endif +#ifndef WOLF_CRYPTO_CB_CMD + #error "WOLFSSL_VERSAL_GEN2_ASU requires WOLF_CRYPTO_CB_CMD" +#endif -/* Route a context copy (WC_ALGO_TYPE_COPY) to the engine that owns the object, - * keyed on the copy sub-algo. Each engine's single entry handles the copy. - * Engines added later (cipher, pk) get a case here. */ +/* Device commands. Register starts the ASU client, and an error here undoes + * the registration. */ +static int wc_AsuCmd(wc_CryptoInfo* info) +{ + int ret = CRYPTOCB_UNAVAILABLE; + + switch (info->cmd.type) { + case WC_CRYPTOCB_CMD_TYPE_REGISTER: + #ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT + ret = wc_AsuClientInit(); + #else + ret = 0; /* application brought the client up before this */ + #endif + break; + case WC_CRYPTOCB_CMD_TYPE_UNREGISTER: + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Send a context copy to whichever engine owns it. */ static int wc_AsuCopy(wc_CryptoInfo* info) { int ret = CRYPTOCB_UNAVAILABLE; @@ -79,8 +118,7 @@ static int wc_AsuCopy(wc_CryptoInfo* info) return ret; } -/* Route a context free (WC_ALGO_TYPE_FREE) to the engine that owns the object, - * keyed on the free sub-algo, the same way as wc_AsuCopy. */ +/* Send a context free to whichever engine owns it. */ static int wc_AsuFree(wc_CryptoInfo* info) { int ret = CRYPTOCB_UNAVAILABLE; @@ -108,11 +146,8 @@ static int wc_AsuFree(wc_CryptoInfo* info) return ret; } -/* Crypto callback dispatcher. Each engine handler runs the full operation - * (looping over ASU transactions as needed) and returns the wolfCrypt result: - * 0 when the ASU handled it, CRYPTOCB_UNAVAILABLE to fall back to software, or a - * negative error. Engine cases are filled in per milestone: M1 hash and rng, - * M2 aes, M3 public key. */ +/* Main dispatcher. Returns 0 when handled, CRYPTOCB_UNAVAILABLE to use + * software, or a negative error. */ static int wc_AsuCryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) { int ret = CRYPTOCB_UNAVAILABLE; @@ -125,41 +160,60 @@ static int wc_AsuCryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) } switch (info->algo_type) { - case WC_ALGO_TYPE_HASH: /* M1 asu_hash */ + case WC_ALGO_TYPE_NONE: /* register/unregister device commands */ + ret = wc_AsuCmd(info); + break; + case WC_ALGO_TYPE_HASH: /* asu_hash */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_HASH ret = wc_AsuHash(info); #endif break; - case WC_ALGO_TYPE_HMAC: /* M1 asu_hmac */ + case WC_ALGO_TYPE_HMAC: /* asu_hmac */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_HMAC ret = wc_AsuHmac(info); #endif break; - case WC_ALGO_TYPE_SEED: /* M1 asu_rng */ - case WC_ALGO_TYPE_RNG: /* M1 asu_rng */ + case WC_ALGO_TYPE_SEED: /* asu_rng */ + case WC_ALGO_TYPE_RNG: /* asu_rng */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_TRNG ret = wc_AsuRng(info); #endif break; - case WC_ALGO_TYPE_CIPHER: /* M2 asu_cipher */ + case WC_ALGO_TYPE_CIPHER: /* asu_cipher */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER ret = wc_AsuCipher(info); #endif break; - case WC_ALGO_TYPE_CMAC: /* M2 asu_cmac */ + case WC_ALGO_TYPE_CMAC: /* asu_cmac */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC ret = wc_AsuCmac(info); #endif break; - case WC_ALGO_TYPE_PK: /* M3 asu_rsa and asu_ecc */ + case WC_ALGO_TYPE_PK: /* asu_rsa, asu_ecc, asu_ecdh, asu_ecies */ #if defined(WOLFSSL_VERSAL_GEN2_ASU_RSA) && !defined(NO_RSA) ret = wc_AsuRsa(info); + #endif + #if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && defined(HAVE_ECC) && \ + !defined(NO_ECC) + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = wc_AsuEcc(info); + } + #endif + #ifdef WC_ASU_ECDH_ENABLED + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = wc_AsuEcdh(info); + } + #endif + #ifdef WC_ASU_ECIES_ENABLED + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = wc_AsuEcies(info); + } #endif break; - case WC_ALGO_TYPE_COPY: /* context copy: route by sub-algo to its engine */ + case WC_ALGO_TYPE_COPY: /* send the copy to its engine */ ret = wc_AsuCopy(info); break; - case WC_ALGO_TYPE_FREE: /* context free: route by sub-algo to its engine */ + case WC_ALGO_TYPE_FREE: /* send the free to its engine */ ret = wc_AsuFree(info); break; default: diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecc.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecc.c new file mode 100644 index 0000000000..303214fa4f --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecc.c @@ -0,0 +1,947 @@ +/* asu_ecc.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECDSA and EdDSA on the ASU. wolfSSL uses DER signatures and the ASU uses + * raw r||s, so this file converts between the two. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +/* A build can still turn ECC off, so check both macros here. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && defined(HAVE_ECC) && \ + !defined(NO_ECC) + +#include +#include +#include +#include +#include +#ifdef HAVE_ED25519 +#include +#endif +#ifdef HAVE_ED448 +#include +#endif + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include "xasu_ecc.h" +#include "xasu_eccinfo.h" +#include "xasu_shainfo.h" +#include "xasu_status.h" +#include "xstatus.h" + +/* Which ASU call the thunk should make. */ +#define WC_ASU_ECC_OP_SIGN 0 /* XAsu_EccGenSign */ +#define WC_ASU_ECC_OP_VERIFY 1 /* XAsu_EccVerifySign */ + +/* Biggest curve we support, P-521 at 66 bytes. */ +#define WC_ASU_ECC_MAX_KEYLEN XASU_ECC_P521_SIZE_IN_BYTES + +/* One ASU ECC request. The buffers live on the heap so the ASU can reach them. */ +typedef struct { + XAsu_EccParams params; + /* private key for sign, or public point for verify */ + byte key[2U * WC_ASU_ECC_MAX_KEYLEN]; + byte digest[XASU_SHA_512_HASH_LEN]; /* message digest, <= 64 bytes */ + /* Keep the result on its own cache line, away from the key above. */ + WC_ASU_ALIGN64 byte sign[2U * WC_ASU_ECC_MAX_KEYLEN]; /* r||s, DMA out */ + int op; +} AsuEccReq; + +/* Holds both pointers so they stay together. Use .req, free .raw. */ +typedef struct { + void* raw; /* what XMALLOC gave back, free this one */ + AsuEccReq* req; /* the aligned request the operation uses */ +} AsuEccMem; + +/* Align the request to 64 bytes so the sign buffer gets its own cache line. + * Returns 0 or MEMORY_E. */ +static int wc_AsuEccReqNew(AsuEccMem* mem) +{ + if (mem == NULL) { + return BAD_FUNC_ARG; + } +#ifdef WC_ASU_DISABLE_CACHE + /* Cache is off, so a plain malloc is fine. */ + mem->raw = XMALLOC(sizeof(AsuEccReq), NULL, DYNAMIC_TYPE_TMP_BUFFER); +#else + mem->raw = XMALLOC(sizeof(AsuEccReq) + 63U, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (mem->raw == NULL) { + mem->req = NULL; + return MEMORY_E; + } +#ifdef WC_ASU_DISABLE_CACHE + mem->req = (AsuEccReq*)mem->raw; +#else + mem->req = (AsuEccReq*)(void*)(((UINTPTR)mem->raw + 63U) & ~(UINTPTR)63U); +#endif + return 0; +} + +/* Wipe the request since it may hold the private key, then free it. */ +static void wc_AsuEccReqFree(AsuEccMem* mem) +{ + if (mem == NULL || mem->req == NULL) { + return; + } + ForceZero(mem->req, sizeof(*mem->req)); + wc_AsuCacheFlush(mem->req, sizeof(*mem->req)); + XFREE(mem->raw, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +/* Queue one ASU ECC operation. */ +static int wc_AsuEccSubmit(XAsu_ClientParams* params, void* ctx) +{ + AsuEccReq* req = (AsuEccReq*)ctx; + + if (params == NULL || req == NULL) { + return XST_FAILURE; + } + switch (req->op) { + case WC_ASU_ECC_OP_SIGN: + return XAsu_EccGenSign(params, &req->params); + case WC_ASU_ECC_OP_VERIFY: + return XAsu_EccVerifySign(params, &req->params); + default: + return XST_FAILURE; + } +} + +/* The DER helpers are gone under NO_ASN, so ECDSA runs in software there. + * EdDSA uses raw signatures and still works. */ +#if (defined(HAVE_ECC_SIGN) || defined(HAVE_ECC_VERIFY)) && !defined(NO_ASN) +/* Turn the wolfSSL curve id into an ASU curve type and size. Curves we do not + * support return an error so wolfSSL uses software. */ +static int wc_AsuEccCurve(ecc_key* key, u32* curveType, u32* keyLen) +{ + u32 type; + u32 len; + + if (key == NULL || curveType == NULL || keyLen == NULL) { + return BAD_FUNC_ARG; + } + if (key->dp == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + switch (key->dp->id) { + case ECC_SECP192R1: + type = (u32)XASU_ECC_NIST_P192; + len = (u32)XASU_ECC_P192_SIZE_IN_BYTES; + break; + case ECC_SECP256R1: + type = (u32)XASU_ECC_NIST_P256; + len = (u32)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_SECP384R1: + type = (u32)XASU_ECC_NIST_P384; + len = (u32)XASU_ECC_P384_SIZE_IN_BYTES; + break; +#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521 + /* Off by default. Stock firmware pads the digest wrong and caps it at + * 64 bytes, which is too small for P-521. */ + case ECC_SECP521R1: + type = (u32)XASU_ECC_NIST_P521; + len = (u32)XASU_ECC_P521_SIZE_IN_BYTES; + break; +#endif +#ifdef HAVE_ECC_BRAINPOOL + /* Brainpool curves work like the NIST ones and all fit in 64 bytes. */ + case ECC_BRAINPOOLP256R1: + type = (u32)XASU_ECC_BRAINPOOL_P256; + len = (u32)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP320R1: + type = (u32)XASU_ECC_BRAINPOOL_P320; + len = (u32)XASU_ECC_P320_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP384R1: + type = (u32)XASU_ECC_BRAINPOOL_P384; + len = (u32)XASU_ECC_P384_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP512R1: + type = (u32)XASU_ECC_BRAINPOOL_P512; + len = (u32)XASU_ECC_P512_SIZE_IN_BYTES; + break; +#endif + default: + return CRYPTOCB_UNAVAILABLE; + } + if ((u32)key->dp->size != len) { + return CRYPTOCB_UNAVAILABLE; + } + *curveType = type; + *keyLen = len; + return 0; +} + +/* The ASU will sign an all zero digest but then fail to verify it, so send + * those to software, which turns them down. */ +static int wc_AsuEccDigestIsZero(const byte* hash, word32 hashLen) +{ + word32 i; + byte acc = 0; + + for (i = 0; i < hashLen; i++) { + acc |= hash[i]; + } + + return acc == 0; +} + +/* Pad a short digest on the left to the curve size so the ASU reads the same + * number as software. A longer digest keeps its leading bytes. */ +static void wc_AsuEccDigest(const byte* hash, word32 hashLen, byte* out, + u32 width) +{ + XMEMSET(out, 0, width); + if (hashLen >= width) { + XMEMCPY(out, hash, width); + } + else { + XMEMCPY(out + (width - hashLen), hash, hashLen); + } +} +#endif /* (HAVE_ECC_SIGN || HAVE_ECC_VERIFY) && !NO_ASN */ + +#if defined(HAVE_ECC_SIGN) && !defined(NO_ASN) +/* ECDSA sign. The ASU returns raw r||s, which we then encode as DER. */ +static int wc_AsuEccSign(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ecc_key* key = info->pk.eccsign.key; + u32 curveType = 0; + u32 keyLen = 0; + u32 digLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (key == NULL || info->pk.eccsign.in == NULL || + info->pk.eccsign.out == NULL || info->pk.eccsign.outlen == NULL) { + return BAD_FUNC_ARG; + } + if (info->pk.eccsign.inlen == 0) { + return CRYPTOCB_UNAVAILABLE; + } + /* wolfSSL checks this after the callback, so signing needs a private key + * here or the ASU would be handed a zero scalar. */ + if (key->type != ECC_PRIVATEKEY && key->type != ECC_PRIVATEKEY_ONLY) { + return CRYPTOCB_UNAVAILABLE; + } + if (wc_AsuEccDigestIsZero(info->pk.eccsign.in, info->pk.eccsign.inlen)) { + return CRYPTOCB_UNAVAILABLE; + } + /* The ASU picks its own random k, so a deterministic or caller set k has + * to run in software. */ +#if defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) + if (key->deterministic) { + return CRYPTOCB_UNAVAILABLE; + } +#endif +#if defined(WOLFSSL_ECDSA_SET_K) || defined(WOLFSSL_ECDSA_SET_K_ONE_LOOP) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) +#ifdef WOLFSSL_NO_MALLOC + if (key->sign_k_set) { + return CRYPTOCB_UNAVAILABLE; + } +#else + if (key->sign_k != NULL) { + return CRYPTOCB_UNAVAILABLE; + } +#endif +#endif + + ret = wc_AsuEccCurve(key, &curveType, &keyLen); + if (ret != 0) { + return ret; + } + /* Read the private key and check it using the exported bytes, which keeps + * the timing steady. */ + /* Digest size for the ASU: the curve size, capped at the ASU limit. */ + digLen = keyLen; + if (digLen > (u32)XASU_SHA_512_HASH_LEN) { + digLen = (u32)XASU_SHA_512_HASH_LEN; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + + XMEMSET(mem.req, 0, sizeof(*mem.req)); + /* If the key does not fit in keyLen bytes we fall back to software. */ + if (mp_to_unsigned_bin_len(wc_ecc_key_get_priv(key), mem.req->key, + (int)keyLen) < 0) { + wc_AsuEccReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + wc_AsuEccDigest(info->pk.eccsign.in, info->pk.eccsign.inlen, + mem.req->digest, digLen); + + mem.req->op = WC_ASU_ECC_OP_SIGN; + mem.req->params.CurveType = curveType; + mem.req->params.KeyLen = keyLen; + mem.req->params.DigestLen = digLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)mem.req->digest; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ecc sign curve=%u keyLen=%u digestLen=%u\r\n", + (unsigned int)curveType, (unsigned int)keyLen, (unsigned int)digLen); + + wc_AsuCacheFlush(mem.req->key, keyLen); + wc_AsuCacheFlush(mem.req->digest, digLen); + wc_AsuCacheFlush(mem.req->sign, 2U * keyLen); + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + + wc_AsuCacheInvalidate(mem.req->sign, 2U * keyLen); + + WC_ASU_PRINTF("[ASU] ecc sign st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + if (status != XST_SUCCESS) { + /* Inputs were already checked, so this is a real hardware error. */ + wc_AsuEccReqFree(&mem); + return WC_HW_E; + } + /* Encode the raw r||s from the ASU as a DER signature. */ + ret = wc_ecc_rs_raw_to_sig(mem.req->sign, keyLen, mem.req->sign + keyLen, + keyLen, info->pk.eccsign.out, info->pk.eccsign.outlen); + + wc_AsuEccReqFree(&mem); + return ret; +} +#endif /* HAVE_ECC_SIGN && !NO_ASN */ + +#if defined(HAVE_ECC_VERIFY) && !defined(NO_ASN) +/* ECDSA verify. The DER signature is turned into raw r||s for the ASU, and + * res is set to 1 only when the ASU says the signature is good. */ +static int wc_AsuEccVerify(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ecc_key* key = info->pk.eccverify.key; + u32 curveType = 0; + u32 keyLen = 0; + u32 digLen; + word32 rLen; + word32 sLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (info->pk.eccverify.res == NULL) { + return BAD_FUNC_ARG; + } + *info->pk.eccverify.res = 0; + + if (key == NULL || info->pk.eccverify.sig == NULL || + info->pk.eccverify.hash == NULL) { + return BAD_FUNC_ARG; + } + if (info->pk.eccverify.hashlen == 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (wc_AsuEccDigestIsZero(info->pk.eccverify.hash, + info->pk.eccverify.hashlen)) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = wc_AsuEccCurve(key, &curveType, &keyLen); + if (ret != 0) { + return ret; + } + /* Digest size for the ASU: the curve size, capped at the ASU limit. */ + digLen = keyLen; + if (digLen > (u32)XASU_SHA_512_HASH_LEN) { + digLen = (u32)XASU_SHA_512_HASH_LEN; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + + XMEMSET(mem.req, 0, sizeof(*mem.req)); + /* Verify needs the public key. Without it the buffer would be all zeros, + * so let software handle it. */ + if (mp_iszero(key->pubkey.x) && mp_iszero(key->pubkey.y)) { + wc_AsuEccReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + /* Public key Qx||Qy, each padded with zeros on the left. */ + if (mp_to_unsigned_bin_len(key->pubkey.x, mem.req->key, (int)keyLen) < 0 || + mp_to_unsigned_bin_len(key->pubkey.y, mem.req->key + keyLen, + (int)keyLen) < 0) { + wc_AsuEccReqFree(&mem); + return WC_HW_E; + } + + /* Turn the DER signature into raw r and s, each keyLen bytes long. */ + rLen = keyLen; + sLen = keyLen; + ret = wc_ecc_sig_to_rs(info->pk.eccverify.sig, info->pk.eccverify.siglen, + mem.req->sign, &rLen, mem.req->sign + keyLen, &sLen); + if (ret != 0 || rLen > keyLen || sLen > keyLen) { + wc_AsuEccReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + if (rLen < keyLen) { + XMEMMOVE(mem.req->sign + (keyLen - rLen), mem.req->sign, rLen); + XMEMSET(mem.req->sign, 0, keyLen - rLen); + } + if (sLen < keyLen) { + XMEMMOVE(mem.req->sign + keyLen + (keyLen - sLen), + mem.req->sign + keyLen, sLen); + XMEMSET(mem.req->sign + keyLen, 0, keyLen - sLen); + } + + wc_AsuEccDigest(info->pk.eccverify.hash, info->pk.eccverify.hashlen, + mem.req->digest, digLen); + + mem.req->op = WC_ASU_ECC_OP_VERIFY; + mem.req->params.CurveType = curveType; + mem.req->params.KeyLen = keyLen; + mem.req->params.DigestLen = digLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)mem.req->digest; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ecc verify curve=%u keyLen=%u digestLen=%u\r\n", + (unsigned int)curveType, (unsigned int)keyLen, (unsigned int)digLen); + + wc_AsuCacheFlush(mem.req->key, 2U * keyLen); + wc_AsuCacheFlush(mem.req->digest, digLen); + wc_AsuCacheFlush(mem.req->sign, 2U * keyLen); + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + + WC_ASU_PRINTF("[ASU] ecc verify st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + /* Only VERIFIED sets res to 1. A finished check that says no is just a + * bad signature. An addl of 0 means the request never ran. */ + if (status == XST_SUCCESS && addl == (word32)XASU_ECC_SIGNATURE_VERIFIED) { + *info->pk.eccverify.res = 1; + ret = 0; + } + else if (addl == 0) { + ret = WC_HW_E; + } + else { + ret = 0; + } + + wc_AsuEccReqFree(&mem); + return ret; +} +#endif /* HAVE_ECC_VERIFY && !NO_ASN */ + +#if defined(HAVE_ED25519) && !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ED25519) + +/* Ed25519 sign. The ASU hashes the message itself, so the message goes in the + * digest field and the 32 byte seed goes in the key field. */ +static int wc_AsuEd25519Sign(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ed25519_key* key = info->pk.ed25519sign.key; + byte* msg = NULL; + word32 msgLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (key == NULL || info->pk.ed25519sign.out == NULL || + info->pk.ed25519sign.outLen == NULL) { + return BAD_FUNC_ARG; + } + /* The ASU only does plain Ed25519. Context and prehash go to software. */ + if (info->pk.ed25519sign.type != (byte)Ed25519 || + info->pk.ed25519sign.contextLen != 0) { + return CRYPTOCB_UNAVAILABLE; + } + /* Signing needs both the seed and the public key, same as software. */ + if (key->privKeySet == 0 || key->pubKeySet == 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (*info->pk.ed25519sign.outLen < ED25519_SIG_SIZE) { + return CRYPTOCB_UNAVAILABLE; + } + msgLen = info->pk.ed25519sign.inLen; + if (msgLen != 0 && info->pk.ed25519sign.in == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + /* Copy the message where the ASU can read it. An empty message still needs + * a valid pointer, so the zeroed digest field is used. */ + if (msgLen != 0) { + msg = (byte*)XMALLOC(msgLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (msg == NULL) { + wc_AsuEccReqFree(&mem); + return MEMORY_E; + } + XMEMCPY(msg, info->pk.ed25519sign.in, msgLen); + } + else { + msg = mem.req->digest; + } + + XMEMCPY(mem.req->key, key->k, ED25519_KEY_SIZE); + + mem.req->op = WC_ASU_ECC_OP_SIGN; + mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED25519; + mem.req->params.KeyLen = (u32)ED25519_KEY_SIZE; + mem.req->params.DigestLen = msgLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)msg; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ed25519 sign msgLen=%u\r\n", (unsigned int)msgLen); + + wc_AsuCacheFlush(mem.req->key, ED25519_KEY_SIZE); + wc_AsuCacheFlush(mem.req->sign, ED25519_SIG_SIZE); + if (msgLen != 0) { + wc_AsuCacheFlush(msg, msgLen); + } + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + + wc_AsuCacheInvalidate(mem.req->sign, ED25519_SIG_SIZE); + /* The ASU only read msg, so free it now. */ + if (msgLen != 0) { + XFREE(msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_ASU_PRINTF("[ASU] ed25519 sign st=%u\r\n", (unsigned int)status); + + if (status != XST_SUCCESS) { + /* Inputs were already checked, so this is a real hardware error. */ + wc_AsuEccReqFree(&mem); + return WC_HW_E; + } + XMEMCPY(info->pk.ed25519sign.out, mem.req->sign, ED25519_SIG_SIZE); + *info->pk.ed25519sign.outLen = ED25519_SIG_SIZE; + wc_AsuEccReqFree(&mem); + return 0; +} + +/* The ed25519 group order, low byte first. S must stay below it. */ +static const byte wc_AsuEd25519Order[ED25519_KEY_SIZE] = { + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 +}; + +/* Return 1 when S is too big, which wolfSSL treats as a bad argument. */ +static int wc_AsuEd25519NonCanonicalS(const byte* sig) +{ + int i; + for (i = (int)ED25519_KEY_SIZE - 1; i >= 0; i--) { + if (sig[ED25519_SIG_SIZE / 2 + i] > wc_AsuEd25519Order[i]) { + return 1; + } + if (sig[ED25519_SIG_SIZE / 2 + i] < wc_AsuEd25519Order[i]) { + return 0; + } + } + return 1; /* every byte matched, so S equals the order and is too big */ +} + +/* Ed25519 verify. The key buffer holds 32 zero bytes then the public key. */ +static int wc_AsuEd25519Verify(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ed25519_key* key = info->pk.ed25519verify.key; + byte* msg = NULL; + word32 msgLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (info->pk.ed25519verify.res == NULL) { + return BAD_FUNC_ARG; + } + *info->pk.ed25519verify.res = 0; + + if (key == NULL || info->pk.ed25519verify.sig == NULL) { + return BAD_FUNC_ARG; + } + if (info->pk.ed25519verify.type != (byte)Ed25519 || + info->pk.ed25519verify.contextLen != 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (key->pubKeySet == 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (info->pk.ed25519verify.sigLen != ED25519_SIG_SIZE) { + return CRYPTOCB_UNAVAILABLE; + } + msgLen = info->pk.ed25519verify.msgLen; + if (msgLen != 0 && info->pk.ed25519verify.msg == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + if (msgLen != 0) { + msg = (byte*)XMALLOC(msgLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (msg == NULL) { + wc_AsuEccReqFree(&mem); + return MEMORY_E; + } + XMEMCPY(msg, info->pk.ed25519verify.msg, msgLen); + } + else { + msg = mem.req->digest; + } + + /* Zeros in the first half, the public key in the second. */ + XMEMCPY(mem.req->key + ED25519_PUB_KEY_SIZE, key->p, ED25519_PUB_KEY_SIZE); + XMEMCPY(mem.req->sign, info->pk.ed25519verify.sig, ED25519_SIG_SIZE); + + mem.req->op = WC_ASU_ECC_OP_VERIFY; + mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED25519; + mem.req->params.KeyLen = (u32)ED25519_KEY_SIZE; + mem.req->params.DigestLen = msgLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)msg; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ed25519 verify msgLen=%u\r\n", (unsigned int)msgLen); + + wc_AsuCacheFlush(mem.req->key, 2U * ED25519_KEY_SIZE); + wc_AsuCacheFlush(mem.req->sign, ED25519_SIG_SIZE); + if (msgLen != 0) { + wc_AsuCacheFlush(msg, msgLen); + } + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + /* The ASU only read msg, so free it now. */ + if (msgLen != 0) { + XFREE(msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_ASU_PRINTF("[ASU] ed25519 verify st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + /* Only VERIFIED passes. An S that is too big is a bad argument, and + * anything else is just a bad signature. */ + if (status == XST_SUCCESS && addl == (word32)XASU_ECC_SIGNATURE_VERIFIED) { + *info->pk.ed25519verify.res = 1; + ret = 0; + } + else if (addl == 0) { + ret = WC_HW_E; + } + else if (wc_AsuEd25519NonCanonicalS(info->pk.ed25519verify.sig)) { + ret = BAD_FUNC_ARG; + } + else { + ret = SIG_VERIFY_E; + } + + wc_AsuEccReqFree(&mem); + return ret; +} + +#endif /* HAVE_ED25519 && !NO_ED25519 */ + +#if defined(HAVE_ED448) && !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ED448) + +/* Ed448 sign. Same idea as Ed25519, with a 57 byte seed. */ +static int wc_AsuEd448Sign(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ed448_key* key = info->pk.ed448sign.key; + byte* msg = NULL; + word32 msgLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (key == NULL || info->pk.ed448sign.out == NULL || + info->pk.ed448sign.outLen == NULL) { + return BAD_FUNC_ARG; + } + /* The ASU only does plain Ed448. Context and prehash go to software. */ + if (info->pk.ed448sign.type != (byte)Ed448 || + info->pk.ed448sign.contextLen != 0) { + return CRYPTOCB_UNAVAILABLE; + } + /* Signing needs both the seed and the public key, same as software. */ + if (key->privKeySet == 0 || key->pubKeySet == 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (*info->pk.ed448sign.outLen < ED448_SIG_SIZE) { + return CRYPTOCB_UNAVAILABLE; + } + msgLen = info->pk.ed448sign.inLen; + if (msgLen != 0 && info->pk.ed448sign.in == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + if (msgLen != 0) { + msg = (byte*)XMALLOC(msgLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (msg == NULL) { + wc_AsuEccReqFree(&mem); + return MEMORY_E; + } + XMEMCPY(msg, info->pk.ed448sign.in, msgLen); + } + else { + msg = mem.req->digest; + } + + XMEMCPY(mem.req->key, key->k, ED448_KEY_SIZE); + + mem.req->op = WC_ASU_ECC_OP_SIGN; + mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED448; + mem.req->params.KeyLen = (u32)ED448_KEY_SIZE; + mem.req->params.DigestLen = msgLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)msg; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ed448 sign msgLen=%u\r\n", (unsigned int)msgLen); + + wc_AsuCacheFlush(mem.req->key, ED448_KEY_SIZE); + wc_AsuCacheFlush(mem.req->sign, ED448_SIG_SIZE); + if (msgLen != 0) { + wc_AsuCacheFlush(msg, msgLen); + } + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + + wc_AsuCacheInvalidate(mem.req->sign, ED448_SIG_SIZE); + /* The ASU only read msg, so free it now. */ + if (msgLen != 0) { + XFREE(msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_ASU_PRINTF("[ASU] ed448 sign st=%u\r\n", (unsigned int)status); + + if (status != XST_SUCCESS) { + /* Inputs were already checked, so this is a real hardware error. */ + wc_AsuEccReqFree(&mem); + return WC_HW_E; + } + XMEMCPY(info->pk.ed448sign.out, mem.req->sign, ED448_SIG_SIZE); + *info->pk.ed448sign.outLen = ED448_SIG_SIZE; + wc_AsuEccReqFree(&mem); + return 0; +} + +/* The ed448 group order, low byte first. S must stay below it. */ +static const byte wc_AsuEd448Order[ED448_KEY_SIZE] = { + 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, + 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, + 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, + 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0x00 +}; + +/* Return 1 when S is too big, which wolfSSL treats as a bad argument. */ +static int wc_AsuEd448NonCanonicalS(const byte* sig) +{ + int i; + for (i = (int)ED448_KEY_SIZE - 1; i >= 0; i--) { + if (sig[ED448_SIG_SIZE / 2 + i] > wc_AsuEd448Order[i]) { + return 1; + } + if (sig[ED448_SIG_SIZE / 2 + i] < wc_AsuEd448Order[i]) { + return 0; + } + } + return 1; /* every byte matched, so S equals the order and is too big */ +} + +/* Ed448 verify. The key buffer holds 57 zero bytes then the public key. */ +static int wc_AsuEd448Verify(wc_CryptoInfo* info) +{ + AsuEccMem mem; + ed448_key* key = info->pk.ed448verify.key; + byte* msg = NULL; + word32 msgLen; + word32 status; + word32 addl = 0; + int ret = 0; + + if (info->pk.ed448verify.res == NULL) { + return BAD_FUNC_ARG; + } + *info->pk.ed448verify.res = 0; + + if (key == NULL || info->pk.ed448verify.sig == NULL) { + return BAD_FUNC_ARG; + } + if (info->pk.ed448verify.type != (byte)Ed448 || + info->pk.ed448verify.contextLen != 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (key->pubKeySet == 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (info->pk.ed448verify.sigLen != ED448_SIG_SIZE) { + return CRYPTOCB_UNAVAILABLE; + } + msgLen = info->pk.ed448verify.msgLen; + if (msgLen != 0 && info->pk.ed448verify.msg == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_AsuEccReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + if (msgLen != 0) { + msg = (byte*)XMALLOC(msgLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (msg == NULL) { + wc_AsuEccReqFree(&mem); + return MEMORY_E; + } + XMEMCPY(msg, info->pk.ed448verify.msg, msgLen); + } + else { + msg = mem.req->digest; + } + + /* Zeros in the first half, the public key in the second. */ + XMEMCPY(mem.req->key + ED448_PUB_KEY_SIZE, key->p, ED448_PUB_KEY_SIZE); + XMEMCPY(mem.req->sign, info->pk.ed448verify.sig, ED448_SIG_SIZE); + + mem.req->op = WC_ASU_ECC_OP_VERIFY; + mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED448; + mem.req->params.KeyLen = (u32)ED448_KEY_SIZE; + mem.req->params.DigestLen = msgLen; + mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key; + mem.req->params.DigestAddr = (u64)(UINTPTR)msg; + mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign; + + WC_ASU_PRINTF("[ASU] ed448 verify msgLen=%u\r\n", (unsigned int)msgLen); + + wc_AsuCacheFlush(mem.req->key, 2U * ED448_KEY_SIZE); + wc_AsuCacheFlush(mem.req->sign, ED448_SIG_SIZE); + if (msgLen != 0) { + wc_AsuCacheFlush(msg, msgLen); + } + + status = wc_AsuTransact(wc_AsuEccSubmit, mem.req, &addl); + /* The ASU only read msg, so free it now. */ + if (msgLen != 0) { + XFREE(msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_ASU_PRINTF("[ASU] ed448 verify st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + /* Same rules as Ed25519 above. */ + if (status == XST_SUCCESS && addl == (word32)XASU_ECC_SIGNATURE_VERIFIED) { + *info->pk.ed448verify.res = 1; + ret = 0; + } + else if (addl == 0) { + ret = WC_HW_E; + } + else if (wc_AsuEd448NonCanonicalS(info->pk.ed448verify.sig)) { + ret = BAD_FUNC_ARG; + } + else { + ret = SIG_VERIFY_E; + } + + wc_AsuEccReqFree(&mem); + return ret; +} + +#endif /* HAVE_ED448 && !NO_ED448 */ + +/* Entry point for ECC. Sends sign and verify to the ASU and lets software + * handle everything else. */ +int wc_AsuEcc(wc_CryptoInfo* info) +{ + if (info == NULL) { + return BAD_FUNC_ARG; + } + if (info->algo_type != WC_ALGO_TYPE_PK) { + return CRYPTOCB_UNAVAILABLE; + } + + switch (info->pk.type) { +#if defined(HAVE_ECC_SIGN) && !defined(NO_ASN) + case WC_PK_TYPE_ECDSA_SIGN: + return wc_AsuEccSign(info); +#endif +#if defined(HAVE_ECC_VERIFY) && !defined(NO_ASN) + case WC_PK_TYPE_ECDSA_VERIFY: + return wc_AsuEccVerify(info); +#endif +#if defined(HAVE_ED25519) && !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ED25519) + case WC_PK_TYPE_ED25519_SIGN: + return wc_AsuEd25519Sign(info); + case WC_PK_TYPE_ED25519_VERIFY: + return wc_AsuEd25519Verify(info); +#endif +#if defined(HAVE_ED448) && !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ED448) + case WC_PK_TYPE_ED448: + return wc_AsuEd448Sign(info); + case WC_PK_TYPE_ED448_VERIFY: + return wc_AsuEd448Verify(info); +#endif + default: + return CRYPTOCB_UNAVAILABLE; + } +} + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_ECC && HAVE_ECC && !NO_ECC */ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecdh.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecdh.c new file mode 100644 index 0000000000..cde9029356 --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecdh.c @@ -0,0 +1,289 @@ +/* asu_ecdh.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECDH on the ASU. It multiplies our private key by the peer public key and + * returns the X coordinate, which is the shared secret. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +/* asu_ecdh.h works out WC_ASU_ECDH_ENABLED from the ECC macros. */ +#include + +#ifdef WC_ASU_ECDH_ENABLED + +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include "xasu_ecc.h" +#include "xasu_eccinfo.h" +#include "xasu_status.h" +#include "xstatus.h" + +/* Biggest curve we support, P-521 at 66 bytes. */ +#define WC_ASU_ECDH_MAX_KEYLEN XASU_ECC_P521_SIZE_IN_BYTES + +/* One ASU ECDH request. The buffers live on the heap so the ASU can reach + * them. */ +typedef struct { + XAsu_EcdhParams params; + byte privKey[WC_ASU_ECDH_MAX_KEYLEN]; /* our private key */ + byte pubKey[2U * WC_ASU_ECDH_MAX_KEYLEN]; /* peer public point */ + /* Keep the shared secret on its own cache line, away from the key above. */ + /* shared secret (DMA result) */ + WC_ASU_ALIGN64 byte secret[WC_ASU_ECDH_MAX_KEYLEN]; +} AsuEcdhReq; + +/* Holds both pointers so they stay together. Use .req, free .raw. */ +typedef struct { + void* raw; /* what XMALLOC gave back, free this one */ + AsuEcdhReq* req; /* the aligned request the operation uses */ +} AsuEcdhMem; + +/* Align the request to 64 bytes so the secret gets its own cache line. + * Returns 0 or MEMORY_E. */ +static int wc_AsuEcdhReqNew(AsuEcdhMem* mem) +{ + if (mem == NULL) { + return BAD_FUNC_ARG; + } +#ifdef WC_ASU_DISABLE_CACHE + /* Cache is off, so a plain malloc is fine. */ + mem->raw = XMALLOC(sizeof(AsuEcdhReq), NULL, DYNAMIC_TYPE_TMP_BUFFER); +#else + mem->raw = XMALLOC(sizeof(AsuEcdhReq) + 63U, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (mem->raw == NULL) { + mem->req = NULL; + return MEMORY_E; + } +#ifdef WC_ASU_DISABLE_CACHE + mem->req = (AsuEcdhReq*)mem->raw; +#else + mem->req = (AsuEcdhReq*)(void*)(((UINTPTR)mem->raw + 63U) & ~(UINTPTR)63U); +#endif + return 0; +} + +/* Wipe the request since it held the private key and secret, then free it. */ +static void wc_AsuEcdhReqFree(AsuEcdhMem* mem) +{ + if (mem == NULL || mem->req == NULL) { + return; + } + ForceZero(mem->req, sizeof(*mem->req)); + wc_AsuCacheFlush(mem->req, sizeof(*mem->req)); + XFREE(mem->raw, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +/* Queue one ASU ECDH operation. */ +static int wc_AsuEcdhSubmit(XAsu_ClientParams* params, void* ctx) +{ + AsuEcdhReq* req = (AsuEcdhReq*)ctx; + + if (params == NULL || req == NULL) { + return XST_FAILURE; + } + return XAsu_EcdhGenSharedSecret(params, &req->params); +} + +/* Turn the wolfSSL curve id into an ASU curve type and size. Curves we do not + * support return an error so wolfSSL uses software. */ +static int wc_AsuEcdhCurve(ecc_key* key, u32* curveType, u32* keyLen) +{ + u32 type; + u32 len; + + if (key == NULL || curveType == NULL || keyLen == NULL) { + return BAD_FUNC_ARG; + } + if (key->dp == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + switch (key->dp->id) { + case ECC_SECP192R1: + type = (u32)XASU_ECC_NIST_P192; + len = (u32)XASU_ECC_P192_SIZE_IN_BYTES; + break; + case ECC_SECP256R1: + type = (u32)XASU_ECC_NIST_P256; + len = (u32)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_SECP384R1: + type = (u32)XASU_ECC_NIST_P384; + len = (u32)XASU_ECC_P384_SIZE_IN_BYTES; + break; +#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521 + /* Uses the same P-521 switch as ECDSA. ECDH has no digest, so the + * firmware padding bug does not apply, but it stays off until tested. */ + case ECC_SECP521R1: + type = (u32)XASU_ECC_NIST_P521; + len = (u32)XASU_ECC_P521_SIZE_IN_BYTES; + break; +#endif +#ifdef HAVE_ECC_BRAINPOOL + case ECC_BRAINPOOLP256R1: + type = (u32)XASU_ECC_BRAINPOOL_P256; + len = (u32)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP320R1: + type = (u32)XASU_ECC_BRAINPOOL_P320; + len = (u32)XASU_ECC_P320_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP384R1: + type = (u32)XASU_ECC_BRAINPOOL_P384; + len = (u32)XASU_ECC_P384_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP512R1: + type = (u32)XASU_ECC_BRAINPOOL_P512; + len = (u32)XASU_ECC_P512_SIZE_IN_BYTES; + break; +#endif + default: + return CRYPTOCB_UNAVAILABLE; + } + if ((u32)key->dp->size != len) { + return CRYPTOCB_UNAVAILABLE; + } + *curveType = type; + *keyLen = len; + return 0; +} + +/* ECDH shared secret. Both keys must be on the same supported curve. */ +int wc_AsuEcdh(wc_CryptoInfo* info) +{ + AsuEcdhMem mem; + ecc_key* priv; + ecc_key* pub; + u32 curveType = 0; + u32 keyLen = 0; + word32 status; + word32 addl = 0; + int ret = 0; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + if (info->algo_type != WC_ALGO_TYPE_PK || + info->pk.type != WC_PK_TYPE_ECDH) { + return CRYPTOCB_UNAVAILABLE; + } + + priv = info->pk.ecdh.private_key; + pub = info->pk.ecdh.public_key; + + if (priv == NULL || pub == NULL || info->pk.ecdh.out == NULL || + info->pk.ecdh.outlen == NULL) { + return BAD_FUNC_ARG; + } + /* wolfSSL checks this after the callback, so our side needs a private key + * here or the ASU would be handed a zero scalar. */ + if (priv->type != ECC_PRIVATEKEY && priv->type != ECC_PRIVATEKEY_ONLY) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = wc_AsuEcdhCurve(priv, &curveType, &keyLen); + if (ret != 0) { + return ret; + } + /* Both keys must be on the same supported curve. */ + if (pub->dp == NULL || pub->dp->id != priv->dp->id) { + return CRYPTOCB_UNAVAILABLE; + } + /* If the peer point is all zeros, let software return the proper error + * instead of the ASU failing. */ + if (mp_iszero(pub->pubkey.x) && mp_iszero(pub->pubkey.y)) { + return CRYPTOCB_UNAVAILABLE; + } + /* This call hands back the plain private key. */ + if (*info->pk.ecdh.outlen < keyLen) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = wc_AsuEcdhReqNew(&mem); + if (ret != 0) { + return ret; + } + + XMEMSET(mem.req, 0, sizeof(*mem.req)); + /* A key too big for keyLen bytes is not something the ASU can take, so + * fall back to software rather than report a hardware error. */ + if (mp_to_unsigned_bin_len(wc_ecc_key_get_priv(priv), mem.req->privKey, + (int)keyLen) < 0) { + wc_AsuEcdhReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + /* The peer point is public data, so a failure here is a real error. */ + if (mp_to_unsigned_bin_len(pub->pubkey.x, mem.req->pubKey, + (int)keyLen) < 0 || + mp_to_unsigned_bin_len(pub->pubkey.y, mem.req->pubKey + keyLen, + (int)keyLen) < 0) { + wc_AsuEcdhReqFree(&mem); + return WC_HW_E; + } + + mem.req->params.CurveType = curveType; + mem.req->params.KeyLen = keyLen; + mem.req->params.PvtKeyAddr = (u64)(UINTPTR)mem.req->privKey; + mem.req->params.PubKeyAddr = (u64)(UINTPTR)mem.req->pubKey; + mem.req->params.SharedSecretAddr = (u64)(UINTPTR)mem.req->secret; + mem.req->params.SharedSecretObjIdAddr = 0; + + WC_ASU_PRINTF("[ASU] ecdh curve=%u keyLen=%u\r\n", + (unsigned int)curveType, (unsigned int)keyLen); + + wc_AsuCacheFlush(mem.req->privKey, keyLen); + wc_AsuCacheFlush(mem.req->pubKey, 2U * keyLen); + /* Flush the output first, or old cache lines could overwrite the secret + * the ASU writes and we would read zeros. */ + wc_AsuCacheFlush(mem.req->secret, keyLen); + + status = wc_AsuTransact(wc_AsuEcdhSubmit, mem.req, &addl); + + wc_AsuCacheInvalidate(mem.req->secret, keyLen); + + WC_ASU_PRINTF("[ASU] ecdh st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + if (status != XST_SUCCESS) { + /* Inputs were already checked, so this is a real hardware error. */ + wc_AsuEcdhReqFree(&mem); + return WC_HW_E; + } + XMEMCPY(info->pk.ecdh.out, mem.req->secret, keyLen); + *info->pk.ecdh.outlen = keyLen; + wc_AsuEcdhReqFree(&mem); + return 0; +} + +#endif /* WC_ASU_ECDH_ENABLED */ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecies.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecies.c new file mode 100644 index 0000000000..a8b44bf7b8 --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_ecies.c @@ -0,0 +1,617 @@ +/* asu_ecies.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECIES on the ASU. The ASU does the ECDH, the key derivation and the AES-GCM + * in one command. Only the default GCM setup is offloaded. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +/* asu_ecies.h computes WC_ASU_ECIES_ENABLED from resolved feature macros. */ +#include + +#ifdef WC_ASU_ECIES_ENABLED + +#include +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include "xasu_ecies.h" +#include "xasu_eciesinfo.h" +#include "xasu_eccinfo.h" +#include "xasu_shainfo.h" +#include "xasu_aesinfo.h" +#include "xstatus.h" + +/* Use the enum names so renumbering in ecc.h cannot pick the wrong scheme. */ +#define WC_ASU_ECIES_AES128_GCM ecAES_128_GCM +#define WC_ASU_ECIES_AES256_GCM ecAES_256_GCM +#define WC_ASU_ECIES_HKDF_SHA256 ecHKDF_SHA256 +#define WC_ASU_ECIES_NONCE_SZ 12 +#define WC_ASU_ECIES_TAG_SZ 16 + +/* Biggest curve we support here, P-384 at 48 bytes. */ +#define WC_ASU_ECIES_MAX_KEYLEN XASU_ECC_P384_SIZE_IN_BYTES + +/* One ASU ECIES request. The fixed size fields live on the heap so the ASU + * can reach them. The message stays in the caller buffers. */ +typedef struct { + XAsu_EciesParams params; + /* peer public point for encrypt, or our private key for decrypt */ + byte rxKey[2U * WC_ASU_ECIES_MAX_KEYLEN]; + /* Keep txKey on its own cache line and round the struct size up. */ + /* ephemeral pub (enc out/dec in) */ + WC_ASU_ALIGN64 byte txKey[2U * WC_ASU_ECIES_MAX_KEYLEN]; + byte iv[WC_ASU_ECIES_NONCE_SZ]; /* GCM nonce */ + byte tag[WC_ASU_ECIES_TAG_SZ]; /* GCM tag */ +} AsuEciesReq; + +/* Holds both pointers so they stay together. Use .req, free .raw. */ +typedef struct { + void* raw; /* what XMALLOC gave back, free this one */ + AsuEciesReq* req; /* the aligned request the operation uses */ +} AsuEciesMem; + +/* Align the request to 64 bytes so txKey lands on a cache line. + * Returns 0 or MEMORY_E. */ +static int wc_AsuEciesReqNew(AsuEciesMem* mem) +{ + if (mem == NULL) { + return BAD_FUNC_ARG; + } +#ifdef WC_ASU_DISABLE_CACHE + /* Cache is off, so a plain malloc is fine. */ + mem->raw = XMALLOC(sizeof(AsuEciesReq), NULL, DYNAMIC_TYPE_TMP_BUFFER); +#else + mem->raw = XMALLOC(sizeof(AsuEciesReq) + 63U, NULL, + DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (mem->raw == NULL) { + mem->req = NULL; + return MEMORY_E; + } +#ifdef WC_ASU_DISABLE_CACHE + mem->req = (AsuEciesReq*)mem->raw; +#else + mem->req = (AsuEciesReq*)(void*)(((UINTPTR)mem->raw + 63U) & ~(UINTPTR)63U); +#endif + return 0; +} + +/* Wipe the request since it may hold the private key and message, then free + * it. */ +static void wc_AsuEciesReqFree(AsuEciesMem* mem) +{ + if (mem == NULL || mem->req == NULL) { + return; + } + ForceZero(mem->req, sizeof(*mem->req)); + wc_AsuCacheFlush(mem->req, sizeof(*mem->req)); + XFREE(mem->raw, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +/* Queue one ASU ECIES operation. IsEncrypt picks which one. */ +typedef struct { + AsuEciesReq* req; + int isEncrypt; +} AsuEciesSubmitCtx; + +static int wc_AsuEciesSubmit(XAsu_ClientParams* params, void* ctx) +{ + AsuEciesSubmitCtx* sc = (AsuEciesSubmitCtx*)ctx; + + if (params == NULL || sc == NULL || sc->req == NULL) { + return XST_FAILURE; + } + if (sc->isEncrypt != 0) { + return XAsu_EciesEncrypt(params, &sc->req->params); + } + return XAsu_EciesDecrypt(params, &sc->req->params); +} + +/* Turn the wolfSSL curve id into an ASU curve type and size. Curves we do not + * support return an error so wolfSSL uses software. */ +static int wc_AsuEciesCurve(ecc_key* key, u8* curveType, u8* keyLen) +{ + if (key == NULL || curveType == NULL || keyLen == NULL) { + return BAD_FUNC_ARG; + } + if (key->dp == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + switch (key->dp->id) { + case ECC_SECP256R1: + *curveType = (u8)XASU_ECC_NIST_P256; + *keyLen = (u8)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_SECP384R1: + *curveType = (u8)XASU_ECC_NIST_P384; + *keyLen = (u8)XASU_ECC_P384_SIZE_IN_BYTES; + break; +#ifdef HAVE_ECC_BRAINPOOL + case ECC_BRAINPOOLP256R1: + *curveType = (u8)XASU_ECC_BRAINPOOL_P256; + *keyLen = (u8)XASU_ECC_P256_SIZE_IN_BYTES; + break; + case ECC_BRAINPOOLP384R1: + *curveType = (u8)XASU_ECC_BRAINPOOL_P384; + *keyLen = (u8)XASU_ECC_P384_SIZE_IN_BYTES; + break; +#endif + default: + return CRYPTOCB_UNAVAILABLE; + } + /* Reject a curve whose size does not match, so we never send the wrong + * number of bytes. */ + if ((u32)key->dp->size != (u32)*keyLen) { + return CRYPTOCB_UNAVAILABLE; + } + return 0; +} + +/* Read the ECIES settings. Only AES-GCM with HKDF-SHA256 is offloaded. */ +static int wc_AsuEciesScheme(ecEncCtx* ctx, u8* aesKeySize, u8* shaType, + u8* shaMode) +{ + byte encAlgo = 0; + byte kdfAlgo = 0; + + if (ctx == NULL || aesKeySize == NULL || shaType == NULL || + shaMode == NULL) { + return BAD_FUNC_ARG; + } + if (wc_ecc_ctx_get_algo(ctx, &encAlgo, &kdfAlgo, NULL) != 0) { + return CRYPTOCB_UNAVAILABLE; + } + if (kdfAlgo != WC_ASU_ECIES_HKDF_SHA256) { + return CRYPTOCB_UNAVAILABLE; + } + if (encAlgo == WC_ASU_ECIES_AES128_GCM) { + *aesKeySize = (u8)XASU_AES_KEY_SIZE_128_BITS; + } + else if (encAlgo == WC_ASU_ECIES_AES256_GCM) { + *aesKeySize = (u8)XASU_AES_KEY_SIZE_256_BITS; + } + else { + return CRYPTOCB_UNAVAILABLE; + } + *shaType = (u8)XASU_SHA2_TYPE; + *shaMode = (u8)XASU_SHA_MODE_256; + return 0; +} + +/* Copy a public point out as Qx||Qy, fixed width, big end first. */ +static int wc_AsuEciesExportPub(ecc_key* key, byte* out, u8 keyLen) +{ + if (mp_to_unsigned_bin_len(key->pubkey.x, out, (int)keyLen) < 0) { + return WC_HW_E; + } + if (mp_to_unsigned_bin_len(key->pubkey.y, out + keyLen, (int)keyLen) < 0) { + return WC_HW_E; + } + return 0; +} + +/* Fill in the settings that encrypt and decrypt both need. */ +static void wc_AsuEciesFillParams(XAsu_EciesParams* p, ecEncCtx* ctx, + u8 curveType, u8 keyLen, u8 aesKeySize, u8 shaType, u8 shaMode, + word32 dataLen) +{ + word32 saltLen = 0; + word32 infoLen = 0; + const byte* salt = NULL; + const byte* info = NULL; + + (void)wc_ecc_ctx_get_kdf_salt(ctx, &salt, &saltLen); + (void)wc_ecc_ctx_get_info(ctx, &info, &infoLen); + + p->EccCurveType = curveType; + p->EccKeyLength = keyLen; + p->ShaType = shaType; + p->ShaMode = shaMode; + p->AesKeySize = aesKeySize; + p->IvLength = (u8)WC_ASU_ECIES_NONCE_SZ; + p->MacLength = (u8)WC_ASU_ECIES_TAG_SZ; + p->DataLength = dataLen; + p->SaltAddr = (u64)(UINTPTR)salt; + p->SaltLen = saltLen; + p->ContextAddr = (u64)(UINTPTR)info; + p->ContextLen = infoLen; +} + +/* ECIES encrypt. The ASU makes its own throwaway key pair, derives the AES + * key and encrypts. */ +static int wc_AsuEciesEncrypt(wc_CryptoInfo* info) +{ + AsuEciesMem mem; + AsuEciesSubmitCtx sc; + /* throwaway key, the private part is not used */ + ecc_key* privKey = info->pk.eciesencrypt.privKey; + ecc_key* pubKey = info->pk.eciesencrypt.pubKey; + const byte* msg = info->pk.eciesencrypt.msg; + word32 msgSz = info->pk.eciesencrypt.msgSz; + byte* out = info->pk.eciesencrypt.out; + ecEncCtx* ctx = info->pk.eciesencrypt.ctx; + u8 curveType = 0; + u8 keyLen = 0; + u8 aesKeySize = 0; + u8 shaType = 0; + u8 shaMode = 0; + word32 pubKeySz; + word32 need; + word32 status; + word32 addl = 0; + int ret; + + /* Return the decline code, not an error, so wolfSSL can use software. */ + if (pubKey == NULL || msg == NULL || out == NULL || + info->pk.eciesencrypt.outSz == NULL || ctx == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* The ASU only writes uncompressed keys, so turn down compressed. */ + if (info->pk.eciesencrypt.compressed != 0) { + return CRYPTOCB_UNAVAILABLE; + } + ret = wc_AsuEciesCurve(pubKey, &curveType, &keyLen); + if (ret != 0) { + return ret; + } + ret = wc_AsuEciesScheme(ctx, &aesKeySize, &shaType, &shaMode); + if (ret != 0) { + return ret; + } + /* Only GCM here. The ASU cannot take extra salt and needs a context, so + * software handles those cases. */ + { + const byte* macSalt = NULL; + const byte* infoP = NULL; + word32 macSaltSz = 0; + word32 infoSz = 0; + int proto = 0; + (void)wc_ecc_ctx_get_mac_salt(ctx, &macSalt, &macSaltSz); + (void)wc_ecc_ctx_get_info(ctx, &infoP, &infoSz); + (void)wc_ecc_ctx_get_protocol(ctx, &proto); + if (macSaltSz > 0U) { + return CRYPTOCB_UNAVAILABLE; + } + if (infoSz == 0U) { + WC_ASU_PRINTF("[ASU] ecies: GCM needs a non-empty KDF context\r\n"); + return CRYPTOCB_UNAVAILABLE; + } + /* The ASU always uses the first half of the derived key, which is the + * client half. A context is never built with protocol zero. */ + if (proto != REQ_RESP_CLIENT) { + return CRYPTOCB_UNAVAILABLE; + } + } + /* The caller passes a key here, but the ASU makes its own. */ + if (privKey == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* Both keys have to be on the same curve. */ + if (privKey->dp == NULL || privKey->dp->id != pubKey->dp->id) { + return CRYPTOCB_UNAVAILABLE; + } + /* If the peer point is all zeros, let software return the proper error. */ + if (mp_iszero(pubKey->pubkey.x) && mp_iszero(pubKey->pubkey.y)) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The ASU client turns down a zero data length, so let software do it. */ + if (msgSz == 0U) { + return CRYPTOCB_UNAVAILABLE; + } + /* uncompressed point: 0x04 then Qx and Qy */ + pubKeySz = 1U + (2U * (word32)keyLen); + /* Check the length first so the size math below cannot overflow. */ + if (msgSz > 0xFFFFFFFFU - pubKeySz - (word32)WC_ASU_ECIES_NONCE_SZ - + (word32)WC_ASU_ECIES_TAG_SZ) { + return BAD_FUNC_ARG; + } + need = pubKeySz + (word32)WC_ASU_ECIES_NONCE_SZ + msgSz + + (word32)WC_ASU_ECIES_TAG_SZ; + if (*info->pk.eciesencrypt.outSz < need) { + return BUFFER_E; + } + + ret = wc_AsuEciesReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + ret = wc_AsuEciesExportPub(pubKey, mem.req->rxKey, keyLen); + if (ret != 0) { + wc_AsuEciesReqFree(&mem); + return ret; + } + /* Make the GCM nonce with a local RNG on the ASU device id. */ + { + WC_RNG rng; + ret = wc_InitRng_ex(&rng, NULL, WOLFSSL_VERSAL_GEN2_ASU_DEVID); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(&rng, mem.req->iv, + WC_ASU_ECIES_NONCE_SZ); + wc_FreeRng(&rng); + } + } + if (ret != 0) { + /* That RNG needs a seed source this build may not have, so decline + * and let wolfSSL try again in software. */ + wc_AsuEciesReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + + wc_AsuEciesFillParams(&mem.req->params, ctx, curveType, keyLen, aesKeySize, + shaType, shaMode, msgSz); + /* peer public key in */ + mem.req->params.RxKeyAddr = (u64)(UINTPTR)mem.req->rxKey; + /* ephemeral public key out */ + mem.req->params.TxKeyAddr = (u64)(UINTPTR)mem.req->txKey; + mem.req->params.IvAddr = (u64)(UINTPTR)mem.req->iv; + /* GCM tag out */ + mem.req->params.MacAddr = (u64)(UINTPTR)mem.req->tag; + /* plaintext in */ + mem.req->params.InDataAddr = (u64)(UINTPTR)msg; + /* ciphertext out, in place */ + mem.req->params.OutDataAddr = (u64)(UINTPTR)(out + pubKeySz + + (word32)WC_ASU_ECIES_NONCE_SZ); + + WC_ASU_PRINTF("[ASU] ecies enc curve=%u keyLen=%u aesKey=%u msgSz=%u\r\n", + (unsigned int)curveType, (unsigned int)keyLen, (unsigned int)aesKeySize, + (unsigned int)msgSz); + + /* Push the inputs out to memory, then reload what the ASU writes. */ + wc_AsuCacheFlush(mem.req, sizeof(*mem.req)); + wc_AsuCacheFlush(msg, msgSz); + if (mem.req->params.SaltLen != 0) { + wc_AsuCacheFlush((const void*)(UINTPTR)mem.req->params.SaltAddr, + mem.req->params.SaltLen); + } + if (mem.req->params.ContextLen != 0) { + wc_AsuCacheFlush((const void*)(UINTPTR)mem.req->params.ContextAddr, + mem.req->params.ContextLen); + } + wc_AsuCacheFlush(out, need); + + sc.req = mem.req; + sc.isEncrypt = 1; + status = wc_AsuTransact(wc_AsuEciesSubmit, &sc, &addl); + + wc_AsuCacheInvalidate(mem.req, sizeof(*mem.req)); + wc_AsuCacheInvalidate(out, need); + + WC_ASU_PRINTF("[ASU] ecies enc st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + if (status != XST_SUCCESS) { + wc_AsuEciesReqFree(&mem); + return WC_HW_E; + } + + /* Build the output wolfSSL expects. The ciphertext is already in place, + * so only the key, nonce and tag are copied. */ + out[0] = (byte)ECC_POINT_UNCOMP; + XMEMCPY(out + 1, mem.req->txKey, 2U * (word32)keyLen); + XMEMCPY(out + pubKeySz, mem.req->iv, WC_ASU_ECIES_NONCE_SZ); + XMEMCPY(out + pubKeySz + (word32)WC_ASU_ECIES_NONCE_SZ + msgSz, + mem.req->tag, WC_ASU_ECIES_TAG_SZ); + *info->pk.eciesencrypt.outSz = need; + wc_AsuEciesReqFree(&mem); + return 0; +} + +/* ECIES decrypt. The ASU derives the AES key from our private key and the + * sender throwaway key, then decrypts and checks the tag. */ +static int wc_AsuEciesDecrypt(wc_CryptoInfo* info) +{ + AsuEciesMem mem; + AsuEciesSubmitCtx sc; + ecc_key* privKey = info->pk.eciesdecrypt.privKey; + const byte* msg = info->pk.eciesdecrypt.msg; + word32 msgSz = info->pk.eciesdecrypt.msgSz; + byte* out = info->pk.eciesdecrypt.out; + ecEncCtx* ctx = info->pk.eciesdecrypt.ctx; + u8 curveType = 0; + u8 keyLen = 0; + u8 aesKeySize = 0; + u8 shaType = 0; + u8 shaMode = 0; + word32 pubKeySz; + word32 ctLen; + word32 status; + word32 addl = 0; + int ret; + + /* Return the decline code so wolfSSL can use software. */ + if (privKey == NULL || msg == NULL || out == NULL || + info->pk.eciesdecrypt.outSz == NULL || ctx == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* Software hands back the sender public point and the ASU cannot, so + * decline when the caller asks for it. */ + if (info->pk.eciesdecrypt.pubKey != NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* wolfSSL checks this after the callback, so our side needs a private key + * here or the ASU would be handed a zero scalar. */ + if (privKey->type != ECC_PRIVATEKEY && + privKey->type != ECC_PRIVATEKEY_ONLY) { + return CRYPTOCB_UNAVAILABLE; + } + /* Check the curve first, since reading the private key below needs it. */ + ret = wc_AsuEciesCurve(privKey, &curveType, &keyLen); + if (ret != 0) { + return ret; + } + /* Read the private key and check it using the exported bytes, which keeps + * the timing steady. */ + ret = wc_AsuEciesScheme(ctx, &aesKeySize, &shaType, &shaMode); + if (ret != 0) { + return ret; + } + /* Only GCM here. The ASU cannot take extra salt and needs a context, so + * software handles those cases. */ + { + const byte* macSalt = NULL; + const byte* infoP = NULL; + word32 macSaltSz = 0; + word32 infoSz = 0; + int proto = 0; + (void)wc_ecc_ctx_get_mac_salt(ctx, &macSalt, &macSaltSz); + (void)wc_ecc_ctx_get_info(ctx, &infoP, &infoSz); + (void)wc_ecc_ctx_get_protocol(ctx, &proto); + if (macSaltSz > 0U) { + return CRYPTOCB_UNAVAILABLE; + } + if (infoSz == 0U) { + WC_ASU_PRINTF("[ASU] ecies: GCM needs a non-empty KDF context\r\n"); + return CRYPTOCB_UNAVAILABLE; + } + /* The ASU always uses the first half of the derived key, which is the + * server half here. A context is never built with protocol zero. */ + if (proto != REQ_RESP_SERVER) { + return CRYPTOCB_UNAVAILABLE; + } + } + + /* The sender key must be uncompressed, so turn down compressed. */ + if (msgSz < 1U || msg[0] != (byte)ECC_POINT_UNCOMP) { + return CRYPTOCB_UNAVAILABLE; + } + pubKeySz = 1U + (2U * (word32)keyLen); + if (msgSz < pubKeySz + (word32)WC_ASU_ECIES_NONCE_SZ + + (word32)WC_ASU_ECIES_TAG_SZ) { + return BAD_FUNC_ARG; + } + ctLen = msgSz - pubKeySz - (word32)WC_ASU_ECIES_NONCE_SZ - + (word32)WC_ASU_ECIES_TAG_SZ; + /* Same as encrypt: a zero data length is turned down by the client. */ + if (ctLen == 0U) { + return CRYPTOCB_UNAVAILABLE; + } + if (*info->pk.eciesdecrypt.outSz < ctLen) { + return BUFFER_E; + } + + ret = wc_AsuEciesReqNew(&mem); + if (ret != 0) { + return ret; + } + XMEMSET(mem.req, 0, sizeof(*mem.req)); + + /* Our private key. The sender public key comes from the message. */ + if (mp_to_unsigned_bin_len(wc_ecc_key_get_priv(privKey), mem.req->rxKey, + (int)keyLen) < 0) { + /* If the key does not fit in keyLen bytes we fall back to software. */ + wc_AsuEciesReqFree(&mem); + return CRYPTOCB_UNAVAILABLE; + } + XMEMCPY(mem.req->txKey, msg + 1, 2U * (word32)keyLen); + XMEMCPY(mem.req->iv, msg + pubKeySz, WC_ASU_ECIES_NONCE_SZ); + XMEMCPY(mem.req->tag, msg + msgSz - (word32)WC_ASU_ECIES_TAG_SZ, + WC_ASU_ECIES_TAG_SZ); + + wc_AsuEciesFillParams(&mem.req->params, ctx, curveType, keyLen, aesKeySize, + shaType, shaMode, ctLen); + /* our private key in */ + mem.req->params.RxKeyAddr = (u64)(UINTPTR)mem.req->rxKey; + /* ephemeral public key in */ + mem.req->params.TxKeyAddr = (u64)(UINTPTR)mem.req->txKey; + mem.req->params.IvAddr = (u64)(UINTPTR)mem.req->iv; + /* GCM tag in */ + mem.req->params.MacAddr = (u64)(UINTPTR)mem.req->tag; + /* ciphertext in */ + mem.req->params.InDataAddr = (u64)(UINTPTR)(msg + pubKeySz + + (word32)WC_ASU_ECIES_NONCE_SZ); + /* plaintext out */ + mem.req->params.OutDataAddr = (u64)(UINTPTR)out; + + WC_ASU_PRINTF("[ASU] ecies dec curve=%u keyLen=%u aesKey=%u ctLen=%u\r\n", + (unsigned int)curveType, (unsigned int)keyLen, (unsigned int)aesKeySize, + (unsigned int)ctLen); + + wc_AsuCacheFlush(mem.req, sizeof(*mem.req)); + wc_AsuCacheFlush(msg, msgSz); + if (mem.req->params.SaltLen != 0) { + wc_AsuCacheFlush((const void*)(UINTPTR)mem.req->params.SaltAddr, + mem.req->params.SaltLen); + } + if (mem.req->params.ContextLen != 0) { + wc_AsuCacheFlush((const void*)(UINTPTR)mem.req->params.ContextAddr, + mem.req->params.ContextLen); + } + wc_AsuCacheFlush(out, ctLen); + + sc.req = mem.req; + sc.isEncrypt = 0; + status = wc_AsuTransact(wc_AsuEciesSubmit, &sc, &addl); + + wc_AsuCacheInvalidate(out, ctLen); + + WC_ASU_PRINTF("[ASU] ecies dec st=%u addl=0x%x\r\n", + (unsigned int)status, (unsigned int)addl); + + if (status != XST_SUCCESS) { + /* A bad tag and a hardware fault look the same here, so wipe the + * output and never hand back unchecked plaintext. */ + ForceZero(out, ctLen); + /* Push the zeros out to memory, or only the cache copy is cleared. */ + wc_AsuCacheFlush(out, ctLen); + *info->pk.eciesdecrypt.outSz = 0; + wc_AsuEciesReqFree(&mem); + return WC_HW_E; + } + *info->pk.eciesdecrypt.outSz = ctLen; + wc_AsuEciesReqFree(&mem); + return 0; +} + +/* Entry point for ECIES, called from the PK dispatcher. */ +int wc_AsuEcies(wc_CryptoInfo* info) +{ + if (info == NULL) { + return BAD_FUNC_ARG; + } + if (info->algo_type != WC_ALGO_TYPE_PK) { + return CRYPTOCB_UNAVAILABLE; + } + if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) { + return wc_AsuEciesEncrypt(info); + } + if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) { + return wc_AsuEciesDecrypt(info); + } + return CRYPTOCB_UNAVAILABLE; +} + +#endif /* WC_ASU_ECIES_ENABLED */ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c index afaa090fb6..22e2368243 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c @@ -19,42 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* ASU hashing for the wolfSSL crypto callback: SHA2 256/384/512, SHA3 - * 256/384/512 and SHAKE256. - * - * Why the message is buffered instead of streamed to the hardware: - * wolfSSL drives hashing as update()...update()...final(), and may have - * several hash contexts in flight at the same time (interleaved). The ASU SHA - * core supports START/UPDATE/FINISH streaming, but it keeps the running hash - * state only inside the core and exposes no way to save and restore it: - * XSha_Start always begins a fresh hash and the digest registers are read - * only (read out at FINISH). The client library therefore allows only one - * multi update stream in progress per priority channel. Because the hardware - * cannot hold more than one partial hash, interleaved hardware streaming is - * physically impossible. - * - * To support arbitrary interleaving correctly, each hash context's message is - * accumulated in its own buffer hung off the wolfSSL hash devCtx (using the - * wolfSSL _wc_Hash_Grow helper), and the digest is produced with a single - * atomic ASU operation (START|UPDATE|FINISH) at final(). Several contexts can - * be mid stream at once, each in its own buffer; the ASU only ever performs - * one complete hash at a time (serialized by wc_AsuTransact). - * - * Because the state lives in devCtx, the copy and free crypto callbacks are - * required: copy gives the destination context its own deep copy of the - * buffer (a plain struct copy would share the pointer), and free releases the - * buffer if a context is freed without being finalized. - * - * SHAKE256 is an extendable output function: the output length chosen at final() - * is carried to this callback in the hash info outSz field (by wc_CryptoCb_Shake). - * An output that fits the ASU response mailbox (WC_ASU_SHAKE_HW_MAX_BYTES) is - * produced in one ASU SHAKE256 operation; a longer output cannot be returned by - * the hardware (see that define) and is computed in software from the same - * accumulated message. SHAKE128 has no ASU mode, so it is declined and runs in - * software. Only the update()/final() hash style routes here; the - * absorb()/squeezeBlocks() streaming XOF (used by ML-KEM and ML-DSA) is not on - * the callback path and stays in software. - */ +/* Hashing on the ASU. The ASU cannot save a partial hash, so each context + * saves its message in its own buffer and hashes it all at final(). */ #ifdef HAVE_CONFIG_H #include @@ -106,8 +72,7 @@ typedef struct { int isSha3; } AsuHashReq; -/* Release a kept message record. The message buffer holds the plaintext that - * was hashed, so it is zeroized before being returned to the allocator. */ +/* Free a saved message. It holds the data we hashed, so wipe it first. */ static void wc_AsuHashKeepFree(AsuHashKeep* keep) { if (keep == NULL) { @@ -120,8 +85,7 @@ static void wc_AsuHashKeepFree(AsuHashKeep* keep) XFREE(keep, NULL, DYNAMIC_TYPE_TMP_BUFFER); } -/* Submit thunk: queue one ASU hash operation. Called by wc_AsuTransact with the - * submit lock held, so it only queues the request. */ +/* Queue one ASU hash operation. The lock is held here, so only queue it. */ static int wc_AsuHashSubmit(XAsu_ClientParams* params, void* ctx) { AsuHashReq* req = (AsuHashReq*)ctx; @@ -138,30 +102,35 @@ static int wc_AsuHashSubmit(XAsu_ClientParams* params, void* ctx) } } -/* Return the size of the hash context struct for the given hash type, or 0 for - * an unsupported type. The copy callback needs this to duplicate the whole - * context (see wc_AsuHashCopy). */ +/* Size of the hash context for this type, or 0 if we do not support it. */ static word32 wc_AsuHashCtxSize(int hashType) { switch (hashType) { +#ifndef NO_SHA256 case WC_HASH_TYPE_SHA256: return (word32)sizeof(wc_Sha256); +#endif +#ifdef WOLFSSL_SHA384 case WC_HASH_TYPE_SHA384: return (word32)sizeof(wc_Sha384); +#endif +#ifdef WOLFSSL_SHA512 case WC_HASH_TYPE_SHA512: return (word32)sizeof(wc_Sha512); +#endif +#ifdef WOLFSSL_SHA3 case WC_HASH_TYPE_SHA3_256: case WC_HASH_TYPE_SHA3_384: case WC_HASH_TYPE_SHA3_512: case WC_HASH_TYPE_SHAKE256: /* wc_Shake is a wc_Sha3 */ return (word32)sizeof(wc_Sha3); +#endif default: return 0; } } -/* Return the address of the devCtx field of the hash context for the given hash - * type, or NULL for an unsupported type. */ +/* Address of the devCtx field for this hash type, or NULL if unsupported. */ static void** wc_AsuHashDevCtx(void* hashCtx, int hashType) { if (hashCtx == NULL) { @@ -169,25 +138,32 @@ static void** wc_AsuHashDevCtx(void* hashCtx, int hashType) } switch (hashType) { +#ifndef NO_SHA256 case WC_HASH_TYPE_SHA256: return &((wc_Sha256*)hashCtx)->devCtx; +#endif +#ifdef WOLFSSL_SHA384 case WC_HASH_TYPE_SHA384: return &((wc_Sha384*)hashCtx)->devCtx; +#endif +#ifdef WOLFSSL_SHA512 case WC_HASH_TYPE_SHA512: return &((wc_Sha512*)hashCtx)->devCtx; +#endif +#ifdef WOLFSSL_SHA3 case WC_HASH_TYPE_SHA3_256: case WC_HASH_TYPE_SHA3_384: case WC_HASH_TYPE_SHA3_512: case WC_HASH_TYPE_SHAKE256: /* wc_Shake is a wc_Sha3 */ return &((wc_Sha3*)hashCtx)->devCtx; +#endif default: return NULL; } } -/* Resolve the hash info to the ASU type and mode, the digest length, and the - * address of the context's devCtx field. Returns 0 if supported, otherwise - * CRYPTOCB_UNAVAILABLE. */ +/* Work out the ASU type, mode, digest length and devCtx for this hash. + * Returns 0 if we support it. */ static int wc_AsuHashResolve(wc_CryptoInfo* info, void*** devCtx, u8* shaType, u8* shaMode, word32* hashLen) { @@ -197,24 +173,26 @@ static int wc_AsuHashResolve(wc_CryptoInfo* info, void*** devCtx, u8* shaType, } switch (info->hash.type) { +#ifndef NO_SHA256 case WC_HASH_TYPE_SHA256: *devCtx = wc_AsuHashDevCtx(info->hash.sha256, info->hash.type); *shaType = XASU_SHA2_TYPE; *shaMode = XASU_SHA_MODE_256; *hashLen = WC_SHA256_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA384 case WC_HASH_TYPE_SHA384: *devCtx = wc_AsuHashDevCtx(info->hash.sha384, info->hash.type); *shaType = XASU_SHA2_TYPE; *shaMode = XASU_SHA_MODE_384; *hashLen = WC_SHA384_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA512 case WC_HASH_TYPE_SHA512: - /* SHA-512/224 and SHA-512/256 share the wc_Sha512 context and reach - * this callback as plain SHA-512 on update() (the variant is only - * known at final()). The ASU does only full SHA-512, and the - * truncated variants use different initial values, so decline them - * here and let wolfSSL run them entirely in software. */ + /* The ASU only does full SHA-512. The shorter versions start from + * different values, so let software handle them. */ if (info->hash.sha512 != NULL && (info->hash.sha512->hashType == WC_HASH_TYPE_SHA512_224 || info->hash.sha512->hashType == WC_HASH_TYPE_SHA512_256)) { @@ -225,6 +203,8 @@ static int wc_AsuHashResolve(wc_CryptoInfo* info, void*** devCtx, u8* shaType, *shaMode = XASU_SHA_MODE_512; *hashLen = WC_SHA512_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA3 case WC_HASH_TYPE_SHA3_256: *devCtx = wc_AsuHashDevCtx(info->hash.sha3, info->hash.type); *shaType = XASU_SHA3_TYPE; @@ -243,24 +223,24 @@ static int wc_AsuHashResolve(wc_CryptoInfo* info, void*** devCtx, u8* shaType, *shaMode = XASU_SHA_MODE_512; *hashLen = WC_SHA3_512_DIGEST_SIZE; break; +#ifdef WOLFSSL_SHAKE256 case WC_HASH_TYPE_SHAKE256: - /* SHAKE is an extendable output function: the digest length is the - * caller's requested output, carried in outSz on the final call. - * SHAKE128 is not a hardware mode, so it falls through to the - * default and runs in software. */ + /* SHAKE lets the caller pick the output length, which arrives in + * outSz. SHAKE128 has no ASU mode and runs in software. */ *devCtx = wc_AsuHashDevCtx(info->hash.sha3, info->hash.type); *shaType = XASU_SHA3_TYPE; *shaMode = XASU_SHA_MODE_SHAKE256; *hashLen = info->hash.outSz; break; +#endif /* WOLFSSL_SHAKE256 */ +#endif /* WOLFSSL_SHA3 */ default: return CRYPTOCB_UNAVAILABLE; } -#ifdef WOLFSSL_HASH_FLAGS - /* Keccak-256 (legacy 0x01 padding) is selected with a hash flag on a SHA3 - * context. The ASU SHA3 core only does NIST SHA3 (0x06) padding, so decline - * Keccak and let wolfSSL compute it in software. */ +#if defined(WOLFSSL_HASH_FLAGS) && defined(WOLFSSL_SHA3) + /* The ASU only does standard SHA3 padding, so older Keccak runs in + * software. */ if (*shaType == XASU_SHA3_TYPE && info->hash.sha3 != NULL && (info->hash.sha3->flags & WC_HASH_SHA3_KECCAK256) != 0) { return CRYPTOCB_UNAVAILABLE; @@ -274,8 +254,7 @@ static int wc_AsuHashResolve(wc_CryptoInfo* info, void*** devCtx, u8* shaType, return 0; } -/* Hash dataLen bytes from data in one atomic ASU operation, writing hashLen - * bytes of digest. */ +/* Hash the whole message in one ASU operation. */ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data, word32 dataLen, byte* digest, word32 hashLen) { @@ -289,12 +268,8 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data, return BAD_FUNC_ARG; } - /* SHAKE256 is an extendable output function with a caller chosen length. The - * ASU reads the result out of the digest registers a 32 bit word at a time, - * so a length that is not a multiple of 4 would drop the final partial word. - * Round the request up to a word boundary into a temporary buffer and copy - * back exactly the bytes asked for. The ASU also caps a single SHAKE squeeze - * at one rate block (XASU_SHAKE_256_MAX_HASH_LEN), which bounds the temp. */ + /* The ASU reads the digest 4 bytes at a time, so round the length up into + * a temporary buffer and copy back only what was asked for. */ if ((shaMode == XASU_SHA_MODE_SHAKE256) && ((hashLen % 4u) != 0u) && (hashLen <= XASU_SHAKE_256_MAX_HASH_LEN)) { outLen = (hashLen + 3u) & ~3u; @@ -325,9 +300,8 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data, WC_ASU_PRINTF("[ASU] hash type=%d mode=%d dataLen=%u hashLen=%u\r\n", (int)shaType, (int)shaMode, (unsigned int)dataLen, (unsigned int)hashLen); - /* The ASU DMAs the input message from memory, so clean it out. The digest is - * delivered back through the response path (a CPU copy), so it needs no - * cache maintenance here. */ + /* The ASU reads the message from memory, so push it out first. The digest + * comes back another way and needs nothing here. */ if (dataLen > 0) { wc_AsuCacheFlush(data, dataLen); } @@ -337,8 +311,7 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data, return WC_HW_E; } - /* Copy back the exact byte count when a temp buffer was used for the SHAKE - * word-alignment round up. */ + /* Copy back only the bytes asked for when a temp buffer was used. */ if (outAddr != digest) { XMEMCPY(digest, xofTmp, hashLen); } @@ -346,22 +319,13 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data, return 0; } -/* The ASU returns a hash through a fixed response mailbox slot of 16 words (64 - * bytes), sized for the largest fixed digest (SHA-512). SHAKE256 is an - * extendable output function, so a requested output up to this size fits in one - * ASU operation and is offloaded; a longer output is computed in software - * instead (see wc_AsuShakeSoftware). - * - * The hardware could in principle emit more by continuing the squeeze a rate - * block at a time, but that path is closed to us: the XAsu_ShaOperationCmd - * "next xof" continue-squeeze flag (ShakeReserved) is documented "NA for client, - * ASUFW internal use", and the ASU server resets the squeeze after every finish. - * So a client cannot chain blocks, and anything past one mailbox stays software. */ +/* The ASU sends the hash back in a 64 byte slot. A SHAKE output that fits goes + * to the ASU, and anything longer is done in software. */ #define WC_ASU_SHAKE_HW_MAX_BYTES 64 -/* Compute SHAKE256 of the already accumulated message in software, for outputs - * larger than the ASU can return. A private context with INVALID_DEVID keeps it - * off the crypto callback (no recursion) so wolfSSL runs its own SHAKE. */ +#ifdef WOLFSSL_SHAKE256 +/* Do SHAKE256 in software for long outputs. The private context uses an + * invalid device id so this does not come back through the callback. */ static int wc_AsuShakeSoftware(const byte* data, word32 dataLen, byte* digest, word32 hashLen) { @@ -387,9 +351,9 @@ static int wc_AsuShakeSoftware(const byte* data, word32 dataLen, byte* digest, wc_Shake256_Free(&shake); return ret; } +#endif /* WOLFSSL_SHAKE256 */ -/* update() and final() handling for WC_ALGO_TYPE_HASH. Internal helper reached - * through the wc_AsuHash dispatcher. */ +/* Handles update and final for a hash. */ static int wc_AsuHashCompute(wc_CryptoInfo* info) { void** devCtxPtr = NULL; @@ -410,7 +374,7 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info) keep = (AsuHashKeep*)(*devCtxPtr); - /* update(): accumulate the message with the wolfSSL grow helper. */ + /* update: add this chunk to the saved message. */ if (info->hash.in != NULL) { if (keep == NULL) { keep = (AsuHashKeep*)XMALLOC(sizeof(AsuHashKeep), NULL, @@ -429,8 +393,7 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info) } } - /* final(): hash the whole accumulated message in one ASU operation, then - * release the buffer. */ + /* final: hash the saved message in one go, then free the buffer. */ if (info->hash.digest != NULL) { const byte* data = NULL; word32 dataLen = 0; @@ -440,14 +403,16 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info) dataLen = keep->used; } - /* SHAKE256 output longer than the ASU response mailbox can carry is - * produced in software from the same accumulated message; everything - * else (the fixed hashes and short SHAKE) is one ASU operation. */ + /* A long SHAKE output is done in software from the same message. + * Everything else is one ASU operation. */ +#ifdef WOLFSSL_SHAKE256 if ((shaMode == XASU_SHA_MODE_SHAKE256) && (hashLen > WC_ASU_SHAKE_HW_MAX_BYTES)) { ret = wc_AsuShakeSoftware(data, dataLen, info->hash.digest, hashLen); } - else { + else +#endif /* WOLFSSL_SHAKE256 */ + { ret = wc_AsuHashOneShot(shaType, shaMode, data, dataLen, info->hash.digest, hashLen); } @@ -465,8 +430,7 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info) return 0; } -/* WC_ALGO_TYPE_COPY handling for a hash context. Internal helper reached - * through the wc_AsuHash dispatcher. */ +/* Handles copying a hash context. */ static int wc_AsuHashCopy(wc_CryptoInfo* info) { void** srcDevCtx; @@ -490,16 +454,12 @@ static int wc_AsuHashCopy(wc_CryptoInfo* info) return CRYPTOCB_UNAVAILABLE; } - /* wolfSSL calls this callback before its own struct copy and skips both that - * copy and its own free of the destination when we return success, so the - * callback owns the entire copy. Free any buffer the destination already - * holds first (it is about to be overwritten), or it would leak. */ + /* wolfSSL skips its own copy when we succeed, so we do all of it. Free + * anything the destination already holds or it would leak. */ wc_AsuHashKeepFree((AsuHashKeep*)(*dstDevCtx)); - /* Duplicate the whole context struct, which carries devId (so the copy keeps - * routing to this port) and the rest of the state. The struct copy leaves the - * destination devCtx pointing at the source buffer, so then replace it with - * the destination's own deep copy of the kept message. */ + /* Copy the whole struct, then give the destination its own copy of the + * saved message so the two do not share a buffer. */ XMEMCPY(info->copy.dst, info->copy.src, ctxSize); srcKeep = (AsuHashKeep*)(*srcDevCtx); @@ -530,8 +490,7 @@ static int wc_AsuHashCopy(wc_CryptoInfo* info) return 0; } -/* WC_ALGO_TYPE_FREE handling for a hash context. Internal helper reached - * through the wc_AsuHash dispatcher. */ +/* Handles freeing a hash context. */ static int wc_AsuHashFree(wc_CryptoInfo* info) { void** devCtx; @@ -553,16 +512,13 @@ static int wc_AsuHashFree(wc_CryptoInfo* info) } } - /* Return unavailable so wolfSSL still runs its own ForceZero. devCtx is now - * NULL, so there is no second free. */ + /* Return unavailable so wolfSSL still wipes the context. devCtx is NULL + * now, so nothing gets freed twice. */ return CRYPTOCB_UNAVAILABLE; } -/* Single entry point for the SHA2/SHA3 engine. The crypto callback dispatcher - * routes every hash related operation here and this handler decides which one it - * is: update/final (WC_ALGO_TYPE_HASH), context copy (WC_ALGO_TYPE_COPY), or - * context free (WC_ALGO_TYPE_FREE). Keeping the whole lifecycle behind one entry - * keeps it owned by this module. */ +/* Entry point for hashing. Sorts out whether this is an update, a final, a + * copy or a free. */ int wc_AsuHash(wc_CryptoInfo* info) { if (info == NULL) { diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hmac.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hmac.c index 33b00eb09f..b67dcb24f1 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hmac.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hmac.c @@ -19,32 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* ASU HMAC for the wolfSSL crypto callback: HMAC over SHA2 256/384/512 and SHA3 - * 256/384/512. - * - * The approach mirrors the SHA hash port (asu_hash.c): wolfSSL drives HMAC as - * update()...update()...final() and may have several contexts in flight, but the - * ASU keeps the running state only inside the core and cannot save and restore - * it. So each context's message is accumulated in its own buffer hung off the - * wolfSSL Hmac devCtx (with the wolfSSL _wc_Hash_Grow helper) and the whole HMAC - * is produced in one atomic ASU operation at final(). The raw key is taken from - * the context: wolfSSL records keyRaw and keyLen on the Hmac whenever the crypto - * callback is enabled, and the ASU HMAC engine performs the key reduction - * internally, so the unmodified user key is passed straight through. - * - * Lifecycle: unlike the hash contexts, wc_HmacCopy does not run through the copy - * crypto callback and wc_HmacFree does not run through the free callback, so no - * copy/free handlers are wired for HMAC. This is safe because the buffer is freed - * in final() (the common path), wc_HmacFree finalizes any context that still owns - * a buffer through this same callback (so an abandoned context is cleaned up), - * and wolfSSL only copies an HMAC context right after the key is set, before any - * update, when devCtx is still NULL (so the shallow struct copy shares nothing). - * - * HMAC output is always the underlying digest size (<= 64 bytes), so unlike SHAKE - * it always fits the ASU response mailbox and is always offloaded for the - * supported MAC types. HMAC over SHA-1, SHA-224 and the truncated SHA-512 - * variants has no ASU mode and is declined to software. - */ +/* HMAC on the ASU. Same idea as asu_hash.c: the message is saved per context + * and the whole HMAC is done in one operation at final(). */ #ifdef HAVE_CONFIG_H #include @@ -91,8 +67,7 @@ typedef struct { XAsu_HmacParams params; } AsuHmacReq; -/* Release a kept message record. The message buffer holds plaintext that was - * MAC'd, so it is zeroized before being returned to the allocator. */ +/* Free a saved message. It holds the data we read, so wipe it first. */ static void wc_AsuHmacKeepFree(AsuHmacKeep* keep) { if (keep == NULL) { @@ -105,8 +80,7 @@ static void wc_AsuHmacKeepFree(AsuHmacKeep* keep) XFREE(keep, NULL, DYNAMIC_TYPE_TMP_BUFFER); } -/* Submit thunk: queue one ASU HMAC operation. Called by wc_AsuTransact with the - * submit lock held, so it only queues the request. */ +/* Queue one ASU HMAC operation. The lock is held here, so only queue it. */ static int wc_AsuHmacSubmit(XAsu_ClientParams* params, void* ctx) { AsuHmacReq* req = (AsuHmacReq*)ctx; @@ -118,8 +92,8 @@ static int wc_AsuHmacSubmit(XAsu_ClientParams* params, void* ctx) return XAsu_HmacCompute(params, &req->params); } -/* Resolve the wolfSSL MAC (hash) type to the ASU SHA type and mode and the HMAC - * output length. Returns 0 if supported, otherwise CRYPTOCB_UNAVAILABLE. */ +/* Work out the ASU type, mode and output length for this MAC type. + * Returns 0 if we support it. */ static int wc_AsuHmacResolve(int macType, u8* shaType, u8* shaMode, word32* hmacLen) { @@ -128,21 +102,28 @@ static int wc_AsuHmacResolve(int macType, u8* shaType, u8* shaMode, } switch (macType) { +#ifndef NO_SHA256 case WC_HASH_TYPE_SHA256: *shaType = XASU_SHA2_TYPE; *shaMode = XASU_SHA_MODE_256; *hmacLen = WC_SHA256_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA384 case WC_HASH_TYPE_SHA384: *shaType = XASU_SHA2_TYPE; *shaMode = XASU_SHA_MODE_384; *hmacLen = WC_SHA384_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA512 case WC_HASH_TYPE_SHA512: *shaType = XASU_SHA2_TYPE; *shaMode = XASU_SHA_MODE_512; *hmacLen = WC_SHA512_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA3 case WC_HASH_TYPE_SHA3_256: *shaType = XASU_SHA3_TYPE; *shaMode = XASU_SHA_MODE_256; @@ -158,6 +139,7 @@ static int wc_AsuHmacResolve(int macType, u8* shaType, u8* shaMode, *shaMode = XASU_SHA_MODE_512; *hmacLen = WC_SHA3_512_DIGEST_SIZE; break; +#endif default: return CRYPTOCB_UNAVAILABLE; } @@ -165,7 +147,7 @@ static int wc_AsuHmacResolve(int macType, u8* shaType, u8* shaMode, return 0; } -/* Compute HMAC over the whole message in one atomic ASU operation. */ +/* Run the whole HMAC in one ASU operation. */ static int wc_AsuHmacOneShot(u8 shaType, u8 shaMode, const byte* key, word32 keyLen, const byte* msg, word32 msgLen, byte* mac, word32 macLen) { @@ -198,9 +180,8 @@ static int wc_AsuHmacOneShot(u8 shaType, u8 shaMode, const byte* key, (int)shaType, (int)shaMode, (unsigned int)keyLen, (unsigned int)msgLen, (unsigned int)macLen); - /* The ASU DMAs the key and message from memory, so clean them out. The MAC - * is delivered back through the response path, so it needs no cache - * maintenance here. */ + /* The ASU reads the key and message from memory, so push them out first. + * The MAC comes back another way and needs nothing here. */ wc_AsuCacheFlush(key, keyLen); if (msgLen > 0) { wc_AsuCacheFlush(msg, msgLen); @@ -214,8 +195,7 @@ static int wc_AsuHmacOneShot(u8 shaType, u8 shaMode, const byte* key, return 0; } -/* update() and final() handling for WC_ALGO_TYPE_HMAC. Internal helper reached - * through the wc_AsuHmac dispatcher. */ +/* Handles update and final for an HMAC. */ static int wc_AsuHmacCompute(wc_CryptoInfo* info) { Hmac* hmac; @@ -239,17 +219,14 @@ static int wc_AsuHmacCompute(wc_CryptoInfo* info) return ret; } - /* The ASU HMAC engine needs a non empty raw key; if wolfSSL did not retain - * one, let it compute the HMAC in software. keyRaw and keyLen are fixed by - * the preceding SetKey, so this decision is the same on every update and - * final for a given context. */ + /* The ASU needs the raw key. If wolfSSL did not keep one, use software. */ if ((hmac->keyRaw == NULL) || (hmac->keyLen == 0)) { return CRYPTOCB_UNAVAILABLE; } keep = (AsuHmacKeep*)hmac->devCtx; - /* update(): accumulate the message with the wolfSSL grow helper. */ + /* update: add this chunk to the saved message. */ if (info->hmac.in != NULL) { if (keep == NULL) { keep = (AsuHmacKeep*)XMALLOC(sizeof(AsuHmacKeep), NULL, @@ -268,8 +245,7 @@ static int wc_AsuHmacCompute(wc_CryptoInfo* info) } } - /* final(): HMAC the whole accumulated message in one ASU operation, then - * release the buffer. */ + /* final: run the saved message in one go, then free the buffer. */ if (info->hmac.digest != NULL) { const byte* msg = NULL; word32 msgLen = 0; @@ -295,11 +271,8 @@ static int wc_AsuHmacCompute(wc_CryptoInfo* info) return 0; } -/* WC_ALGO_TYPE_COPY handling for an HMAC context. Unlike the hash copy callback, - * wc_HmacCopy performs the struct copy itself (and deep copies the inner hash), - * then calls this only to fix up the kept message: the shallow struct copy left - * the destination sharing the source buffer pointer, so replace it with the - * destination's own deep copy. Internal helper reached through wc_AsuHmac. */ +/* Handles copying an HMAC context. wolfSSL copies the struct itself, so this + * only gives the destination its own copy of the saved message. */ static int wc_AsuHmacCopy(wc_CryptoInfo* info) { Hmac* src; @@ -349,9 +322,8 @@ static int wc_AsuHmacCopy(wc_CryptoInfo* info) return 0; } -/* WC_ALGO_TYPE_FREE handling for an HMAC context: release the accumulated - * message buffer so an abandoned context (updated but never finalized) is freed - * without a stray ASU operation. Internal helper reached through wc_AsuHmac. */ +/* Handles freeing an HMAC context, so a context that was never finished does + * not leak its saved message. */ static int wc_AsuHmacFree(wc_CryptoInfo* info) { Hmac* hmac; @@ -373,15 +345,13 @@ static int wc_AsuHmacFree(wc_CryptoInfo* info) } } - /* Return unavailable so wolfSSL still runs its own cleanup. devCtx is now - * NULL, so there is no second free. */ + /* Return unavailable so wolfSSL still cleans up. devCtx is NULL now, so + * nothing gets freed twice. */ return CRYPTOCB_UNAVAILABLE; } -/* Single entry point for the HMAC engine. The crypto callback dispatcher routes - * every HMAC related operation here and this handler decides which it is: update - * and final (WC_ALGO_TYPE_HMAC), context copy (WC_ALGO_TYPE_COPY) or context - * free (WC_ALGO_TYPE_FREE). */ +/* Entry point for HMAC. Sorts out whether this is an update, a final, a copy + * or a free. */ int wc_AsuHmac(wc_CryptoInfo* info) { if (info == NULL) { diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c index 4c3a0705bc..dc5d783a45 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c @@ -35,6 +35,16 @@ #include "xil_util.h" #include "xstatus.h" +#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT + #include "xilmailbox.h" + #include "xparameters.h" + + /* IPI channel the client reaches the ASU on. */ + #ifndef WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR + #define WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR XPAR_XIPIPSU_0_BASEADDR + #endif +#endif + #ifdef WOLFSSL_VERSAL_GEN2_ASU_RTC #include "xiltimer.h" #include "xrtcpsu.h" @@ -45,8 +55,42 @@ #endif #endif -/* Shared response handler registered with every ASU request. The ASU client - * passes the AsuWait record back through the callback reference. */ +#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT + +int wc_AsuClientInit(void) +{ + /* The client keeps the mailbox for every later request; ready makes a + * repeat register a no-op. */ + static XMailbox mailbox; + static int ready = 0; + s32 status; + + if (ready) { + return 0; + } + + status = (s32)XMailbox_Initialize(&mailbox, + WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR); + if (status != XST_SUCCESS) { + WC_ASU_PRINTF("ASU mailbox initialize failed: %08x\r\n", + (unsigned int)status); + return WC_HW_E; + } + + status = (s32)XAsu_ClientInit(&mailbox); + if (status != XST_SUCCESS) { + WC_ASU_PRINTF("ASU client initialize failed: %08x\r\n", + (unsigned int)status); + return WC_HW_E; + } + + ready = 1; + return 0; +} + +#endif /* !WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT */ + +/* Called when an ASU request finishes. The client hands our record back. */ static void wc_AsuResponseHandler(void* ref, u32 status) { AsuWait* wait = (AsuWait*)ref; @@ -70,8 +114,8 @@ void wc_AsuWaitPrepare(AsuWait* wait, XAsu_ClientParams* params) } #ifndef WC_ASU_WAIT_TIMEOUT_US - /* Per poll cycle in microseconds; the loop re-arms until the flag is set, so a - * timeout just repeats the wait rather than failing the transaction. */ + /* How long each wait lasts. A timeout just waits again, it is not an + * error. */ #define WC_ASU_WAIT_TIMEOUT_US 1000000U #endif @@ -79,11 +123,11 @@ word32 wc_AsuWaitDone(AsuWait* wait) { while (wait->Done == 0) { #ifdef XYIELD - /* Hand the wait to an RTOS/scheduler when the app defines XYIELD. */ + /* Let the scheduler run if the app defines XYIELD. */ XYIELD(); #else - /* Poll the completion flag with a bounded wait, re-arming until it is set. - * Done is one byte, so mask the low byte of the word Xil_WaitForEvent reads. */ + /* Wait for the done flag. It is one byte, so mask the low byte of the + * word this call reads. */ (void)Xil_WaitForEvent((UINTPTR)&wait->Done, 0xFFU, 1U, WC_ASU_WAIT_TIMEOUT_US); #endif @@ -92,10 +136,8 @@ word32 wc_AsuWaitDone(AsuWait* wait) return wait->Status; } -/* When WC_ASU_DISABLE_CACHE is set (mirrored from XASU_DISABLE_CACHE in - * asu_settings.h) the data cache is off for the whole application, so buffer - * maintenance is unnecessary and these become no ops. Otherwise the cache is on - * and the port cleans inputs and invalidates outputs around each ASU access. */ +/* With the cache off these do nothing. With it on they push inputs out and + * reload outputs around each ASU call. */ void wc_AsuCacheFlush(const void* addr, word32 len) { @@ -110,8 +152,8 @@ void wc_AsuCacheFlush(const void* addr, word32 len) #endif } -/* Exact extent: callers flush the buffer before the op, so the edge cache lines - * hold nothing stale to write back and neighboring data keeps its new value. */ +/* Callers flush before the operation, so the end lines hold nothing stale and + * nearby data keeps its value. */ void wc_AsuCacheInvalidate(void* addr, word32 len) { if (addr == NULL || len == 0) { @@ -129,13 +171,8 @@ void wc_AsuCacheInvalidate(void* addr, word32 len) /* ----------------------------------------------------------------------- */ /* Transaction and concurrency (ticketing) */ /* ----------------------------------------------------------------------- */ -/* The ASU associates a unique id with each call and routes its completion back - * to the request's own callback, so that id is the ticket: every transaction - * gets its own AsuWait and several run concurrently. The only shared state that - * needs guarding is the submit, since the client request allocation is not - * thread safe. Locking uses the wolfSSL crypto hardware mutex (enabled for the - * multi threaded build in asu_settings.h, a no op otherwise) and is held only - * across the submit, never across the wait. */ +/* Each call gets its own id and its own wait record, so several can run at + * once. Only the submit needs a lock, and the lock is dropped before waiting. */ word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, word32* additionalStatus) { XAsu_ClientParams params; @@ -146,8 +183,7 @@ word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, word32* additionalStatus) return (word32)XST_FAILURE; } - /* The prepared params carry this request's completion context, which the - * ASU associates with the unique id it assigns. */ + /* These params carry the record the ASU hands back when it is done. */ wc_AsuWaitPrepare(&wait, ¶ms); wolfSSL_CryptHwMutexLock(); @@ -159,8 +195,7 @@ word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, word32* additionalStatus) return (word32)status; } - /* Wait on our own completion outside the lock, so other callers submit and - * run concurrently up to the ASU queue depth. */ + /* Wait outside the lock so other callers can submit while we wait. */ status = (s32)wc_AsuWaitDone(&wait); WC_ASU_PRINTF("[ASU] op done status=%d\r\n", (int)status); @@ -174,9 +209,8 @@ word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, word32* additionalStatus) #ifdef WOLFSSL_VERSAL_GEN2_ASU_RTC -/* Timer and RTC. The benchmark time base is the Cortex A78 generic timer, which - * is free running and needs no init. The system RTC is brought up by - * wc_AsuTimerInit and read with wc_AsuRtcSeconds for wall clock timestamps. */ +/* Timer and clock. The A78 timer always runs and needs no setup. The system + * clock is started here and read for wall clock times. */ static XRtcPsu asuRtc; static int asuRtcReady = 0; @@ -228,11 +262,10 @@ word32 wc_AsuRtcSeconds(void) #if defined(WOLFSSL_USER_CURRTIME) -/* Benchmark time source. benchmark.c declares this extern when - * WOLFSSL_USER_CURRTIME is set and calls it to time each operation. */ +/* Time source the benchmark uses to time each operation. */ double current_time(int reset) { - (void)reset; /* the generic timer counter is free running */ + (void)reset; /* the timer always runs, nothing to reset */ return wc_AsuTimerSeconds(); } diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 71b031a408..98ec7499b2 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -574,8 +574,7 @@ int wolfCrypt_Init(void) #endif /* Register the Versal Gen2 ASU device so wolfCrypt operations route to the - * ASU hardware. The ASU client must already be initialized by the - * application with XAsu_ClientInit. */ + * ASU hardware. Registering also brings the ASU client up. */ #if defined(WOLFSSL_VERSAL_GEN2_ASU) && defined(WOLF_CRYPTO_CB) ret = wc_AsuCryptoCb_RegisterDevice(WOLFSSL_VERSAL_GEN2_ASU_DEVID); if (ret != 0) { diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index e111218f47..0389a20256 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -1109,6 +1109,10 @@ WOLFSSL_API int wc_ecc_ctx_get_kdf_salt(ecEncCtx* ctx, const byte** salt, word32* sz); WOLFSSL_API int wc_ecc_ctx_get_info(ecEncCtx* ctx, const byte** info, word32* sz); +WOLFSSL_API +int wc_ecc_ctx_get_mac_salt(ecEncCtx* ctx, const byte** salt, word32* sz); +WOLFSSL_API +int wc_ecc_ctx_get_protocol(ecEncCtx* ctx, int* protocol); #endif /* WOLF_CRYPTO_CB */ WOLFSSL_API const byte* wc_ecc_ctx_get_own_salt(ecEncCtx* ctx); diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index 6deb44ebc9..7422134de7 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -105,6 +105,18 @@ noinst_HEADERS+= \ wolfssl/wolfcrypt/port/xilinx/xil-sha3.h \ wolfssl/wolfcrypt/port/xilinx/xil-versal-glue.h \ wolfssl/wolfcrypt/port/xilinx/xil-versal-trng.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecc.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecdh.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecies.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_hash.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_hmac.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_rng.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_rsa.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h \ + wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h \ wolfssl/wolfcrypt/port/caam/caam_driver.h \ wolfssl/wolfcrypt/port/caam/caam_error.h \ wolfssl/wolfcrypt/port/caam/caam_qnx.h \ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h index a168c66d37..cfdb35790e 100644 --- a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h @@ -19,9 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* wolfSSL crypto callback device for the Versal Gen2 ASU. Registering this - * device routes wolfCrypt operations to the ASU hardware engines, with a - * software fallback for anything the ASU does not handle. */ +/* Crypto callback device for the ASU. Anything the ASU cannot do falls back + * to software. */ #ifndef WOLFSSL_VERSAL_GEN2_ASU_CRYPTOCB_H #define WOLFSSL_VERSAL_GEN2_ASU_CRYPTOCB_H @@ -36,9 +35,8 @@ extern "C" { #endif -/* Register the ASU device with the wolfSSL crypto callback framework. The ASU - * client must already be initialized with XAsu_ClientInit. Pass the same devId - * that WC_USE_DEVID is set to so wolfSSL routes operations to this device. */ +/* Register the ASU device, which also starts the ASU client. Use the same + * devId as WC_USE_DEVID. */ WOLFSSL_API int wc_AsuCryptoCb_RegisterDevice(int devId); /* Remove the ASU device from the crypto callback framework. */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecc.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecc.h new file mode 100644 index 0000000000..bfe731290b --- /dev/null +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecc.h @@ -0,0 +1,50 @@ +/* asu_ecc.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECDSA and EdDSA on the ASU. Curves we do not support run in software. + * See asu_ecc.c. */ + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_ECC_H +#define WOLFSSL_VERSAL_GEN2_ASU_ECC_H + +#include + +/* Nothing here exists in a build without ECC. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && defined(HAVE_ECC) && \ + !defined(NO_ECC) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ECC entry point. Returns 0, CRYPTOCB_UNAVAILABLE to use software, or a + * negative error. */ +WOLFSSL_LOCAL int wc_AsuEcc(wc_CryptoInfo* info); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_ECC && HAVE_ECC && !NO_ECC */ + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_ECC_H */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecdh.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecdh.h new file mode 100644 index 0000000000..bc6c825fc5 --- /dev/null +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecdh.h @@ -0,0 +1,54 @@ +/* asu_ecdh.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECDH shared secret on the ASU. Curves we do not support run in software. + * See asu_ecdh.c. */ + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_ECDH_H +#define WOLFSSL_VERSAL_GEN2_ASU_ECDH_H + +#include + +/* Turned on from the ECC macros, and compiles to nothing without them. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECDH) && defined(HAVE_ECC) && \ + !defined(NO_ECC) && defined(HAVE_ECC_DHE) + #define WC_ASU_ECDH_ENABLED +#endif + +#ifdef WC_ASU_ECDH_ENABLED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ECDH entry point. Returns 0, CRYPTOCB_UNAVAILABLE to use software, or a + * negative error. */ +WOLFSSL_LOCAL int wc_AsuEcdh(wc_CryptoInfo* info); + +#ifdef __cplusplus +} +#endif + +#endif /* WC_ASU_ECDH_ENABLED */ + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_ECDH_H */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecies.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecies.h new file mode 100644 index 0000000000..ebddf4195d --- /dev/null +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecies.h @@ -0,0 +1,56 @@ +/* asu_ecies.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* ECIES on the ASU, which does the ECDH, key derivation and AES-GCM. + * See asu_ecies.c. */ + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_ECIES_H +#define WOLFSSL_VERSAL_GEN2_ASU_ECIES_H + +#include + +/* Turned on from the ECC and AES macros. Only the default scheme is used. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECIES) && defined(HAVE_ECC) && \ + !defined(NO_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(NO_AES) && \ + defined(HAVE_AESGCM) && defined(WOLFSSL_ECIES_GEN_IV) && \ + !defined(WOLFSSL_ECIES_OLD) && !defined(WOLFSSL_ECIES_ISO18033) + #define WC_ASU_ECIES_ENABLED +#endif + +#ifdef WC_ASU_ECIES_ENABLED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ECIES entry point. Returns 0, CRYPTOCB_UNAVAILABLE to use software, or a + * negative error. */ +WOLFSSL_LOCAL int wc_AsuEcies(wc_CryptoInfo* info); + +#ifdef __cplusplus +} +#endif + +#endif /* WC_ASU_ECIES_ENABLED */ + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_ECIES_H */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h index d29a543f56..62d1276bb9 100644 --- a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h @@ -19,30 +19,24 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* Compile time configuration for the Versal Gen2 ASU port. This header holds - * only preprocessor macros and pulls in no BSP headers, so wolfSSL settings.h - * can include it to select engines and map WC_USE_DEVID before the unmodified - * wolfcrypt test and benchmark read it. +/* Build settings for the ASU port. Macros only, no BSP headers, so settings.h + * can include it early. * - * Engine selection: - * WOLFSSL_VERSAL_GEN2_ASU enables the port and must always be defined in - * user_settings.h. With only that defined, every supported engine is - * offloaded. To offload a subset, also define one or more of the engine - * macros below, in which case only those are offloaded: + * WOLFSSL_VERSAL_GEN2_ASU turns the port on and is always needed. On its own + * it offloads every engine we support. Name one or more of these instead and + * only those are offloaded: * WOLFSSL_VERSAL_GEN2_ASU_TRNG * WOLFSSL_VERSAL_GEN2_ASU_HASH * WOLFSSL_VERSAL_GEN2_ASU_HMAC * WOLFSSL_VERSAL_GEN2_ASU_CIPHER * WOLFSSL_VERSAL_GEN2_ASU_CMAC - * WOLFSSL_VERSAL_GEN2_ASU_RSA (not auto-enabled under NO_RSA; enabling it - * implicitly defines WOLF_CRYPTO_CB_RSA_PAD so the ASU performs the - * full padded PSS/OAEP operation, not just the modexp) - * WOLFSSL_VERSAL_GEN2_ASU_ECC - * An engine macro on its own does not enable the port. + * WOLFSSL_VERSAL_GEN2_ASU_RSA + * WOLFSSL_VERSAL_GEN2_ASU_ECC (also covers ECDH and ECIES) * - * Opt-out (keep the RSA engine, drop the padding path back to software): - * WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD - RSA on, all padding in software - * (the raw modexp still offloads) + * Other switches: + * WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD - RSA on, padding in software + * WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR - IPI channel to use + * WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT - app starts the client itself */ #ifndef WOLFSSL_VERSAL_GEN2_ASU_SETTINGS_H @@ -50,80 +44,136 @@ #ifdef WOLFSSL_VERSAL_GEN2_ASU -/* The port routes operations through the wolfSSL crypto callback framework. */ +/* The port works through the wolfSSL crypto callback. */ #ifndef WOLF_CRYPTO_CB #define WOLF_CRYPTO_CB #endif -/* If the port is on but no specific engine was requested, enable the full - * supported set. */ +/* Register command, where the port brings the ASU client up. */ +#ifndef WOLF_CRYPTO_CB_CMD + #define WOLF_CRYPTO_CB_CMD +#endif + +/* No engine was named, so turn them all on. */ #if !defined(WOLFSSL_VERSAL_GEN2_ASU_TRNG) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_HASH) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_HMAC) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_CIPHER) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_CMAC) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_RSA) && \ - !defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) + !defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_ECDH) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_ECIES) #define WOLFSSL_VERSAL_GEN2_ASU_TRNG #define WOLFSSL_VERSAL_GEN2_ASU_HASH #define WOLFSSL_VERSAL_GEN2_ASU_HMAC #define WOLFSSL_VERSAL_GEN2_ASU_CIPHER #define WOLFSSL_VERSAL_GEN2_ASU_CMAC - /* Do not auto-enable RSA under NO_RSA. asu_rsa.c also compiles to nothing - * on a late NO_RSA, so this is a clean default, not the sole guard. */ + /* Leave RSA off in a build without RSA. */ #ifndef NO_RSA #define WOLFSSL_VERSAL_GEN2_ASU_RSA #endif + /* HAVE_ECC is decided later, so set this now and let asu_ecc.c check. */ #define WOLFSSL_VERSAL_GEN2_ASU_ECC #endif -/* WOLF_CRYPTO_CB_RSA_PAD on with RSA. WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD opts - * out; it changes wc_CryptoInfo layout, set identically in lib and app. */ +/* Turn on RSA padding in hardware. This changes a struct layout, so the + * library and the app must agree. */ #if defined(WOLFSSL_VERSAL_GEN2_ASU_RSA) && \ !defined(WOLF_CRYPTO_CB_RSA_PAD) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD) #define WOLF_CRYPTO_CB_RSA_PAD #endif -/* Device id for the ASU crypto callback; set WOLFSSL_VERSAL_GEN2_ASU_DEVID (or - * WC_USE_DEVID) to any int but INVALID_DEVID (-2), an id not an address. */ +/* ECDH and ECIES come along with ECC when their features are built. */ +#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC + /* Those feature macros are decided later, so check user macros here. */ + #if !defined(NO_ECC_DHE) && !defined(WC_NO_RNG) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_ECDH) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ECDH) + #define WOLFSSL_VERSAL_GEN2_ASU_ECDH + #endif + /* The ASU cannot match those two older ECIES layouts. */ + #if defined(HAVE_ECC_ENCRYPT) && defined(WOLFSSL_ECIES_GEN_IV) && \ + !defined(NO_AES) && \ + !defined(WOLFSSL_ECIES_OLD) && !defined(WOLFSSL_ECIES_ISO18033) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_ECIES) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ECIES) + #define WOLFSSL_VERSAL_GEN2_ASU_ECIES + #endif +#endif + +/* Turn off engines whose algorithm is not in the build. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_CMAC) && !defined(WOLFSSL_CMAC) + #undef WOLFSSL_VERSAL_GEN2_ASU_CMAC +#endif +#if defined(WOLFSSL_VERSAL_GEN2_ASU_HMAC) && defined(NO_HMAC) + #undef WOLFSSL_VERSAL_GEN2_ASU_HMAC +#endif +#if defined(WOLFSSL_VERSAL_GEN2_ASU_CIPHER) && defined(NO_AES) + #undef WOLFSSL_VERSAL_GEN2_ASU_CIPHER +#endif + +/* Requirements the enabled engines place on the wolfCrypt configuration. */ + +/* The port always handles context copy and free, so ask for both. */ +#ifndef WOLF_CRYPTO_CB_COPY + #define WOLF_CRYPTO_CB_COPY +#endif +#ifndef WOLF_CRYPTO_CB_FREE + #define WOLF_CRYPTO_CB_FREE +#endif + +/* The hash and HMAC engines accumulate the message with _wc_Hash_Grow. */ +#if defined(WOLFSSL_VERSAL_GEN2_ASU_HASH) || \ + defined(WOLFSSL_VERSAL_GEN2_ASU_HMAC) + #ifndef WOLFSSL_HASH_KEEP + #define WOLFSSL_HASH_KEEP + #endif +#endif + +/* The hash engine uses the hashType field to tell the SHA-512 sizes apart. */ +#ifdef WOLFSSL_VERSAL_GEN2_ASU_HASH + #ifndef WOLFSSL_SHA512_HASHTYPE + #define WOLFSSL_SHA512_HASHTYPE + #endif +#endif + +/* Device id for the callback. Any number except -2 works. It is an id, not + * an address. */ #ifndef WOLFSSL_VERSAL_GEN2_ASU_DEVID #define WOLFSSL_VERSAL_GEN2_ASU_DEVID 0x4153 /* 'AS' for ASU */ #endif -/* Let the unmodified wolfcrypt test and benchmark route every operation through - * this device by giving their devId the ASU value. */ +/* Give the test and benchmark the same id so their work goes to the ASU. */ #ifndef WC_USE_DEVID #define WC_USE_DEVID WOLFSSL_VERSAL_GEN2_ASU_DEVID #endif -/* When the timer and RTC are turned on (WOLFSSL_VERSAL_GEN2_ASU_RTC in - * user_settings.h), supply the benchmark current_time() hook from the port. */ +/* With the timer on, the port supplies the benchmark time source. */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_RTC #ifndef WOLFSSL_USER_CURRTIME #define WOLFSSL_USER_CURRTIME #endif #endif -/* Mirror XASU_DISABLE_CACHE into the port macro WC_ASU_DISABLE_CACHE. When set, - * the cache is off, port skips buffer maintenance, else cleans/invalidates. */ +/* Copy the BSP cache switch into our own macro. With the cache off the port + * skips all the flush and reload work. */ #ifdef XASU_DISABLE_CACHE #ifndef WC_ASU_DISABLE_CACHE #define WC_ASU_DISABLE_CACHE #endif #endif -/* 64-byte align the port's DMA buffers, but only with the cache on. With the - * cache off there is no cache line to keep to itself, so this becomes nothing. - * XALIGNED is a plain compiler attribute, so it has no library-wide effects. */ +/* Align buffers to 64 bytes when the cache is on. With it off this does + * nothing. */ #ifdef WC_ASU_DISABLE_CACHE #define WC_ASU_ALIGN64 #else #define WC_ASU_ALIGN64 XALIGNED(64) #endif -/* Threading. Ticketing concurrency that keeps the ASU queue busy is compiled - * out for a SINGLE_THREADED build, which uses the wolfSSL crypto HW mutex. */ +/* A single threaded build uses the wolfSSL hardware mutex instead. */ #ifdef SINGLE_THREADED #undef WOLFSSL_VERSAL_GEN2_ASU_SINGLE_THREADED #define WOLFSSL_VERSAL_GEN2_ASU_SINGLE_THREADED diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h index 3fd2c13e02..f19dc33266 100644 --- a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h @@ -19,8 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* Shared helpers for the Versal Gen2 ASU port: the asynchronous request to - * synchronous completion bridge and cache maintenance for ASU buffers. */ +/* Shared helpers for the ASU port: waiting on a request and cache handling. */ #ifndef WOLFSSL_VERSAL_GEN2_ASU_UTIL_H #define WOLFSSL_VERSAL_GEN2_ASU_UTIL_H @@ -36,9 +35,8 @@ extern "C" { #endif -/* Debug print to confirm operations are going through the ASU port. Enable by - * defining WOLFSSL_VERSAL_GEN2_ASU_DEBUG in user_settings.h. Prints over the - * standalone console (xil_printf); compiles out otherwise. */ +/* Debug print to show work going through the ASU. Turn it on with + * WOLFSSL_VERSAL_GEN2_ASU_DEBUG in user_settings.h. */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_DEBUG #include "xil_printf.h" #define WC_ASU_PRINTF(...) xil_printf(__VA_ARGS__) @@ -46,49 +44,40 @@ extern "C" { #define WC_ASU_PRINTF(...) ((void)0) #endif -/* Completion record for one asynchronous ASU request. The ASU client invokes - * the shared response handler from its mailbox path, which fills this record; - * the submitting code then spins until Done is set. */ +/* One record per request. The handler fills it in and the caller waits for + * Done to be set. */ typedef struct AsuWait { - volatile byte Done; /* set when the response handler has run */ - volatile word32 Status; /* server status captured by the handler */ + volatile byte Done; /* set once the handler has run */ + volatile word32 Status; /* status the handler saved */ } AsuWait; -/* Initialize Wait and point ClientParams at the shared response handler. The - * request is configured as high priority and secure by default. */ +#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT +/* Open the IPI mailbox and bring the ASU client up on it. 0 or WC_HW_E. */ +WOLFSSL_LOCAL int wc_AsuClientInit(void); +#endif + +/* Set up the wait record and point the params at the handler. */ WOLFSSL_LOCAL void wc_AsuWaitPrepare(AsuWait* wait, XAsu_ClientParams* params); -/* Block until the request bound to Wait completes and return the server - * status. Single threaded baremetal client, so a busy wait is correct. */ +/* Wait for the request to finish and return its status. */ WOLFSSL_LOCAL word32 wc_AsuWaitDone(AsuWait* wait); -/* Clean a buffer out to memory so the ASU sees the latest CPU writes. */ +/* Push a buffer out to memory so the ASU sees the newest data. */ WOLFSSL_LOCAL void wc_AsuCacheFlush(const void* addr, word32 len); -/* Invalidate a buffer so the CPU reads what the ASU wrote to memory. */ +/* Reload a buffer so the CPU sees what the ASU wrote. */ WOLFSSL_LOCAL void wc_AsuCacheInvalidate(void* addr, word32 len); /* ----------------------------------------------------------------------- */ /* Transaction and concurrency (ticketing) */ /* ----------------------------------------------------------------------- */ -/* Submit function for one ASU transaction. The implementation fills its - * request from ctx and calls the matching XAsu_* client API with the prepared - * ClientParams, returning the client submission status (XST_SUCCESS when the - * request was queued). It is called with the submit lock held, so it must only - * queue the request, never wait. */ +/* Fills in a request and queues it. Called with the lock held, so it must + * only queue the request and never wait. */ typedef int (*AsuSubmitFn)(XAsu_ClientParams* params, void* ctx); -/* Run one ASU transaction and return the server status (XST_SUCCESS on success, - * otherwise a failure status). If additionalStatus is not NULL it receives the - * server AdditionalStatus field, used by operations like AES GCM tag checks. - * - * With WOLFSSL_VERSAL_GEN2_ASU_SINGLE_THREADED this is submit then wait under - * the wolfSSL crypto hardware mutex. Otherwise it takes a FIFO ticket, submits - * under the short submit lock so the non thread safe client allocation is - * serialized, hands the turn to the next waiter, then waits on its own - * completion outside the lock, so up to the ASU queue depth of requests run - * concurrently across threads. */ +/* Run one ASU operation and return its status. additionalStatus, when given, + * receives the extra status field used by things like the GCM tag check. */ WOLFSSL_LOCAL word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, word32* additionalStatus); @@ -96,11 +85,8 @@ WOLFSSL_LOCAL word32 wc_AsuTransact(AsuSubmitFn submit, void* ctx, /* ----------------------------------------------------------------------- */ /* Timer and RTC (optional, for benchmarking) */ /* ----------------------------------------------------------------------- */ -/* The entire timer and RTC facility is gated by WOLFSSL_VERSAL_GEN2_ASU_RTC, - * turned on in user_settings.h, so it compiles out completely for a build that - * does not benchmark. When enabled it provides the benchmark current_time() - * hook from the Cortex A78 generic timer and an optional system RTC wall clock - * read. */ +/* All of this is gated by WOLFSSL_VERSAL_GEN2_ASU_RTC, so it disappears in a + * build that does not benchmark. */ #ifdef WOLFSSL_VERSAL_GEN2_ASU_RTC /* Bring up the time source. Returns 0 on success. */ @@ -109,12 +95,10 @@ WOLFSSL_LOCAL int wc_AsuTimerInit(void); /* Raw monotonic count from the generic timer. */ WOLFSSL_LOCAL word64 wc_AsuTimerCount(void); -/* Monotonic time in seconds from the generic timer. This is what the benchmark - * current_time() hook reports. */ +/* Seconds from the timer, which is what the benchmark reports. */ WOLFSSL_LOCAL double wc_AsuTimerSeconds(void); -/* Wall clock time in seconds from the system RTC. One second resolution, so it - * is for timestamps, not per operation timing. */ +/* Wall clock seconds. Only good to one second, so use it for timestamps. */ WOLFSSL_LOCAL word32 wc_AsuRtcSeconds(void); #endif /* WOLFSSL_VERSAL_GEN2_ASU_RTC */