This commit adds ECDH test cases based on rfc5114.

Signed-off-by: Arek Kusztal <arkadiuszx.kusz...@intel.com>
---
 app/test/test_cryptodev_asym.c         | 357 +++++++++++++++++++++++++++++++++
 app/test/test_cryptodev_ecdh_vectors.h | 144 +++++++++++++
 2 files changed, 501 insertions(+)
 create mode 100644 app/test/test_cryptodev_ecdh_vectors.h

diff --git a/app/test/test_cryptodev_asym.c b/app/test/test_cryptodev_asym.c
index 0fea47c42e..a097023011 100644
--- a/app/test/test_cryptodev_asym.c
+++ b/app/test/test_cryptodev_asym.c
@@ -23,6 +23,7 @@
 #include "test_cryptodev_rsa_test_vectors.h"
 #include "test_cryptodev_asym_util.h"
 #include "test.h"
+#include "test_cryptodev_ecdh_vectors.h"
 
 #define TEST_NUM_BUFS 10
 #define TEST_NUM_SESSIONS 4
@@ -2362,6 +2363,358 @@ test_ecdsa_verify(const void *input_vector)
        return TEST_SUCCESS;
 }
 
+/*
+ * This function is split into 3 steps: STEP1, STEP1.5, STEP2
+ * Steps 1 and 2 are just DH steps (Public key and shared secret generation).
+ * Step 1.5 is Public Key verification (if point belongs to elliptic curve)
+ */
+static int
+test_ecdh(const void *input_vector)
+{
+       const struct ECDH_test_vector *test_vector = input_vector;
+       struct crypto_testsuite_params_asym *ts_params = &testsuite_params;
+       struct rte_mempool *op_mpool = ts_params->op_mpool;
+       struct rte_crypto_asym_xform xform = {
+               .next = NULL,
+               .xform_type = RTE_CRYPTO_ASYM_XFORM_ECDH,
+               .ec.curve_id = test_vector->curve_id
+       };
+       const uint8_t dev_id = ts_params->valid_devs[0];
+
+       uint8_t alice_x[test_vector->curve_bytesize],
+               alice_y[test_vector->curve_bytesize];
+       uint8_t bob_x[test_vector->curve_bytesize],
+               bob_y[test_vector->curve_bytesize];
+       uint8_t alice_xZ[test_vector->curve_bytesize],
+               alice_yZ[test_vector->curve_bytesize];
+       uint8_t bob_xZ[test_vector->curve_bytesize],
+               bob_yZ[test_vector->curve_bytesize];
+       struct rte_crypto_op *alice_op = NULL, *bob_op = NULL;
+
+       alice_op = rte_crypto_op_alloc(op_mpool,
+               RTE_CRYPTO_OP_TYPE_ASYMMETRIC);
+       bob_op = rte_crypto_op_alloc(op_mpool,
+               RTE_CRYPTO_OP_TYPE_ASYMMETRIC);
+
+       /*
+        * STEP 1a:
+        * Generate Alice public key
+        */
+
+       alice_op->sess_type = RTE_CRYPTO_OP_SESSIONLESS;
+       alice_op->asym->xform = &xform;
+       alice_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_PUB_KEY_GENERATE;
+       alice_op->asym->ecdh.priv_key = test_vector->alice_d;
+       alice_op->asym->ecdh.pub_key.x.data = alice_x;
+       alice_op->asym->ecdh.pub_key.y.data = alice_y;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &alice_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               return -1;
+       }
+
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &alice_op, 1) == 0)
+               rte_pause();
+
+       if (alice_op->asym->ecdh.pub_key.x.length !=
+                       test_vector->alice_q.x.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Alice public key length (X coeff) 
Received length = %lu Expeceted length = %lu\n",
+                               test_vector->alice_q.x.length,
+                               alice_op->asym->ecdh.pub_key.x.length
+                       );
+               goto error;
+       }
+       if (alice_op->asym->ecdh.pub_key.y.length !=
+                       test_vector->alice_q.y.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Alice public key length (Y coeff) 
Received length = %lu Expeceted length = %lu\n",
+                                       test_vector->alice_q.y.length,
+                                       alice_op->asym->ecdh.pub_key.y.length
+                               );
+               goto error;
+       }
+       if (memcmp(alice_op->asym->ecdh.pub_key.x.data,
+                       test_vector->alice_q.x.data,
+                       test_vector->alice_q.x.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Alice public key X\n");
+               debug_hexdump(stdout, "Generated PublicKey x (Alice):",
+                       alice_op->asym->ecdh.pub_key.x.data,
+                       alice_op->asym->ecdh.pub_key.x.length);
+               goto error;
+       }
+       if (memcmp(alice_op->asym->ecdh.pub_key.y.data,
+                       test_vector->alice_q.y.data,
+                       test_vector->alice_q.y.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Alice public key Y\n");
+               debug_hexdump(stdout, "Generated PublicKey y (Alice):",
+                               alice_op->asym->ecdh.pub_key.y.data,
+                               alice_op->asym->ecdh.pub_key.y.length);
+               goto error;
+       }
+
+
+       /*
+        * STEP 1b:
+        * Generate Bob public key
+        */
+
+       bob_op->sess_type = RTE_CRYPTO_OP_SESSIONLESS;
+       bob_op->asym->xform = &xform;
+       bob_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_PUB_KEY_GENERATE;
+       bob_op->asym->ecdh.priv_key = test_vector->bob_d;
+       bob_op->asym->ecdh.pub_key.x.data = bob_x;
+       bob_op->asym->ecdh.pub_key.y.data = bob_y;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &bob_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               goto error;
+       }
+
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &bob_op, 1) == 0)
+               rte_pause();
+
+       if (bob_op->asym->ecdh.pub_key.x.length !=
+                       test_vector->bob_q.x.length) {
+               RTE_LOG(ERR, USER1,
+                       "Incorrect Bob's public key length (X coeff) Received 
length = %lu Expeceted length = %lu\n",
+                       test_vector->bob_q.x.length,
+                       bob_op->asym->ecdh.pub_key.x.length
+               );
+               goto error;
+       }
+       if (bob_op->asym->ecdh.pub_key.y.length !=
+                       test_vector->bob_q.y.length) {
+               RTE_LOG(ERR, USER1,
+                       "Incorrect Bob's public key length (Y coeff) Received 
length = %lu Expeceted length = %lu\n",
+                       test_vector->bob_q.y.length,
+                       bob_op->asym->ecdh.pub_key.y.length
+               );
+               goto error;
+       }
+       if (memcmp(bob_op->asym->ecdh.pub_key.x.data,
+                       test_vector->bob_q.x.data,
+                       test_vector->bob_q.x.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Bob's public key X\n");
+               debug_hexdump(stdout,
+                       "Generated PublicKey x (bob):",
+                       bob_op->asym->ecdh.pub_key.x.data,
+                       bob_op->asym->ecdh.pub_key.x.length
+               );
+               goto error;
+       }
+       if (memcmp(bob_op->asym->ecdh.pub_key.y.data,
+                       test_vector->bob_q.y.data,
+                       test_vector->bob_q.y.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Bob's public key Y\n");
+               debug_hexdump(stdout,
+                       "Generated PublicKey y (bob):",
+                       bob_op->asym->ecdh.pub_key.y.data,
+                       bob_op->asym->ecdh.pub_key.y.length
+               );
+               goto error;
+       }
+
+       /*
+        * STEP 1.5a:
+        * Alice verifies Bob's public key, reusing op, other parameters
+        * then already set, only what Alice needs to do is to set public
+        * key of Bob and change key exchange type to VERIFY.
+        */
+
+       alice_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_PUB_KEY_VERIFY;
+       alice_op->asym->ecdh.pub_key.x.data = bob_x;
+       alice_op->asym->ecdh.pub_key.x.length =
+               bob_op->asym->ecdh.pub_key.x.length;
+       alice_op->asym->ecdh.pub_key.y.data = bob_y;
+       alice_op->asym->ecdh.pub_key.y.length =
+               bob_op->asym->ecdh.pub_key.y.length;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &alice_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               goto error;
+       }
+
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &alice_op, 1) == 0)
+               rte_pause();
+
+       if (alice_op->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+               RTE_LOG(ERR, USER1,
+                               "line %u FAILED: %s", __LINE__,
+                               "Failed to verify Bob's public key, it does not 
belongs to curve points\n"
+                       );
+               return TEST_FAILED;
+       }
+
+       /*
+        * STEP 1.5b:
+        * Bob verifies Alice's public key, reusing op, other parameters
+        * then already set, only what Bob needs to do is to set public
+        * key of Alice and change key exchange type to VERIFY.
+        */
+
+       bob_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_PUB_KEY_VERIFY;
+       bob_op->asym->ecdh.pub_key.x.data = alice_x;
+       bob_op->asym->ecdh.pub_key.x.length =
+               alice_op->asym->ecdh.pub_key.x.length;
+       bob_op->asym->ecdh.pub_key.y.data = alice_y;
+       bob_op->asym->ecdh.pub_key.y.length =
+               alice_op->asym->ecdh.pub_key.y.length;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &bob_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               goto error;
+       }
+
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &bob_op, 1) == 0)
+               rte_pause();
+
+       if (bob_op->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+               RTE_LOG(ERR, USER1,
+                               "line %u FAILED: %s", __LINE__,
+                               "Failed to verify Alice's public key, it does 
not belongs to curve points\n"
+                       );
+               goto error;
+       }
+
+       /*
+        * STEP 2a:
+        * Alice generates shared secret Z, since Bob's public key was already
+        * set when doing verification only key exchange type need to be
+        * changed, and setting place where Z will be copied by the PMD.
+        */
+
+       alice_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_SHARED_SECRET_COMPUTE;
+       alice_op->asym->ecdh.shared_secret.x.data = alice_xZ;
+       alice_op->asym->ecdh.shared_secret.y.data = alice_yZ;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &alice_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               goto error;
+       }
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &alice_op, 1) == 0)
+               rte_pause();
+
+       if (alice_op->asym->ecdh.shared_secret.x.length !=
+                       test_vector->Z.x.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Alice's shared secret length X 
Received = %lu Expected = %lu\n",
+                               alice_op->asym->ecdh.shared_secret.x.length,
+                               test_vector->Z.x.length
+               );
+               goto error;
+       }
+       if (alice_op->asym->ecdh.shared_secret.y.length !=
+                       test_vector->Z.y.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Alice's shared secret length Y 
Received = %lu Expected = %lu\n",
+                               alice_op->asym->ecdh.shared_secret.y.length,
+                               test_vector->Z.y.length
+               );
+               goto error;
+       }
+
+       if (memcmp(alice_op->asym->ecdh.shared_secret.x.data,
+                       test_vector->Z.x.data,
+                       test_vector->Z.x.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Alice's shared secret X\n");
+               debug_hexdump(stdout,
+                       "Generated SharedSecret x (Alice):",
+                       alice_op->asym->ecdh.shared_secret.x.data,
+                       alice_op->asym->ecdh.shared_secret.x.length
+               );
+               goto error;
+       }
+       if (memcmp(alice_op->asym->ecdh.shared_secret.y.data,
+                       test_vector->Z.y.data,
+                       test_vector->Z.y.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Alice's shared secret Y\n");
+               debug_hexdump(stdout,
+                       "Generated SharedSecret y (Alice):",
+                       alice_op->asym->ecdh.shared_secret.y.data,
+                       alice_op->asym->ecdh.shared_secret.y.length
+               );
+               goto error;
+       }
+
+       /*
+        * STEP 2b:
+        * Bob generates shared secret Z, since Alice's public key was already
+        * set when doing verification only key exchange type need to be
+        * changed, and setting place where Z will be copied by the PMD.
+        */
+
+       bob_op->asym->ecdh.ke_type = RTE_CRYPTO_ASYM_KE_SHARED_SECRET_COMPUTE;
+       bob_op->asym->ecdh.shared_secret.x.data = bob_xZ;
+       bob_op->asym->ecdh.shared_secret.y.data = bob_yZ;
+
+       if (rte_cryptodev_enqueue_burst(dev_id, 0, &bob_op, 1) != 1) {
+               RTE_LOG(ERR, USER1, "Error sending packet for sign\n");
+               goto error;
+       }
+
+       while (rte_cryptodev_dequeue_burst(dev_id, 0, &bob_op, 1) == 0)
+               rte_pause();
+
+       if (bob_op->asym->ecdh.shared_secret.x.length !=
+                       test_vector->Z.x.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Bob's shared secret length X 
Received = %lu Expected = %lu\n",
+                               bob_op->asym->ecdh.shared_secret.x.length,
+                               test_vector->Z.x.length
+               );
+               goto error;
+       }
+       if (bob_op->asym->ecdh.shared_secret.y.length !=
+                       test_vector->Z.y.length) {
+               RTE_LOG(ERR, USER1,
+                               "Incorrect Bob's shared secret length Y 
Received = %lu Expected = %lu\n",
+                               bob_op->asym->ecdh.shared_secret.y.length,
+                               test_vector->Z.y.length
+               );
+               goto error;
+       }
+
+       if (memcmp(bob_op->asym->ecdh.shared_secret.x.data,
+                       test_vector->Z.x.data,
+                       test_vector->Z.x.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Bob's shared secret X\n");
+               debug_hexdump(stdout,
+                       "Generated SharedSecret x (Bob):",
+                       bob_op->asym->ecdh.shared_secret.x.data,
+                       bob_op->asym->ecdh.shared_secret.x.length
+               );
+               goto error;
+       }
+       if (memcmp(bob_op->asym->ecdh.shared_secret.y.data,
+                       test_vector->Z.y.data,
+                       test_vector->Z.y.length)
+       ) {
+               RTE_LOG(ERR, USER1, "Incorrect Bob's shared secret Y\n");
+               debug_hexdump(stdout,
+                       "Generated SharedSecret y (Bob):",
+                       bob_op->asym->ecdh.shared_secret.y.data,
+                       bob_op->asym->ecdh.shared_secret.y.length
+               );
+               goto error;
+       }
+
+       /* Both shared secret where correctly verified -> Test passed */
+
+       return TEST_SUCCESS;
+error:
+       rte_crypto_op_free(alice_op);
+       rte_crypto_op_free(bob_op);
+       return TEST_FAILED;
+}
+
 static struct unit_test_suite cryptodev_openssl_asym_testsuite  = {
        .suite_name = "Crypto Device OPENSSL ASYM Unit Test Suite",
        .setup = testsuite_setup,
@@ -2391,6 +2744,10 @@ static struct unit_test_suite 
cryptodev_qat_asym_testsuite  = {
        .teardown = testsuite_teardown,
        .unit_test_cases = {
                TEST_CASE_NAMED_WITH_DATA(
+                       "Test - ECDH secp256r1, RFC 5114 256 Random",
+                       ut_setup_asym, ut_teardown_asym,
+                       test_ecdh, &rfc5114_secp256r1),
+               TEST_CASE_NAMED_WITH_DATA(
                        "Test - ECDSA secp256r1 signature generation",
                        ut_setup_asym, ut_teardown_asym,
                        test_ecdsa_sign, &ecdsa_param_secp256r1),
diff --git a/app/test/test_cryptodev_ecdh_vectors.h 
b/app/test/test_cryptodev_ecdh_vectors.h
new file mode 100644
index 0000000000..553e33de38
--- /dev/null
+++ b/app/test/test_cryptodev_ecdh_vectors.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#ifndef __TEST_CRYPTODEV_ECDH_VECTORS_H__
+#define __TEST_CRYPTODEV_ECDH_VECTORS_H__
+
+#include "rte_crypto_asym.h"
+
+/*
+ * Elliptic Curve Diffie-Hellman test vector struct
+ * Peers are named Alice and Bob
+ * q - is a public key
+ * d - is a private key
+ * Z - is a shared secret
+ */
+
+struct ECDH_test_vector {
+       enum rte_crypto_curve_id curve_id;
+       int curve_bytesize;
+       rte_crypto_uint alice_d;
+       rte_crypto_uint bob_d;
+       struct rte_crypto_ec_point alice_q;
+       struct rte_crypto_ec_point bob_q;
+       struct rte_crypto_ec_point Z;
+};
+
+/*
+ * Elliptic Curve Diffie-Hellman test
+ * It consist of three phases:
+ * - Generation of public key based on private key
+ * - Verification of peer's public key
+ * - Generation of shared secret
+ * Peers in tests are named Alice and Bob
+ */
+
+/* RFC 5114 256-bit Random ECP Group Data */
+
+/*
+ * Alice's parameters
+ */
+static uint8_t rfc5114_256b_dA[] = {
+       0x81, 0x42, 0x64, 0x14, 0x5F, 0x2F, 0x56, 0xF2,
+       0xE9, 0x6A, 0x8E, 0x33, 0x7A, 0x12, 0x84, 0x99,
+       0x3F, 0xAF, 0x43, 0x2A, 0x5A, 0xBC, 0xE5, 0x9E,
+       0x86, 0x7B, 0x72, 0x91, 0xD5, 0x07, 0xA3, 0xAF,
+};
+
+static uint8_t rfc5114_256b_x_qA[] = {
+       0x2A, 0xF5, 0x02, 0xF3, 0xBE, 0x89, 0x52, 0xF2,
+       0xC9, 0xB5, 0xA8, 0xD4, 0x16, 0x0D, 0x09, 0xE9,
+       0x71, 0x65, 0xBE, 0x50, 0xBC, 0x42, 0xAE, 0x4A,
+       0x5E, 0x8D, 0x3B, 0x4B, 0xA8, 0x3A, 0xEB, 0x15,
+};
+
+static uint8_t rfc5114_256b_y_qA[] = {
+       0xEB, 0x0F, 0xAF, 0x4C, 0xA9, 0x86, 0xC4, 0xD3,
+       0x86, 0x81, 0xA0, 0xF9, 0x87, 0x2D, 0x79, 0xD5,
+       0x67, 0x95, 0xBD, 0x4B, 0xFF, 0x6E, 0x6D, 0xE3,
+       0xC0, 0xF5, 0x01, 0x5E, 0xCE, 0x5E, 0xFD, 0x85,
+};
+
+/*
+ * Bob's parameters
+ */
+static uint8_t rfc5114_256b_dB[] = {
+       0x2C, 0xE1, 0x78, 0x8E, 0xC1, 0x97, 0xE0, 0x96,
+       0xDB, 0x95, 0xA2, 0x00, 0xCC, 0x0A, 0xB2, 0x6A,
+       0x19, 0xCE, 0x6B, 0xCC, 0xAD, 0x56, 0x2B, 0x8E,
+       0xEE, 0x1B, 0x59, 0x37, 0x61, 0xCF, 0x7F, 0x41,
+};
+
+static uint8_t rfc5114_256b_x_qB[] = {
+       0xB1, 0x20, 0xDE, 0x4A, 0xA3, 0x64, 0x92, 0x79,
+       0x53, 0x46, 0xE8, 0xDE, 0x6C, 0x2C, 0x86, 0x46,
+       0xAE, 0x06, 0xAA, 0xEA, 0x27, 0x9F, 0xA7, 0x75,
+       0xB3, 0xAB, 0x07, 0x15, 0xF6, 0xCE, 0x51, 0xB0,
+};
+
+static uint8_t rfc5114_256b_y_qB[] = {
+       0x9F, 0x1B, 0x7E, 0xEC, 0xE2, 0x0D, 0x7B, 0x5E,
+       0xD8, 0xEC, 0x68, 0x5F, 0xA3, 0xF0, 0x71, 0xD8,
+       0x37, 0x27, 0x02, 0x70, 0x92, 0xA8, 0x41, 0x13,
+       0x85, 0xC3, 0x4D, 0xDE, 0x57, 0x08, 0xB2, 0xB6,
+};
+
+static uint8_t rfc5114_256b_x_Z[] = {
+       0xDD, 0x0F, 0x53, 0x96, 0x21, 0x9D, 0x1E, 0xA3,
+       0x93, 0x31, 0x04, 0x12, 0xD1, 0x9A, 0x08, 0xF1,
+       0xF5, 0x81, 0x1E, 0x9D, 0xC8, 0xEC, 0x8E, 0xEA,
+       0x7F, 0x80, 0xD2, 0x1C, 0x82, 0x0C, 0x27, 0x88,
+};
+
+static uint8_t rfc5114_256b_y_Z[] = {
+       0x03, 0x57, 0xDC, 0xCD, 0x4C, 0x80, 0x4D, 0x0D,
+       0x8D, 0x33, 0xAA, 0x42, 0xB8, 0x48, 0x83, 0x4A,
+       0xA5, 0x60, 0x5F, 0x9A, 0xB0, 0xD3, 0x72, 0x39,
+       0xA1, 0x15, 0xBB, 0xB6, 0x47, 0x93, 0x6F, 0x50,
+};
+
+static struct ECDH_test_vector rfc5114_secp256r1 = {
+       .curve_id = RTE_CRYPTO_EC_GROUP_SECP256R1,
+       .curve_bytesize = 32,
+       .alice_d = {
+               .data = rfc5114_256b_dA,
+               .length = sizeof(rfc5114_256b_dA),
+       },
+       .alice_q = {
+               .x = {
+                       .data = rfc5114_256b_x_qA,
+                       .length = sizeof(rfc5114_256b_x_qA),
+               },
+               .y = {
+                       .data = rfc5114_256b_y_qA,
+                       .length = sizeof(rfc5114_256b_y_qA),
+               },
+       },
+       .bob_d = {
+               .data = rfc5114_256b_dB,
+               .length = sizeof(rfc5114_256b_dB)
+       },
+       .bob_q = {
+               .x = {
+                       .data = rfc5114_256b_x_qB,
+                       .length = sizeof(rfc5114_256b_x_qB),
+               },
+               .y = {
+                       .data = rfc5114_256b_y_qB,
+                       .length = sizeof(rfc5114_256b_y_qB),
+               },
+       },
+       .Z = {
+               .x = {
+                       .data = rfc5114_256b_x_Z,
+                       .length = sizeof(rfc5114_256b_x_Z),
+               },
+               .y = {
+                       .data = rfc5114_256b_y_Z,
+                       .length = sizeof(rfc5114_256b_y_Z),
+               }
+       }
+};
+
+#endif
\ No newline at end of file
-- 
2.13.6

Reply via email to