/* eccsi.c * * Copyright (C) 2006-2022 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 #endif #include #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif #ifdef WOLFCRYPT_HAVE_ECCSI #include #include #include #ifdef WOLFSSL_HAVE_SP_ECC #include #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(¶ms->order, #ifdef WOLFCRYPT_ECCSI_CLIENT ¶ms->a, ¶ms->b, ¶ms->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(¶ms->prime); mp_free(¶ms->b); mp_free(¶ms->a); #endif mp_free(¶ms->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(¶ms->a, key->ecc.dp->Af, MP_RADIX_HEX); if (err == 0) { params->haveA = 1; } } if ((err == 0) && (!params->haveB)) { err = mp_read_radix(¶ms->b, key->ecc.dp->Bf, MP_RADIX_HEX); if (err == 0) { params->haveB = 1; } } if ((err == 0) && (!params->havePrime)) { err = mp_read_radix(¶ms->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, ¶ms->a, ¶ms->prime, 0); key->params.haveBase = 0; if (err == 0) { err = ecc_projective_add_point(params->base, a, res, ¶ms->a, ¶ms->prime, mp); } if ((err == 0) && map) { err = ecc_map(res, ¶ms->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, ¶ms->a, ¶ms->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, ¶ms->a, ¶ms->prime, 0); if (err == 0) { err = ecc_projective_add_point(res, a, res, &key->params.a, ¶ms->prime, mp); } if ((err == 0) && map) { err = ecc_map(res, ¶ms->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(¶ms->prime, &mp); } /* Step 1: Validate PVT is on curve */ if (err == 0) { err = wc_ecc_is_point(pvt, ¶ms->a, ¶ms->b, ¶ms->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(¶ms->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(¶ms->prime, &mp); } /* Step 1: Validate PVT is on curve */ if (err == 0) { err = wc_ecc_is_point(pvt, ¶ms->a, ¶ms->b, ¶ms->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, ¶ms->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 */