/* eccsi.c
 *
 * Copyright (C) 2006-2021 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */



#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif

#include <wolfssl/wolfcrypt/settings.h>

#ifdef NO_INLINE
    #include <wolfssl/wolfcrypt/misc.h>
#else
    #define WOLFSSL_MISC_INCLUDED
    #include <wolfcrypt/src/misc.c>
#endif

#ifdef WOLFCRYPT_HAVE_ECCSI

#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/eccsi.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#ifdef WOLFSSL_HAVE_SP_ECC
    #include <wolfssl/wolfcrypt/sp.h>
#endif

/**
 * Initialize the components of the ECCSI key and use the specified curve.
 *
 * Must be called before performing any operations.
 * Free the ECCSI key with wc_FreeEccsiKey() when no longer needed.
 *
 * @param  [in]  key    ECCSI key to initialize.
 * @param  [in]  heap   Heap hint.
 * @param  [in]  devId  Device identifier.
 *                      Use INVALID_DEVID when no device used.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wc_InitEccsiKey_ex(EccsiKey* key, int keySz, int curveId, void* heap,
        int devId)
{
    int err = 0;
    ecc_key* ecc = NULL;
    ecc_key* pubkey = NULL;
    EccsiKeyParams* params = NULL;

    if (key == NULL) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        XMEMSET(key, 0, sizeof(*key));
        key->heap = heap;
        params = &key->params;

        err = wc_ecc_init_ex(&key->ecc, heap, devId);
    }
    if (err == 0) {
        ecc = &key->ecc;
        err = wc_ecc_init_ex(&key->pubkey, heap, devId);
    }
    if (err == 0) {
        key->pvt = wc_ecc_new_point_h(heap);
        if (key->pvt == NULL) {
            err = MEMORY_E;
        }
    }
    if (err == 0) {
        pubkey = &key->pubkey;
        err = mp_init_multi(&params->order,
#ifdef WOLFCRYPT_ECCSI_CLIENT
                &params->a, &params->b, &params->prime, &key->tmp, &key->ssk
#else
                NULL, NULL, NULL, NULL, NULL
#endif
                );
    }
    if (err == 0) {
        err = wc_ecc_set_curve(&key->ecc, keySz, curveId);
    }
    if (err == 0) {
        err = wc_ecc_set_curve(&key->pubkey, keySz, curveId);
    }

    if (err != 0) {
        wc_ecc_free(pubkey);
        wc_ecc_free(ecc);
    }

    return err;
}

/**
 * Initialize the components of the ECCSI key.
 * Default curve used: NIST_P256 (ECC_SECP256R1)
 *
 * Must be called before performing any operations.
 * Free the ECCSI key with wc_FreeEccsiKey() when no longer needed.
 *
 * @param  [in]  key    ECCSI key to initialize.
 * @param  [in]  heap   Heap hint.
 * @param  [in]  devId  Device identifier.
 *                      Use INVALID_DEVID when no device used.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wc_InitEccsiKey(EccsiKey* key, void* heap, int devId)
{
    return wc_InitEccsiKey_ex(key, 32, ECC_SECP256R1, heap, devId);
}

/**
 * Frees memory associated with components of the ECCIS key.
 *
 * Must be called when finished with the ECCIS key.
 *
 * @param  [in]  key  ECCIS key.
 */
void wc_FreeEccsiKey(EccsiKey* key)
{
    if (key != NULL) {
        EccsiKeyParams* params = &key->params;

        wc_ecc_del_point_h(params->base, key->heap);
#ifdef WOLFCRYPT_ECCSI_CLIENT
        mp_free(&key->ssk);
        mp_free(&key->tmp);
        mp_free(&params->prime);
        mp_free(&params->b);
        mp_free(&params->a);
#endif
        mp_free(&params->order);
        wc_ecc_del_point_h(key->pvt, key->heap);
        wc_ecc_free(&key->pubkey);
        wc_ecc_free(&key->ecc);
        XMEMSET(key, 0, sizeof(*key));
    }
}

/*
 * Order, as a hex string in the ECC object, loaded into mp_int in key.
 * Flags that the order is available so it isn't loaded multiple times.
 *
 * @param  [in]  key  ECCSI key.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_load_order(EccsiKey* key)
{
    int err = 0;

    if (!key->params.haveOrder) {
        err = mp_read_radix(&key->params.order, key->ecc.dp->order,
                MP_RADIX_HEX);
        if (err == 0) {
            key->params.haveOrder = 1;
        }
    }

    return err;
}

#ifdef WOLFCRYPT_ECCSI_CLIENT
/*
 * Parameters, as a hex strings in the ECC object, loaded into mp_ints in key.
 *
 * Parameters loaded: order, A, B, prime.
 * Flags that each parameter is available so they aren't loaded multiple times.
 *
 * @param  [in]  key  ECCSI key.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_load_ecc_params(EccsiKey* key)
{
    int err = 0;
    EccsiKeyParams* params = &key->params;

    err = eccsi_load_order(key);
    if ((err == 0) && (!params->haveA)) {
        err = mp_read_radix(&params->a, key->ecc.dp->Af, MP_RADIX_HEX);
        if (err == 0) {
            params->haveA = 1;
        }
    }
    if ((err == 0) && (!params->haveB)) {
        err = mp_read_radix(&params->b, key->ecc.dp->Bf, MP_RADIX_HEX);
        if (err == 0) {
            params->haveB = 1;
        }
    }
    if ((err == 0) && (!params->havePrime)) {
        err = mp_read_radix(&params->prime, key->ecc.dp->prime, MP_RADIX_HEX);
        if (err == 0) {
            params->havePrime = 1;
        }
    }

    return err;
}
#endif /* WOLFCRYPT_ECCSI_CLIENT */

/*
 * Get the base point, hex encoded in the ECC object, as an ecc_point.
 *
 * Flags that base is available so it isn't loaded multiple times.

 * @param  [in]   key   ECCSI key.
 * @param  [out]  base  Base point of curve.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_load_base(EccsiKey* key)
{
    int err = 0;
    EccsiKeyParams* params = &key->params;

    if (!params->haveBase) {
        if (params->base == NULL) {
            params->base = wc_ecc_new_point_h(key->heap);
            if (params->base == NULL) {
                err = MEMORY_E;
            }
        }
        if (err == 0) {
            err = mp_read_radix(params->base->x, key->ecc.dp->Gx, MP_RADIX_HEX);
        }
        if (err == 0) {
            err = mp_read_radix(params->base->y, key->ecc.dp->Gy, MP_RADIX_HEX);
        }
        if (err == 0) {
            err = mp_set(params->base->z, 1);
        }
        if (err == 0) {
            params->haveBase = 1;
        }
    }

    return err;
}

/*
 * Encode the base point of the curve.
 *
 * Base point is hex encoded in the ECC object or cached as an ECC point from
 * previous load calls.
 *
 * @param  [in]   key     ECCSI key.
 * @param  [out]  data    Buffer to encode base point into.
 * @param  [out]  dataSz  Length of base point in bytes.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_encode_base(EccsiKey* key, byte* data, word32* dataSz)
{
    int err;
    int idx = wc_ecc_get_curve_idx(key->ecc.dp->id);

    err = eccsi_load_base(key);
    if (err == 0) {
        err = wc_ecc_export_point_der(idx, key->params.base, data, dataSz);
    }

    return err;
}

#ifndef WOLFSSL_HAVE_SP_ECC
/*
 * Convert the KPAK to montgomery form.
 *
 * The KPAK is needed in Montgomery form for verification.
 *
 * @param  [in]  key      ECCSI key.
 * @return  0 on success.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_kpak_to_mont(EccsiKey* key)
{
    int err = 0;
    ecc_point* kpak = &key->ecc.pubkey;
    mp_int* mu = &key->tmp;
    mp_int* prime = &key->params.prime;

    if (!key->kpakMont) {
        err = mp_montgomery_calc_normalization(mu, prime);
        if (err == 0) {
            err = mp_mulmod(kpak->x, mu, prime, kpak->x);
        }
        if (err == 0) {
            err = mp_mulmod(kpak->y, mu, prime, kpak->y);
        }
        if (err == 0) {
            err = mp_mulmod(kpak->z, mu, prime, kpak->z);
        }
        if (err == 0) {
            key->kpakMont = 1;
        }
    }

    return err;
}
#endif

/*
 * Convert the KPAK from montgomery form.
 *
 * The KPAK is needed in Montgomery form for verification.
 *
 * @param  [in]  key      ECCSI key.
 * @return  0 on success.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_kpak_from_mont(EccsiKey* key)
{
    int err = 0;
    ecc_point* kpak = &key->ecc.pubkey;
    mp_digit mp;
    mp_int* prime = &key->params.prime;

    if (key->kpakMont) {
        err = mp_montgomery_setup(prime, &mp);
        if (err == 0) {
            err = mp_montgomery_reduce(kpak->x, prime, mp);
        }
        if (err == 0) {
            err = mp_montgomery_reduce(kpak->y, prime, mp);
        }
        if (err == 0) {
            err = mp_montgomery_reduce(kpak->z, prime, mp);
        }
        if (err == 0) {
            key->kpakMont = 0;
        }
    }

    return err;
}

/*
 * Compute HS = hash( G | KPAK | ID | PVT )
 *
 * Use when making a (SSK,PVT) pair, signing and verifying.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   id        Identity to create hash from.
 * @param  [in]   idSz      Length of identity in bytes.
 * @param  [in]   pvt       Public Validation Token (PVT) as an ECC point.
 * @param  [out]  hash      Buffer to hold hash data.
 * @param  [out]  hashSz    Length of hash data in bytes.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_compute_hs(EccsiKey* key, enum wc_HashType hashType,
        const byte* id, word32 idSz, ecc_point* pvt, byte* hash, byte* hashSz)
{
    int err;
    word32 dataSz = 0;
    int idx = wc_ecc_get_curve_idx(key->ecc.dp->id);
    ecc_point* kpak = &key->ecc.pubkey;
    int hash_inited = 0;

    /* HS = hash( G | KPAK | ID | PVT ) */
    err = wc_HashInit_ex(&key->hash, hashType, key->heap, INVALID_DEVID);
    if (err == 0) {
        hash_inited = 1;
        /* Base Point - G */
        dataSz = sizeof(key->data);
        err = eccsi_encode_base(key, key->data, &dataSz);
    }
    if (err == 0) {
        err = wc_HashUpdate(&key->hash, hashType, key->data, dataSz);
    }
    if (err == 0) {
        err = eccsi_kpak_from_mont(key);
    }
    if (err == 0) {
        dataSz = sizeof(key->data);
        /* KPAK - public key */
        err = wc_ecc_export_point_der(idx, kpak, key->data, &dataSz);
    }
    if (err == 0) {
        err = wc_HashUpdate(&key->hash, hashType, key->data, dataSz);
    }
    if (err == 0) {
        /* Id - Signer's ID */
        err = wc_HashUpdate(&key->hash, hashType, id, idSz);
    }
    if (err == 0) {
        dataSz = sizeof(key->data);
        /* PVT - Public Validation Token */
        err = wc_ecc_export_point_der(idx, pvt, key->data, &dataSz);
    }
    if (err == 0) {
        /* PVT - Public Validation Token */
        err = wc_HashUpdate(&key->hash, hashType, key->data, dataSz);
    }
    if (err == 0) {
        err = wc_HashFinal(&key->hash, hashType, hash);
    }

    if (err == 0) {
        *hashSz = (byte)wc_HashGetDigestSize(hashType);
    }

    if (hash_inited) {
        (void)wc_HashFree(&key->hash, hashType);
    }

    return err;
}

#ifdef WOLFCRYPT_ECCSI_KMS
/**
 * Generate KMS Secret Auth Key (KSAK) and KMS Public Auth Key (KPAK).
 *
 * RFC 6507, Section 4.2
 *
 * Called when establishing a new KMS.\n
 * KSAK must be kept secret while KPAK is required by clients for signing
 * and verifying.\n
 * Export key using wc_ExportEccsiKey(), once generated, to reuse the key.\n
 * Export KPAK using wc_ExportEccsiPublicKey(), once generate to send to
 * clients.
 *
 * Creates a random private key and multiplies it by the base point to calculate
 * the public key.
 *
 * @param  [in]  key      ECCSI key.
 * @param  [in]  rng      Random number generator.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or rng is NULL.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_MakeEccsiKey(EccsiKey* key, WC_RNG* rng)
{
    int err = 0;

    if ((key == NULL) || (rng == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = wc_ecc_make_key_ex(rng, key->ecc.dp->size, &key->ecc,
                key->ecc.dp->id);
    }

    return err;
}

/*
 * Encode a point into a buffer.
 *
 * X and y ordinate of point concatenated. Each number is zero padded tosize.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]      point    ECC point to encode.
 * @param  [in]      size     Size of prime in bytes - maximum ordinate length.
 * @param  [out]     data     Buffer to hold encoded data.
 *                            NULL when needing length of encoded data.
 * @param  [in,out]  sz       In, the size of the buffer in bytes.
 *                            Out, the size of the encoded data in bytes.
 * @param  [in]      raw      On 0, prepend descriptor byte.
 *                            On 1, only include ordinates.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or sz is NULL.
 * @return  LENGTH_ONLY_E when data is NULL - sz will hold the size in bytes of
 *          the encoded data.
 * @return  BUFFER_E when size of buffer is too small.
 */
static int eccsi_encode_point(ecc_point* point, word32 size, byte* data,
        word32* sz, int raw)
{
    int err = 0;

    if (data == NULL) {
        *sz = size * 2 + !raw;
        err = LENGTH_ONLY_E;
    }
    if ((err == 0) && (*sz < size * 2 + !raw)) {
        err = BUFFER_E;
    }

    if (err == 0) {
        if (!raw) {
            data[0] = 0x04;
            data++;
        }

        /* Write out the point's x ordinate into key size bytes. */
        err = mp_to_unsigned_bin_len(point->x, data, size);
    }
    if (err == 0) {
        data += size;
        /* Write out the point's y ordinate into key size bytes. */
        err = mp_to_unsigned_bin_len(point->y, data, size);
    }
    if (err == 0) {
        *sz = size * 2 + !raw;
    }

    return err;
}

/*
 * Decode the data into an ECC point.
 *
 * X and y ordinate of point concatenated. Each number is zero padded to
 * key size. Supports prepended descriptor byte (0x04).
 *
 * @param  [out]  point  ECC point to encode.
 * @param  [in]   size   Size of prime in bytes - maximum ordinate length.
 * @param  [in]   data   Encoded public key.
 * @param  [in]   sz     Size of the encoded public key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or z is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  ASN_PARSE_E when format byte is invalid.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_decode_point(ecc_point* point, word32 size, const byte* data,
        word32 sz)
{
    int err = 0;

    if ((sz != size * 2) && (sz != size * 2 + 1)) {
        err = BUFFER_E;
    }

    if ((err == 0) && (sz & 1)) {
        if (data[0] != 0x04) {
            err = ASN_PARSE_E;
        }
        data++;
    }

    if (err == 0) {
        /* Read the public key point's x value from key size bytes. */
        err = mp_read_unsigned_bin(point->x, data, size);
    }
    if (err == 0) {
        data += size;
        /* Read the public key point's y value from key size bytes. */
        err = mp_read_unsigned_bin(point->y, data, size);
    }
    if (err == 0) {
        err = mp_set(point->z, 1);
    }

    return err;
}

/*
 * Encode the ECCSI key.
 *
 * Encodes the private key as big-endian bytes of fixed length.
 * Encodes the public key x and y ordinates as big-endian bytes of fixed length.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [out]     data  Buffer to hold encoded ECCSI key.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails (WOLFSSL_SMALL_STACK).
 */
static int eccsi_encode_key(EccsiKey* key, byte* data)
{
    int err;
    word32 sz = (word32)key->ecc.dp->size * 2;

    /* Write out the secret value into key size bytes. */
    err = mp_to_unsigned_bin_len(&key->ecc.k, data, key->ecc.dp->size);
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Write the public key. */
        err = eccsi_encode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size,
                data, &sz, 1);
    }

    return err;
}

/**
 * Export the ECCSI key as encoded public/private ECC key.
 *
 * Use when saving the KMS key pair.
 *
 * Private key, x ordinate of public key and y ordinate of public key
 * concatenated. Each number is zero padded to key size.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [out]     data  Buffer to hold encoded ECCSI key.
 *                         NULL when requesting required length.
 * @param  [in,out]  sz    On in, size of buffer in bytes.
 *                         On out, size of encoded ECCSI key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or sz is NULL
 * @return  BAD_STATE_E when no key to export.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 * @return  BUFFER_E when the buffer passed in is too small.
 * @return  MEMORY_E when dynamic memory allocation fails (WOLFSSL_SMALL_STACK).
 */
int wc_ExportEccsiKey(EccsiKey* key, byte* data, word32* sz)
{
    int err = 0;

    if ((key == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY)) {
        err = BAD_STATE_E;
    }

    if (err == 0) {
        if (data == NULL) {
            *sz = key->ecc.dp->size * 3;
            err = LENGTH_ONLY_E;
        }
        else if (*sz < (word32)key->ecc.dp->size * 3) {
            err = BUFFER_E;
        }
        else {
            *sz = key->ecc.dp->size * 3;
        }
    }
    if (err == 0) {
        err = eccsi_kpak_from_mont(key);
    }
    if (err == 0) {
        /* Encode key */
        err = eccsi_encode_key(key, data);
    }

    return err;
}

/*
 * Import the ECCSI key as encoded public/private ECC key.
 *
 * Decodes the private key as big-endian bytes of fixed length.
 * Decodes the public key x and y ordinates as big-endian bytes of fixed length.
 *
 * @param  [in]  key   ECCSI key.
 * @param  [in]  data  Buffer holding encoded ECCSI key.
 * @return  0 on success.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_decode_key(EccsiKey* key, const byte* data)
{
    int err;

    /* Read the secret value from key size bytes. */
    err = mp_read_unsigned_bin(&key->ecc.k, data, key->ecc.dp->size);
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Read public key. */
        err = eccsi_decode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size,
                data, key->ecc.dp->size * 2);
    }

    return err;
}

/**
 * Import the ECCSI key as encoded public/private ECC key.
 *
 * Use when restoring the KMS key pair.
 *
 * Private key, x ordinate of public key and y ordinate of public key
 * concatenated. Each number is zero padded to key size.
 *
 * @param  [in]  key   ECCSI key.
 * @param  [in]  data  Buffer holding encoded ECCSI key.
 * @param  [in]  sz    Size of encoded ECCSI key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or data is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_ImportEccsiKey(EccsiKey* key, const byte* data, word32 sz)
{
    int err = 0;

    if ((key == NULL) || (data == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (sz != (word32)key->ecc.dp->size * 3)) {
        err = BUFFER_E;
    }

    if (err == 0) {
        key->kpakMont = 0;

        /* Decode key */
        err = eccsi_decode_key(key, data);
    }
    if (err == 0) {
        key->ecc.type = ECC_PRIVATEKEY;
    }

    return err;
}

/**
 * Export the ECCSI private key.
 *
 * Use when saving the KMS key.
 *
 * Private key is zero padded to key size.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [out]     data  Buffer to hold encoded ECCSI private key.
 *                         NULL when requesting required length.
 * @param  [in,out]  sz    On in, size of buffer in bytes.
 *                         On out, size of encoded ECCSI private key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or sz is NULL
 * @return  BAD_STATE_E when no key to export.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 * @return  BUFFER_E when the buffer passed in is too small.
 * @return  MEMORY_E when dynamic memory allocation fails (WOLFSSL_SMALL_STACK).
 */
int wc_ExportEccsiPrivateKey(EccsiKey* key, byte* data, word32* sz)
{
    int err = 0;

    if ((key == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY)) {
        err = BAD_STATE_E;
    }

    if (err == 0) {
        if (data == NULL) {
            *sz = key->ecc.dp->size;
            err = LENGTH_ONLY_E;
        }
        else if (*sz < (word32)key->ecc.dp->size) {
            err = BUFFER_E;
        }
        else {
            *sz = key->ecc.dp->size;
        }
    }
    if (err == 0) {
        err = mp_to_unsigned_bin_len(&key->ecc.k, data, key->ecc.dp->size);
    }

    return err;
}

/**
 * Import the ECCSI private key.
 *
 * Use when restoring the KMS key pair.
 *
 * Private key is zero padded to key size.
 *
 * @param  [in]  key   ECCSI key.
 * @param  [in]  data  Buffer holding encoded ECCSI private key.
 * @param  [in]  sz    Size of encoded ECCSI private key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or data is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_ImportEccsiPrivateKey(EccsiKey* key, const byte* data, word32 sz)
{
    int err = 0;

    if ((key == NULL) || (data == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (sz != (word32)key->ecc.dp->size)) {
        err = BUFFER_E;
    }

    if (err == 0) {
        err = mp_read_unsigned_bin(&key->ecc.k, data, key->ecc.dp->size);
    }

    return err;
}

/**
 * Export the KMS Public Auth Key (KPAK) from the ECCSI object.
 *
 * KPAK is required by all clients in order to perform cryptographic operations.
 *
 * X and y ordinate of public key concatenated. Each number is zero padded to
 * key size.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]      key      ECCSI key.
 * @param  [out]     data     Buffer to hold the encoded public key.
 * @param  [in,out]  sz       On in, size of buffer in bytes.
 *                            On out, length of encoded public key in bytes.
 * @param  [in]      raw   On 0, prepend descriptor byte.
 *                         On 1, only include ordinates.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or sz is NULL.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 * @return  BUFFER_E when the buffer passed in is too small.
 */
int wc_ExportEccsiPublicKey(EccsiKey* key, byte* data, word32* sz, int raw)
{
    int err = 0;

    if ((key == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY) &&
            (key->ecc.type != ECC_PUBLICKEY)) {
        err = BAD_STATE_E;
    }

    if ((err == 0) && (data != NULL)) {
        err = eccsi_kpak_from_mont(key);
    }
    if (err == 0) {
        /* Write out public key. */
        err = eccsi_encode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size,
            data, sz, raw);
    }

    return err;
}

/*
 * Generates an (SSK, PVT) Pair - signing key pair.
 *
 * RFC 6507, Section 5.1.1
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   rng       Random number generator.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   id        Identity to create hash from.
 * @param  [in]   idSz      Length of identity in bytes.
 * @param  [out]  ssk       Secret Signing Key as an MP integer.
 * @param  [out]  pvt       Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_make_pair(EccsiKey* key, WC_RNG* rng,
        enum wc_HashType hashType, const byte* id, word32 idSz, mp_int* ssk,
        ecc_point* pvt)
{
    int err = 0;
    byte hashSz = 0;
    int genTryCnt = 0;

    do {
        /* Don't infinitely make pairs when random number generator fails. */
        if ((++genTryCnt) > ECCSI_MAX_GEN_COUNT) {
            err = RNG_FAILURE_E;
        }

        if (err == 0) {
            wc_ecc_free(&key->pubkey);

            /* Step 1 and 2: Generate ephemeral key - v, PVT = [v]G */
            err = wc_ecc_make_key_ex(rng, key->ecc.dp->size, &key->pubkey,
                    key->ecc.dp->id);
        }
        if (err == 0) {
            err = wc_ecc_copy_point(&key->pubkey.pubkey, pvt);
        }

        /* Step 3: Compute HS */
        if (err == 0) {
            hashSz = (byte)sizeof(key->data);
            err = eccsi_compute_hs(key, hashType, id, idSz, pvt, key->data,
                    &hashSz);
        }

        /* Step 4: Compute SSK = ( KSAK + HS * v ) modulo q */
        if (err == 0) {
            err = mp_read_unsigned_bin(ssk, key->data, hashSz);
        }
        if (err == 0) {
            err = mp_mulmod(ssk, &key->pubkey.k, &key->params.order, ssk);
        }
        if (err == 0) {
            err = mp_addmod(ssk, &key->ecc.k, &key->params.order, ssk);
        }
    }
    while ((err == 0) && (mp_iszero(ssk) ||
            (mp_cmp(ssk, &key->ecc.k) == MP_EQ)));
    /* Step 5: ensure SSK and HS are non-zero (code lines above) */

    /* Step 6: Copy out SSK (done during calc) and PVT. Erase v */
    mp_forcezero(&key->pubkey.k);

    return err;
}

/**
 * Generates an (SSK, PVT) Pair - signing key pair.
 *
 * RFC 6507, Section 5.1.1
 *
 * ID should include information to indicate a revocation date.\n
 * SSK must be zeroized after sending to client.\n
 * SSK is sent to signing client only.\n
 * PVT is sent to all client types.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   rng       Random number generator.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   id        Identity to create hash from.
 * @param  [in]   idSz      Length of identity in bytes.
 * @param  [out]  ssk       Secret Signing Key as an MP integer.
 * @param  [out]  pvt       Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, rng, id, ssk or pvt is NULL.
 * @return  BAD_STATE_E when curve not set (key not set).
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_MakeEccsiPair(EccsiKey* key, WC_RNG* rng, enum wc_HashType hashType,
        const byte* id, word32 idSz, mp_int* ssk, ecc_point* pvt)
{
    int err = 0;

    if ((key == NULL) || (rng == NULL) || (id == NULL) || (ssk == NULL) ||
            (pvt == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY)) {
        err = BAD_STATE_E;
    }

    if (err == 0) {
        err = eccsi_load_order(key);
    }
    if (err == 0) {
        err = eccsi_make_pair(key, rng, hashType, id, idSz, ssk, pvt);
    }

    return err;
}

/**
 * Encode the SSK and PVT into a buffer.
 *
 * SSK and PVT required by client signing messages.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [in]      ssk   Secret Signing Key as an MP integer.
 * @param  [in]      pvt   Public Validation Token (PVT) as an ECC point.
 * @param  [out]     data  Buffer to encode key pair into.
 * @param  [in,out]  sz    In, size of buffer in bytes.
 *                         Out, size of encoded pair data in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, ssk, pvt or sz is NULL.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 */
int wc_EncodeEccsiPair(const EccsiKey* key, mp_int* ssk, ecc_point* pvt,
        byte* data, word32* sz)
{
    int err = 0;

    if ((key == NULL) || (ssk == NULL) || (pvt == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if ((err == 0) && (data == NULL)) {
        *sz = key->ecc.dp->size * 3;
        err = LENGTH_ONLY_E;
    }
    if ((err == 0) && (*sz < (word32)(key->ecc.dp->size * 3))) {
        err = BUFFER_E;
    }

    if (err == 0) {
        err = mp_to_unsigned_bin_len(ssk, data, key->ecc.dp->size);
    }
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Write out the PVT's x ordinate into key size bytes. */
        err = mp_to_unsigned_bin_len(pvt->x, data, key->ecc.dp->size);
    }
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Write out the PVT's y ordinate into key size bytes. */
        err = mp_to_unsigned_bin_len(pvt->y, data, key->ecc.dp->size);
    }
    if (err == 0) {
        *sz = key->ecc.dp->size * 3;
    }

    return err;
}

/**
 * Encode the Secret Signing Key (SSK).
 *
 * Use when saving the key pair.
 *
 * SSK is zero padded to key size.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [in]      ssk   Secret Signing Key as an MP integer.
 * @param  [out]     data  Buffer to hold encoded SSK.
 *                         NULL when requesting required length.
 * @param  [in,out]  sz    On in, size of buffer in bytes.
 *                         On out, size of encoded ECCSI key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, ssk or sz is NULL
 * @return  BAD_STATE_E when no key to export.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 * @return  BUFFER_E when the buffer passed in is too small.
 * @return  MEMORY_E when dynamic memory allocation fails (WOLFSSL_SMALL_STACK).
 */
int wc_EncodeEccsiSsk(const EccsiKey* key, mp_int* ssk, byte* data, word32* sz)
{
    int err = 0;

    if ((key == NULL) || (ssk == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY)) {
        err = BAD_STATE_E;
    }

    if (err == 0) {
        if (data == NULL) {
            *sz = key->ecc.dp->size;
            err = LENGTH_ONLY_E;
        }
        else if (*sz < (word32)key->ecc.dp->size) {
            err = BUFFER_E;
        }
        else {
            *sz = key->ecc.dp->size;
        }
    }
    if (err == 0) {
        err = mp_to_unsigned_bin_len(ssk, data, key->ecc.dp->size);
    }

    return err;
}

/**
 * Decode the Secret Signing Key (SSK).
 *
 * Use when restoring the key pair.
 *
 * SSK is zero padded to key size.
 *
 * @param  [in]   key   ECCSI key.
 * @param  [in]   data  Buffer holding encoded ECCSI key.
 * @param  [in]   sz    Size of encoded ECCSI key in bytes.
 * @param  [out]  ssk   Secret Signing Key as an MP integer.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, data or ssk is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_DecodeEccsiSsk(const EccsiKey* key, const byte* data, word32 sz,
        mp_int* ssk)
{
    int err = 0;

    if ((key == NULL) || (data == NULL) || (ssk == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (sz != (word32)key->ecc.dp->size)) {
        err = BUFFER_E;
    }

    if (err == 0) {
        err = mp_read_unsigned_bin(ssk, data, key->ecc.dp->size);
    }

    return err;
}

/**
 * Encode the PVT into a buffer.
 *
 * PVT required by client verifying messages.
 *
 * X and y ordinate of public key concatenated. Each number is zero padded to
 * key size.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]      key   ECCSI key.
 * @param  [in]      pvt   Public Validation Token (PVT) as an ECC point.
 * @param  [out]     data  Buffer to encode key pair into.
 * @param  [in,out]  sz    In, size of buffer in bytes.
 *                         Out, size of encoded pair data in bytes.
 * @param  [in]      raw   On 0, prepend descriptor byte.
 *                         On 1, only include ordinates.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, pvt or sz is NULL.
 * @return  BAD_STATE_E when PVT has not been set.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 */
int wc_EncodeEccsiPvt(const EccsiKey* key, ecc_point* pvt, byte* data,
        word32* sz, int raw)
{
    int err = 0;

    if ((key == NULL) || (pvt == NULL) || (sz == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = eccsi_encode_point(pvt, (word32)key->ecc.dp->size, data, sz, raw);
    }

    return err;
}

#endif /* WOLFCRYPT_ECCSI_KMS */

#ifdef WOLFCRYPT_ECCSI_CLIENT
/**
 * Decode the SSK and PVT data into separate variables.
 *
 * A signing client decodes the data so that it can validate the pair and sign.
 *
 * @param  [in]   key   ECCSI key.
 * @param  [in]   data  Buffer holding key pair data.
 * @param  [in]   sz    Size of data in bytes.
 * @param  [out]  ssk   Secret Signing Key as an MP integer.
 * @param  [out]  pvt   Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, data, ssk or pvt is NULL.
 * @return  LENGTH_ONLY_E when data is NULL - sz is set.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_DecodeEccsiPair(const EccsiKey* key, const byte* data, word32 sz,
        mp_int* ssk, ecc_point* pvt)
{
    int err = 0;

    if ((key == NULL) || (data == NULL) || (ssk == NULL) || (pvt == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (sz != (word32)(key->ecc.dp->size * 3))) {
        err = BUFFER_E;
    }

    if (err == 0) {
        /* Read the SSK value from key size bytes. */
        err = mp_read_unsigned_bin(ssk, data, key->ecc.dp->size);
    }
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Read the PVT's x value from key size bytes. */
        err = mp_read_unsigned_bin(pvt->x, data, key->ecc.dp->size);
    }
    if (err == 0) {
        data += key->ecc.dp->size;
        /* Read the PVT's y value from key size bytes. */
        err = mp_read_unsigned_bin(pvt->y, data, key->ecc.dp->size);
    }
    if (err == 0) {
        err = mp_set(pvt->z, 1);
    }

    return err;
}

/**
 * Decode the PVT data into an ECC point.
 *
 * A verifying client decodes the data so that it can verify a message.
 *
 * X and y ordinate of public key concatenated. Each number is zero padded to
 * key size.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]   key   ECCSI key.
 * @param  [in]   data  Buffer holding PVT data.
 * @param  [in]   sz    Size of data in bytes.
 * @param  [out]  pvt   Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, data, ssk or pvt is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  ASN_PARSE_E when format byte is invalid.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_DecodeEccsiPvt(const EccsiKey* key, const byte* data, word32 sz,
        ecc_point* pvt)
{
    int err = 0;

    if ((key == NULL) || (data == NULL) || (pvt == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = eccsi_decode_point(pvt, (word32)key->ecc.dp->size, data, sz);
    }

    return err;
}

/**
 * Decode the PVT data, from a signature, into an ECC point.
 *
 * A verifying client decodes the data so that it can calculate the identity
 * hash.
 *
 * X and y ordinate of public key concatenated. Each number is zero padded to
 * key size.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]   key   ECCSI key.
 * @param  [in]   sig   Buffer holding signature data.
 * @param  [in]   sz    Size of data in bytes.
 * @param  [out]  pvt   Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, data, ssk or pvt is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  ASN_PARSE_E when format byte is invalid.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_DecodeEccsiPvtFromSig(const EccsiKey* key, const byte* sig, word32 sz,
        ecc_point* pvt)
{
    int err = 0;

    if ((key == NULL) || (sig == NULL) || (pvt == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        word32 rSz = key->ecc.dp->size * 2;
        err = eccsi_decode_point(pvt, (word32)key->ecc.dp->size, sig + rSz,
                sz - rSz);
    }

    return err;
}

/**
 * Import the KMS Public Auth Key (KPAK) into the ECCSI object.
 *
 * Clients import the KPAK to perform cryptographic operations.
 *
 * X and y ordinate of public key concatenated. Each number is zero padded to
 * key size.
 * Descriptor byte (0x04) is prepended when not raw.
 *
 * @param  [in]  key      ECCSI key.
 * @param  [in]  data     Encoded public key as an array of bytes.
 * @param  [in]  sz       Length of encoded KPAK in bytes.
 * @param  [in]  trusted  1 when public key is trusted.
 *                        0 when validation is required to be performed.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or data is NULL.
 * @return  BUFFER_E when size of data is not equal to the expected size.
 * @return  ASN_PARSE_E when format byte is invalid.
 * @return  ECC_OUT_OF_RANGE_E when point is invalid.
 * @return  ECC_INF_E when point is at infinity and invalid.
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 */
int wc_ImportEccsiPublicKey(EccsiKey* key, const byte* data, word32 sz,
        int trusted)
{
    int err = 0;

    if ((key == NULL) || (data == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        key->kpakMont = 0;

        /* Read the public key. */
        err = eccsi_decode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size,
                data, sz);
    }
    if (err == 0) {
        key->ecc.type = ECC_PUBLICKEY;
    }
    if ((err == 0) && (!trusted)) {
       err = wc_ecc_check_key(&key->ecc);
    }

    return err;
}

/*
 * Scalar multiply the base point of the curve and add a point.
 *
 * @param  [in]   key   ECCSI key.
 * @param  [in]   n     MP integer representing scalar to multiply by.
 * @param  [in]   a     ECC point to add.
 * @param  [out]  res   ECC point representation of the resulting point.
 * @param  [in]   mp    Montgomery reduction multiplier.
 * @param  [in]   map   0 indicates to leave in projective representation.
 *                      1 indicates map projective point to affine.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_mulmod_base_add(EccsiKey* key, const mp_int* n,
        ecc_point* a, ecc_point* res, mp_digit mp, int map)
{
    int err = 0;

#ifdef WOLFSSL_HAVE_SP_ECC
#ifndef WOLFSSL_SP_NO_256
    if ((key->ecc.idx != ECC_CUSTOM_IDX) &&
            (ecc_sets[key->ecc.idx].id == ECC_SECP256R1)) {
        err = sp_ecc_mulmod_base_add_256(n, a, 1, res, map, key->heap);
    }
    else
#endif
#endif
#ifndef WOLFSSL_SP_MATH
    {
        EccsiKeyParams* params = &key->params;
        err = wc_ecc_mulmod(n, params->base, params->base, &params->a,
                &params->prime, 0);
        key->params.haveBase = 0;
        if (err == 0) {
            err = ecc_projective_add_point(params->base, a, res, &params->a,
                    &params->prime, mp);
        }
        if ((err == 0) && map) {
            err = ecc_map(res, &params->prime, mp);
        }
    }
#else
    {
        err = NOT_COMPILED_IN;
    }
    (void)mp;
#endif

    return err;
}

/*
 * Scalar multiply a point on the curve.
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   n      MP integer representing scalar to multiply by.
 * @param  [in]   point  ECC point representation of a point on the curve.
 * @param  [out]  res    ECC point representation of the resulting point.
 * @param  [in]   map    0 indicates to leave in projective representation.
 *                       1 indicates map projective point to affine.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_mulmod_point(EccsiKey* key, const mp_int* n, ecc_point* point,
        ecc_point* res, int map)
{
    int err;

#ifdef WOLFSSL_HAVE_SP_ECC
#ifndef WOLFSSL_SP_NO_256
    if ((key->ecc.idx != ECC_CUSTOM_IDX) &&
            (ecc_sets[key->ecc.idx].id == ECC_SECP256R1)) {
        err = sp_ecc_mulmod_256(n, point, res, map, key->heap);
    }
    else
#endif
#endif
    {
        EccsiKeyParams* params = &key->params;

        err = wc_ecc_mulmod(n, point, res, &params->a, &params->prime, map);
    }

    return err;
}

/*
 * Scalar multiply a point on the curve and add a.
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   n      MP integer representing scalar to multiply by.
 * @param  [in]   point  ECC point representation of a point on the curve.
 * @param  [in]   a      ECC point to add.
 * @param  [out]  res    ECC point representation of the resulting point.
 * @param  [in]   mp     Montgomery reduction multiplier.
 * @param  [in]   map    0 indicates to leave in projective representation.
 *                       1 indicates map projective point to affine.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_mulmod_point_add(EccsiKey* key, const mp_int* n,
        ecc_point* point, ecc_point* a, ecc_point* res, mp_digit mp, int map)
{
#ifdef WOLFSSL_HAVE_SP_ECC
#ifndef WOLFSSL_SP_NO_256
    int err = NOT_COMPILED_IN;

    if ((key->ecc.idx != ECC_CUSTOM_IDX) &&
            (ecc_sets[key->ecc.idx].id == ECC_SECP256R1)) {
        err = sp_ecc_mulmod_add_256(n, point, a, 0, res, map, key->heap);
    }

    (void)mp;

    return err;
#endif
#else
    int err;
    EccsiKeyParams* params = &key->params;

    err = wc_ecc_mulmod(n, point, res, &params->a, &params->prime, 0);
    if (err == 0) {
        err = ecc_projective_add_point(res, a, res, &key->params.a,
                &params->prime, mp);
    }
    if ((err == 0) && map) {
        err = ecc_map(res, &params->prime, mp);
    }

    return err;
#endif
}

/**
 * Validate an (SSV, PVT) Pair.
 *
 * RFC 6507, Section 5.1.2
 *
 * A signing client should validate the key pair before first use.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   id        Identity to create hash from.
 * @param  [in]   idSz      Length of identity in bytes.
 * @param  [in]   ssk       Secret Signing Key as an MP integer.
 * @param  [in]   pvt       Public Validation Token (PVT) as an ECC point.
 * @param  [out]  valid     1 when pair is valid and 0 otherwise.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, id, ssk, pvt or valid is NULL.
 * @return  BAD_STATE_E when curve not set (key not set).
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  IS_POINT_E when point is not on the curve.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_ValidateEccsiPair(EccsiKey* key, enum wc_HashType hashType,
        const byte* id, word32 idSz, const mp_int* ssk, ecc_point* pvt,
        int* valid)
{
    int err = 0;
    ecc_point* res = NULL;
    mp_int* hs = NULL;
    mp_digit mp = 0;
    byte hashSz = 0;
    EccsiKeyParams* params = NULL;

    if ((key == NULL) || (id == NULL) || (ssk == NULL) || (pvt == NULL) ||
            (valid == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY) &&
            (key->ecc.type != ECC_PUBLICKEY)) {
        err = BAD_STATE_E;
    }

    if (err != 0)
        return err;

    SAVE_VECTOR_REGISTERS(return _svr_ret;);

    params = &key->params;
    hs = &key->tmp;
    res = &key->pubkey.pubkey;

    err = eccsi_load_base(key);

    if (err == 0) {
       err = eccsi_load_ecc_params(key);
    }
    if (err == 0) {
        err = mp_montgomery_setup(&params->prime, &mp);
    }

    /* Step 1: Validate PVT is on curve */
    if (err == 0) {
        err = wc_ecc_is_point(pvt, &params->a, &params->b, &params->prime);
        if (err == -1) {
            err = IS_POINT_E;
        }
    }

    /* Step 2: Compute HS = hash( G | KPAK | ID | PVT ) */
    if (err == 0) {
        hashSz = (byte)sizeof(key->data);
        /* Converts KPAK from mont. */
        err = eccsi_compute_hs(key, hashType, id, idSz, pvt, key->data,
                &hashSz);
    }

    /* Step 3: Validate that KPAK = [SSK]G - [HS]PVT */
    if (err == 0) {
        err = mp_read_unsigned_bin(hs, key->data, hashSz);
    }
    /* [HS]PVT */
    if (err == 0) {
        err = eccsi_mulmod_point(key, hs, pvt, res, 0);
    }
    /* -[HS]PVT */
    if (err == 0) {
        err = mp_sub(&params->prime, res->y, res->y);
    }
    /* [SSK]G + -[HS]PVT */
    if (err == 0) {
        err = eccsi_mulmod_base_add(key, ssk, res, res, mp, 1);
    }
    if (valid != NULL) {
        *valid = (err == 0);
        if (err == 0) {
            ecc_point* kpak = &key->ecc.pubkey;
            /* Compare KPAK and [SSK]G + -[HS]PVT */
            *valid = (wc_ecc_cmp_point(res, kpak) == MP_EQ);
        }
    }

    RESTORE_VECTOR_REGISTERS();

    return err;
}

/**
 * Validate Public Validation Token (PVT) is on the curve.
 *
 * RFC 6507, Section 5.1.2, Step 1
 *
 * A verifying client should validate the PVT before first use.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   pvt       Public Validation Token (PVT) as an ECC point.
 * @param  [out]  valid     1 when PVT is valid and 0 otherwise.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, pvt or valid is NULL.
 * @return  BAD_STATE_E when curve not set (key not set).
 * @return  MP_MEM or MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_ValidateEccsiPvt(EccsiKey* key, const ecc_point* pvt, int* valid)
{
    int err = 0;

    if ((key == NULL)| (pvt == NULL) || (valid == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = wc_ecc_set_curve(&key->pubkey, key->ecc.dp->size,
                key->ecc.dp->id);
    }
    if (err == 0) {
        err = wc_ecc_copy_point(pvt, &key->pubkey.pubkey);
    }
    if (err == 0) {
        *valid = (wc_ecc_check_key(&key->pubkey) == 0);
    }

    return err;
}

/**
 * Creates the Hash of the ID and PVT with the ECCSI key.
 *
 * The hash ID is required as input to the sign and verify operations.\n
 * Signing clients may cache this value.
 *
 * RFC 6507, Section 5.2.1, Step 3
 *
 * Set the calculated hash internally for use.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   id        Identity to create hash from.
 * @param  [in]   idSz      Length of identity in bytes.
 * @param  [in]   pvt       Public Validation Token (PVT) as an ECC point.
 * @param  [out]  hash      Buffer to hold hash result.
 * @param  [out]  hashSz    Length of hash data in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, id, pvt, hash or hashSz is NULL.
 * @return  BAD_STATE_E when public key not set.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_HashEccsiId(EccsiKey* key, enum wc_HashType hashType, const byte* id,
        word32 idSz, ecc_point* pvt, byte* hash, byte* hashSz)
{
    int err = 0;

    if ((key == NULL) || (id == NULL) || (pvt == NULL) || (hash == NULL) ||
            (hashSz == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY) &&
            (key->ecc.type != ECC_PUBLICKEY)) {
        err = BAD_STATE_E;
    }
    /* Load the curve parameters for operations */
    if (err == 0) {
       err = eccsi_load_ecc_params(key);
    }
    if (err == 0) {
        err = eccsi_compute_hs(key, hashType, id, idSz, pvt, hash, hashSz);
    }
    if (err == 0) {
        XMEMCPY(key->idHash, hash, *hashSz);
        key->idHashSz = *hashSz;
    }

    return err;
}

/**
 * Set the identity hash for use with signing/verification.
 *
 * @param  [in]  key     ECCSI key.
 * @param  [in]  hash    Buffer with hash of identity.
 * @param  [in]  hashSz  Length of hash data in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key or hash is NULL, or hashSz is greater than
 *          WC_MAX_DIGEST_SIZE.
 */
int wc_SetEccsiHash(EccsiKey* key, const byte* hash, byte hashSz)
{
    int err = 0;

    if ((key == NULL) || (hash == NULL) || (hashSz > WC_MAX_DIGEST_SIZE)) {
        err = BAD_FUNC_ARG;
    }
    if (err == 0) {
        XMEMCPY(key->idHash, hash, hashSz);
        key->idHashSz = hashSz;
    }

    return err;
}

/**
 * Set an (SSV, PVT) Pair for signing.
 *
 * @param  [in]   key  ECCSI key.
 * @param  [in]   ssk  Secret Signing Key as an MP integer.
 * @param  [in]   pvt  Public Validation Token (PVT) as an ECC point.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, ssk or pvt is NULL.
 * @return  MP math errors when copy fails
 */
int wc_SetEccsiPair(EccsiKey* key, const mp_int* ssk, const ecc_point* pvt)
{
    int err = 0;

    if ((key == NULL) || (ssk == NULL) || (pvt == NULL)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = mp_copy(ssk, &key->ssk);
    }

    if (err == 0) {
        err = wc_ecc_copy_point(pvt, key->pvt);
    }

    return err;
}

#ifdef ECCSI_ORDER_MORE_BITS_THAN_PRIME
/*
 * Fit the number to the maximum number of bytes.
 *
 * If the number is too big then subtract from order.
 * RFC 6507, Section 5.2.1, Note at end.
 * This should only happen when order is larger than prime in bits.
 *
 * @param  [in]   a      MP integer to fix.
 * @param  [in]   order  MP integer representing order of curve.
 * @param  [in]   m      Maximum number of bytes to encode into.
 * @param  [out]  r      MP integer that is the result after fixing.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_fit_to_octets(const mp_int* a, mp_int* order, int m,
        mp_int* r)
{
    int err;

    if (mp_count_bits(a) > m * 8) {
        err = mp_sub(order, (mp_int*)a, r);
    }
    else
    {
        err = mp_copy(a, r);
    }

    return err;
}
#else
/*
 * Fit the number to the maximum number of bytes.
 *
 * If the number is too big then subtract from order.
 * RFC 6507, Section 5.2.1, Note at end.
 * This should only happen when order is larger than prime in bits.
 *
 * @param  [in]   a      MP integer to fix.
 * @param  [in]   order  MP integer representing order of curve.
 * @param  [in]   m      Maximum number of bytes to encode into.
 * @param  [out]  r      MP integer that is the result after fixing.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int eccsi_fit_to_octets(const mp_int* a, const mp_int* order, int m,
        mp_int* r)
{
    (void)order;
    (void)m;

    /* Duplicate line to stop static analyzer complaining. */
    return mp_copy(a, r);
}
#endif

/*
 * Compute the HE = hash( HS | r | M ), hash value of signature.
 *
 * Partial result required for signing and verification.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   r         MP integer that is the first signature element.
 * @param  [in]   msg       Message of signature.
 * @param  [in]   msgSz     Length of message in bytes.
 * @param  [out]  he        Signature hash.
 * @param  [out]  heSz      Length of signature hash in bytes
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_compute_he(EccsiKey* key, enum wc_HashType hashType,
        mp_int* r, const byte* msg, word32 msgSz, byte* he, word32* heSz)
{
    int err = 0;
    word32 dataSz = key->ecc.dp->size;
    int hash_inited = 0;

    /* HE = hash( HS | r | M ) */
    err = wc_HashInit_ex(&key->hash, hashType, key->heap, INVALID_DEVID);
    if (err == 0) {
        hash_inited = 1;
        /* HS */
        err = wc_HashUpdate(&key->hash, hashType, key->idHash, key->idHashSz);
    }
    if (err == 0) {
        err = mp_to_unsigned_bin_len(r, key->data, dataSz);
    }
    if (err == 0) {
        /* r */
        err = wc_HashUpdate(&key->hash, hashType, key->data, dataSz);
    }
    if (err == 0) {
        /* M */
        err = wc_HashUpdate(&key->hash, hashType, msg, msgSz);
    }
    if (err == 0) {
        err = wc_HashFinal(&key->hash, hashType, he);
    }
    if (err == 0) {
        *heSz = wc_HashGetDigestSize(hashType);
    }

    if (hash_inited) {
        (void)wc_HashFree(&key->hash, hashType);
    }

    return err;
}

/*
 * Encode the signature = ( r | s | PVT )
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   r      MP integer that is the first signature element.
 * @param  [in]   s      MP integer that is the second signature element.
 * @param  [in]   pvt    ECC point representing Public Validation Token.
 * @param  [out]  sig    Signature of message.
 * @param  [out]  sigSz  Length of signature in bytes.
 */
static int eccsi_encode_sig(const EccsiKey* key, mp_int* r, mp_int* s,
        byte* sig, word32* sigSz)
{
    int err;
    word32 sz = key->ecc.dp->size;

    err = mp_to_unsigned_bin_len(r, sig, sz);
    if (err == 0) {
        err = mp_to_unsigned_bin_len(s, sig + sz, sz);
    }
    if (err == 0) {
        *sigSz = key->ecc.dp->size * 2 + 1;
        err = wc_ecc_export_point_der(wc_ecc_get_curve_idx(key->ecc.dp->id),
                 key->pvt, sig + sz * 2, sigSz);
    }
    if (err == 0) {
        *sigSz = sz * 4 + 1;
    }

    return err;
}

/*
 * Sign the ECCSI hash (of ID with the key) to two mp_int objects: r and s.
 *
 * RFC 6507, Section 5.2.1, Steps 1 to 4
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   rng       Random number generator.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   msg       Message to sign.
 * @param  [in]   msgSz     Length of message in bytes.
 * @param  [out]  r         First big number integer part of signature.
 * @param  [out]  s         Second big number integer part of signature.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_gen_sig(EccsiKey* key, WC_RNG* rng, enum wc_HashType hashType,
        const byte* msg, word32 msgSz, mp_int* r, mp_int* s)
{
    int err = 0;
    word32 sz = key->ecc.dp->size;
    word32 heSz = 0;
    const mp_int* jx = NULL;
    mp_int* he = &key->tmp;
    int genTryCnt = 0;

    do {
        /* Don't infinitely gen sigs when random number generator fails. */
        if ((++genTryCnt) > ECCSI_MAX_GEN_COUNT) {
            err = RNG_FAILURE_E;
        }

        if (err == 0) {
            wc_ecc_free(&key->pubkey);

            /* Step 1 and 2: Generate ephemeral key - j, J = [j]G, r = Jx */
            err = wc_ecc_make_key_ex(rng, sz, &key->pubkey, key->ecc.dp->id);
        }
        if (err == 0) {
            jx = key->pubkey.pubkey.x;
            err = eccsi_fit_to_octets(jx, &key->params.order, sz, r);
        }

        /* Step 3: Compute HE = hash( HS | r | M ) */
        if (err == 0) {
            err = eccsi_compute_he(key, hashType, r, msg, msgSz, key->data,
                    &heSz);
        }

        /* Step 4: Verify that HE + r * SSK is non-zero modulo q */
        if (err == 0) {
            err = mp_read_unsigned_bin(he, key->data, heSz);
        }
        /* s' = r * SSK */
        if (err == 0) {
            err = mp_mulmod(r, &key->ssk, &key->params.order, s);
        }
        /* s' = HE + r * SSK */
        if (err == 0) {
            err = mp_addmod(he, s, &key->params.order, s);
        }
    }
    while ((err == 0) && (mp_iszero(s) || (mp_cmp(s, he) == MP_EQ)));

    return err;
}


/**
 * Sign the ECCSI hash (of ID with the key).
 *
 * RFC 6507, Section 5.2.1
 *
 * Must have imported KPAK using wc_ImportEccsiPublicKey() before calling.\n
 * Use wc_HashEccsiId() to calculate the hash and wc_SetEccsiHash() to set
 * the identity hash to use.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   rng       Random number generator.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   msg       Message to sign.
 * @param  [in]   msgSz     Length of message in bytes.
 * @param  [out]  sig       Signature of message.
 * @param  [out]  sigSz     Length of signature in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, rng, msg or sigSz is NULL.
 * @return  BAD_STATE_E when the curve or id hash has not been set (no key set).
 * @return  LENGTH_ONLY_E when sig is NULL - sigSz is set.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
int wc_SignEccsiHash(EccsiKey* key, WC_RNG* rng, enum wc_HashType hashType,
        const byte* msg, word32 msgSz, byte* sig, word32* sigSz)
{
    int err = 0;
    mp_int* r = NULL;
    mp_int* s = NULL;
    mp_int* j = NULL;
    word32 sz = 0;

    if ((key == NULL) || (rng == NULL) || (msg == NULL) || (sigSz == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (key->ecc.type != ECC_PUBLICKEY) &&
            (key->ecc.type != ECC_PRIVATEKEY)) {
        err = BAD_STATE_E;
    }
    if ((err == 0) && (sig != NULL) && (key->idHashSz == 0)) {
        err = BAD_STATE_E;
    }

    if (err == 0)  {
        sz = key->ecc.dp->size;
        if (sig == NULL) {
            *sigSz = sz * 4 + 1;
            err = LENGTH_ONLY_E;
        }
    }
    if ((err == 0) && (*sigSz < sz * 4 + 1)) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        r = key->pubkey.pubkey.y;
        s = key->pubkey.pubkey.z;

        err = eccsi_load_order(key);
    }

    if (err == 0) {
        /* Steps 1 to 4. */
        err = eccsi_gen_sig(key, rng, hashType, msg, msgSz, r, s);
    }

    /* Step 5: s' = ( (( HE + r * SSK )^-1) * j ) modulo q, erase j */
    if (err == 0) {
        err = mp_invmod(s, &key->params.order, s);
    }
    if (err == 0) {
        j  = &key->pubkey.k;
        err = mp_mulmod(s, j, &key->params.order, s);
    }
    if (err == 0) {
        mp_forcezero(j);

        /* Step 6: s = s' fitted */
        err = eccsi_fit_to_octets(s, &key->params.order, sz, s);
    }

    /* Step 7: Output Signature = ( r | s | PVT ) */
    if (err == 0) {
        err = eccsi_encode_sig(key, r, s, sig, sigSz);
    }

    return err;
}

/*
 * Decode the s part of the signature = ( r | s | PVT )
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   sig    Signature of message.
 * @param  [in]   sigSz  Length of signature in bytes.
 * @param  [out]  s      MP integer that is the second signature element.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_decode_sig_s(const EccsiKey* key, const byte* sig,
        word32 sigSz, mp_int* s)
{
    int err = 0;
    word32 sz = key->ecc.dp->size;

    if (sigSz != sz * 4 + 1) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = mp_read_unsigned_bin(s, sig + sz, sz);
    }

    return err;
}

/*
 * Decode the r and pvt part of the signature = ( r | s | PVT )
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   sig    Signature of message.
 * @param  [in]   sigSz  Length of signature in bytes.
 * @param  [out]  r      MP integer that is the first signature element.
 * @param  [out]  pvt    ECC point representing Public Validation Token.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other -ve value when an internal operation fails.
 */
static int eccsi_decode_sig_r_pvt(const EccsiKey* key, const byte* sig,
        word32 sigSz, mp_int* r, ecc_point* pvt)
{
    int err = 0;
    word32 sz = key->ecc.dp->size;

    if (sigSz != sz * 4 + 1) {
        err = BAD_FUNC_ARG;
    }

    if (err == 0) {
        err = mp_read_unsigned_bin(r, sig, sz);
    }
    if (err == 0) {
        /* must free previous public point otherwise wc_ecc_import_point_der
         * could leak memory */
        mp_clear(pvt->x);
        mp_clear(pvt->y);
        mp_clear(pvt->z);

        err = wc_ecc_import_point_der(sig + sz * 2, sz * 2 + 1,
                wc_ecc_get_curve_idx(key->ecc.dp->id), pvt);
    }

    return err;
}

/*
 * Calculate Y point as part of verification process.
 *
 * Y = [HS]PVT + KPAK
 *
 * @param  [in]   key      ECCSI key.
 * @param  [in]   pvt      ECC point representing Public Validation Token.
 * @param  [in]   mp       Montgomery reduction multiplier.
 * @param  [out]  y        ECC point representing calculated value Y.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other value when an an internal operation fails.
 */
static int eccsi_calc_y(EccsiKey* key, ecc_point* pvt, mp_digit mp,
        ecc_point* y)
{
    int err;
    mp_int* hs = &key->ssk;

    err = mp_read_unsigned_bin(hs, key->idHash, key->idHashSz);
#ifndef WOLFSSL_HAVE_SP_ECC
    /* Need KPAK in montgomery form. */
    if (err == 0) {
        err = eccsi_kpak_to_mont(key);
    }
#endif
    /* [HS]PVT + KPAK */
    if (err == 0) {
        ecc_point* kpak = &key->ecc.pubkey;
        err = eccsi_mulmod_point_add(key, hs, pvt, kpak, y, mp, 1);
    }

    return err;
}

/*
 * Calculate J point as part of verification process.
 *
 * J = [s]( [HE]G + [r]Y )
 *
 * @param  [in]   key    ECCSI key.
 * @param  [in]   hem    MP int representation of HE = Hash (hs, r and message).
 * @param  [in]   sig    Signature of message.
 * @param  [in]   sigSz  Length of signature in bytes.
 * @param  [in]   y      ECC point representing [r]Y.
 * @param  [in]   mp     Montgomery reduction multiplier.
 * @param  [out]  j      ECC point representing calculated value J.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other value when an an internal operation fails.
 */
static int eccsi_calc_j(EccsiKey* key, const mp_int* hem, const byte* sig,
        word32 sigSz, ecc_point* y, mp_digit mp, ecc_point* j)
{
    int err;
    mp_int* s = &key->tmp;

    /* [HE]G + [r]Y */
    err = eccsi_mulmod_base_add(key, hem, y, j, mp, 1);
    if (err == 0) {
        err = eccsi_decode_sig_s(key, sig, sigSz, s);
    }
    /* [s]( [HE]G + [r]Y ) */
    if (err == 0) {
        err = eccsi_mulmod_point(key, s, j, j, 1);
    }

    return err;
}

/**
 * Verify the ECCSI hash (of ID with the key).
 *
 * RFC 6507, Section 5.2.2
 *
 * Must have imported KPAK using wc_ImportEccsiPublicKey() before calling.\n
 * Use wc_HashEccsiId() to calculate the hash and wc_SetEccsiHash() to set
 * the identity hash to use.
 *
 * @param  [in]   key       ECCSI key.
 * @param  [in]   hashType  Type of hash algorithm. e.g. WC_SHA256
 * @param  [in]   msg       Message to verify.
 * @param  [in]   msgSz     Length of message in bytes.
 * @param  [in]   sig       Signature of message.
 * @param  [in]   sigSz     Length of signature in bytes.
 * @param  [out]  verified  1 when the signature was verified and 0 otherwise.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when key, hash, msg, sig or ret is NULL.
 * @return  BAD_STATE_E when the curve or id hash has not been set (no key set).
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other value when an an internal operation fails.
 */
int wc_VerifyEccsiHash(EccsiKey* key, enum wc_HashType hashType,
        const byte* msg, word32 msgSz, const byte* sig, word32 sigSz,
        int* verified)
{
    int err = 0;
    byte* he = NULL;
    word32 heSz = 0;
    mp_int* r = NULL;
    mp_int* jx = NULL;
    mp_int* hem = NULL;
    ecc_point* pvt = NULL;
    ecc_point* y = NULL;
    ecc_point* j = NULL;
    mp_digit mp = 0;
    EccsiKeyParams* params = NULL;

    if ((key == NULL) || (msg == NULL) || (sig == NULL) || (verified == NULL)) {
        err = BAD_FUNC_ARG;
    }
    if ((err == 0) && (key->ecc.type != ECC_PRIVATEKEY) &&
            (key->ecc.type != ECC_PUBLICKEY)) {
        err = BAD_STATE_E;
    }
    if ((err == 0) && (key->idHashSz == 0)) {
        err = BAD_STATE_E;
    }

    if (err != 0)
        return err;

    SAVE_VECTOR_REGISTERS(return _svr_ret;);

    /* Decode the signature into components. */
    r = &key->pubkey.k;
    pvt = &key->pubkey.pubkey;
    err = eccsi_decode_sig_r_pvt(key, sig, sigSz, r, pvt);

    /* Load the curve parameters for operations */
    if (err == 0) {
        err = eccsi_load_base(key);
    }
    if (err == 0) {
        err = eccsi_load_ecc_params(key);
    }
    if (err == 0) {
        params = &key->params;
        err = mp_montgomery_setup(&params->prime, &mp);
    }

    /* Step 1: Validate PVT is on curve */
    if (err == 0) {
        err = wc_ecc_is_point(pvt, &params->a, &params->b, &params->prime);
    }

    /* Step 2: Compute HS = hash( G | KPAK | ID | PVT )
     * HS is key->idHash, key->idHashSz */

    /* Step 3: Compute HE = hash( HS | r | M ) */
    if (err == 0) {
        he = key->data;
        err = eccsi_compute_he(key, hashType, r, msg, msgSz, he, &heSz);
    }

    /* Step 4: Y = [HS]PVT + KPAK */
    if (err == 0) {
        y = pvt;
        err = eccsi_calc_y(key, pvt, mp, y);
    }

    /* Step 5: Compute J = [s]( [HE]G + [r]Y ) */
    /* [r]Y */
    if (err == 0) {
        hem = &key->tmp;
        err = mp_read_unsigned_bin(hem, he, heSz);
    }
    if (err == 0) {
        err = eccsi_mulmod_point(key, r, y, y, 0);
    }
    if (err == 0) {
        j = params->base;
        err = eccsi_calc_j(key, hem, sig, sigSz, y, mp, j);
        key->params.haveBase = 0;
    }

    /* Step 6: Jx fitting, compare with r */
    if (err == 0) {
        jx = &key->tmp;
        err = eccsi_fit_to_octets(j->x, &params->order, key->ecc.dp->size, jx);
    }

    if (verified != NULL) {
        *verified = ((err == 0) && (mp_cmp(jx, r) == MP_EQ));
    }

    RESTORE_VECTOR_REGISTERS();

    return err;
}
#endif /* WOLFCRYPT_ECCSI_CLIENT */

#endif /* WOLFCRYPT_HAVE_ECCSI */

