new file mode 100644
@@ -0,0 +1,425 @@
+From 2741f67d1cd56887991fc09d6dccc9b25b3ed79b Mon Sep 17 00:00:00 2001
+From: Sean Parkinson <sean@wolfssl.com>
+Date: Tue, 3 Mar 2026 23:18:52 +1000
+Subject: [PATCH] RISC-V 32 no mul SP C: implement multiplication
+
+No multiplication instructions when M extension not included.
+Standard implementation of __muldi3 is not constant time.
+Include a constant time implementation when SP_NO_MUL_INSTRUCTION is
+defined
+Define it when compiling for RISC-V 32 and no multiplication extension.
+
+Also fix get_entry in SP C implementation to do constant time
+comparison.
+
+(cherry picked from commit 71226b68b69404206c74694715f11bb6630750dc)
+
+CVE: CVE-2026-3580
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/71226b68b69404206c74694715f11bb6630750dc]
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ .wolfssl_known_macro_extras | 1 +
+ wolfcrypt/src/sp_arm32.c | 24 +++++++---
+ wolfcrypt/src/sp_armthumb.c | 24 +++++++---
+ wolfcrypt/src/sp_c32.c | 77 +++++++++++++++++++++++++++++++--
+ wolfcrypt/src/sp_c64.c | 12 +++--
+ wolfcrypt/src/sp_cortexm.c | 24 +++++++---
+ wolfcrypt/src/sp_x86_64_asm.asm | 2 +-
+ wolfssl/wolfcrypt/sp.h | 4 ++
+ 8 files changed, 143 insertions(+), 25 deletions(-)
+
+diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras
+index 3e728fa5f..301b1a211 100644
+--- a/.wolfssl_known_macro_extras
++++ b/.wolfssl_known_macro_extras
+@@ -1000,6 +1000,7 @@ __must_check
+ __ppc64__
+ __ppc__
+ __riscv
++__riscv_mul
+ __riscv_xlen
+ __s390x__
+ __sparc
+diff --git a/wolfcrypt/src/sp_arm32.c b/wolfcrypt/src/sp_arm32.c
+index a70eb35eb..ed8cd0296 100644
+--- a/wolfcrypt/src/sp_arm32.c
++++ b/wolfcrypt/src/sp_arm32.c
+@@ -75850,7 +75850,9 @@ static void sp_256_get_entry_16_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -76271,7 +76273,9 @@ static void sp_256_get_entry_256_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -93989,7 +93993,9 @@ static void sp_384_get_entry_16_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -94426,7 +94432,9 @@ static void sp_384_get_entry_256_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -121504,7 +121512,9 @@ static void sp_521_get_entry_16_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -121961,7 +121971,9 @@ static void sp_521_get_entry_256_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+diff --git a/wolfcrypt/src/sp_armthumb.c b/wolfcrypt/src/sp_armthumb.c
+index 4868f7f93..0f112aaef 100644
+--- a/wolfcrypt/src/sp_armthumb.c
++++ b/wolfcrypt/src/sp_armthumb.c
+@@ -101420,7 +101420,9 @@ static void sp_256_get_entry_16_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -101841,7 +101843,9 @@ static void sp_256_get_entry_256_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -112269,7 +112273,9 @@ static void sp_384_get_entry_16_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -112706,7 +112712,9 @@ static void sp_384_get_entry_256_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -125892,7 +125900,9 @@ static void sp_521_get_entry_16_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -126349,7 +126359,9 @@ static void sp_521_get_entry_256_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+diff --git a/wolfcrypt/src/sp_c32.c b/wolfcrypt/src/sp_c32.c
+index 10d646a81..13e3abe00 100644
+--- a/wolfcrypt/src/sp_c32.c
++++ b/wolfcrypt/src/sp_c32.c
+@@ -63,6 +63,71 @@
+
+ #ifndef WOLFSSL_SP_ASM
+ #if SP_WORD_SIZE == 32
++#ifdef SP_NO_MUL_INSTRUCTION
++sp_uint64 __muldi3(sp_uint64 a, sp_uint64 b);
++sp_uint64 __muldi3(sp_uint64 a, sp_uint64 b)
++{
++ sp_uint64 r;
++ sp_uint64 am[16];
++
++ /* if b is negative, convert it to positive and negate a. */
++ r = 0 - (b >> 63);
++ a = a ^ r;
++ b = b ^ r;
++ a -= r;
++ b -= r;
++
++#if defined(WOLFSSL_SP_SMALL)
++ int i;
++
++ am[0] = 0;
++ for (i = 1; i < 16; i++) {
++ am[i] = am[i-1] + a;
++ }
++
++ r = am[(b >> 28) & 0xf];
++ for (i = 24; i >= 0; i -= 4) {
++ r <<= 4;
++ r += am[(b >> i) & 0xf];
++ }
++#else
++ am[ 0] = 0;
++ am[ 1] = a;
++ am[ 2] = a << 1;
++ am[ 3] = am[ 2] + a;
++ am[ 4] = a << 2;
++ am[ 5] = am[ 4] + a;
++ am[ 6] = am[ 5] + a;
++ am[ 7] = am[ 6] + a;
++ am[ 8] = a << 3;
++ am[ 9] = am[ 8] + a;
++ am[10] = am[ 9] + a;
++ am[11] = am[10] + a;
++ am[12] = am[11] + a;
++ am[13] = am[12] + a;
++ am[14] = am[13] + a;
++ am[15] = am[14] + a;
++
++ r = am[(b >> 28) & 0xf];
++ r <<= 4;
++ r += am[(b >> 24) & 0xf];
++ r <<= 4;
++ r += am[(b >> 20) & 0xf];
++ r <<= 4;
++ r += am[(b >> 16) & 0xf];
++ r <<= 4;
++ r += am[(b >> 12) & 0xf];
++ r <<= 4;
++ r += am[(b >> 8) & 0xf];
++ r <<= 4;
++ r += am[(b >> 4) & 0xf];
++ r <<= 4;
++ r += am[(b >> 0) & 0xf];
++#endif
++
++ return r;
++}
++#endif /* SP_NO_MUL_INSTRUCTION */
+ #define SP_PRINT_NUM(var, name, total, words, bits) \
+ do { \
+ int ii; \
+@@ -22891,7 +22956,9 @@ static void sp_256_get_entry_256_9(sp_point_256* r,
+ r->y[7] = 0;
+ r->y[8] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -30408,7 +30475,9 @@ static void sp_384_get_entry_256_15(sp_point_384* r,
+ r->y[13] = 0;
+ r->y[14] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -37975,7 +38044,9 @@ static void sp_521_get_entry_256_21(sp_point_521* r,
+ r->y[19] = 0;
+ r->y[20] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+diff --git a/wolfcrypt/src/sp_c64.c b/wolfcrypt/src/sp_c64.c
+index 06dc0bd69..66397f64f 100644
+--- a/wolfcrypt/src/sp_c64.c
++++ b/wolfcrypt/src/sp_c64.c
+@@ -23795,7 +23795,9 @@ static void sp_256_get_entry_256_5(sp_point_256* r,
+ r->y[3] = 0;
+ r->y[4] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint64)i - (sp_uint64)idx) >> 63) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint64)idx - (sp_uint64)i) >> 63) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -30747,7 +30749,9 @@ static void sp_384_get_entry_256_7(sp_point_384* r,
+ r->y[5] = 0;
+ r->y[6] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint64)i - (sp_uint64)idx) >> 63) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint64)idx - (sp_uint64)i) >> 63) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -38160,7 +38164,9 @@ static void sp_521_get_entry_256_9(sp_point_521* r,
+ r->y[7] = 0;
+ r->y[8] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint64)i - (sp_uint64)idx) >> 63) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint64)idx - (sp_uint64)i) >> 63) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+diff --git a/wolfcrypt/src/sp_cortexm.c b/wolfcrypt/src/sp_cortexm.c
+index fc756ffbe..d4af12332 100644
+--- a/wolfcrypt/src/sp_cortexm.c
++++ b/wolfcrypt/src/sp_cortexm.c
+@@ -37267,7 +37267,9 @@ static void sp_256_get_entry_16_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -37688,7 +37690,9 @@ static void sp_256_get_entry_256_8(sp_point_256* r,
+ r->y[6] = 0;
+ r->y[7] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -47354,7 +47358,9 @@ static void sp_384_get_entry_16_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -47791,7 +47797,9 @@ static void sp_384_get_entry_256_12(sp_point_384* r,
+ r->y[10] = 0;
+ r->y[11] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -59584,7 +59592,9 @@ static void sp_521_get_entry_16_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 16; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+@@ -60041,7 +60051,9 @@ static void sp_521_get_entry_256_17(sp_point_521* r,
+ r->y[15] = 0;
+ r->y[16] = 0;
+ for (i = 1; i < 256; i++) {
+- mask = (sp_digit)0 - (i == idx);
++ sp_digit gte = (sp_digit)((((sp_uint32)i - (sp_uint32)idx) >> 31) - 1);
++ sp_digit lte = (sp_digit)((((sp_uint32)idx - (sp_uint32)i) >> 31) - 1);
++ mask = gte & lte;
+ r->x[0] |= mask & table[i].x[0];
+ r->x[1] |= mask & table[i].x[1];
+ r->x[2] |= mask & table[i].x[2];
+diff --git a/wolfcrypt/src/sp_x86_64_asm.asm b/wolfcrypt/src/sp_x86_64_asm.asm
+index 4df93a976..30bdc8add 100644
+--- a/wolfcrypt/src/sp_x86_64_asm.asm
++++ b/wolfcrypt/src/sp_x86_64_asm.asm
+@@ -1,6 +1,6 @@
+ ; /* sp_x86_64_asm.asm */
+ ; /*
+-; * Copyright (C) 2006-2025 wolfSSL Inc.
++; * Copyright (C) 2006-2026 wolfSSL Inc.
+ ; *
+ ; * This file is part of wolfSSL.
+ ; *
+diff --git a/wolfssl/wolfcrypt/sp.h b/wolfssl/wolfcrypt/sp.h
+index 9e7a9c945..fcfd2dd8e 100644
+--- a/wolfssl/wolfcrypt/sp.h
++++ b/wolfssl/wolfcrypt/sp.h
+@@ -26,6 +26,10 @@
+ #include <wolfssl/wolfcrypt/types.h>
+ #include <wolfssl/wolfcrypt/settings.h>
+
++#if defined(__riscv) && (__riscv_xlen == 32) && !defined(__riscv_mul)
++ #define SP_NO_MUL_INSTRUCTION
++#endif
++
+ #if defined(WOLFSSL_HAVE_SP_RSA) || defined(WOLFSSL_HAVE_SP_DH) || \
+ defined(WOLFSSL_HAVE_SP_ECC)
+ #ifdef _WIN32_WCE
@@ -28,6 +28,7 @@ SRC_URI = " \
file://CVE-2025-7394-5.patch \
file://CVE-2025-7394-6.patch \
file://CVE-2026-1005.patch \
+ file://CVE-2026-3580.patch \
"
SRCREV = "b077c81eb635392e694ccedbab8b644297ec0285"