summaryrefslogtreecommitdiff
path: root/wolfcrypt/src/port/Espressif/esp32_mp.c
diff options
context:
space:
mode:
Diffstat (limited to 'wolfcrypt/src/port/Espressif/esp32_mp.c')
-rw-r--r--wolfcrypt/src/port/Espressif/esp32_mp.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/wolfcrypt/src/port/Espressif/esp32_mp.c b/wolfcrypt/src/port/Espressif/esp32_mp.c
new file mode 100644
index 0000000..2174089
--- /dev/null
+++ b/wolfcrypt/src/port/Espressif/esp32_mp.c
@@ -0,0 +1,514 @@
+/* esp32_mp.c
+ *
+ * Copyright (C) 2006-2020 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
+ */
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+#include <wolfssl/wolfcrypt/settings.h>
+
+#include "wolfssl/wolfcrypt/logging.h"
+
+#if !defined(NO_RSA) || defined(HAVE_ECC)
+
+#if defined(WOLFSSL_ESP32WROOM32_CRYPT_RSA_PRI) && \
+ !defined(NO_WOLFSSL_ESP32WROOM32_CRYPT_RSA_PRI)
+
+#ifdef NO_INLINE
+ #include <wolfssl/wolfcrypt/misc.h>
+#else
+ #define WOLFSSL_MISC_INCLUDED
+ #include <wolfcrypt/src/misc.c>
+#endif
+#include <wolfssl/wolfcrypt/tfm.h>
+
+static const char* const TAG = "wolfssl_mp";
+
+#define ESP_HW_RSAMAX_BIT 4096
+#define ESP_HW_MULTI_RSAMAX_BITS 2048
+#define ESP_HW_RSAMIN_BIT 512
+#define BYTE_TO_WORDS(s) (((s+3)>>2)) /* (s+(4-1))/ 4 */
+#define BITS_TO_WORDS(s) (((s+31)>>3)>>2) /* (s+(32-1))/ 8/ 4*/
+
+#define MP_NG -1
+
+/* mutex */
+static wolfSSL_Mutex mp_mutex;
+static int espmp_CryptHwMutexInit = 0;
+/*
+* check if the hw is ready before accessing it
+*/
+static int esp_mp_hw_wait_clean()
+{
+ int timeout = 0;
+ while(++timeout < ESP_RSA_TIMEOUT && DPORT_REG_READ(RSA_CLEAN_REG) != 1){}
+
+ if(timeout >= ESP_RSA_TIMEOUT) {
+ ESP_LOGE(TAG, "waiting hw ready is time-outed.");
+ return MP_NG;
+ }
+ return MP_OKAY;
+}
+/*
+* lock hw engine.
+* this should be called before using engine.
+*/
+static int esp_mp_hw_lock()
+{
+ int ret = 0;
+
+ if(espmp_CryptHwMutexInit == 0) {
+ ret = esp_CryptHwMutexInit(&mp_mutex);
+ if(ret == 0){
+ espmp_CryptHwMutexInit = 1;
+ } else {
+ ESP_LOGE(TAG, "mp mutx initialization failed.");
+ return MP_NG;
+ }
+ }
+ /* lock hardware */
+ ret = esp_CryptHwMutexLock(&mp_mutex, portMAX_DELAY);
+ if(ret != 0) {
+ ESP_LOGE(TAG, "mp engine lock failed.");
+ return MP_NG;
+ }
+ /* Enable RSA hardware */
+ periph_module_enable(PERIPH_RSA_MODULE);
+
+ return ret;
+}
+/*
+* Release hw engine
+*/
+static void esp_mp_hw_unlock( void )
+{
+ /* Disable RSA hardware */
+ periph_module_disable(PERIPH_RSA_MODULE);
+
+ /* unlock */
+ esp_CryptHwMutexUnLock(&mp_mutex);
+}
+/* this is based on an article by Cetin Kaya Koc, A New Algorithm for Inversion*/
+/* mod p^k, June 28 2017. */
+static int esp_calc_Mdash(mp_int *M, word32 k, mp_digit* md)
+{
+ int i;
+ int xi;
+ int b0 = 1;
+ int bi;
+ word32 N = 0;
+ word32 x;
+
+ N = M->dp[0];
+ bi = b0;
+ x = 0;
+
+ for(i = 0; i < k; i++) {
+ xi = bi % 2;
+ if(xi < 0){
+ xi *= -1;
+ }
+ bi = (bi - N * xi) / 2;
+ x |= (xi << i);
+ }
+ /* 2's complement */
+ *md = ~x + 1;
+ return MP_OKAY;
+}
+/* start hw process */
+static void process_start(word32 reg)
+{
+ /* clear interrupt */
+ DPORT_REG_WRITE(RSA_INTERRUPT_REG, 1);
+ /* start process */
+ DPORT_REG_WRITE(reg, 1);
+}
+/* wait until done */
+static int wait_uitil_done(word32 reg)
+{
+ int timeout = 0;
+ /* wait until done && not timeout */
+ while(1) {
+ if(++timeout < ESP_RSA_TIMEOUT && DPORT_REG_READ(reg) == 1){
+ break;
+ }
+ }
+
+ /* clear interrupt */
+ DPORT_REG_WRITE(RSA_INTERRUPT_REG, 1);
+
+ if(timeout >= ESP_RSA_TIMEOUT) {
+ ESP_LOGE(TAG, "rsa operation is time-outed.");
+ return MP_NG;
+ }
+
+ return MP_OKAY;
+}
+/* read data from memory into mp_init */
+static void esp_memblock_to_mpint(word32 mem_address, mp_int* mp, word32 numwords)
+{
+ esp_dport_access_read_buffer((uint32_t*)mp->dp, mem_address, numwords);
+ mp->used = numwords;
+}
+
+/* write mp_init into memory block */
+static void esp_mpint_to_memblock(word32 mem_address, const mp_int* mp,
+ const word32 bits,
+ const word32 hwords)
+{
+ word32 i;
+ word32 len = (bits / 8 + ((bits & 7) != 0 ? 1 : 0));
+
+ len = (len+sizeof(word32)-1)/sizeof(word32);
+
+ for(i=0;i < hwords; i++) {
+ if(i < len) {
+ DPORT_REG_WRITE(mem_address + (i * sizeof(word32)), mp->dp[i]);
+ } else {
+ DPORT_REG_WRITE(mem_address + (i * sizeof(word32)), 0);
+ }
+ }
+}
+/* return needed hw words. */
+/* supported words length */
+/* words : {16 , 32, 48, 64, 80, 96, 112, 128} */
+/* bits : {512,1024, 1536, 2048, 2560, 3072, 3584, 4096} */
+static word32 words2hwords(word32 wd)
+{
+ const word32 shit_ = 4;
+
+ return (((wd + 0xf)>>shit_)<<shit_);
+}
+/* count the number of words is needed for bits */
+static word32 bits2words(word32 bits)
+{
+ /* 32 bits */
+ const word32 d = sizeof(word32) * WOLFSSL_BIT_SIZE;
+
+ return((bits + (d - 1))/d);
+}
+/* get rinv */
+static int esp_get_rinv(mp_int *rinv, mp_int *M, word32 exp)
+{
+ int ret = 0;
+
+ /* 2^(exp)*/
+ if((ret = mp_2expt(rinv, exp)) != MP_OKAY) {
+ ESP_LOGE(TAG, "failed to calculate mp_2expt()");
+ return ret;
+ }
+
+ /* r_inv = R^2 mod M(=P) */
+ if(ret == 0 && (ret = mp_mod(rinv, M, rinv)) != MP_OKAY){
+ ESP_LOGE(TAG, "failed to calculate mp_mod()");
+ return ret;
+ }
+
+ return ret;
+}
+/* Z = X * Y; */
+int esp_mp_mul(fp_int* X, fp_int* Y, fp_int* Z)
+{
+ int ret = 0;
+ int neg = (X->sign == Y->sign)? MP_ZPOS : MP_NEG;
+
+ word32 Xs;
+ word32 Ys;
+ word32 Zs;
+ word32 maxWords_sz;
+ word32 hwWords_sz;
+
+ /* ask bits number */
+ Xs = mp_count_bits(X);
+ Ys = mp_count_bits(Y);
+ Zs = Xs + Ys;
+
+ /* maximum bits and words for writing to hw */
+ maxWords_sz = bits2words(max(Xs, Ys));
+ hwWords_sz = words2hwords(maxWords_sz);
+
+ /* sanity check */
+ if((hwWords_sz<<5) > ESP_HW_MULTI_RSAMAX_BITS) {
+ ESP_LOGW(TAG, "exceeds max bit length(2048)");
+ return -2;
+ }
+
+ /*Steps to use hw in the following order:
+ * 1. wait until clean hw engine
+ * 2. Write(2*N/512bits - 1 + 8) to MULT_MODE_REG
+ * 3. Write X and Y to memory blocks
+ * need to write data to each memory block only according to the length
+ * of the number.
+ * 4. Write 1 to MUL_START_REG
+ * 5. Wait for the first operation to be done. Poll INTERRUPT_REG until it reads 1.
+ * (Or until the INTER interrupt is generated.)
+ * 6. Write 1 to RSA_INTERRUPT_REG to clear the interrupt.
+ * 7. Read the Z from RSA_Z_MEM
+ * 8. Write 1 to RSA_INTERUPT_REG to clear the interrupt.
+ * 9. Release the hw engine
+ */
+ /* lock hw for use */
+ if((ret = esp_mp_hw_lock()) != MP_OKAY)
+ return ret;
+
+ if((ret = esp_mp_hw_wait_clean()) != MP_OKAY){
+ return ret;
+ }
+
+ /* step.1 (2*N/512) => N/256. 512 bits => 16 words */
+ DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hwWords_sz >> 3) - 1 + 8);
+ /* step.2 write X, M and r_inv into memory */
+ esp_mpint_to_memblock(RSA_MEM_X_BLOCK_BASE, X, Xs, hwWords_sz);
+ /* Y(let-extend) */
+ esp_mpint_to_memblock(RSA_MEM_Z_BLOCK_BASE + (hwWords_sz<<2), Y, Ys, hwWords_sz);
+ /* step.3 start process */
+ process_start(RSA_MULT_START_REG);
+
+ /* step.4,5 wait until done */
+ wait_uitil_done(RSA_INTERRUPT_REG);
+ /* step.6 read the result form MEM_Z */
+ esp_memblock_to_mpint(RSA_MEM_Z_BLOCK_BASE, Z, BITS_TO_WORDS(Zs));
+
+ /* step.7 clear and release hw */
+ esp_mp_hw_unlock();
+
+ Z->sign = (Z->used > 0)? neg : MP_ZPOS;
+
+ return ret;
+}
+/* Z = X * Y (mod M) */
+int esp_mp_mulmod(fp_int* X, fp_int* Y, fp_int* M, fp_int* Z)
+{
+ int ret = 0;
+ int negcheck = 0;
+ word32 Xs;
+ word32 Ys;
+ word32 Ms;
+ word32 maxWords_sz;
+ word32 hwWords_sz;
+ word32 zwords;
+
+ mp_int r_inv;
+ mp_int tmpZ;
+ mp_digit mp;
+
+ /* neg check */
+ if(X->sign != Y->sign) {
+ /* X*Y becomes negative */
+ negcheck = 1;
+ }
+ /* ask bits number */
+ Xs = mp_count_bits(X);
+ Ys = mp_count_bits(Y);
+ Ms = mp_count_bits(M);
+
+ /* maximum bits and words for writing to hw */
+ maxWords_sz = bits2words(max(Xs, max(Ys, Ms)));
+ zwords = bits2words(min(Ms, Xs + Ys));
+ hwWords_sz = words2hwords(maxWords_sz);
+
+ if((hwWords_sz<<5) > ESP_HW_RSAMAX_BIT) {
+ ESP_LOGE(TAG, "exceeds hw maximum bits");
+ return -2;
+ }
+ /* calculate r_inv = R^2 mode M
+ * where: R = b^n, and b = 2^32
+ * accordingly R^2 = 2^(n*32*2)
+ */
+ ret = mp_init_multi(&tmpZ, &r_inv, NULL, NULL, NULL, NULL);
+ if(ret == 0 && (ret = esp_get_rinv(&r_inv, M, (hwWords_sz<<6))) != MP_OKAY) {
+ ESP_LOGE(TAG, "calculate r_inv failed.");
+ mp_clear(&tmpZ);
+ mp_clear(&r_inv);
+ return ret;
+ }
+ /* lock hw for use */
+ if((ret = esp_mp_hw_lock()) != MP_OKAY){
+ mp_clear(&tmpZ);
+ mp_clear(&r_inv);
+ return ret;
+ }
+ /* Calculate M' */
+ if((ret = esp_calc_Mdash(M, 32/* bits */, &mp)) != MP_OKAY) {
+ ESP_LOGE(TAG, "failed to calculate M dash");
+ mp_clear(&tmpZ);
+ mp_clear(&r_inv);
+ return -1;
+ }
+ /*Steps to use hw in the following order:
+ * 1. wait until clean hw engine
+ * 2. Write(N/512bits - 1) to MULT_MODE_REG
+ * 3. Write X,M(=G, X, P) to memory blocks
+ * need to write data to each memory block only according to the length
+ * of the number.
+ * 4. Write M' to M_PRIME_REG
+ * 5. Write 1 to MODEXP_START_REG
+ * 6. Wait for the first operation to be done. Poll INTERRUPT_REG until it reads 1.
+ * (Or until the INTER interrupt is generated.)
+ * 7. Write 1 to RSA_INTERRUPT_REG to clear the interrupt.
+ * 8. Write Y to RSA_X_MEM
+ * 9. Write 1 to RSA_MULT_START_REG
+ * 10. Wait for the second operation to be completed. Poll INTERRUPT_REG until it reads 1.
+ * 11. Read the Z from RSA_Z_MEM
+ * 12. Write 1 to RSA_INTERUPT_REG to clear the interrupt.
+ * 13. Release the hw engine
+ */
+
+ if((ret = esp_mp_hw_wait_clean()) != MP_OKAY){
+ return ret;
+ }
+ /* step.1 512 bits => 16 words */
+ DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hwWords_sz >> 4) - 1);
+
+ /* step.2 write X, M and r_inv into memory */
+ esp_mpint_to_memblock(RSA_MEM_X_BLOCK_BASE, X, Xs, hwWords_sz);
+ esp_mpint_to_memblock(RSA_MEM_M_BLOCK_BASE, M, Ms, hwWords_sz);
+ esp_mpint_to_memblock(RSA_MEM_Z_BLOCK_BASE, &r_inv, mp_count_bits(&r_inv),
+ hwWords_sz);
+ /* step.3 write M' into memory */
+ DPORT_REG_WRITE(RSA_M_DASH_REG, mp);
+ /* step.4 start process */
+ process_start(RSA_MULT_START_REG);
+
+ /* step.5,6 wait until done */
+ wait_uitil_done(RSA_INTERRUPT_REG);
+ /* step.7 Y to MEM_X */
+ esp_mpint_to_memblock(RSA_MEM_X_BLOCK_BASE, Y, Ys, hwWords_sz);
+
+ /* step.8 start process */
+ process_start(RSA_MULT_START_REG);
+
+ /* step.9,11 wait until done */
+ wait_uitil_done(RSA_INTERRUPT_REG);
+
+ /* step.12 read the result from MEM_Z */
+ esp_memblock_to_mpint(RSA_MEM_Z_BLOCK_BASE, &tmpZ, zwords);
+
+ /* step.13 clear and release hw */
+ esp_mp_hw_unlock();
+
+ /* additional steps */
+ /* this needs for known issue when Z is greater than M */
+ if(mp_cmp(&tmpZ, M)==FP_GT) {
+ /* Z -= M */
+ mp_sub(&tmpZ, M, &tmpZ);
+ }
+ if(negcheck) {
+ mp_sub(M, &tmpZ, &tmpZ);
+ }
+
+ mp_copy(&tmpZ, Z);
+
+ mp_clear(&tmpZ);
+ mp_clear(&r_inv);
+
+ return ret;
+}
+/* Z = X^Y mod M */
+int esp_mp_exptmod(fp_int* X, fp_int* Y, word32 Ys, fp_int* M, fp_int* Z)
+{
+ int ret = 0;
+
+ word32 Xs;
+ word32 Ms;
+ word32 maxWords_sz;
+ word32 hwWords_sz;
+
+ mp_int r_inv;
+ mp_digit mp;
+
+ /* ask bits number */
+ Xs = mp_count_bits(X);
+ Ms = mp_count_bits(M);
+ /* maximum bits and words for writing to hw */
+ maxWords_sz = bits2words(max(Xs, max(Ys, Ms)));
+ hwWords_sz = words2hwords(maxWords_sz);
+
+ if((hwWords_sz<<5) > ESP_HW_RSAMAX_BIT) {
+ ESP_LOGE(TAG, "exceeds hw maximum bits");
+ return -2;
+ }
+ /* calculate r_inv = R^2 mode M
+ * where: R = b^n, and b = 2^32
+ * accordingly R^2 = 2^(n*32*2)
+ */
+ ret = mp_init(&r_inv);
+ if(ret == 0 && (ret = esp_get_rinv(&r_inv, M, (hwWords_sz<<6))) != MP_OKAY) {
+ ESP_LOGE(TAG, "calculate r_inv failed.");
+ mp_clear(&r_inv);
+ return ret;
+ }
+ /* lock and init the hw */
+ if((ret = esp_mp_hw_lock()) != MP_OKAY) {
+ mp_clear(&r_inv);
+ return ret;
+ }
+ /* calc M' */
+ /* if Pm is odd, uses mp_montgomery_setup() */
+ if((ret = esp_calc_Mdash(M, 32/* bits */, &mp)) != MP_OKAY) {
+ ESP_LOGE(TAG, "failed to calculate M dash");
+ mp_clear(&r_inv);
+ return -1;
+ }
+
+ /*Steps to use hw in the following order:
+ * 1. Write(N/512bits - 1) to MODEXP_MODE_REG
+ * 2. Write X, Y, M and r_inv to memory blocks
+ * need to write data to each memory block only according to the length
+ * of the number.
+ * 3. Write M' to M_PRIME_REG
+ * 4. Write 1 to MODEXP_START_REG
+ * 5. Wait for the operation to be done. Poll INTERRUPT_REG until it reads 1.
+ * (Or until the INTER interrupt is generated.)
+ * 6. Read the result Z(=Y) from Z_MEM
+ * 7. Write 1 to INTERRUPT_REG to clear the interrupt.
+ */
+ if((ret = esp_mp_hw_wait_clean()) != MP_OKAY){
+ return ret;
+ }
+
+ /* step.1 */
+ DPORT_REG_WRITE(RSA_MODEXP_MODE_REG, (hwWords_sz >> 4) - 1);
+ /* step.2 write G, X, P, r_inv and M' into memory */
+ esp_mpint_to_memblock(RSA_MEM_X_BLOCK_BASE, X, Xs, hwWords_sz);
+ esp_mpint_to_memblock(RSA_MEM_Y_BLOCK_BASE, Y, Ys, hwWords_sz);
+ esp_mpint_to_memblock(RSA_MEM_M_BLOCK_BASE, M, Ms, hwWords_sz);
+ esp_mpint_to_memblock(RSA_MEM_Z_BLOCK_BASE, &r_inv, mp_count_bits(&r_inv),
+ hwWords_sz);
+ /* step.3 write M' into memory */
+ DPORT_REG_WRITE(RSA_M_DASH_REG, mp);
+ /* step.4 start process */
+ process_start(RSA_START_MODEXP_REG);
+
+ /* step.5 wait until done */
+ wait_uitil_done(RSA_INTERRUPT_REG);
+ /* step.6 read a result form memory */
+ esp_memblock_to_mpint(RSA_MEM_Z_BLOCK_BASE, Z, BITS_TO_WORDS(Ms));
+ /* step.7 clear and release hw */
+ esp_mp_hw_unlock();
+
+ mp_clear(&r_inv);
+
+ return ret;
+}
+#endif /* !NO_RSA || HAVE_ECC */
+#endif /* (WOLFSS_ESP32WROOM32_CRYPT) && (NO_WOLFSSL_ESP32WROOM32_CRYPT_RES_PRI)*/