Newer
Older
mbed-os / connectivity / drivers / mbedtls / TARGET_NUVOTON / TARGET_M460 / ecp / crypto_ecc_hw.c
@Jay Sridharan Jay Sridharan on 31 Dec 2022 21 KB Merge upstream changes into mbed-ce (#117)
/*
 * Copyright (c) 2022, Nuvoton Technology Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbedtls/common.h"

#if defined(MBEDTLS_ECP_C)

#include "mbedtls/ecp.h"
#include "mbedtls/error.h"

#if defined(MBEDTLS_ECP_ALT) || defined(MBEDTLS_ECP_INTERNAL_ALT)

#include "mbedtls/platform.h"
#include "mbedtls/platform_util.h"
#include "mbed_toolchain.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "mbed_toolchain.h"
#include "nu_bitutil.h"
#include "nu_timer.h"
#include "crypto-misc.h"
#include "crypto_ecc_hw.h"
#include "ecp_helper.h"

/* Enable ECC debug */
//#define NU_CRYPTO_ECC_ENABLE_DEBUG

/* Max key size supported */
#define NU_ECC_MAXKEYBITS           571
/* Max ECC big-number words */
#define NU_ECC_BIGNUM_MAXWORD       18
/* words in limb  */
#define wiL                         (sizeof (mbedtls_mpi_uint) / sizeof (uint32_t))
/* Min MPI limbs for ECC big-number */
#define NU_ECC_BIGNUM_MINLIMB       (NU_ECC_BIGNUM_MAXWORD / wiL)

/*
 * Convert between words and number of limbs
 * Divide first in order to avoid potential overflows
 */
#define WORDS_TO_LIMBS(i) ( (i) / wiL + ( (i) % wiL != 0 ) )

/* Notes for Crypto ECC H/W port
 *
 * The following point operations are not supported and will cause engine to trap:
 * 1. P + P. Workaround by 2*P.
 * 2. m*P with SCAP enabled, esp m = 2 or close to (order - 1). Cannot work around by
 *    fallback to S/W, because following operations are easily to fail with data error.
 *    Disable SCAP temporarily.
 */

int crypto_ecc_capable(const mbedtls_ecp_group *grp)
{
    /* Curve types
     *
     * - Short Weierstrass
     * - Montgomery
     */
    mbedtls_ecp_curve_type curve_type = mbedtls_ecp_get_type(grp);
    if (curve_type == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
        return 1;
    } else if (curve_type == MBEDTLS_ECP_TYPE_MONTGOMERY) {
        if (grp->id == MBEDTLS_ECP_DP_CURVE25519 ||
            grp->id == MBEDTLS_ECP_DP_CURVE448) {
            return 1;
        } else {
            return 0;
        }
    } else {
        return 0;
    }
}

int crypto_ecc_init(const mbedtls_ecp_group *grp)
{
    /* Behavior of mbedtls_internal_ecp_init()/mbedtls_internal_ecp_free()
     *
     * mbedtls_internal_ecp_init()/mbedtls_internal_ecp_free() are like pre-op/post-op calls
     * and they guarantee:
     *
     * 1. Paired
     * 2. No overlapping
     * 3. Upper public function cannot return when ECP alter. is still activated.
     */

    /* Acquire ownership of ECC accelerator */
    crypto_ecc_acquire();

    /* Initialize crypto module */
    crypto_init();

    /* Release ownership of ECC accelerator */
    crypto_ecc_release();

    return 0;
}

void crypto_ecc_free(const mbedtls_ecp_group *grp)
{
    /* Acquire ownership of ECC accelerator */
    crypto_ecc_acquire();

    /* Uninit crypto module */
    crypto_uninit();

    /* Release ownership of ECC accelerator */
    crypto_ecc_release();
}

int crypto_ecc_run_eccop_add(const mbedtls_ecp_group *grp,
                             mbedtls_ecp_point *R,
                             const mbedtls_ecp_point *P,
                             const mbedtls_ecp_point *Q,
                             bool blinding)
{
    /* SCAP is applicable only for PM. */
    blinding = false;

    return crypto_ecc_run_eccop(grp, R, NULL, P, NULL, Q, ECCOP_POINT_ADD, blinding);
}

int crypto_ecc_run_eccop_double(const mbedtls_ecp_group *grp,
                                mbedtls_ecp_point *R,
                                const mbedtls_ecp_point *P,
                                bool blinding)
{
    /* SCAP is applicable only for PM. */
    blinding = false;

    return crypto_ecc_run_eccop(grp, R, NULL, P, NULL, NULL, ECCOP_POINT_DOUBLE, blinding);
}

int crypto_ecc_run_eccop_mul(const mbedtls_ecp_group *grp,
                             mbedtls_ecp_point *R,
                             const mbedtls_mpi *m,
                             const mbedtls_ecp_point *P,
                             bool blinding)
{
    /* NOTE: Engine can trap when SCAP is enabled. See above. */
    blinding = false;
    return crypto_ecc_run_eccop(grp, R, m, P, NULL, NULL, ECCOP_POINT_MUL, blinding);
}

int crypto_ecc_run_eccop(const mbedtls_ecp_group *grp,
                         mbedtls_ecp_point *R,
                         const mbedtls_mpi *m,
                         const mbedtls_ecp_point *P,
                         MBED_UNUSED const mbedtls_mpi *n,
                         const mbedtls_ecp_point *Q,
                         uint32_t eccop,
                         bool blinding)
{
    /* Check necessary arguments for all ECC operations */
    if (grp == NULL || R == NULL) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Check grp->P is positive */
    if (mbedtls_mpi_cmp_int(&grp->P, 0) <= 0) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Check supported maximum key bits */
    if (grp->pbits > NU_ECC_MAXKEYBITS) {
        return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
    }

    /* NOTE: Engine doesn't support P + Q when P and Q are the same. Workaround by 2*P */
    if (eccop == ECCOP_POINT_ADD) {
        if (P == NULL || Q == NULL) {
            return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
        }
        if (mbedtls_ecp_point_cmp(P, Q) == 0) {
            return crypto_ecc_run_eccop(grp, R, NULL, P, NULL, NULL, ECCOP_POINT_DOUBLE, blinding);
        }
    }

    /* Acquire ownership of ECC accelerator */
    crypto_ecc_acquire();

    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    bool ecc_done;

    mbedtls_mpi N_;
    const mbedtls_mpi *Np;

    mbedtls_mpi_init(&N_);

    /* Use ECP_HELPER_MPI_NORM(Np, N1, N_, P) to get normalized MPI
     *
     * N_: Holds normalized MPI if the passed-in MPI N1 is not
     * Np: Pointer to normalized MPI, which could be N1 or N_
     */

    /* Check necessary arguments and handle special cases for specified ECC operation 
     *
     * ECCOP_POINT_MUL      R = m*P
     * ECCOP_POINT_ADD      R = P + Q
     * ECCOP_POINT_DOUBLE   R = 2*P
     *
     * ECC accelerator doesn't support R = 0, and we need to detect it.
     */
    if (eccop == ECCOP_POINT_MUL) {
        /* R = m*P */
        if (m == NULL || P == NULL) {
            ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
            goto cleanup;
        }

         /* R = 0*P = 0 or R = P = 0 */
        if (mbedtls_mpi_cmp_int(m, 0) == 0 || mbedtls_mpi_cmp_int(&P->Z, 0) == 0) {
            ret = mbedtls_ecp_set_zero(R);
            goto cleanup;
        }

        /* R = 1*P */
        if (mbedtls_mpi_cmp_int(m, 1) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, P));
            /* Unnecessary because passed-in P and Q are required to be normalized */
            //MBEDTLS_MPI_CHK(internal_ecp_normalize(grp, R));
            goto cleanup;
        }

        /* R = m*P = (multiple of order)*G = 0 */
        /* NOTE: If grp->N (order) is a prime, we could detect R = 0 for all m*P cases
         *       by just checking if m is a multiple of grp->N. Otherwise, sigh. */
        /* TODO: Find an approach to detecting R = 0 for all m*P cases */
        ECP_HELPER_MPI_NORM(&Np, *m, N_, grp->N);
        if (mbedtls_mpi_cmp_int(Np, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_set_zero(R));
            goto cleanup;
        }
    } else if (eccop == ECCOP_POINT_ADD) {
        /* R = P + Q */
        if (P == NULL || Q == NULL) {
            ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
            goto cleanup;
        }

        /* R = 0 + Q = Q */
        if (mbedtls_mpi_cmp_int(&P->Z, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, Q));
            /* Unnecessary because passed-in P and Q are required to be normalized */
            //MBEDTLS_MPI_CHK(internal_ecp_normalize(grp, R));
            goto cleanup;
        }

        /* R = P + 0 = P */
        if (mbedtls_mpi_cmp_int(&Q->Z, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, P));
            /* Unnecessary because passed-in P and Q are required to be normalized */
            //MBEDTLS_MPI_CHK(internal_ecp_normalize(grp, R));
            goto cleanup;
        }

        /* R = P + Q = P + (-P) = 0 */
        MBEDTLS_MPI_CHK(crypto_ecc_run_modop(&N_, &P->Y, &Q->Y, &grp->P, grp->pbits, MODOP_ADD));
        if (mbedtls_mpi_cmp_int(&N_, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_set_zero(R));
            goto cleanup;
        }
    } else if (eccop == ECCOP_POINT_DOUBLE) {
        /* R = 2*P */
        if (P == NULL) {
            ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
            goto cleanup;
        }

        /* R = 2*0 = 0 */
        if (mbedtls_mpi_cmp_int(&P->Z, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_set_zero(R));
            goto cleanup;
        }

        /* R = 2*P = P + P = P + (-P) = 0 */
        MBEDTLS_MPI_CHK(crypto_ecc_run_modop(&N_, &P->Y, &P->Y, &grp->P, grp->pbits, MODOP_ADD));
        if (mbedtls_mpi_cmp_int(&N_, 0) == 0) {
            MBEDTLS_MPI_CHK(mbedtls_ecp_set_zero(R));
            goto cleanup;
        }
    } else {
        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
        goto cleanup;
    }

    /* ECC curve type */
    mbedtls_ecp_curve_type curve_type = mbedtls_ecp_get_type(grp);
    uint32_t curve_sel = 0;
    switch (curve_type) {
    case MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:
        curve_sel = 0;                      // Short Weierstrass
        break;

    case MBEDTLS_ECP_TYPE_MONTGOMERY:
        curve_sel = CRPT_ECC_CTL_CSEL_Msk;  // Montgomery
        break;

    default:
        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
        goto cleanup;
    }

    /* Enable ECC interrupt */
    ECC_ENABLE_INT(CRPT);

    /* For safe, recover from previous failure if ever */
    MBEDTLS_MPI_CHK(crypto_ecc_abort(5*1000*1000));

    /* Configure ECC curve coefficient A */
    if (curve_type == MBEDTLS_ECP_TYPE_MONTGOMERY) {
        /*
         * In S/W impl, A is used as (A + 2) / 4. Figure out its original value for engine.
         * https://github.com/ARMmbed/mbed-os/blob/2eb06e76208588afc6cb7580a8dd64c5429a10ce/connectivity/mbedtls/include/mbedtls/ecp.h#L219-L220
         */
        MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int(&N_, &grp->A, 4));
        MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&N_, &N_, 2));
        Np = &N_;
    } else {
        /* Special case for A = -3 */
        if (grp->A.p == NULL) {
            MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&N_, -3));
            ECP_HELPER_MPI_NORM(&Np, N_, N_, grp->P);
        } else {
            ECP_HELPER_MPI_NORM(&Np, grp->A, N_, grp->P);
        }
    }
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_A, NU_ECC_BIGNUM_MAXWORD));

    /* Configure ECC curve coefficient B */
    if (curve_type == MBEDTLS_ECP_TYPE_MONTGOMERY) {
        /*
         * In S/W impl, B is unused. Actually, B is 1 for Curve25519/Curve448.
         * https://github.com/ARMmbed/mbed-os/blob/2eb06e76208588afc6cb7580a8dd64c5429a10ce/connectivity/mbedtls/include/mbedtls/ecp.h#L221-L222
         */
        MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&N_, 1));
        Np = &N_;
    } else {
        ECP_HELPER_MPI_NORM(&Np, grp->B, N_, grp->P);
    }
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_B, NU_ECC_BIGNUM_MAXWORD));

    /* Configure ECC prime modulus */ 
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(&grp->P, (uint32_t *) CRPT->ECC_N, NU_ECC_BIGNUM_MAXWORD));

    /* Configure ECC scalar for point multiplication 
     *
     * Normalize m to within [1, order - 1] which ECCOP_POINT_MUL supports
     * Special cases R = 0 should have been detected out above.
     */
    if (eccop == ECCOP_POINT_MUL) {
        ECP_HELPER_MPI_NORM(&Np, *m, N_, grp->N);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_K, NU_ECC_BIGNUM_MAXWORD));
        /* To enable SCAP, must write order of G into X2 */
        if (blinding) {
            MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(&grp->N, (uint32_t *) CRPT->ECC_X2, NU_ECC_BIGNUM_MAXWORD));
        }
    }

    /* Configure ECC point (X1, Y1) */
    ECP_HELPER_MPI_NORM(&Np, P->X, N_, grp->P);
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_X1, NU_ECC_BIGNUM_MAXWORD));
    if (curve_type == MBEDTLS_ECP_TYPE_MONTGOMERY) {
        /* Deduce Y from X for Montgomery curve
         *
         * For Montgomery curve, y-coord is absent. Deduce it from x-coord.
         * https://github.com/ARMmbed/mbed-os/blob/2eb06e76208588afc6cb7580a8dd64c5429a10ce/connectivity/mbedtls/source/ecp_curves.c#L702-L706
         * https://github.com/ARMmbed/mbed-os/blob/2eb06e76208588afc6cb7580a8dd64c5429a10ce/connectivity/mbedtls/source/ecp_curves.c#L741-L745
         *
         * Per real test, y-coord is necessary for engine.
         */
        MBEDTLS_MPI_CHK(ecp_helper_deduce_y(grp, &N_, &P->X));
        Np = &N_;
    } else {
        ECP_HELPER_MPI_NORM(&Np, P->Y, N_, grp->P);
    }
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_Y1, NU_ECC_BIGNUM_MAXWORD));

    /* Configure ECC points (X2, Y2) */
    if (eccop == ECCOP_POINT_ADD) {
        ECP_HELPER_MPI_NORM(&Np, Q->X, N_, grp->P);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_X2, NU_ECC_BIGNUM_MAXWORD));
        if (curve_type == MBEDTLS_ECP_TYPE_MONTGOMERY) {
            /* Deduce Y from X (see above) */
            MBEDTLS_MPI_CHK(ecp_helper_deduce_y(grp, &N_, &Q->X));
            Np = &N_;
        } else {
            ECP_HELPER_MPI_NORM(&Np, Q->Y, N_, grp->P);
        }
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_Y2, NU_ECC_BIGNUM_MAXWORD));
    }

    /* Configure for point operation */
    uint32_t ecc_ctl = 0 |
        CRPT_ECC_CTL_START_Msk |                    // Start
        CRPT_ECC_CTL_FSEL_Msk |                     // Prime field (GF(p))
        eccop |                                     // Point operation
        curve_sel |                                 // Curve selection
        (grp->pbits << CRPT_ECC_CTL_CURVEM_Pos) |   // Key length of elliptic curve
        (blinding ? CRPT_ECC_CTL_SCAP_Msk : 0) |    // SCAP
        0;

    crypto_ecc_prestart();
#if defined(NU_CRYPTO_ECC_ENABLE_DEBUG) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
    mbedtls_printf("[CRPT][ECC] Crypto ECC ...\n");
#endif
    CRPT->ECC_CTL = ecc_ctl;
    ecc_done = crypto_ecc_wait2(1000*1000); // 1s timeout
#if defined(NU_CRYPTO_ECC_ENABLE_DEBUG) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
    mbedtls_printf("[CRPT][ECC] Crypto ECC ... %s\n", ecc_done ? "Done" : "Error");
#endif

    /* For safe, recover from current failure */
    if (!ecc_done) {
        crypto_ecc_abort(5*1000*1000);
    }

    /* Disable ECC interrupt */
    ECC_DISABLE_INT(CRPT);

    MBEDTLS_MPI_CHK(ecc_done ? 0 : MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED);

    /* (X1, Y1) hold the normalized result. */
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_read_eccreg(&R->X, (uint32_t *) CRPT->ECC_X1, NU_ECC_BIGNUM_MAXWORD));
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_read_eccreg(&R->Y, (uint32_t *) CRPT->ECC_Y1, NU_ECC_BIGNUM_MAXWORD));
    MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&R->Z, 1));

cleanup:

    mbedtls_mpi_free(&N_);

    /* Release ownership of ECC accelerator */
    crypto_ecc_release();

    return ret;
}

int crypto_ecc_run_modop(mbedtls_mpi *r,
                         const mbedtls_mpi *o1,
                         const mbedtls_mpi *o2,
                         const mbedtls_mpi *p,
                         uint32_t pbits,
                         uint32_t modop)
{
    if (r == NULL || 
        o1 == NULL ||
        o2 == NULL ||
        p == NULL) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Check o1/o2 are not negative */
    if (mbedtls_mpi_cmp_int(o1, 0) < 0 ||
        mbedtls_mpi_cmp_int(o2, 0) < 0) {
        return MBEDTLS_ERR_MPI_NEGATIVE_VALUE;
    }

    /* Check p is positive */
    if (mbedtls_mpi_cmp_int(p, 0) <= 0) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Check supported maximum key bits */
    if (pbits > NU_ECC_MAXKEYBITS) {
        return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
    }

    /* Check MODOP operations are legal */
    if (modop != MODOP_DIV &&
        modop != MODOP_MUL &&
        modop != MODOP_ADD &&
        modop != MODOP_SUB) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Check divisor is not zero in MODOP_DIV operation */
    if (modop == MODOP_DIV && mbedtls_mpi_cmp_int(o2, 0) == 0) {
        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
    }

    /* Acquire ownership of ECC accelerator */
    crypto_ecc_acquire();

    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    bool ecc_done;

    mbedtls_mpi N_;
    const mbedtls_mpi *Np;

    mbedtls_mpi_init(&N_);

    /* Enable ECC interrupt */
    ECC_ENABLE_INT(CRPT);

    /* For safe, recover from previous failure if ever */
    MBEDTLS_MPI_CHK(crypto_ecc_abort(5*1000*1000));

    /* Use ECP_HELPER_MPI_NORM(Np, N1, N_, P) to get normalized MPI
     *
     * N_: Holds normalized MPI if the passed-in MPI N1 is not
     * Np: Pointer to normalized MPI, which could be N1 or N_
     */

    if (modop == MODOP_MUL ||
        modop == MODOP_ADD ||
        modop == MODOP_SUB) {
        ECP_HELPER_MPI_NORM(&Np, *o1, N_, *p);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_X1, NU_ECC_BIGNUM_MAXWORD));
        ECP_HELPER_MPI_NORM(&Np, *o2, N_, *p);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_Y1, NU_ECC_BIGNUM_MAXWORD));
    } else if (modop == MODOP_DIV) {
        ECP_HELPER_MPI_NORM(&Np, *o2, N_, *p);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_X1, NU_ECC_BIGNUM_MAXWORD));
        ECP_HELPER_MPI_NORM(&Np, *o1, N_, *p);
        MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(Np, (uint32_t *) CRPT->ECC_Y1, NU_ECC_BIGNUM_MAXWORD));
    } else {
        MBEDTLS_MPI_CHK(MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
    }

    MBEDTLS_MPI_CHK(crypto_ecc_mpi_write_eccreg(p, (uint32_t *) CRPT->ECC_N, NU_ECC_BIGNUM_MAXWORD));

    /* Configure for modulus operation */
    uint32_t ecc_ctl = 0 |
        CRPT_ECC_CTL_START_Msk |                    // Start
        CRPT_ECC_CTL_FSEL_Msk |                     // Prime field (GF(p))
        ECCOP_MODULE |                              // No point operation
        modop |                                     // Modulus operation
        (pbits << CRPT_ECC_CTL_CURVEM_Pos) |        // Key length of elliptic curve
        0;

    crypto_ecc_prestart();
#if defined(NU_CRYPTO_ECC_ENABLE_DEBUG) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
    mbedtls_printf("[CRPT][ECC] Crypto Modulus ...\n");
#endif
    CRPT->ECC_CTL = ecc_ctl;
    ecc_done = crypto_ecc_wait2(1000*1000); // 1s timeout
#if defined(NU_CRYPTO_ECC_ENABLE_DEBUG) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
    mbedtls_printf("[CRPT][ECC] Crypto Modulus ... %s\n", ecc_done ? "Done" : "Fallback");
#endif

    /* For safe, recover from current failure */
    if (!ecc_done) {
        crypto_ecc_abort(5*1000*1000);
    }

    /* Disable ECC interrupt */
    ECC_DISABLE_INT(CRPT);

    MBEDTLS_MPI_CHK(ecc_done ? 0 : MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED);

    /* X1 holds the result. */
    MBEDTLS_MPI_CHK(crypto_ecc_mpi_read_eccreg(r, (uint32_t *) CRPT->ECC_X1, NU_ECC_BIGNUM_MAXWORD));

cleanup:

    mbedtls_mpi_free(&N_);

    /* Release ownership of ECC accelerator */
    crypto_ecc_release();

    return ret;
}

int crypto_ecc_mpi_read_eccreg(mbedtls_mpi *x, const volatile uint32_t *eccreg, size_t eccreg_num)
{
    if (x == NULL) {
        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
    }
    
    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    size_t i, n;

    for (n = eccreg_num; n > 0; n --) {
        if (eccreg[n - 1] != 0) {
            break;
        }
    }
    
    MBEDTLS_MPI_CHK(mbedtls_mpi_lset(x, 0));
    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(x, WORDS_TO_LIMBS(n)));

    for (i = 0; i < n; i ++) {
        x->p[i / wiL] |= ((mbedtls_mpi_uint) eccreg[i]) << ((i % wiL) << 5);
    }

cleanup:

    return ret;
}

int crypto_ecc_mpi_write_eccreg( const mbedtls_mpi *x, volatile uint32_t *eccreg, size_t eccreg_num )
{
    if (x == NULL) {
        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
    }
    
    if (mbedtls_mpi_cmp_int(x, 0) < 0) {
        return MBEDTLS_ERR_MPI_NEGATIVE_VALUE;
    }
    
    size_t i, n;

    /* How many words needed? */
    n = (mbedtls_mpi_size(x) + sizeof (uint32_t) - 1) / sizeof (uint32_t);

    if (eccreg_num < n) {
        return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL;
    }

    /* Fill non-zero part */
    for (i = 0; i < n; i ++) {
        eccreg[i] = (uint32_t) (x->p[i / wiL] >> ((i % wiL) << 5));
    }
    
    /* Zeroize remaining part
     *
     * crypto_zeroize32() has excluded optimization doubt, so we can safely set H/W registers to 0 via it.
     */
    crypto_zeroize32((uint32_t *) eccreg + n, eccreg_num - n);
    
    return 0;
}

int crypto_ecc_abort(uint32_t timeout_us)
{
    /* Acquire ownership of ECC H/W */
    crypto_ecc_acquire();

    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;

    CRPT->ECC_CTL = CRPT_ECC_CTL_STOP_Msk;
    struct nu_countdown_ctx_s cd_ctx;
    nu_countdown_init(&cd_ctx, timeout_us);
    while (CRPT->ECC_STS & CRPT_ECC_STS_BUSY_Msk) {
        if (nu_countdown_expired(&cd_ctx)) {
            break;
        }
    }
    nu_countdown_free(&cd_ctx);
    if (CRPT->ECC_STS & CRPT_ECC_STS_BUSY_Msk) {
#if defined(NU_CRYPTO_ECC_ENABLE_DEBUG) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
        mbedtls_printf("[CRPT][ECC] Crypto ECC ... Busy\n");
#endif
        ret = MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
        goto cleanup;
    }

    ret = 0;

cleanup:

    /* Release ownership of ECC accelerator */
    crypto_ecc_release();

    return ret;
}

#endif /* MBEDTLS_ECP_ALT || MBEDTLS_ECP_INTERNAL_ALT */
#endif /* MBEDTLS_ECP_C */