Add a Wycheproof JSON vector validation example for cryptodev PMDs. Support these algorithms when advertised by the selected PMD: - AEAD: AES-GCM, AES-CCM, SM4-GCM, ChaCha20-Poly1305 - MAC: AES-CMAC, AES-GMAC, HMAC SHA-1/SHA-2/SHA-3/SM3 - Asymmetric: DSA (P1363 verify), ECDSA (P1363 verify), ECDH (ecpoint shared-secret compute)
Validate valid vectors against generated ciphertexts, tags, plaintexts, digests, shared secrets, or signature verification, and require the expected rejection for invalid vectors. Digest inputs for DSA and ECDSA use the symmetric auth path, selecting a separate symmetric-capable device when the target device is asymmetric-only. Skip parameter combinations outside PMD capability ranges and identify recognized vector families without a compatible DPDK transform. A --debug option lists every failed or skipped vector. Add Meson and standalone build integration, with usage documentation. Signed-off-by: Kai Ji <[email protected]> --- MAINTAINERS | 5 + doc/guides/sample_app_ug/index.rst | 1 + .../sample_app_ug/wycheproof_validation.rst | 45 + examples/meson.build | 1 + examples/wycheproof_validation/Makefile | 40 + examples/wycheproof_validation/main.c | 1982 +++++++++++++++++ examples/wycheproof_validation/meson.build | 17 + 7 files changed, 2091 insertions(+) create mode 100644 doc/guides/sample_app_ug/wycheproof_validation.rst create mode 100644 examples/wycheproof_validation/Makefile create mode 100644 examples/wycheproof_validation/main.c create mode 100644 examples/wycheproof_validation/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index e99a65d197..c20b037b6d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2041,6 +2041,11 @@ M: Gowrishankar Muthukrishnan <[email protected]> F: examples/fips_validation/ F: doc/guides/sample_app_ug/fips_validation.rst +Wycheproof validation example +M: Kai Ji <[email protected]> +F: examples/wycheproof_validation/ +F: doc/guides/sample_app_ug/wycheproof_validation.rst + Flow filtering example M: Ori Kam <[email protected]> F: examples/flow_filtering/ diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index f12623bb66..29313fe3de 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -53,6 +53,7 @@ Sample Applications User Guides ptpclient ptp_tap_relay_sw fips_validation + wycheproof_validation ipsec_secgw bbdev_app ntb diff --git a/doc/guides/sample_app_ug/wycheproof_validation.rst b/doc/guides/sample_app_ug/wycheproof_validation.rst new file mode 100644 index 0000000000..3cc90e6dd0 --- /dev/null +++ b/doc/guides/sample_app_ug/wycheproof_validation.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2026 Intel Corporation. + +Wycheproof Validation Example +============================ + +Overview +-------- + +This example validates a DPDK cryptodev implementation against the Google +Wycheproof JSON test vectors. + +The application reads one JSON file or a directory of JSON files at runtime and +checks the supported algorithm families against the selected PMD. It can be used +with a PMD that advertises AEAD, MAC, DSA, ECDH, or ECDSA support. + +Build +----- + +Build the example from the DPDK tree with Meson: + +.. code-block:: console + + meson setup build -Dexamples=wycheproof_validation -Denable_drivers=crypto/openssl + meson compile -C build + +Standalone Makefile builds are also supported from the example directory. + +Run +--- + +Run the example with an OpenSSL-backed cryptodev and a vector file: + +.. code-block:: console + + ./build/examples/dpdk-wycheproof_validation --vdev crypto_openssl -- \ + --vectors ../wycheproof/testvectors_v1/aes_gcm_test.json \ + --cryptodev crypto_openssl --debug + +The program accepts a single JSON file or a directory of JSON files. Use +``--debug`` to print each failed or skipped vector with its reason. + +The exit code is nonzero when a parsed and supported vector fails validation; +unsupported vectors, capability-skipped cases, and acceptable results are reported +in the summary without forcing a failure. diff --git a/examples/meson.build b/examples/meson.build index 25d9c88457..000d05863a 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -59,6 +59,7 @@ all_examples = [ 'vm_power_manager/guest_cli', 'vmdq', 'vmdq_dcb', + 'wycheproof_validation', ] # on install, skip copying all meson.build files diff --git a/examples/wycheproof_validation/Makefile b/examples/wycheproof_validation/Makefile new file mode 100644 index 0000000000..8cc0293364 --- /dev/null +++ b/examples/wycheproof_validation/Makefile @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 + +APP = wycheproof_validation +SRCS-y := main.c + +PKGCONF ?= pkg-config + +ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0) +$(error "no installation of DPDK found") +endif +ifneq ($(shell $(PKGCONF) --exists jansson && echo 0),0) +$(error "Jansson is required") +endif + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null) +CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk jansson) +LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk jansson) +LDFLAGS_STATIC = $(shell $(PKGCONF) --static --libs libdpdk jansson) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + test -d build && rmdir -p build || true diff --git a/examples/wycheproof_validation/main.c b/examples/wycheproof_validation/main.c new file mode 100644 index 0000000000..410f8671ae --- /dev/null +++ b/examples/wycheproof_validation/main.c @@ -0,0 +1,1982 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 + */ + +#include <dirent.h> +#include <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <jansson.h> + +#include <rte_cryptodev.h> +#include <rte_cycles.h> +#include <rte_eal.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_malloc.h> + +#define APP_NAME "wycheproof_validation" +#define DEFAULT_MBUF_DATA_ROOM 16384 +#define MAX_IV_LEN 512 +#define MAX_EC_BYTESIZE 66 +#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op)) + +struct wycheproof_data { + uint8_t *key; + uint8_t *iv; + uint8_t *aad; + uint8_t *msg; + uint8_t *ct; + uint8_t *tag; + uint32_t key_len; + uint32_t iv_len; + uint32_t aad_len; + uint32_t msg_len; + uint32_t ct_len; + uint32_t tag_len; + json_int_t tc_id; + const char *result; +}; + +struct app_env { + uint8_t dev_id; + uint8_t hash_dev_id; + bool hash_dev_own; + uint32_t mbuf_data_room; + const char *vectors_path; + bool debug; + struct rte_mempool *mbuf_pool; + struct rte_mempool *session_pool; + struct rte_mempool *op_pool; + struct rte_mempool *asym_session_pool; + struct rte_mempool *asym_op_pool; +}; + +struct app_stats { + uint64_t passed; + uint64_t failed; + uint64_t skipped_capability; + uint64_t skipped_acceptable; + uint64_t skipped_unsupported; +}; + +static struct app_env env = { + .mbuf_data_room = DEFAULT_MBUF_DATA_ROOM, +}; + +static void +usage(const char *program) +{ + printf("%s [EAL options] -- --vectors PATH [--cryptodev NAME | --cryptodev-id ID] [--debug]\n", + program); +} + +static void +debug_vector(const char *type, const char *algorithm, + const struct wycheproof_data *vector, const char *reason) +{ + if (env.debug) + printf("%s %s tcId=%" JSON_INTEGER_FORMAT " result=%s: %s\n", type, + algorithm, vector->tc_id, vector->result, reason); +} + +static void +debug_file(const char *type, const char *path, const char *reason) +{ + if (env.debug) + printf("%s %s: %s\n", type, path, reason); +} + +static int +parse_uint32(const char *text, uint32_t *value) +{ + char *end; + unsigned long parsed; + + errno = 0; + parsed = strtoul(text, &end, 10); + if (errno != 0 || *text == '\0' || *end != '\0' || parsed > UINT32_MAX) + return -EINVAL; + + *value = parsed; + return 0; +} + +static int +parse_args(int argc, char **argv) +{ + static const struct option options[] = { + { "vectors", required_argument, NULL, 'v' }, + { "cryptodev", required_argument, NULL, 'c' }, + { "cryptodev-id", required_argument, NULL, 'd' }, + { "mbuf-dataroom", required_argument, NULL, 'm' }, + { "debug", no_argument, NULL, 'D' }, + { NULL, 0, NULL, 0 }, + }; + bool have_device = false; + int option; + + if (rte_cryptodev_count() == 0) + return -ENODEV; + env.dev_id = 0; + + while ((option = getopt_long(argc, argv, "v:c:d:m:D", options, NULL)) != -1) { + uint32_t value; + int dev_id; + + switch (option) { + case 'v': + env.vectors_path = optarg; + break; + case 'c': + dev_id = rte_cryptodev_get_dev_id(optarg); + if (dev_id < 0) + return dev_id; + env.dev_id = dev_id; + have_device = true; + break; + case 'd': + if (parse_uint32(optarg, &value) != 0 || + !rte_cryptodev_is_valid_dev(value)) + return -EINVAL; + env.dev_id = value; + have_device = true; + break; + case 'm': + if (parse_uint32(optarg, &env.mbuf_data_room) != 0 || + env.mbuf_data_room < RTE_PKTMBUF_HEADROOM || + env.mbuf_data_room > UINT16_MAX) + return -EINVAL; + break; + case 'D': + env.debug = true; + break; + default: + return -EINVAL; + } + } + + if (env.vectors_path == NULL) + return -EINVAL; + if (!have_device) + printf("Using cryptodev %u (%s)\n", env.dev_id, + rte_cryptodev_name_get(env.dev_id)); + + return 0; +} + +static int +app_init(void) +{ + struct rte_cryptodev_config config = { rte_socket_id(), 1, 0 }; + struct rte_cryptodev_qp_conf queue_pair = { 128, NULL }; + uint32_t session_size; + int ret; + + ret = rte_cryptodev_configure(env.dev_id, &config); + if (ret < 0) + return ret; + + env.mbuf_pool = rte_pktmbuf_pool_create("WYCHEPROOF_MBUF_POOL", 64, 0, 0, + env.mbuf_data_room, rte_socket_id()); + if (env.mbuf_pool == NULL) { + ret = -rte_errno; + goto error; + } + + session_size = rte_cryptodev_sym_get_private_session_size(env.dev_id); + { + /* The hashing device may differ from the target device, so size the + * session pool for the largest sym session across all devices. + */ + uint8_t d; + + for (d = 0; d < rte_cryptodev_count(); d++) { + uint32_t s = rte_cryptodev_sym_get_private_session_size(d); + + if (s > session_size) + session_size = s; + } + } + env.session_pool = rte_cryptodev_sym_session_pool_create("WYCHEPROOF_SESSION_POOL", + 64, session_size, 0, 0, rte_socket_id()); + if (env.session_pool == NULL) { + ret = -rte_errno; + goto error; + } + + /* priv_size is measured from the end of the sym op; the IV lives there. */ + env.op_pool = rte_crypto_op_pool_create("WYCHEPROOF_OP_POOL", + RTE_CRYPTO_OP_TYPE_SYMMETRIC, 64, 0, MAX_IV_LEN, rte_socket_id()); + if (env.op_pool == NULL) { + ret = -rte_errno; + goto error; + } + + /* Asymmetric pools are best-effort; DSA/ECDSA paths gate on the + * device asymmetric feature flag at runtime. + */ + env.asym_session_pool = rte_cryptodev_asym_session_pool_create( + "WYCHEPROOF_ASYM_SESS", 16, 0, 0, rte_socket_id()); + env.asym_op_pool = rte_crypto_op_pool_create("WYCHEPROOF_ASYM_OP", + RTE_CRYPTO_OP_TYPE_ASYMMETRIC, 64, 0, 0, rte_socket_id()); + + queue_pair.mp_session = env.session_pool; + ret = rte_cryptodev_queue_pair_setup(env.dev_id, 0, &queue_pair, + rte_socket_id()); + if (ret < 0) + goto error; + + ret = rte_cryptodev_start(env.dev_id); + if (ret < 0) + goto error; + + /* Digest computation for DSA/ECDSA needs a symmetric-auth device. The + * target device may be asym-only (for example QAT), so pick a separate + * sym-capable device for hashing when needed. + */ + env.hash_dev_id = env.dev_id; + env.hash_dev_own = false; + { + struct rte_cryptodev_sym_capability_idx hash_idx = { + .type = RTE_CRYPTO_SYM_XFORM_AUTH, + .algo.auth = RTE_CRYPTO_AUTH_SHA256, + }; + uint8_t count = rte_cryptodev_count(); + uint8_t d; + + if (rte_cryptodev_sym_capability_get(env.dev_id, &hash_idx) == NULL) { + for (d = 0; d < count; d++) { + struct rte_cryptodev_qp_conf hash_qp = { 128, NULL }; + + if (d == env.dev_id) + continue; + if (rte_cryptodev_sym_capability_get(d, &hash_idx) == NULL) + continue; + if (rte_cryptodev_configure(d, &config) < 0) + continue; + hash_qp.mp_session = env.session_pool; + if (rte_cryptodev_queue_pair_setup(d, 0, &hash_qp, + rte_socket_id()) < 0 || + rte_cryptodev_start(d) < 0) { + rte_cryptodev_close(d); + continue; + } + env.hash_dev_id = d; + env.hash_dev_own = true; + break; + } + } + } + + return 0; + +error: + rte_mempool_free(env.asym_op_pool); + rte_mempool_free(env.asym_session_pool); + rte_mempool_free(env.op_pool); + rte_mempool_free(env.session_pool); + rte_mempool_free(env.mbuf_pool); + env.asym_op_pool = NULL; + env.asym_session_pool = NULL; + env.op_pool = NULL; + env.session_pool = NULL; + env.mbuf_pool = NULL; + return ret; +} + +static void +app_uninit(void) +{ + if (env.hash_dev_own) { + rte_cryptodev_stop(env.hash_dev_id); + rte_cryptodev_close(env.hash_dev_id); + } + rte_cryptodev_stop(env.dev_id); + rte_cryptodev_close(env.dev_id); + rte_mempool_free(env.asym_op_pool); + rte_mempool_free(env.asym_session_pool); + rte_mempool_free(env.op_pool); + rte_mempool_free(env.session_pool); + rte_mempool_free(env.mbuf_pool); +} + +static void +free_vector(struct wycheproof_data *vector) +{ + rte_free(vector->key); + rte_free(vector->iv); + rte_free(vector->aad); + rte_free(vector->msg); + rte_free(vector->ct); + rte_free(vector->tag); + memset(vector, 0, sizeof(*vector)); +} + +static int +hex_value(char character) +{ + if (character >= '0' && character <= '9') + return character - '0'; + if (character >= 'a' && character <= 'f') + return character - 'a' + 10; + if (character >= 'A' && character <= 'F') + return character - 'A' + 10; + return -1; +} + +static int +decode_hex(json_t *value, uint8_t **buffer, uint32_t *length) +{ + const char *hex; + size_t hex_length; + uint8_t *decoded; + size_t index; + + if (!json_is_string(value)) + return -EINVAL; + + hex = json_string_value(value); + hex_length = strlen(hex); + if ((hex_length & 1) != 0 || hex_length / 2 > UINT32_MAX) + return -EINVAL; + + *length = hex_length / 2; + if (*length == 0) + return 0; + + decoded = rte_malloc(NULL, *length, 0); + if (decoded == NULL) + return -ENOMEM; + + for (index = 0; index < *length; index++) { + int high = hex_value(hex[index * 2]); + int low = hex_value(hex[index * 2 + 1]); + + if (high < 0 || low < 0) { + rte_free(decoded); + return -EINVAL; + } + decoded[index] = (high << 4) | low; + } + + *buffer = decoded; + return 0; +} + +static int +parse_vector(json_t *test, struct wycheproof_data *vector) +{ + json_t *tc_id; + int ret; + + memset(vector, 0, sizeof(*vector)); + tc_id = json_object_get(test, "tcId"); + vector->result = json_string_value(json_object_get(test, "result")); + if (!json_is_integer(tc_id) || vector->result == NULL) + return -EINVAL; + vector->tc_id = json_integer_value(tc_id); + + ret = decode_hex(json_object_get(test, "key"), &vector->key, &vector->key_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "iv"), &vector->iv, &vector->iv_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "aad"), &vector->aad, &vector->aad_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "msg"), &vector->msg, &vector->msg_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "ct"), &vector->ct, &vector->ct_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "tag"), &vector->tag, &vector->tag_len); + if (ret != 0) + goto error; + + return 0; + +error: + free_vector(vector); + return ret; +} + +static int +parse_mac_vector(json_t *test, struct wycheproof_data *vector) +{ + json_t *tc_id; + int ret; + + memset(vector, 0, sizeof(*vector)); + tc_id = json_object_get(test, "tcId"); + vector->result = json_string_value(json_object_get(test, "result")); + if (!json_is_integer(tc_id) || vector->result == NULL) + return -EINVAL; + vector->tc_id = json_integer_value(tc_id); + + ret = decode_hex(json_object_get(test, "key"), &vector->key, &vector->key_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "msg"), &vector->msg, &vector->msg_len); + if (ret != 0) + goto error; + ret = decode_hex(json_object_get(test, "tag"), &vector->tag, &vector->tag_len); + if (ret != 0) + goto error; + + return 0; + +error: + free_vector(vector); + return ret; +} + +static int +parse_mac_with_iv_vector(json_t *test, struct wycheproof_data *vector) +{ + int ret; + + ret = parse_mac_vector(test, vector); + if (ret != 0) + return ret; + ret = decode_hex(json_object_get(test, "iv"), &vector->iv, &vector->iv_len); + if (ret != 0) + free_vector(vector); + return ret; +} + +/* Bounded wait for one completed op; NULL on timeout so a stuck PMD fails the + * vector instead of hanging the tool. + */ +static struct rte_crypto_op * +dequeue_one(uint8_t dev_id) +{ + struct rte_crypto_op *completed = NULL; + uint64_t deadline = rte_get_timer_cycles() + rte_get_timer_hz(); + + while (rte_cryptodev_dequeue_burst(dev_id, 0, &completed, 1) == 0) { + if (rte_get_timer_cycles() > deadline) + return NULL; + rte_pause(); + } + return completed; +} + +static int +check_aead_capability(const struct wycheproof_data *vector, + enum rte_crypto_aead_algorithm algorithm) +{ + struct rte_cryptodev_sym_capability_idx index = { + .type = RTE_CRYPTO_SYM_XFORM_AEAD, + .algo.aead = algorithm, + }; + const struct rte_cryptodev_symmetric_capability *capability; + + capability = rte_cryptodev_sym_capability_get(env.dev_id, &index); + if (capability == NULL) + return -ENOTSUP; + + return rte_cryptodev_sym_capability_check_aead(capability, vector->key_len, + vector->tag_len, vector->aad_len, vector->iv_len); +} + +static int +run_aead(const struct wycheproof_data *vector, enum rte_crypto_aead_algorithm algorithm, + enum rte_crypto_aead_operation operation, uint8_t **output, uint8_t **digest, + enum rte_crypto_op_status *status) +{ + struct rte_crypto_sym_xform xform = { 0 }; + struct rte_cryptodev_sym_session *session = NULL; + struct rte_crypto_op *operation_op = NULL; + struct rte_mbuf *mbuf = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_crypto_sym_op *sym_op; + uint8_t *aad = NULL; + uint8_t *input; + uint32_t input_len; + int ret = -ENOMEM; + + *output = NULL; + *digest = NULL; + xform.type = RTE_CRYPTO_SYM_XFORM_AEAD; + xform.aead.algo = algorithm; + xform.aead.op = operation; + xform.aead.key.data = vector->key; + xform.aead.key.length = vector->key_len; + xform.aead.iv.offset = IV_OFFSET; + xform.aead.iv.length = vector->iv_len; + xform.aead.aad_length = vector->aad_len; + xform.aead.digest_length = vector->tag_len; + + session = rte_cryptodev_sym_session_create(env.dev_id, &xform, env.session_pool); + if (session == NULL) + goto out; + + operation_op = rte_crypto_op_alloc(env.op_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC); + if (operation_op == NULL) + goto out; + + mbuf = rte_pktmbuf_alloc(env.mbuf_pool); + if (mbuf == NULL) + goto out; + input = operation == RTE_CRYPTO_AEAD_OP_ENCRYPT ? vector->msg : vector->ct; + input_len = operation == RTE_CRYPTO_AEAD_OP_ENCRYPT ? vector->msg_len : vector->ct_len; + if (input_len > rte_pktmbuf_tailroom(mbuf)) { + ret = -EMSGSIZE; + goto out; + } + if (input_len != 0) + memcpy(rte_pktmbuf_append(mbuf, input_len), input, input_len); + + *digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE); + if (*digest == NULL && vector->tag_len != 0) + goto out; + if (operation == RTE_CRYPTO_AEAD_OP_DECRYPT && vector->tag_len != 0) + memcpy(*digest, vector->tag, vector->tag_len); + + sym_op = operation_op->sym; + sym_op->m_src = mbuf; + sym_op->aead.data.offset = 0; + sym_op->aead.data.length = input_len; + if (algorithm == RTE_CRYPTO_AEAD_AES_CCM) { + aad = rte_zmalloc(NULL, RTE_ALIGN_CEIL(vector->aad_len + 18, 16), 0); + if (aad == NULL) + goto out; + if (vector->aad_len != 0) + memcpy(aad + 18, vector->aad, vector->aad_len); + sym_op->aead.aad.data = aad; + sym_op->aead.aad.phys_addr = rte_malloc_virt2iova(aad); + memcpy(rte_crypto_op_ctod_offset(operation_op, uint8_t *, IV_OFFSET) + 1, + vector->iv, vector->iv_len); + } else { + sym_op->aead.aad.data = vector->aad; + sym_op->aead.aad.phys_addr = rte_malloc_virt2iova(vector->aad); + memcpy(rte_crypto_op_ctod_offset(operation_op, uint8_t *, IV_OFFSET), vector->iv, + vector->iv_len); + } + sym_op->aead.digest.data = *digest; + sym_op->aead.digest.phys_addr = rte_malloc_virt2iova(*digest); + rte_crypto_op_attach_sym_session(operation_op, session); + + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &operation_op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + + *status = completed->status; + if (*status == RTE_CRYPTO_OP_STATUS_SUCCESS && input_len != 0) { + *output = rte_malloc(NULL, input_len, 0); + if (*output == NULL) + goto out; + memcpy(*output, rte_pktmbuf_mtod(mbuf, uint8_t *), input_len); + } + ret = 0; + +out: + rte_free(aad); + rte_pktmbuf_free(mbuf); + rte_crypto_op_free(operation_op); + rte_cryptodev_sym_session_free(env.dev_id, session); + if (ret != 0) { + rte_free(*output); + rte_free(*digest); + *output = NULL; + *digest = NULL; + } + return ret; +} + +static int +run_hmac(const struct wycheproof_data *vector, enum rte_crypto_auth_algorithm algorithm, + enum rte_crypto_auth_operation operation, uint8_t **digest, + enum rte_crypto_op_status *status) +{ + struct rte_crypto_sym_xform xform = { 0 }; + struct rte_cryptodev_sym_session *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_mbuf *mbuf = NULL; + const struct rte_cryptodev_symmetric_capability *capability; + struct rte_cryptodev_sym_capability_idx index = { + .type = RTE_CRYPTO_SYM_XFORM_AUTH, + .algo.auth = algorithm, + }; + int ret = -ENOMEM; + + *digest = NULL; + capability = rte_cryptodev_sym_capability_get(env.dev_id, &index); + if (capability == NULL || rte_cryptodev_sym_capability_check_auth(capability, + vector->key_len, vector->tag_len, 0) != 0) + return -ENOTSUP; + + xform.type = RTE_CRYPTO_SYM_XFORM_AUTH; + xform.auth.algo = algorithm; + xform.auth.op = operation; + xform.auth.key.data = vector->key; + xform.auth.key.length = vector->key_len; + xform.auth.digest_length = vector->tag_len; + session = rte_cryptodev_sym_session_create(env.dev_id, &xform, env.session_pool); + if (session == NULL) + goto out; + + op = rte_crypto_op_alloc(env.op_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC); + if (op == NULL) + goto out; + mbuf = rte_pktmbuf_alloc(env.mbuf_pool); + if (mbuf == NULL) + goto out; + if (vector->msg_len > rte_pktmbuf_tailroom(mbuf)) { + ret = -EMSGSIZE; + goto out; + } + if (vector->msg_len != 0) + memcpy(rte_pktmbuf_append(mbuf, vector->msg_len), vector->msg, vector->msg_len); + + *digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE); + if (*digest == NULL && vector->tag_len != 0) + goto out; + if (operation == RTE_CRYPTO_AUTH_OP_VERIFY && vector->tag_len != 0) + memcpy(*digest, vector->tag, vector->tag_len); + + op->sym->m_src = mbuf; + op->sym->auth.data.offset = 0; + op->sym->auth.data.length = vector->msg_len; + op->sym->auth.digest.data = *digest; + op->sym->auth.digest.phys_addr = rte_malloc_virt2iova(*digest); + rte_crypto_op_attach_sym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + *status = completed->status; + ret = 0; + +out: + rte_pktmbuf_free(mbuf); + rte_crypto_op_free(op); + rte_cryptodev_sym_session_free(env.dev_id, session); + if (ret != 0) { + rte_free(*digest); + *digest = NULL; + } + return ret; +} + +static int +run_gmac(const struct wycheproof_data *vector, enum rte_crypto_auth_operation operation, + uint8_t **digest, enum rte_crypto_op_status *status) +{ + struct rte_crypto_sym_xform xform = { 0 }; + struct rte_cryptodev_sym_session *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_mbuf *mbuf = NULL; + const struct rte_cryptodev_symmetric_capability *capability; + struct rte_cryptodev_sym_capability_idx index = { + .type = RTE_CRYPTO_SYM_XFORM_AUTH, + .algo.auth = RTE_CRYPTO_AUTH_AES_GMAC, + }; + int ret = -ENOMEM; + + *digest = NULL; + capability = rte_cryptodev_sym_capability_get(env.dev_id, &index); + if (capability == NULL || rte_cryptodev_sym_capability_check_auth(capability, + vector->key_len, vector->tag_len, vector->iv_len) != 0) + return -ENOTSUP; + + xform.type = RTE_CRYPTO_SYM_XFORM_AUTH; + xform.auth.algo = RTE_CRYPTO_AUTH_AES_GMAC; + xform.auth.op = operation; + xform.auth.key.data = vector->key; + xform.auth.key.length = vector->key_len; + xform.auth.iv.offset = IV_OFFSET; + xform.auth.iv.length = vector->iv_len; + xform.auth.digest_length = vector->tag_len; + session = rte_cryptodev_sym_session_create(env.dev_id, &xform, env.session_pool); + if (session == NULL) + goto out; + + op = rte_crypto_op_alloc(env.op_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC); + if (op == NULL) + goto out; + mbuf = rte_pktmbuf_alloc(env.mbuf_pool); + if (mbuf == NULL) + goto out; + if (vector->msg_len > rte_pktmbuf_tailroom(mbuf)) { + ret = -EMSGSIZE; + goto out; + } + if (vector->msg_len != 0) + memcpy(rte_pktmbuf_append(mbuf, vector->msg_len), vector->msg, vector->msg_len); + + *digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE); + if (*digest == NULL && vector->tag_len != 0) + goto out; + if (operation == RTE_CRYPTO_AUTH_OP_VERIFY && vector->tag_len != 0) + memcpy(*digest, vector->tag, vector->tag_len); + + op->sym->m_src = mbuf; + op->sym->auth.data.offset = 0; + op->sym->auth.data.length = vector->msg_len; + op->sym->auth.digest.data = *digest; + op->sym->auth.digest.phys_addr = rte_malloc_virt2iova(*digest); + memcpy(rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET), vector->iv, vector->iv_len); + rte_crypto_op_attach_sym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + *status = completed->status; + ret = 0; + +out: + rte_pktmbuf_free(mbuf); + rte_crypto_op_free(op); + rte_cryptodev_sym_session_free(env.dev_id, session); + if (ret != 0) { + rte_free(*digest); + *digest = NULL; + } + return ret; +} + +static void +validate_aead_vector(const struct wycheproof_data *vector, + enum rte_crypto_aead_algorithm algorithm, const char *name, + struct app_stats *stats) +{ + uint8_t *output = NULL; + uint8_t *digest = NULL; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + int ret; + + if (strcmp(vector->result, "acceptable") == 0) { + stats->skipped_acceptable++; + debug_vector("SKIP", name, vector, "acceptable result policy"); + return; + } + if (strcmp(vector->result, "valid") != 0 && strcmp(vector->result, "invalid") != 0) { + stats->skipped_unsupported++; + debug_vector("SKIP", name, vector, "unknown result"); + return; + } + /* CCM writes the IV one byte into the reserved area (after the flags byte). */ + if (vector->iv_len + (algorithm == RTE_CRYPTO_AEAD_AES_CCM ? 1u : 0u) > MAX_IV_LEN || + vector->msg_len != vector->ct_len || + vector->msg_len > env.mbuf_data_room) { + stats->skipped_unsupported++; + debug_vector("SKIP", name, vector, "unsupported message length"); + return; + } + if (check_aead_capability(vector, algorithm) != 0) { + stats->skipped_capability++; + debug_vector("SKIP", name, vector, "PMD capability"); + return; + } + + if (strcmp(vector->result, "valid") == 0) { + ret = run_aead(vector, algorithm, RTE_CRYPTO_AEAD_OP_ENCRYPT, &output, &digest, + &status); + if (ret != 0 || status != RTE_CRYPTO_OP_STATUS_SUCCESS || + memcmp(output, vector->ct, vector->ct_len) != 0 || + memcmp(digest, vector->tag, vector->tag_len) != 0) + goto failed; + rte_free(output); + rte_free(digest); + output = NULL; + digest = NULL; + ret = run_aead(vector, algorithm, RTE_CRYPTO_AEAD_OP_DECRYPT, &output, &digest, + &status); + if (ret != 0 || status != RTE_CRYPTO_OP_STATUS_SUCCESS || + memcmp(output, vector->msg, vector->msg_len) != 0) + goto failed; + } else { + ret = run_aead(vector, algorithm, RTE_CRYPTO_AEAD_OP_DECRYPT, &output, &digest, + &status); + if (ret != 0 || status != RTE_CRYPTO_OP_STATUS_AUTH_FAILED) + goto failed; + } + + stats->passed++; + rte_free(output); + rte_free(digest); + return; + +failed: + if (env.debug) + printf("FAIL %s tcId=%" JSON_INTEGER_FORMAT " result=%s status=%d ret=%d\n", + name, vector->tc_id, vector->result, status, ret); + stats->failed++; + rte_free(output); + rte_free(digest); +} + +static int +process_aead(json_t *root, enum rte_crypto_aead_algorithm algorithm, + const char *name, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + + json_array_foreach(groups, group_index, group) { + json_t *tests = json_object_get(group, "tests"); + json_t *test; + size_t test_index; + + if (!json_is_array(tests)) + return -EINVAL; + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + int ret = parse_vector(test, &vector); + + if (ret != 0) { + if (env.debug) + printf("SKIP %s group=%zu test=%zu: malformed vector\n", + name, group_index, test_index); + stats->skipped_unsupported++; + continue; + } + validate_aead_vector(&vector, algorithm, name, stats); + free_vector(&vector); + } + } + + return 0; +} + +static int +process_hmac(json_t *root, enum rte_crypto_auth_algorithm algorithm, + const char *name, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + json_array_foreach(groups, group_index, group) { + json_t *tests = json_object_get(group, "tests"); + json_t *test; + size_t test_index; + + if (!json_is_array(tests)) + return -EINVAL; + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + uint8_t *digest = NULL; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + int ret; + + ret = parse_mac_vector(test, &vector); + if (ret != 0) { + if (env.debug) + printf("SKIP %s group=%zu test=%zu: malformed vector\n", + name, group_index, test_index); + stats->skipped_unsupported++; + continue; + } + if (strcmp(vector.result, "acceptable") == 0) { + stats->skipped_acceptable++; + debug_vector("SKIP", name, &vector, "acceptable result policy"); + } else if (strcmp(vector.result, "valid") == 0) { + ret = run_hmac(&vector, algorithm, RTE_CRYPTO_AUTH_OP_GENERATE, + &digest, &status); + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", name, &vector, "PMD capability"); + } else if (ret != 0 || status != RTE_CRYPTO_OP_STATUS_SUCCESS || + memcmp(digest, vector.tag, vector.tag_len) != 0) { + debug_vector("FAIL", name, &vector, "generated tag mismatch"); + stats->failed++; + } else + stats->passed++; + } else if (strcmp(vector.result, "invalid") == 0) { + ret = run_hmac(&vector, algorithm, RTE_CRYPTO_AUTH_OP_VERIFY, + &digest, &status); + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", name, &vector, "PMD capability"); + } else if (ret != 0 || status != RTE_CRYPTO_OP_STATUS_AUTH_FAILED) { + debug_vector("FAIL", name, &vector, "authentication unexpectedly succeeded"); + stats->failed++; + } else + stats->passed++; + } else { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "unknown result"); + } + rte_free(digest); + free_vector(&vector); + } + } + return 0; +} + +static int +process_gmac(json_t *root, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + json_array_foreach(groups, group_index, group) { + json_t *tests = json_object_get(group, "tests"); + json_t *test; + size_t test_index; + + if (!json_is_array(tests)) + return -EINVAL; + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + uint8_t *digest = NULL; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + int ret; + + ret = parse_mac_with_iv_vector(test, &vector); + if (ret != 0) { + if (env.debug) + printf("SKIP AES-GMAC group=%zu test=%zu: malformed vector\n", + group_index, test_index); + stats->skipped_unsupported++; + continue; + } + if (vector.iv_len > MAX_IV_LEN) { + stats->skipped_unsupported++; + debug_vector("SKIP", "AES-GMAC", &vector, "IV too large"); + rte_free(digest); + free_vector(&vector); + continue; + } + if (strcmp(vector.result, "valid") == 0) + ret = run_gmac(&vector, RTE_CRYPTO_AUTH_OP_GENERATE, + &digest, &status); + else if (strcmp(vector.result, "invalid") == 0) + ret = run_gmac(&vector, RTE_CRYPTO_AUTH_OP_VERIFY, + &digest, &status); + else { + stats->skipped_acceptable++; + debug_vector("SKIP", "AES-GMAC", &vector, "acceptable result policy"); + ret = 0; + } + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", "AES-GMAC", &vector, "PMD capability"); + } else if ((strcmp(vector.result, "valid") == 0 && + (ret != 0 || status != RTE_CRYPTO_OP_STATUS_SUCCESS || + memcmp(digest, vector.tag, vector.tag_len) != 0)) || + (strcmp(vector.result, "invalid") == 0 && + (ret != 0 || status != RTE_CRYPTO_OP_STATUS_AUTH_FAILED))) { + debug_vector("FAIL", "AES-GMAC", &vector, "unexpected operation result"); + stats->failed++; + } else if (strcmp(vector.result, "valid") == 0 || + strcmp(vector.result, "invalid") == 0) + stats->passed++; + rte_free(digest); + free_vector(&vector); + } + } + return 0; +} + +struct dsa_group { + uint8_t *p; + uint8_t *q; + uint8_t *g; + uint8_t *y; + uint32_t p_len; + uint32_t q_len; + uint32_t g_len; + uint32_t y_len; + enum rte_crypto_auth_algorithm hash; + uint32_t digest_len; +}; + +static int +map_sha(const char *name, enum rte_crypto_auth_algorithm *algo, uint32_t *digest_len) +{ + if (strcmp(name, "SHA-1") == 0) { + *algo = RTE_CRYPTO_AUTH_SHA1; + *digest_len = 20; + } else if (strcmp(name, "SHA-224") == 0) { + *algo = RTE_CRYPTO_AUTH_SHA224; + *digest_len = 28; + } else if (strcmp(name, "SHA-256") == 0) { + *algo = RTE_CRYPTO_AUTH_SHA256; + *digest_len = 32; + } else if (strcmp(name, "SHA-384") == 0) { + *algo = RTE_CRYPTO_AUTH_SHA384; + *digest_len = 48; + } else if (strcmp(name, "SHA-512") == 0) { + *algo = RTE_CRYPTO_AUTH_SHA512; + *digest_len = 64; + } else { + return -ENOTSUP; + } + return 0; +} + +/* Compute a plain (keyless) message digest through the cryptodev auth path. */ +static int +compute_hash(const uint8_t *msg, uint32_t msg_len, enum rte_crypto_auth_algorithm algo, + uint8_t *digest, uint32_t digest_len) +{ + struct rte_crypto_sym_xform xform = { 0 }; + struct rte_cryptodev_sym_session *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_mbuf *mbuf = NULL; + const struct rte_cryptodev_symmetric_capability *capability; + struct rte_cryptodev_sym_capability_idx index = { + .type = RTE_CRYPTO_SYM_XFORM_AUTH, + .algo.auth = algo, + }; + uint8_t *op_digest = NULL; + int ret = -ENOMEM; + + capability = rte_cryptodev_sym_capability_get(env.hash_dev_id, &index); + if (capability == NULL || rte_cryptodev_sym_capability_check_auth(capability, + 0, digest_len, 0) != 0) + return -ENOTSUP; + + xform.type = RTE_CRYPTO_SYM_XFORM_AUTH; + xform.auth.algo = algo; + xform.auth.op = RTE_CRYPTO_AUTH_OP_GENERATE; + xform.auth.digest_length = digest_len; + session = rte_cryptodev_sym_session_create(env.hash_dev_id, &xform, env.session_pool); + if (session == NULL) + goto out; + + op = rte_crypto_op_alloc(env.op_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC); + if (op == NULL) + goto out; + mbuf = rte_pktmbuf_alloc(env.mbuf_pool); + if (mbuf == NULL) + goto out; + if (msg_len > rte_pktmbuf_tailroom(mbuf)) { + ret = -EMSGSIZE; + goto out; + } + if (msg_len != 0) + memcpy(rte_pktmbuf_append(mbuf, msg_len), msg, msg_len); + + op_digest = rte_malloc(NULL, digest_len, RTE_CACHE_LINE_SIZE); + if (op_digest == NULL) + goto out; + op->sym->m_src = mbuf; + op->sym->auth.data.offset = 0; + op->sym->auth.data.length = msg_len; + op->sym->auth.digest.data = op_digest; + op->sym->auth.digest.phys_addr = rte_malloc_virt2iova(op_digest); + rte_crypto_op_attach_sym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.hash_dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.hash_dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + if (completed->status != RTE_CRYPTO_OP_STATUS_SUCCESS) { + ret = -EIO; + goto out; + } + memcpy(digest, op_digest, digest_len); + ret = 0; + +out: + rte_free(op_digest); + rte_pktmbuf_free(mbuf); + rte_crypto_op_free(op); + rte_cryptodev_sym_session_free(env.hash_dev_id, session); + return ret; +} + +static int +run_dsa_verify(const struct dsa_group *group, const uint8_t *msg, uint32_t msg_len, + const uint8_t *r, uint32_t r_len, const uint8_t *s, uint32_t s_len, + enum rte_crypto_op_status *status) +{ + struct rte_crypto_asym_xform xform = { 0 }; + void *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_cryptodev_info dev_info; + struct rte_cryptodev_asym_capability_idx index = { + .type = RTE_CRYPTO_ASYM_XFORM_DSA, + }; + const struct rte_cryptodev_asymmetric_xform_capability *capability; + uint8_t digest[64]; + int ret; + + if (env.asym_session_pool == NULL || env.asym_op_pool == NULL) + return -ENOTSUP; + rte_cryptodev_info_get(env.dev_id, &dev_info); + if ((dev_info.feature_flags & RTE_CRYPTODEV_FF_ASYMMETRIC_CRYPTO) == 0) + return -ENOTSUP; + capability = rte_cryptodev_asym_capability_get(env.dev_id, &index); + if (capability == NULL || rte_cryptodev_asym_xform_capability_check_optype(capability, + RTE_CRYPTO_ASYM_OP_VERIFY) == 0) + return -ENOTSUP; + + ret = compute_hash(msg, msg_len, group->hash, digest, group->digest_len); + if (ret != 0) + return ret; + + xform.xform_type = RTE_CRYPTO_ASYM_XFORM_DSA; + xform.next = NULL; + xform.dsa.p.data = group->p; + xform.dsa.p.length = group->p_len; + xform.dsa.q.data = group->q; + xform.dsa.q.length = group->q_len; + xform.dsa.g.data = group->g; + xform.dsa.g.length = group->g_len; + + ret = -ENOMEM; + if (rte_cryptodev_asym_session_create(env.dev_id, &xform, env.asym_session_pool, + &session) < 0 || session == NULL) + goto out; + op = rte_crypto_op_alloc(env.asym_op_pool, RTE_CRYPTO_OP_TYPE_ASYMMETRIC); + if (op == NULL) + goto out; + op->asym->dsa.op_type = RTE_CRYPTO_ASYM_OP_VERIFY; + /* DSA verify operates on the message digest. */ + op->asym->dsa.message.data = digest; + op->asym->dsa.message.length = group->digest_len; + op->asym->dsa.r.data = (uint8_t *)(uintptr_t)r; + op->asym->dsa.r.length = r_len; + op->asym->dsa.s.data = (uint8_t *)(uintptr_t)s; + op->asym->dsa.s.length = s_len; + op->asym->dsa.y.data = group->y; + op->asym->dsa.y.length = group->y_len; + rte_crypto_op_attach_asym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + *status = completed->status; + ret = 0; + +out: + rte_crypto_op_free(op); + if (session != NULL) + rte_cryptodev_asym_session_free(env.dev_id, session); + return ret; +} + +static int +process_dsa_p1363(json_t *root, const char *name, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + json_array_foreach(groups, group_index, group) { + json_t *pub = json_object_get(group, "publicKey"); + json_t *tests = json_object_get(group, "tests"); + struct dsa_group dsa = { 0 }; + const char *sha; + json_t *test; + size_t test_index; + + if (!json_is_object(pub) || !json_is_array(tests)) + return -EINVAL; + sha = json_string_value(json_object_get(group, "sha")); + if (sha == NULL || map_sha(sha, &dsa.hash, &dsa.digest_len) != 0) { + stats->skipped_unsupported++; + continue; + } + if (decode_hex(json_object_get(pub, "p"), &dsa.p, &dsa.p_len) != 0 || + decode_hex(json_object_get(pub, "q"), &dsa.q, &dsa.q_len) != 0 || + decode_hex(json_object_get(pub, "g"), &dsa.g, &dsa.g_len) != 0 || + decode_hex(json_object_get(pub, "y"), &dsa.y, &dsa.y_len) != 0) { + rte_free(dsa.p); + rte_free(dsa.q); + rte_free(dsa.g); + rte_free(dsa.y); + stats->skipped_unsupported++; + continue; + } + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + uint8_t *sig = NULL; + uint32_t sig_len = 0; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + json_t *tc_id; + int ret; + + memset(&vector, 0, sizeof(vector)); + tc_id = json_object_get(test, "tcId"); + vector.result = json_string_value(json_object_get(test, "result")); + if (!json_is_integer(tc_id) || vector.result == NULL) { + stats->skipped_unsupported++; + continue; + } + vector.tc_id = json_integer_value(tc_id); + if (decode_hex(json_object_get(test, "msg"), &vector.msg, + &vector.msg_len) != 0 || + decode_hex(json_object_get(test, "sig"), + &sig, &sig_len) != 0) { + rte_free(vector.msg); + rte_free(sig); + stats->skipped_unsupported++; + continue; + } + if (strcmp(vector.result, "acceptable") == 0) { + stats->skipped_acceptable++; + debug_vector("SKIP", name, &vector, "acceptable result policy"); + rte_free(vector.msg); + rte_free(sig); + continue; + } + if ((sig_len & 1) != 0 || sig_len == 0) { + debug_vector("SKIP", name, &vector, "malformed P1363 signature"); + stats->skipped_unsupported++; + rte_free(vector.msg); + rte_free(sig); + continue; + } + ret = run_dsa_verify(&dsa, vector.msg, vector.msg_len, sig, sig_len / 2, + sig + sig_len / 2, sig_len / 2, &status); + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", name, &vector, "PMD capability"); + } else if ((strcmp(vector.result, "valid") == 0 && + (ret != 0 || status != RTE_CRYPTO_OP_STATUS_SUCCESS)) || + (strcmp(vector.result, "invalid") == 0 && + (ret != 0 || status == RTE_CRYPTO_OP_STATUS_SUCCESS))) { + debug_vector("FAIL", name, &vector, "unexpected verification result"); + stats->failed++; + } else if (strcmp(vector.result, "valid") == 0 || + strcmp(vector.result, "invalid") == 0) { + stats->passed++; + } else { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "unknown result"); + } + rte_free(vector.msg); + rte_free(sig); + } + rte_free(dsa.p); + rte_free(dsa.q); + rte_free(dsa.g); + rte_free(dsa.y); + } + return 0; +} + +static int +map_curve(const char *name, enum rte_crypto_curve_id *curve, uint32_t *bytesize) +{ + if (strcmp(name, "secp224r1") == 0) { + *curve = RTE_CRYPTO_EC_GROUP_SECP224R1; + *bytesize = 28; + } else if (strcmp(name, "secp256r1") == 0) { + *curve = RTE_CRYPTO_EC_GROUP_SECP256R1; + *bytesize = 32; + } else if (strcmp(name, "secp384r1") == 0) { + *curve = RTE_CRYPTO_EC_GROUP_SECP384R1; + *bytesize = 48; + } else if (strcmp(name, "secp521r1") == 0) { + *curve = RTE_CRYPTO_EC_GROUP_SECP521R1; + *bytesize = 66; + } else { + return -ENOTSUP; + } + return 0; +} + +/* Left-align an unsigned big-endian value into a fixed-width field. */ +static bool +value_equals_padded(const uint8_t *a, uint32_t a_len, const uint8_t *b, uint32_t b_len) +{ + while (a_len > 0 && a[0] == 0) { + a++; + a_len--; + } + while (b_len > 0 && b[0] == 0) { + b++; + b_len--; + } + return a_len == b_len && memcmp(a, b, a_len) == 0; +} + +/* Left-pad a big-endian value into a fixed-width field: strip leading zeros, + * reject values wider than the field, zero-pad the remaining prefix. + */ +static int +normalize_operand(const uint8_t *value, uint32_t value_len, uint8_t *out, uint32_t bytesize) +{ + while (value_len > 0 && value[0] == 0) { + value++; + value_len--; + } + if (value_len > bytesize) + return -EINVAL; + memset(out, 0, bytesize); + memcpy(out + bytesize - value_len, value, value_len); + return 0; +} + +static int +run_ecdh_ecpoint(enum rte_crypto_curve_id curve, uint32_t bytesize, + const uint8_t *priv, uint32_t priv_len, const uint8_t *pub_x, + const uint8_t *pub_y, uint8_t **shared_x, uint32_t *shared_len, + enum rte_crypto_op_status *status) +{ + struct rte_crypto_asym_xform xform = { 0 }; + void *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_cryptodev_info dev_info; + struct rte_cryptodev_asym_capability_idx index = { + .type = RTE_CRYPTO_ASYM_XFORM_ECDH, + }; + const struct rte_cryptodev_asymmetric_xform_capability *capability; + uint8_t *out_x = NULL; + uint8_t *out_y = NULL; + uint8_t priv_buf[MAX_EC_BYTESIZE]; + int ret; + + *shared_x = NULL; + if (env.asym_session_pool == NULL || env.asym_op_pool == NULL) + return -ENOTSUP; + rte_cryptodev_info_get(env.dev_id, &dev_info); + if ((dev_info.feature_flags & RTE_CRYPTODEV_FF_ASYMMETRIC_CRYPTO) == 0) + return -ENOTSUP; + capability = rte_cryptodev_asym_capability_get(env.dev_id, &index); + if (capability == NULL) + return -ENOTSUP; + if (rte_cryptodev_asym_xform_capability_check_optype(capability, + (enum rte_crypto_asym_op_type) + RTE_CRYPTO_ASYM_KE_SHARED_SECRET_COMPUTE) == 0) + return -ENOTSUP; + + if (bytesize > sizeof(priv_buf)) + return -ENOTSUP; + if (normalize_operand(priv, priv_len, priv_buf, bytesize) != 0) + return -EINVAL; + + xform.xform_type = RTE_CRYPTO_ASYM_XFORM_ECDH; + xform.next = NULL; + xform.ec.curve_id = curve; + xform.ec.pkey.data = priv_buf; + xform.ec.pkey.length = bytesize; + xform.ec.q.x.data = (uint8_t *)(uintptr_t)pub_x; + xform.ec.q.x.length = bytesize; + xform.ec.q.y.data = (uint8_t *)(uintptr_t)pub_y; + xform.ec.q.y.length = bytesize; + + ret = -ENOMEM; + out_x = rte_zmalloc(NULL, bytesize, 0); + out_y = rte_zmalloc(NULL, bytesize, 0); + if (out_x == NULL || out_y == NULL) + goto out; + /* Session-create failure after the capability check is a PMD failure. */ + if (rte_cryptodev_asym_session_create(env.dev_id, &xform, env.asym_session_pool, + &session) < 0 || session == NULL) { + ret = -EIO; + goto out; + } + op = rte_crypto_op_alloc(env.asym_op_pool, RTE_CRYPTO_OP_TYPE_ASYMMETRIC); + if (op == NULL) + goto out; + op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_SHARED_SECRET_COMPUTE; + op->asym->ecdh.priv_key.data = priv_buf; + op->asym->ecdh.priv_key.length = bytesize; + op->asym->ecdh.pub_key.x.data = (uint8_t *)(uintptr_t)pub_x; + op->asym->ecdh.pub_key.x.length = bytesize; + op->asym->ecdh.pub_key.y.data = (uint8_t *)(uintptr_t)pub_y; + op->asym->ecdh.pub_key.y.length = bytesize; + op->asym->ecdh.shared_secret.x.data = out_x; + op->asym->ecdh.shared_secret.x.length = bytesize; + op->asym->ecdh.shared_secret.y.data = out_y; + op->asym->ecdh.shared_secret.y.length = bytesize; + rte_crypto_op_attach_asym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + *status = completed->status; + if (completed->status == RTE_CRYPTO_OP_STATUS_SUCCESS) { + *shared_x = out_x; + *shared_len = completed->asym->ecdh.shared_secret.x.length; + out_x = NULL; + } + ret = 0; + +out: + rte_free(out_x); + rte_free(out_y); + rte_crypto_op_free(op); + if (session != NULL) + rte_cryptodev_asym_session_free(env.dev_id, session); + return ret; +} + +static int +process_ecdh_ecpoint(json_t *root, const char *name, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + json_array_foreach(groups, group_index, group) { + json_t *tests = json_object_get(group, "tests"); + enum rte_crypto_curve_id curve; + uint32_t bytesize; + const char *curve_name; + json_t *test; + size_t test_index; + + if (!json_is_array(tests)) + return -EINVAL; + curve_name = json_string_value(json_object_get(group, "curve")); + if (curve_name == NULL || map_curve(curve_name, &curve, &bytesize) != 0) { + stats->skipped_unsupported++; + continue; + } + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + uint8_t *pub = NULL; + uint8_t *priv = NULL; + uint8_t *shared = NULL; + uint8_t *result_x = NULL; + uint32_t pub_len = 0, priv_len = 0, shared_len = 0, result_len = 0; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + bool matched; + json_t *tc_id; + int ret; + + memset(&vector, 0, sizeof(vector)); + tc_id = json_object_get(test, "tcId"); + vector.result = json_string_value(json_object_get(test, "result")); + if (!json_is_integer(tc_id) || vector.result == NULL) { + stats->skipped_unsupported++; + continue; + } + vector.tc_id = json_integer_value(tc_id); + if (decode_hex(json_object_get(test, "public"), &pub, &pub_len) != 0 || + decode_hex(json_object_get(test, "private"), &priv, + &priv_len) != 0 || + decode_hex(json_object_get(test, "shared"), &shared, + &shared_len) != 0) { + rte_free(pub); + rte_free(priv); + rte_free(shared); + stats->skipped_unsupported++; + continue; + } + if (strcmp(vector.result, "acceptable") == 0) { + stats->skipped_acceptable++; + debug_vector("SKIP", name, &vector, "acceptable result policy"); + goto next; + } + /* Only uncompressed points (0x04 || x || y) map to the DPDK EC point. */ + if (pub_len != 1 + 2 * bytesize || pub[0] != 0x04) { + debug_vector("SKIP", name, &vector, "non-uncompressed public point"); + stats->skipped_unsupported++; + goto next; + } + /* Invalid-curve rejection cases carry no expected shared secret; + * on-curve validation is a separate ECDH PUB_KEY_VERIFY op, not + * part of the raw shared-secret compute primitive. + */ + if (shared_len == 0) { + debug_vector("SKIP", name, &vector, + "no expected shared secret (point validation out of scope)"); + stats->skipped_unsupported++; + goto next; + } + ret = run_ecdh_ecpoint(curve, bytesize, priv, priv_len, pub + 1, + pub + 1 + bytesize, &result_x, &result_len, &status); + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", name, &vector, "PMD capability"); + goto next; + } + if (ret == -EINVAL) { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "operand wider than curve"); + goto next; + } + matched = ret == 0 && status == RTE_CRYPTO_OP_STATUS_SUCCESS && + value_equals_padded(result_x, result_len, shared, shared_len); + if (strcmp(vector.result, "valid") == 0) { + if (ret == 0 && status == RTE_CRYPTO_OP_STATUS_SUCCESS && matched) { + stats->passed++; + } else if (ret == 0 && status == RTE_CRYPTO_OP_STATUS_SUCCESS) { + debug_vector("FAIL", name, &vector, "wrong shared secret"); + stats->failed++; + } else { + /* PMD returned an error for a valid vector. */ + debug_vector("FAIL", name, &vector, "PMD could not compute"); + stats->failed++; + } + } else if (strcmp(vector.result, "invalid") == 0) { + if (matched) { + debug_vector("FAIL", name, &vector, "produced forbidden secret"); + stats->failed++; + } else { + stats->passed++; + } + } else { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "unknown result"); + } +next: + rte_free(result_x); + rte_free(pub); + rte_free(priv); + rte_free(shared); + } + } + return 0; +} + +struct ecdsa_group { + uint8_t *wx; + uint8_t *wy; + uint32_t wx_len; + uint32_t wy_len; + enum rte_crypto_curve_id curve; + uint32_t bytesize; + enum rte_crypto_auth_algorithm hash; + uint32_t digest_len; +}; + +static int +run_ecdsa_verify(const struct ecdsa_group *group, const uint8_t *msg, uint32_t msg_len, + const uint8_t *r, uint32_t r_len, const uint8_t *s, uint32_t s_len, + enum rte_crypto_op_status *status) +{ + struct rte_crypto_asym_xform xform = { 0 }; + void *session = NULL; + struct rte_crypto_op *op = NULL; + struct rte_crypto_op *completed = NULL; + struct rte_cryptodev_info dev_info; + struct rte_cryptodev_asym_capability_idx index = { + .type = RTE_CRYPTO_ASYM_XFORM_ECDSA, + }; + const struct rte_cryptodev_asymmetric_xform_capability *capability; + uint8_t digest[64]; + uint8_t wx_buf[MAX_EC_BYTESIZE]; + uint8_t wy_buf[MAX_EC_BYTESIZE]; + uint32_t e_len; + int ret; + + if (env.asym_session_pool == NULL || env.asym_op_pool == NULL) + return -ENOTSUP; + rte_cryptodev_info_get(env.dev_id, &dev_info); + if ((dev_info.feature_flags & RTE_CRYPTODEV_FF_ASYMMETRIC_CRYPTO) == 0) + return -ENOTSUP; + capability = rte_cryptodev_asym_capability_get(env.dev_id, &index); + if (capability == NULL || rte_cryptodev_asym_xform_capability_check_optype(capability, + RTE_CRYPTO_ASYM_OP_VERIFY) == 0) + return -ENOTSUP; + + ret = compute_hash(msg, msg_len, group->hash, digest, group->digest_len); + if (ret != 0) + return ret; + /* ECDSA uses e = leftmost Ln bits of the hash (byte-aligned truncation). */ + e_len = group->digest_len > group->bytesize ? group->bytesize : group->digest_len; + + if (group->bytesize > sizeof(wx_buf)) + return -ENOTSUP; + if (normalize_operand(group->wx, group->wx_len, wx_buf, group->bytesize) != 0 || + normalize_operand(group->wy, group->wy_len, wy_buf, group->bytesize) != 0) + return -EINVAL; + + xform.xform_type = RTE_CRYPTO_ASYM_XFORM_ECDSA; + xform.next = NULL; + xform.ec.curve_id = group->curve; + xform.ec.q.x.data = wx_buf; + xform.ec.q.x.length = group->bytesize; + xform.ec.q.y.data = wy_buf; + xform.ec.q.y.length = group->bytesize; + + ret = -ENOMEM; + /* Session-create failure after the capability check is a PMD failure. */ + if (rte_cryptodev_asym_session_create(env.dev_id, &xform, env.asym_session_pool, + &session) < 0 || session == NULL) { + ret = -EIO; + goto out; + } + op = rte_crypto_op_alloc(env.asym_op_pool, RTE_CRYPTO_OP_TYPE_ASYMMETRIC); + if (op == NULL) + goto out; + op->asym->ecdsa.op_type = RTE_CRYPTO_ASYM_OP_VERIFY; + op->asym->ecdsa.message.data = digest; + op->asym->ecdsa.message.length = e_len; + op->asym->ecdsa.r.data = (uint8_t *)(uintptr_t)r; + op->asym->ecdsa.r.length = r_len; + op->asym->ecdsa.s.data = (uint8_t *)(uintptr_t)s; + op->asym->ecdsa.s.length = s_len; + rte_crypto_op_attach_asym_session(op, session); + if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &op, 1) != 1) { + ret = -EIO; + goto out; + } + completed = dequeue_one(env.dev_id); + if (completed == NULL) { + ret = -ETIMEDOUT; + goto out; + } + *status = completed->status; + ret = 0; + +out: + rte_crypto_op_free(op); + if (session != NULL) + rte_cryptodev_asym_session_free(env.dev_id, session); + return ret; +} + +static int +process_ecdsa_p1363(json_t *root, const char *name, struct app_stats *stats) +{ + json_t *groups = json_object_get(root, "testGroups"); + json_t *group; + size_t group_index; + + if (!json_is_array(groups)) + return -EINVAL; + json_array_foreach(groups, group_index, group) { + json_t *pub = json_object_get(group, "publicKey"); + json_t *tests = json_object_get(group, "tests"); + struct ecdsa_group ec = { 0 }; + const char *sha; + const char *curve_name; + json_t *test; + size_t test_index; + + if (!json_is_object(pub) || !json_is_array(tests)) + return -EINVAL; + sha = json_string_value(json_object_get(group, "sha")); + curve_name = json_string_value(json_object_get(pub, "curve")); + if (sha == NULL || curve_name == NULL || + map_sha(sha, &ec.hash, &ec.digest_len) != 0 || + map_curve(curve_name, &ec.curve, &ec.bytesize) != 0) { + stats->skipped_unsupported++; + continue; + } + if (decode_hex(json_object_get(pub, "wx"), &ec.wx, &ec.wx_len) != 0 || + decode_hex(json_object_get(pub, "wy"), &ec.wy, &ec.wy_len) != 0) { + rte_free(ec.wx); + rte_free(ec.wy); + stats->skipped_unsupported++; + continue; + } + json_array_foreach(tests, test_index, test) { + struct wycheproof_data vector; + uint8_t *sig = NULL; + uint32_t sig_len = 0; + enum rte_crypto_op_status status = RTE_CRYPTO_OP_STATUS_ERROR; + json_t *tc_id; + int ret; + + memset(&vector, 0, sizeof(vector)); + tc_id = json_object_get(test, "tcId"); + vector.result = json_string_value(json_object_get(test, "result")); + if (!json_is_integer(tc_id) || vector.result == NULL) { + stats->skipped_unsupported++; + continue; + } + vector.tc_id = json_integer_value(tc_id); + if (decode_hex(json_object_get(test, "msg"), &vector.msg, + &vector.msg_len) != 0 || + decode_hex(json_object_get(test, "sig"), + &sig, &sig_len) != 0) { + rte_free(vector.msg); + rte_free(sig); + stats->skipped_unsupported++; + continue; + } + if (strcmp(vector.result, "acceptable") == 0) { + stats->skipped_acceptable++; + debug_vector("SKIP", name, &vector, "acceptable result policy"); + rte_free(vector.msg); + rte_free(sig); + continue; + } + if (sig_len != 2 * ec.bytesize) { + debug_vector("SKIP", name, &vector, + "non-canonical P1363 signature size"); + stats->skipped_unsupported++; + rte_free(vector.msg); + rte_free(sig); + continue; + } + ret = run_ecdsa_verify(&ec, vector.msg, vector.msg_len, sig, sig_len / 2, + sig + sig_len / 2, sig_len / 2, &status); + if (ret == -ENOTSUP) { + stats->skipped_capability++; + debug_vector("SKIP", name, &vector, "PMD capability"); + } else if (ret == -EINVAL) { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "operand wider than curve"); + } else if (strcmp(vector.result, "valid") == 0) { + if (ret == 0 && status == RTE_CRYPTO_OP_STATUS_SUCCESS) + stats->passed++; + else { + debug_vector("FAIL", name, &vector, "valid signature rejected"); + stats->failed++; + } + } else if (strcmp(vector.result, "invalid") == 0) { + if (ret == 0 && status == RTE_CRYPTO_OP_STATUS_SUCCESS) { + debug_vector("FAIL", name, &vector, "invalid signature accepted"); + stats->failed++; + } else { + stats->passed++; + } + } else { + stats->skipped_unsupported++; + debug_vector("SKIP", name, &vector, "unknown result"); + } + rte_free(vector.msg); + rte_free(sig); + } + rte_free(ec.wx); + rte_free(ec.wy); + } + return 0; +} + +static int +process_file(const char *path, struct app_stats *stats) +{ + json_error_t error; + json_t *root; + const char *algorithm; + const char *schema; + int ret; + + root = json_load_file(path, 0, &error); + if (root == NULL) { + if (env.debug) + printf("SKIP %s: JSON error at line %d: %s\n", path, error.line, + error.text); + return 0; + } + algorithm = json_string_value(json_object_get(root, "algorithm")); + schema = json_string_value(json_object_get(root, "schema")); + if (algorithm != NULL && schema != NULL && strcmp(algorithm, "AES-GCM") == 0 && + strcmp(schema, "aead_test_schema_v1.json") == 0) { + printf("Processing AES-GCM vectors: %s\n", path); + ret = process_aead(root, RTE_CRYPTO_AEAD_AES_GCM, "AES-GCM", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "AES-CCM") == 0 && + strcmp(schema, "aead_test_schema_v1.json") == 0) { + printf("Processing AES-CCM vectors: %s\n", path); + ret = process_aead(root, RTE_CRYPTO_AEAD_AES_CCM, "AES-CCM", stats); + } else if (algorithm != NULL && schema != NULL && + strcmp(algorithm, "CHACHA20-POLY1305") == 0 && + strcmp(schema, "aead_test_schema_v1.json") == 0) { + printf("Processing ChaCha20-Poly1305 vectors: %s\n", path); + ret = process_aead(root, RTE_CRYPTO_AEAD_CHACHA20_POLY1305, + "ChaCha20-Poly1305", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "SM4-GCM") == 0 && + strcmp(schema, "aead_test_schema_v1.json") == 0) { + printf("Processing SM4-GCM vectors: %s\n", path); + ret = process_aead(root, RTE_CRYPTO_AEAD_SM4_GCM, "SM4-GCM", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA256") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA256 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA256_HMAC, "HMAC-SHA256", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA1") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA1 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA1_HMAC, "HMAC-SHA1", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA224") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA224 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA224_HMAC, "HMAC-SHA224", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA384") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA384 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA384_HMAC, "HMAC-SHA384", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA512") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA512 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA512_HMAC, "HMAC-SHA512", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA3-224") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA3-224 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA3_224_HMAC, "HMAC-SHA3-224", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA3-256") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA3-256 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA3_256_HMAC, "HMAC-SHA3-256", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA3-384") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA3-384 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA3_384_HMAC, "HMAC-SHA3-384", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSHA3-512") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SHA3-512 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SHA3_512_HMAC, "HMAC-SHA3-512", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "HMACSM3") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing HMAC-SM3 vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_SM3_HMAC, "HMAC-SM3", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "AES-CMAC") == 0 && + strcmp(schema, "mac_test_schema_v1.json") == 0) { + printf("Processing AES-CMAC vectors: %s\n", path); + ret = process_hmac(root, RTE_CRYPTO_AUTH_AES_CMAC, "AES-CMAC", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "AES-GMAC") == 0 && + strcmp(schema, "mac_with_iv_test_schema_v1.json") == 0) { + printf("Processing AES-GMAC vectors: %s\n", path); + ret = process_gmac(root, stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "DSA") == 0 && + strcmp(schema, "dsa_p1363_verify_schema_v1.json") == 0) { + printf("Processing DSA (P1363) vectors: %s\n", path); + ret = process_dsa_p1363(root, "DSA", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "ECDH") == 0 && + strcmp(schema, "ecdh_ecpoint_test_schema_v1.json") == 0) { + printf("Processing ECDH (ecpoint) vectors: %s\n", path); + ret = process_ecdh_ecpoint(root, "ECDH", stats); + } else if (algorithm != NULL && schema != NULL && strcmp(algorithm, "ECDSA") == 0 && + strcmp(schema, "ecdsa_p1363_verify_schema_v1.json") == 0) { + printf("Processing ECDSA (P1363) vectors: %s\n", path); + ret = process_ecdsa_p1363(root, "ECDSA", stats); + } else if (algorithm != NULL && + (strncmp(algorithm, "AES-", strlen("AES-")) == 0 || + strncmp(algorithm, "SEED-", strlen("SEED-")) == 0 || + strcmp(algorithm, "SM4-CCM") == 0 || + strncmp(algorithm, "HMAC", strlen("HMAC")) == 0 || + strncmp(algorithm, "RSA", strlen("RSA")) == 0 || + strcmp(algorithm, "DSA") == 0 || + strcmp(algorithm, "ECDH") == 0 || + strcmp(algorithm, "ECDSA") == 0)) { + debug_file("SKIP", path, algorithm); + stats->skipped_unsupported++; + ret = 0; + } else { + debug_file("SKIP", path, "unsupported vector file"); + stats->skipped_unsupported++; + ret = 0; + } + json_decref(root); + return ret; +} + +static int +process_path(const char *path, struct app_stats *stats) +{ + struct stat path_stat; + DIR *directory; + struct dirent *entry; + int ret = 0; + + if (stat(path, &path_stat) != 0) + return -errno; + if (!S_ISDIR(path_stat.st_mode)) + return process_file(path, stats); + + directory = opendir(path); + if (directory == NULL) + return -errno; + while ((entry = readdir(directory)) != NULL) { + char file_path[PATH_MAX]; + size_t name_length = strlen(entry->d_name); + + if (name_length < 6 || strcmp(entry->d_name + name_length - 5, ".json") != 0) + continue; + if (snprintf(file_path, sizeof(file_path), "%s/%s", path, entry->d_name) >= + (int)sizeof(file_path)) { + ret = -ENAMETOOLONG; + break; + } + ret = process_file(file_path, stats); + if (ret != 0) + break; + } + closedir(directory); + return ret; +} + +int +main(int argc, char **argv) +{ + struct app_stats stats = { 0 }; + int eal_args; + int ret; + + eal_args = rte_eal_init(argc, argv); + if (eal_args < 0) + rte_exit(EXIT_FAILURE, "Cannot initialize EAL\n"); + argc -= eal_args; + argv += eal_args; + + ret = parse_args(argc, argv); + if (ret != 0) { + usage(argv[0]); + ret = EXIT_FAILURE; + goto out_eal; + } + ret = app_init(); + if (ret != 0) { + printf("Cannot initialize cryptodev %u: %s\n", env.dev_id, rte_strerror(-ret)); + ret = EXIT_FAILURE; + goto out_eal; + } + + ret = process_path(env.vectors_path, &stats); + if (ret != 0) + printf("Vector processing failed: %s\n", rte_strerror(-ret)); + printf("Summary: passed=%" PRIu64 " failed=%" PRIu64 + " skipped_capability=%" PRIu64 " skipped_acceptable=%" PRIu64 + " skipped_unsupported=%" PRIu64 "\n", stats.passed, stats.failed, + stats.skipped_capability, stats.skipped_acceptable, stats.skipped_unsupported); + app_uninit(); + ret = ret == 0 && stats.failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + +out_eal: + rte_eal_cleanup(); + return ret; +} diff --git a/examples/wycheproof_validation/meson.build b/examples/wycheproof_validation/meson.build new file mode 100644 index 0000000000..7f18b4cef3 --- /dev/null +++ b/examples/wycheproof_validation/meson.build @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +deps += ['cryptodev'] + +if not dpdk_conf.has('RTE_HAS_JANSSON') + build = false + message('Skipping wycheproof_validation: Jansson is required') +else + ext_deps += jansson_dep + sources = files('main.c') +endif -- 2.34.1

