aboutsummaryrefslogtreecommitdiff
path: root/test/validation/api/crypto/crypto_op_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/validation/api/crypto/crypto_op_test.c')
-rw-r--r--test/validation/api/crypto/crypto_op_test.c616
1 files changed, 616 insertions, 0 deletions
diff --git a/test/validation/api/crypto/crypto_op_test.c b/test/validation/api/crypto/crypto_op_test.c
new file mode 100644
index 000000000..f2703c5cc
--- /dev/null
+++ b/test/validation/api/crypto/crypto_op_test.c
@@ -0,0 +1,616 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2024 Nokia
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <packet_common.h>
+#include "crypto_op_test.h"
+#include "util.h"
+
+#define MAX_FAILURE_PRINTS 20
+
+#define MAX_IGNORED_RANGES 3
+
+/*
+ * Output packet parts that we ignore since they have undefined values
+ */
+typedef struct ignore_t {
+ uint32_t byte_offset; /* offset to a byte which has bits to be ignored */
+ uint8_t byte_mask; /* mask of ignored bits in the byte */
+ struct {
+ uint32_t offset;
+ uint32_t length;
+ } ranges[MAX_IGNORED_RANGES]; /* byte ranges to be ignored */
+ uint32_t num_ranges;
+} ignore_t;
+
+/* Add room for bytes that are not included in ref->length */
+#define MAX_EXP_DATA_LEN (MAX_DATA_LEN + 200)
+
+/*
+ * Expected packet data
+ */
+typedef struct expected_t {
+ uint8_t data[MAX_EXP_DATA_LEN];
+ uint32_t len;
+ ignore_t ignore;
+} expected_t;
+
+int crypto_op(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ odp_bool_t *ok,
+ const odp_crypto_packet_op_param_t *op_params,
+ odp_crypto_op_type_t session_op_type,
+ odp_crypto_op_type_t op_type)
+{
+ int rc;
+ odp_event_t event, event2;
+ odp_crypto_packet_result_t result;
+ odp_event_subtype_t subtype;
+ odp_packet_t orig_pkt_out;
+
+ if (op_type == ODP_CRYPTO_OP_TYPE_LEGACY)
+ *pkt_out = pkt_in;
+ else if (op_type == ODP_CRYPTO_OP_TYPE_BASIC)
+ *pkt_out = ODP_PACKET_INVALID;
+ orig_pkt_out = *pkt_out;
+
+ if (suite_context.op_mode == ODP_CRYPTO_SYNC) {
+ rc = odp_crypto_op(&pkt_in, pkt_out, op_params, 1);
+ if (rc <= 0) {
+ CU_FAIL("Failed odp_crypto_packet_op()");
+ goto fail;
+ }
+ } else {
+ odp_packet_t *out_param = pkt_out;
+
+ if (session_op_type == ODP_CRYPTO_OP_TYPE_BASIC)
+ out_param = NULL;
+
+ rc = odp_crypto_op_enq(&pkt_in, out_param, op_params, 1);
+ if (rc <= 0) {
+ CU_FAIL("Failed odp_crypto_op_enq()");
+ goto fail;
+ }
+
+ /* Get crypto completion event from compl_queue. */
+ CU_ASSERT_FATAL(NULL != suite_context.compl_queue_deq);
+ do {
+ event = suite_context.compl_queue_deq();
+ } while (event == ODP_EVENT_INVALID);
+
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event));
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == subtype);
+
+ *pkt_out = odp_crypto_packet_from_event(event);
+ }
+
+ if (op_type != ODP_CRYPTO_OP_TYPE_BASIC)
+ CU_ASSERT(*pkt_out == orig_pkt_out);
+
+ event = odp_packet_to_event(*pkt_out);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event));
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == subtype);
+ CU_ASSERT(odp_packet_subtype(*pkt_out) == ODP_EVENT_PACKET_CRYPTO);
+
+ event2 = odp_crypto_packet_to_event(*pkt_out);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event2));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event2));
+ CU_ASSERT(odp_event_to_u64(event) == odp_event_to_u64(event2));
+
+ rc = odp_crypto_result(&result, *pkt_out);
+ if (rc < -1)
+ CU_FAIL("Failed odp_crypto_result()");
+ CU_ASSERT(rc == 0 || rc == -1);
+
+ if (op_type == ODP_CRYPTO_OP_TYPE_OOP &&
+ suite_context.op_mode == ODP_CRYPTO_ASYNC)
+ CU_ASSERT(result.pkt_in == pkt_in);
+
+ *ok = (rc == 0);
+
+ return 0;
+fail:
+ odp_packet_free(pkt_in);
+ if (op_type == ODP_CRYPTO_OP_TYPE_OOP)
+ odp_packet_free(*pkt_out);
+ return -1;
+}
+
+/*
+ * Try to adjust packet so that the first segment holds 'first_seg_len' bytes
+ * of packet data (+ tailroom if first_seg_len is longer than the packet).
+ *
+ * If 'first_seg_len' is zero, do not try to add segments but make headroom
+ * zero.
+ *
+ * Packet data bytes are not preserved.
+ */
+static void adjust_segments(odp_packet_t *pkt, uint32_t first_seg_len)
+{
+ uint32_t shift;
+
+ shift = odp_packet_headroom(*pkt) + first_seg_len;
+
+ if (odp_packet_extend_head(pkt, shift, NULL, NULL) < 0) {
+ CU_FAIL("odp_packet_extend_head() failed\n");
+ return;
+ }
+ if (odp_packet_trunc_tail(pkt, shift, NULL, NULL) < 0) {
+ CU_FAIL("odp_packet_trunc_tail() failed\n");
+ return;
+ }
+ /*
+ * ODP API does not seem to guarantee that we ever have a multi-segment
+ * packet at this point, but we can print a message about it.
+ */
+ if (first_seg_len == 1 &&
+ first_seg_len != odp_packet_seg_len(*pkt))
+ printf("Could not create a segmented packet for testing.\n");
+}
+
+static void write_header_and_trailer(odp_packet_t pkt,
+ uint32_t header_len, uint32_t trailer_len)
+{
+ uint32_t trailer_offset = odp_packet_len(pkt) - trailer_len;
+ uint32_t max_len = header_len > trailer_len ? header_len : trailer_len;
+
+ if (!max_len)
+ return;
+
+ uint8_t buffer[max_len];
+ int rc;
+
+ fill_with_pattern(buffer, sizeof(buffer));
+
+ rc = odp_packet_copy_from_mem(pkt, 0, header_len, buffer);
+ CU_ASSERT(rc == 0);
+ rc = odp_packet_copy_from_mem(pkt, trailer_offset, trailer_len, buffer);
+ CU_ASSERT(rc == 0);
+}
+
+static void prepare_crypto_ranges(const crypto_op_test_param_t *param,
+ odp_packet_data_range_t *cipher_range,
+ odp_packet_data_range_t *auth_range)
+{
+ uint32_t c_scale = param->session.cipher_range_in_bits ? 8 : 1;
+ uint32_t a_scale = param->session.auth_range_in_bits ? 8 : 1;
+
+ *cipher_range = param->cipher_range;
+ *auth_range = param->auth_range;
+ cipher_range->offset += c_scale * param->header_len;
+ auth_range->offset += a_scale * param->header_len;
+}
+
+static int prepare_input_packet(const crypto_op_test_param_t *param,
+ odp_packet_t *pkt_in)
+{
+ crypto_test_reference_t *ref = param->ref;
+ uint32_t reflength = ref_length_in_bytes(ref);
+ odp_packet_t pkt;
+ uint32_t digest_offset = param->digest_offset;
+ uint32_t pkt_len;
+
+ pkt_len = param->header_len + reflength + param->trailer_len;
+ if (param->digest_offset == param->header_len + reflength)
+ pkt_len += ref->digest_length;
+ if (pkt_len == 0)
+ pkt_len = 1;
+
+ pkt = odp_packet_alloc(suite_context.pool, pkt_len);
+
+ CU_ASSERT(pkt != ODP_PACKET_INVALID);
+ if (pkt == ODP_PACKET_INVALID)
+ return -1;
+
+ if (param->adjust_segmentation)
+ adjust_segments(&pkt, param->first_seg_len);
+
+ write_header_and_trailer(pkt, param->header_len, param->trailer_len);
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ odp_packet_copy_from_mem(pkt, param->header_len,
+ reflength, ref->plaintext);
+ } else {
+ odp_packet_copy_from_mem(pkt, param->header_len,
+ reflength, ref->ciphertext);
+ odp_packet_copy_from_mem(pkt, digest_offset,
+ ref->digest_length,
+ ref->digest);
+ if (param->wrong_digest) {
+ uint8_t byte = ~ref->digest[0];
+
+ odp_packet_copy_from_mem(pkt, digest_offset, 1, &byte);
+ }
+ }
+ *pkt_in = pkt;
+ return 0;
+}
+
+static void prepare_oop_output_packet(const crypto_op_test_param_t *param,
+ odp_packet_t *pkt_out,
+ uint32_t pkt_len)
+{
+ uint32_t reflength = ref_length_in_bytes(param->ref);
+ const uint32_t oop_extra_len = 5;
+ uint32_t trl_len;
+ uint32_t hdr_len;
+ uint32_t oop_len;
+
+ oop_len = pkt_len + param->oop_shift + oop_extra_len;
+ *pkt_out = odp_packet_alloc(suite_context.pool, oop_len);
+ CU_ASSERT_FATAL(*pkt_out != ODP_PACKET_INVALID);
+
+ uint8_t buf[oop_len];
+
+ memset(buf, 0x55, sizeof(buf));
+ odp_packet_copy_from_mem(*pkt_out, 0, sizeof(buf), buf);
+
+ hdr_len = param->header_len + param->oop_shift;
+ trl_len = oop_len - hdr_len - reflength;
+
+ write_header_and_trailer(*pkt_out, hdr_len, trl_len);
+
+ /* have different metadata than in the input packet */
+ memset(odp_packet_user_area(*pkt_out), 0xab,
+ odp_packet_user_area_size(*pkt_out));
+}
+
+static int is_packet_data_equal(odp_packet_t pkt_1, odp_packet_t pkt_2)
+{
+ uint32_t len = odp_packet_len(pkt_1);
+ uint8_t buf_1[len];
+ uint8_t buf_2[len];
+
+ if (len != odp_packet_len(pkt_2) ||
+ odp_packet_copy_to_mem(pkt_1, 0, len, buf_1) ||
+ odp_packet_copy_to_mem(pkt_2, 0, len, buf_2))
+ return 0;
+
+ return !memcmp(buf_1, buf_2, len);
+}
+
+static int is_in_range(uint32_t offs, uint32_t range_offs, uint32_t range_len)
+{
+ return offs >= range_offs && offs < range_offs + range_len;
+}
+
+static void add_ignored_range(ignore_t *ign, uint32_t offs, uint32_t len)
+{
+ if (len == 0)
+ return;
+ CU_ASSERT_FATAL(ign->num_ranges < MAX_IGNORED_RANGES);
+ ign->ranges[ign->num_ranges].offset = offs;
+ ign->ranges[ign->num_ranges].length = len;
+ ign->num_ranges++;
+}
+
+static void clear_ignored_data(const ignore_t *ign, uint8_t *data, uint32_t data_len)
+{
+ CU_ASSERT_FATAL(ign->byte_offset < data_len);
+ data[ign->byte_offset] &= ~ign->byte_mask;
+
+ for (uint32_t n = 0; n < ign->num_ranges; n++) {
+ uint32_t offset = ign->ranges[n].offset;
+ uint32_t length = ign->ranges[n].length;
+
+ CU_ASSERT(offset + length <= data_len);
+ memset(data + offset, 0, length);
+ }
+}
+
+static void prepare_ignore_info(const crypto_op_test_param_t *param,
+ uint32_t shift,
+ uint32_t cipher_offset,
+ uint32_t cipher_len,
+ uint32_t auth_offset,
+ uint32_t auth_len,
+ ignore_t *ignore)
+{
+ memset(ignore, 0, sizeof(*ignore));
+
+ /*
+ * Leftover bits in the last byte of the cipher range of bit mode
+ * ciphers have undefined values.
+ */
+ if (param->session.cipher_range_in_bits &&
+ param->ref->cipher != ODP_CIPHER_ALG_NULL) {
+ uint8_t leftover_bits = ref_length_in_bits(param->ref) % 8;
+
+ ignore->byte_offset = cipher_offset + cipher_len - 1 + shift;
+ if (leftover_bits > 0)
+ ignore->byte_mask = ~(0xff << (8 - leftover_bits));
+ else
+ ignore->byte_mask = 0;
+ }
+
+ /*
+ * In decode sessions the bytes in the hash location have
+ * undefined values.
+ */
+ if (param->ref->auth != ODP_AUTH_ALG_NULL &&
+ param->session.op == ODP_CRYPTO_OP_DECODE) {
+ uint32_t offs = param->digest_offset;
+
+ if (param->op_type != ODP_CRYPTO_OP_TYPE_OOP ||
+ is_in_range(offs, cipher_offset, cipher_len) ||
+ is_in_range(offs, auth_offset, auth_len)) {
+ add_ignored_range(ignore,
+ param->digest_offset + shift,
+ param->ref->digest_length);
+ }
+ }
+
+ /* Decrypted bytes are undefined if authentication fails. */
+ if (param->session.op == ODP_CRYPTO_OP_DECODE &&
+ param->wrong_digest) {
+ add_ignored_range(ignore, cipher_offset + shift, cipher_len);
+ /* In OOP case, auth range may not get copied */
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP)
+ add_ignored_range(ignore, auth_offset + shift, auth_len);
+ }
+}
+
+static void prepare_expected_data(const crypto_op_test_param_t *param,
+ const odp_packet_data_range_t *cipher_range,
+ const odp_packet_data_range_t *auth_range,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out,
+ expected_t *ex)
+{
+ uint32_t digest_offset = param->digest_offset;
+ uint32_t cipher_offset = cipher_range->offset;
+ uint32_t cipher_len = cipher_range->length;
+ uint32_t auth_offset = auth_range->offset;
+ uint32_t auth_len = auth_range->length;
+ const int32_t shift = param->op_type == ODP_CRYPTO_OP_TYPE_OOP ? param->oop_shift
+ : 0;
+ const odp_packet_t base_pkt = param->op_type == ODP_CRYPTO_OP_TYPE_OOP ? pkt_out
+ : pkt_in;
+ int rc;
+ uint32_t cipher_offset_in_ref = param->cipher_range.offset;
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE)
+ digest_offset += shift;
+
+ if (param->session.cipher_range_in_bits) {
+ cipher_offset_in_ref /= 8;
+ cipher_offset /= 8;
+ cipher_len = (cipher_len + 7) / 8;
+ }
+ if (param->session.auth_range_in_bits) {
+ auth_offset /= 8;
+ auth_len = (auth_len + 7) / 8;
+ }
+ if (param->ref->auth == ODP_AUTH_ALG_AES_GCM ||
+ param->ref->auth == ODP_AUTH_ALG_AES_CCM ||
+ param->ref->auth == ODP_AUTH_ALG_CHACHA20_POLY1305) {
+ /* auth range is ignored with AEAD algorithms */
+ auth_len = 0;
+ }
+
+ /* copy all data from base packet */
+ ex->len = odp_packet_len(base_pkt);
+ CU_ASSERT_FATAL(ex->len <= sizeof(ex->data));
+ rc = odp_packet_copy_to_mem(base_pkt, 0, ex->len, ex->data);
+ CU_ASSERT(rc == 0);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP && auth_len > 0) {
+ /* copy auth range from input packet */
+ rc = odp_packet_copy_to_mem(pkt_in, auth_offset, auth_len,
+ ex->data + auth_offset + shift);
+ CU_ASSERT(rc == 0);
+ }
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ /* copy hash first */
+ memcpy(ex->data + digest_offset,
+ param->ref->digest,
+ param->ref->digest_length);
+ /*
+ * Copy ciphertext, possibly overwriting hash.
+ * The other order (hash overwriting some cipher
+ * text) does not work in any real use case anyway.
+ */
+ memcpy(ex->data + cipher_offset + shift,
+ param->ref->ciphertext + cipher_offset_in_ref,
+ cipher_len);
+ } else {
+ memcpy(ex->data + cipher_offset + shift,
+ param->ref->plaintext + cipher_offset_in_ref,
+ cipher_len);
+ }
+
+ prepare_ignore_info(param, shift,
+ cipher_offset, cipher_len,
+ auth_offset, auth_len,
+ &ex->ignore);
+}
+
+static void print_data(const char *title, uint8_t *data, uint32_t len)
+{
+ static uint64_t limit;
+
+ if (limit++ > MAX_FAILURE_PRINTS)
+ return;
+
+ printf("%s\n", title);
+ for (uint32_t n = 0; n < len ; n++) {
+ printf(" %02x", data[n]);
+ if ((n + 1) % 16 == 0)
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void check_output_packet_data(odp_packet_t pkt, expected_t *ex)
+{
+ int rc;
+ uint8_t pkt_data[ex->len];
+
+ CU_ASSERT(odp_packet_len(pkt) == ex->len);
+ rc = odp_packet_copy_to_mem(pkt, 0, ex->len, pkt_data);
+ CU_ASSERT(rc == 0);
+
+ clear_ignored_data(&ex->ignore, pkt_data, sizeof(pkt_data));
+ clear_ignored_data(&ex->ignore, ex->data, sizeof(ex->data));
+
+ if (memcmp(pkt_data, ex->data, ex->len)) {
+ CU_FAIL("packet data does not match expected data");
+ print_data("packet:", pkt_data, ex->len);
+ print_data("expected:", ex->data, ex->len);
+ }
+}
+
+static int is_digest_in_cipher_range(const crypto_op_test_param_t *param,
+ const odp_crypto_packet_op_param_t *op_params)
+{
+ /*
+ * Do not use op_params.hash_result_offset here as it refers to
+ * the output packet which (in the OOP case) might be shifted
+ * relative to the input packet.
+ */
+ uint32_t d_offset = param->digest_offset;
+
+ if (param->session.cipher_range_in_bits)
+ d_offset *= 8;
+
+ return d_offset >= op_params->cipher_range.offset &&
+ d_offset < op_params->cipher_range.offset + op_params->cipher_range.length;
+}
+
+static void do_test_crypto_op(const crypto_op_test_param_t *param)
+{
+ odp_bool_t ok = false;
+ odp_packet_t pkt;
+ odp_packet_t pkt_copy = ODP_PACKET_INVALID;
+ odp_packet_t pkt_out = ODP_PACKET_INVALID;
+ test_packet_md_t md_in, md_out, md_out_orig;
+ expected_t expected;
+ odp_crypto_packet_op_param_t op_params = {
+ .session = param->session.session,
+ .cipher_iv_ptr = param->ref->cipher_iv,
+ .auth_iv_ptr = param->ref->auth_iv,
+ .hash_result_offset = param->digest_offset,
+ .aad_ptr = param->ref->aad,
+ .dst_offset_shift = param->oop_shift,
+ .null_crypto = param->null_crypto,
+ };
+ odp_bool_t failure_allowed = false;
+
+ /*
+ * Test detection of wrong digest value in input packet
+ * only when decoding and using non-null auth algorithm.
+ */
+ if (param->wrong_digest &&
+ (param->ref->auth == ODP_AUTH_ALG_NULL ||
+ param->session.op == ODP_CRYPTO_OP_ENCODE))
+ return;
+
+ prepare_crypto_ranges(param, &op_params.cipher_range, &op_params.auth_range);
+ if (prepare_input_packet(param, &pkt))
+ return;
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ prepare_oop_output_packet(param, &pkt_out, odp_packet_len(pkt));
+
+ pkt_copy = odp_packet_copy(pkt, suite_context.pool);
+ CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID);
+ test_packet_get_md(pkt_out, &md_out_orig);
+
+ /* Non-zero-length ranges do not have to be supported. */
+ if ((param->ref->cipher == ODP_CIPHER_ALG_NULL &&
+ op_params.cipher_range.length != 0))
+ failure_allowed = true;
+ if ((param->ref->auth == ODP_AUTH_ALG_NULL &&
+ op_params.auth_range.length != 0))
+ failure_allowed = true;
+ }
+
+ prepare_expected_data(param, &op_params.cipher_range, &op_params.auth_range,
+ pkt, pkt_out, &expected);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP &&
+ param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ /*
+ * In this type of sessions digest offset is an offset to the output
+ * packet, so apply the shift.
+ */
+ op_params.hash_result_offset += param->oop_shift;
+ }
+
+ test_packet_set_md(pkt);
+ test_packet_get_md(pkt, &md_in);
+
+ if (crypto_op(pkt, &pkt_out, &ok, &op_params,
+ param->session.op_type, param->op_type))
+ return;
+
+ test_packet_get_md(pkt_out, &md_out);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ test_packet_md_t md;
+
+ /* check that input packet has not changed */
+ CU_ASSERT(is_packet_data_equal(pkt, pkt_copy));
+ odp_packet_free(pkt_copy);
+ test_packet_get_md(pkt, &md);
+ CU_ASSERT(test_packet_is_md_equal(&md, &md_in));
+ odp_packet_free(pkt);
+
+ /* check that metadata of output packet has not changed */
+ CU_ASSERT(test_packet_is_md_equal(&md_out, &md_out_orig));
+ } else {
+ CU_ASSERT(test_packet_is_md_equal(&md_out, &md_in));
+ }
+
+ if (param->ref->cipher != ODP_CIPHER_ALG_NULL &&
+ param->ref->auth != ODP_AUTH_ALG_NULL &&
+ is_digest_in_cipher_range(param, &op_params)) {
+ /*
+ * Not all implementations support digest offset in cipher
+ * range, so allow crypto op failure without further checks
+ * in this case.
+ */
+ failure_allowed = true;
+ }
+
+ if (!ok && failure_allowed)
+ goto out;
+
+ if (param->wrong_digest) {
+ CU_ASSERT(!ok);
+ } else {
+ CU_ASSERT(ok);
+ }
+
+ check_output_packet_data(pkt_out, &expected);
+out:
+ odp_packet_free(pkt_out);
+}
+
+void test_crypto_op(const crypto_op_test_param_t *param)
+{
+ crypto_op_test_param_t null_param = *param;
+ crypto_test_reference_t ref = *param->ref;
+
+ if (param->session.null_crypto_enable && param->null_crypto) {
+ null_param = *param;
+ null_param.ref = &ref;
+ ref = *param->ref;
+ ref.cipher = ODP_CIPHER_ALG_NULL;
+ ref.auth = ODP_AUTH_ALG_NULL;
+ ref.digest_length = 0;
+ memcpy(ref.ciphertext, ref.plaintext, sizeof(ref.ciphertext));
+ param = &null_param;
+ }
+ do_test_crypto_op(param);
+}