/** * @file ecc_math.c * @version $Format:%h%d$ * * Elliptic curve mathematical operations for Matrix Crypto. */ /* * Copyright (c) 2013-2018 Rambus Inc. * Copyright (c) PeerSec Networks, 2002-2011 * All Rights Reserved * * The latest version of this code is available at http://www.matrixssl.org * * This software is open source; 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. * * This General Public License does NOT permit incorporating this software * into proprietary programs. If you are unable to comply with the GPL, a * commercial license for this software may be purchased from Rambus at * http://www.rambus.com/ * * This program is distributed in 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * http://www.gnu.org/copyleft/gpl.html */ /******************************************************************************/ #include "../cryptoImpl.h" #ifdef USE_MATRIX_ECC /******************************************************************************/ psEccPoint_t *eccNewPoint(psPool_t *pool, short size); void eccFreePoint(psEccPoint_t *p); int32_t eccMulmod(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G, psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int); int32_t eccProjectiveAddPoint(psPool_t *pool, const psEccPoint_t *P, const psEccPoint_t *Q, psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp, pstm_int *tmp_int); int32_t eccMap(psPool_t *pool, psEccPoint_t *P, const pstm_int *modulus, const pstm_digit *mp); static int32_t eccProjectiveDblPoint(psPool_t *pool, const psEccPoint_t *P, psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp, const pstm_int *A); /******************************************************************************/ static uint8_t get_digit_count(const pstm_int *a) { return a->used; } static pstm_digit get_digit(const pstm_int *a, uint8_t n) { return (n >= a->used) ? (pstm_digit) 0 : a->dp[n]; } # ifdef USE_CONSTANT_TIME_ECC_MULMOD /******************************************************************************/ /** Perform a point multiplication in a timing-resistant manner. @param[in] pool Memory pool @param[in] k The scalar to multiply by @param[in] G The base point @param[out] R Destination for kG @param modulus The modulus of the field the ECC curve is in @param map Boolean whether to map back to affine or not (1==map) @param[in,out] tmp_int Temporary scratch big integer (memory optimization) @return PS_SUCCESS on success, < 0 on error */ int32_t eccMulmodCt(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G, psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int) { psEccPoint_t *tG, *M[3]; int32 i, j, err; pstm_int mu; pstm_digit mp; unsigned long buf; int32 bitcnt, mode, digidx; /* init montgomery reduction */ err = pstm_montgomery_setup(modulus, &mp); if (err != PS_SUCCESS) { return err; } err = pstm_init_size(pool, &mu, modulus->alloc); if (err != PS_SUCCESS) { return err; } err = pstm_montgomery_calc_normalization(&mu, modulus); if (err != PS_SUCCESS) { pstm_clear(&mu); return err; } /* alloc ram for window temps */ for (i = 0; i < 3; i++) { M[i] = eccNewPoint(pool, (G->x.used * 2) + 1); if (M[i] == NULL) { for (j = 0; j < i; j++) { eccFreePoint(M[j]); } pstm_clear(&mu); return PS_MEM_FAIL; } } /* make a copy of G incase R==G */ tG = eccNewPoint(pool, G->x.alloc); if (tG == NULL) { err = PS_MEM_FAIL; goto done; } /* tG = G and convert to montgomery */ if (pstm_cmp_d(&mu, 1) == PSTM_EQ) { if ((err = pstm_copy(&G->x, &tG->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&G->y, &tG->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&G->z, &tG->z)) != PS_SUCCESS) { goto done; } } else { if ((err = pstm_mulmod(pool, &G->x, &mu, modulus, &tG->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_mulmod(pool, &G->y, &mu, modulus, &tG->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_mulmod(pool, &G->z, &mu, modulus, &tG->z)) != PS_SUCCESS) { goto done; } } pstm_clear(&mu); /* M[0] = tG = P. */ err = pstm_copy(&tG->x, &M[0]->x); if (err != PS_SUCCESS) { goto done; } err = pstm_copy(&tG->y, &M[0]->y); if (err != PS_SUCCESS) { goto done; } err = pstm_copy(&tG->z, &M[0]->z); if (err != PS_SUCCESS) { goto done; } /* M[1] = 2P. */ err = eccProjectiveDblPoint(pool, tG, M[1], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } /* setup sliding window */ mode = 0; bitcnt = 1; buf = 0; digidx = get_digit_count(k) - 1; /* perform ops */ for (;; ) { /* grab next digit as required */ if (--bitcnt == 0) { if (digidx == -1) { break; } buf = get_digit(k, digidx); bitcnt = DIGIT_BIT; --digidx; } /* grab the next msb from the ltiplicand */ i = (buf >> (DIGIT_BIT - 1)) & 1; buf <<= 1; if (mode == 0 && i == 0) { /* Dummy operations. */ err = eccProjectiveAddPoint(pool, M[0], M[1], M[2], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } err = eccProjectiveDblPoint(pool, M[1], M[2], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } continue; } if (mode == 0 && i == 1) { mode = 1; /* Dummy operations. */ err = eccProjectiveAddPoint(pool, M[0], M[1], M[2], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } err = eccProjectiveDblPoint(pool, M[1], M[2], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } continue; } /* M[i^1] = M[0] + M[1]. */ err = eccProjectiveAddPoint(pool, M[0], M[1], M[i^1], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } /* M[i] = 2M[i] */ err = eccProjectiveDblPoint(pool, M[i], M[i], modulus, &mp, tmp_int); if (err != PS_SUCCESS) { goto done; } } err = pstm_copy(&M[0]->x, &R->x); if (err != PS_SUCCESS) { goto done; } err = pstm_copy(&M[0]->y, &R->y); if (err != PS_SUCCESS) { goto done; } err = pstm_copy(&M[0]->z, &R->z); if (err != PS_SUCCESS) { goto done; } /* map R back from projective space */ if (map) { err = eccMap(pool, R, modulus, &mp); } else { err = PS_SUCCESS; } done: pstm_clear(&mu); eccFreePoint(tG); for (i = 0; i < 3; i++) { eccFreePoint(M[i]); } return err; } # endif /* USE_CONSTANT_TIME_ECC_MULMOD */ # ifndef USE_CONSTANT_TIME_ECC_MULMOD /******************************************************************************/ /** Perform a point multiplication @param[in] pool Memory pool @param[in] k The scalar to multiply by @param[in] G The base point @param[out] R Destination for kG @param modulus The modulus of the field the ECC curve is in @param map Boolean whether to map back to affine or not (1==map) @param[in,out] tmp_int Temporary scratch big integer (memory optimization) @return PS_SUCCESS on success, < 0 on error */ /* size of sliding window, don't change this! */ # define ECC_MULMOD_WINSIZE 4 int32_t eccMulmodOld(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G, psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int) { psEccPoint_t *tG, *M[8]; /* @note large on stack */ int32 i, j, err; pstm_int mu; pstm_digit mp; unsigned long buf; int32 first, bitbuf, bitcpy, bitcnt, mode, digidx; /* init montgomery reduction */ if ((err = pstm_montgomery_setup(modulus, &mp)) != PS_SUCCESS) { return err; } if ((err = pstm_init_size(pool, &mu, modulus->alloc)) != PS_SUCCESS) { return err; } if ((err = pstm_montgomery_calc_normalization(&mu, modulus)) != PS_SUCCESS) { pstm_clear(&mu); return err; } /* alloc ram for window temps */ for (i = 0; i < 8; i++) { M[i] = eccNewPoint(pool, (G->x.used * 2) + 1); if (M[i] == NULL) { for (j = 0; j < i; j++) { eccFreePoint(M[j]); } pstm_clear(&mu); return PS_MEM_FAIL; } } /* make a copy of G incase R==G */ tG = eccNewPoint(pool, G->x.alloc); if (tG == NULL) { err = PS_MEM_FAIL; goto done; } /* tG = G and convert to montgomery */ if (pstm_cmp_d(&mu, 1) == PSTM_EQ) { if ((err = pstm_copy(&G->x, &tG->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&G->y, &tG->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&G->z, &tG->z)) != PS_SUCCESS) { goto done; } } else { if ((err = pstm_mulmod(pool, &G->x, &mu, modulus, &tG->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_mulmod(pool, &G->y, &mu, modulus, &tG->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_mulmod(pool, &G->z, &mu, modulus, &tG->z)) != PS_SUCCESS) { goto done; } } pstm_clear(&mu); /* calc the M tab, which holds kG for k==8..15 */ /* M[0] == 8G */ if ((err = eccProjectiveDblPoint(pool, tG, M[0], modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } if ((err = eccProjectiveDblPoint(pool, M[0], M[0], modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } if ((err = eccProjectiveDblPoint(pool, M[0], M[0], modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } /* now find (8+k)G for k=1..7 */ for (j = 9; j < 16; j++) { if ((err = eccProjectiveAddPoint(pool, M[j - 9], tG, M[j - 8], modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } } /* setup sliding window */ mode = 0; bitcnt = 1; buf = 0; digidx = get_digit_count(k) - 1; bitcpy = bitbuf = 0; first = 1; /* perform ops */ for (;; ) { /* grab next digit as required */ if (--bitcnt == 0) { if (digidx == -1) { break; } buf = get_digit(k, digidx); bitcnt = DIGIT_BIT; --digidx; } /* grab the next msb from the ltiplicand */ i = (buf >> (DIGIT_BIT - 1)) & 1; buf <<= 1; /* skip leading zero bits */ if (mode == 0 && i == 0) { continue; } /* if the bit is zero and mode == 1 then we double */ if (mode == 1 && i == 0) { if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } continue; } /* else we add it to the window */ bitbuf |= (i << (ECC_MULMOD_WINSIZE - ++bitcpy)); mode = 2; if (bitcpy == ECC_MULMOD_WINSIZE) { /* if this is the first window we do a simple copy */ if (first == 1) { /* R = kG [k = first window] */ if ((err = pstm_copy(&M[bitbuf - 8]->x, &R->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&M[bitbuf - 8]->y, &R->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&M[bitbuf - 8]->z, &R->z)) != PS_SUCCESS) { goto done; } first = 0; } else { /* normal window */ /* ok window is filled so double as required and add */ /* double first */ for (j = 0; j < ECC_MULMOD_WINSIZE; j++) { if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } } /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */ if ((err = eccProjectiveAddPoint(pool, R, M[bitbuf - 8], R, modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } } /* empty window and reset */ bitcpy = bitbuf = 0; mode = 1; } } /* if bits remain then double/add */ if (mode == 2 && bitcpy > 0) { /* double then add */ for (j = 0; j < bitcpy; j++) { /* only double if we have had at least one add first */ if (first == 0) { if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } } bitbuf <<= 1; if ((bitbuf & (1 << ECC_MULMOD_WINSIZE)) != 0) { if (first == 1) { /* first add, so copy */ if ((err = pstm_copy(&tG->x, &R->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&tG->y, &R->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&tG->z, &R->z)) != PS_SUCCESS) { goto done; } first = 0; } else { /* then add */ if ((err = eccProjectiveAddPoint(pool, R, tG, R, modulus, &mp, tmp_int)) != PS_SUCCESS) { goto done; } } } } } /* map R back from projective space */ if (map) { err = eccMap(pool, R, modulus, &mp); } else { err = PS_SUCCESS; } done: pstm_clear(&mu); eccFreePoint(tG); for (i = 0; i < 8; i++) { eccFreePoint(M[i]); } return err; } # endif /* !USE_CONSTANT_TIME_ECC_MULMOD */ int32_t eccMulmod(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G, psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int) { # ifdef USE_CONSTANT_TIME_ECC_MULMOD return eccMulmodCt(pool, k, G, R, modulus, map, tmp_int); # else # warning Using non-constant-time ECC scalar multiplication return eccMulmodOld(pool, k, G, R, modulus, map, tmp_int); # endif } int32 eccTestPoint(psPool_t *pool, psEccPoint_t *P, pstm_int *prime, pstm_int *b) { pstm_int t1, t2; uint32 paDlen; pstm_digit *paD; int32 err; if ((err = pstm_init(pool, &t1)) < 0) { return err; } if ((err = pstm_init(pool, &t2)) < 0) { pstm_clear(&t1); return err; } /* Pre-allocated digit. TODO: haven't fully explored max paDlen */ paDlen = (prime->used * 2 + 1) * sizeof(pstm_digit); if ((paD = psMalloc(pool, paDlen)) == NULL) { pstm_clear(&t1); pstm_clear(&t2); return PS_MEM_FAIL; } /* compute y^2 */ if ((err = pstm_sqr_comba(pool, &P->y, &t1, paD, paDlen)) < 0) { goto error; } /* compute x^3 */ if ((err = pstm_sqr_comba(pool, &P->x, &t2, paD, paDlen)) < 0) { goto error; } if ((err = pstm_mod(pool, &t2, prime, &t2)) < 0) { goto error; } if ((err = pstm_mul_comba(pool, &P->x, &t2, &t2, paD, paDlen)) < 0) { goto error; } /* compute y^2 - x^3 */ if ((err = pstm_sub(&t1, &t2, &t1)) < 0) { goto error; } /* compute y^2 - x^3 + 3x */ if ((err = pstm_add(&t1, &P->x, &t1)) < 0) { goto error; } if ((err = pstm_add(&t1, &P->x, &t1)) < 0) { goto error; } if ((err = pstm_add(&t1, &P->x, &t1)) < 0) { goto error; } if ((err = pstm_mod(pool, &t1, prime, &t1)) < 0) { goto error; } while (pstm_cmp_d(&t1, 0) == PSTM_LT) { if ((err = pstm_add(&t1, prime, &t1)) < 0) { goto error; } } while (pstm_cmp(&t1, prime) != PSTM_LT) { if ((err = pstm_sub(&t1, prime, &t1)) < 0) { goto error; } } /* compare to b */ if (pstm_cmp(&t1, b) != PSTM_EQ) { psTraceCrypto("Supplied EC public point not on curve\n"); err = PS_LIMIT_FAIL; } else { err = PS_SUCCESS; } error: psFree(paD, pool); pstm_clear(&t1); pstm_clear(&t2); return err; } /******************************************************************************/ /** Add two ECC points @param P The point to add @param Q The point to add @param[out] R The destination of the double @param modulus The modulus of the field the ECC curve is in @param mp The "b" value from montgomery_setup() @return PS_SUCCESS on success */ int32_t eccProjectiveAddPoint(psPool_t *pool, const psEccPoint_t *P, const psEccPoint_t *Q, psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp, pstm_int *tmp_int) { pstm_int t1, t2, x, y, z; pstm_digit *paD; int32 err; uint32 paDlen; paD = NULL; if (pstm_init_size(pool, &t1, P->x.alloc) < 0) { return PS_MEM_FAIL; } err = PS_MEM_FAIL; if (pstm_init_size(pool, &t2, P->x.alloc) < 0) { goto ERR_T1; } if (pstm_init_size(pool, &x, P->x.alloc) < 0) { goto ERR_T2; } if (pstm_init_size(pool, &y, P->y.alloc) < 0) { goto ERR_X; } if (pstm_init_size(pool, &z, P->z.alloc) < 0) { goto ERR_Y; } /* should we dbl instead? */ if ((err = pstm_sub(modulus, &Q->y, &t1)) != PS_SUCCESS) { goto done; } if ((pstm_cmp(&P->x, &Q->x) == PSTM_EQ) && /* (&Q->z != NULL && pstm_cmp(&P->z, &Q->z) == PSTM_EQ) && */ (pstm_cmp(&P->z, &Q->z) == PSTM_EQ) && (pstm_cmp(&P->y, &Q->y) == PSTM_EQ || pstm_cmp(&P->y, &t1) == PSTM_EQ)) { pstm_clear_multi(&t1, &t2, &x, &y, &z, NULL, NULL, NULL); return eccProjectiveDblPoint(pool, P, R, modulus, mp, tmp_int); } if ((err = pstm_copy(&P->x, &x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&P->y, &y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&P->z, &z)) != PS_SUCCESS) { goto done; } /* Pre-allocated digit. Used for mul, sqr, AND reduce*/ paDlen = (modulus->used * 2 + 1) * sizeof(pstm_digit); if ((paD = psMalloc(pool, paDlen)) == NULL) { err = PS_MEM_FAIL; goto done; } /* if Z is one then these are no-operations */ if (pstm_cmp_d(&Q->z, 1) != PSTM_EQ) { /* T1 = Z' * Z' */ if ((err = pstm_sqr_comba(pool, &Q->z, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = X * T1 */ if ((err = pstm_mul_comba(pool, &t1, &x, &x, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = Z' * T1 */ if ((err = pstm_mul_comba(pool, &Q->z, &t1, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Y = Y * T1 */ if ((err = pstm_mul_comba(pool, &t1, &y, &y, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &y, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } } /* T1 = Z*Z */ if ((err = pstm_sqr_comba(pool, &z, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T2 = X' * T1 */ if ((err = pstm_mul_comba(pool, &Q->x, &t1, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = Z * T1 */ if ((err = pstm_mul_comba(pool, &z, &t1, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = Y' * T1 */ if ((err = pstm_mul_comba(pool, &Q->y, &t1, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Y = Y - T1 */ if ((err = pstm_sub(&y, &t1, &y)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&y, 0) == PSTM_LT) { if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; } } /* T1 = 2T1 */ if ((err = pstm_add(&t1, &t1, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } /* T1 = Y + T1 */ if ((err = pstm_add(&t1, &y, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } /* X = X - T2 */ if ((err = pstm_sub(&x, &t2, &x)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&x, 0) == PSTM_LT) { if ((err = pstm_add(&x, modulus, &x)) != PS_SUCCESS) { goto done; } } /* T2 = 2T2 */ if ((err = pstm_add(&t2, &t2, &t2)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t2, modulus) != PSTM_LT) { if ((err = pstm_sub(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } /* T2 = X + T2 */ if ((err = pstm_add(&t2, &x, &t2)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t2, modulus) != PSTM_LT) { if ((err = pstm_sub(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } /* if Z' != 1 */ if (pstm_cmp_d(&Q->z, 1) != PSTM_EQ) { /* Z = Z * Z' */ if ((err = pstm_mul_comba(pool, &z, &Q->z, &z, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &z, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } } /* Z = Z * X */ if ((err = pstm_mul_comba(pool, &z, &x, &z, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &z, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = T1 * X */ if ((err = pstm_mul_comba(pool, &t1, &x, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = X * X */ if ((err = pstm_sqr_comba(pool, &x, &x, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T2 = T2 * x */ if ((err = pstm_mul_comba(pool, &t2, &x, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = T1 * X */ if ((err = pstm_mul_comba(pool, &t1, &x, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = Y*Y */ if ((err = pstm_sqr_comba(pool, &y, &x, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = X - T2 */ if ((err = pstm_sub(&x, &t2, &x)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&x, 0) == PSTM_LT) { if ((err = pstm_add(&x, modulus, &x)) != PS_SUCCESS) { goto done; } } /* T2 = T2 - X */ if ((err = pstm_sub(&t2, &x, &t2)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&t2, 0) == PSTM_LT) { if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } /* T2 = T2 - X */ if ((err = pstm_sub(&t2, &x, &t2)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&t2, 0) == PSTM_LT) { if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } /* T2 = T2 * Y */ if ((err = pstm_mul_comba(pool, &t2, &y, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Y = T2 - T1 */ if ((err = pstm_sub(&t2, &t1, &y)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&y, 0) == PSTM_LT) { if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; } } /* Y = Y/2 */ if (pstm_isodd(&y)) { if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; } } if ((err = pstm_div_2(&y, &y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&x, &R->x)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&y, &R->y)) != PS_SUCCESS) { goto done; } if ((err = pstm_copy(&z, &R->z)) != PS_SUCCESS) { goto done; } err = PS_SUCCESS; done: pstm_clear(&z); ERR_Y: pstm_clear(&y); ERR_X: pstm_clear(&x); ERR_T2: pstm_clear(&t2); ERR_T1: pstm_clear(&t1); if (paD) { psFree(paD, pool); } return err; } /******************************************************************************/ /** Double an ECC point @param[in] P The point to double @param[out] R The destination of the double @param[in] modulus The modulus of the field the ECC curve is in @param[in] mp The "b" value from montgomery_setup() @param[in] A The "A" of the field the ECC curve is in @return PS_SUCCESS on success */ static int32_t eccProjectiveDblPoint(psPool_t *pool, const psEccPoint_t *P, psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp, const pstm_int *A) { pstm_int t1, t2; pstm_digit *paD; uint32 paDlen; int32 err, initSize; if (P != R) { if (pstm_copy(&P->x, &R->x) < 0) { return PS_MEM_FAIL; } if (pstm_copy(&P->y, &R->y) < 0) { return PS_MEM_FAIL; } if (pstm_copy(&P->z, &R->z) < 0) { return PS_MEM_FAIL; } } initSize = R->x.used; if (R->y.used > initSize) { initSize = R->y.used; } if (R->z.used > initSize) { initSize = R->z.used; } if (pstm_init_size(pool, &t1, (initSize * 2) + 1) < 0) { return PS_MEM_FAIL; } if (pstm_init_size(pool, &t2, (initSize * 2) + 1) < 0) { pstm_clear(&t1); return PS_MEM_FAIL; } /* Pre-allocated digit. Used for mul, sqr, AND reduce*/ paDlen = (modulus->used * 2 + 1) * sizeof(pstm_digit); if ((paD = psMalloc(pool, paDlen)) == NULL) { err = PS_MEM_FAIL; goto done; } /* t1 = Z * Z */ if ((err = pstm_sqr_comba(pool, &R->z, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Z = Y * Z */ if ((err = pstm_mul_comba(pool, &R->z, &R->y, &R->z, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &R->z, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Z = 2Z */ if ((err = pstm_add(&R->z, &R->z, &R->z)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&R->z, modulus) != PSTM_LT) { if ((err = pstm_sub(&R->z, modulus, &R->z)) != PS_SUCCESS) { goto done; } } /* compute into T1 M=3(X+Z^2)(X-Z^2) */ if (A == NULL) { /* T2 = X - T1 */ if ((err = pstm_sub(&R->x, &t1, &t2)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&t2, 0) == PSTM_LT) { if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } /* T1 = X + T1 */ if ((err = pstm_add(&t1, &R->x, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } /* T2 = T1 * T2 */ if ((err = pstm_mul_comba(pool, &t1, &t2, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = 2T2 */ if ((err = pstm_add(&t2, &t2, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } /* T1 = T1 + T2 */ if ((err = pstm_add(&t1, &t2, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } } else { /* compute into T1 M=3X^2 + A Z^4 */ pstm_int t3, t4; if (pstm_init_size(pool, &t3, (initSize * 2) + 1) < 0) { return PS_MEM_FAIL; } if (pstm_init_size(pool, &t4, (initSize * 2) + 1) < 0) { pstm_clear(&t3); return PS_MEM_FAIL; } /* T3 = X * X */ if ((err = pstm_sqr_comba(pool, &R->x, &t3, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t3, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T4 = 2T3 */ if ((err = pstm_add(&t3, &t3, &t4)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t4, modulus) != PSTM_LT) { if ((err = pstm_sub(&t4, modulus, &t4)) != PS_SUCCESS) { goto done; } } /* T3 = T3 + T4 */ if ((err = pstm_add(&t3, &t4, &t3)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t3, modulus) != PSTM_LT) { if ((err = pstm_sub(&t3, modulus, &t3)) != PS_SUCCESS) { goto done; } } /* T4 = T1 * T1 */ if ((err = pstm_sqr_comba(pool, &t1, &t4, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_mod(pool, &t4, modulus, &t4)) != PS_SUCCESS) { goto done; } /* T4 = T4 * A */ if ((err = pstm_mul_comba(pool, &t4, A, &t4, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t4, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T1 = T3 + T4 */ if ((err = pstm_add(&t3, &t4, &t1)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&t1, modulus) != PSTM_LT) { if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; } } pstm_clear_multi(&t3, &t4, NULL, NULL, NULL, NULL, NULL, NULL); } /* Y = 2Y */ if ((err = pstm_add(&R->y, &R->y, &R->y)) != PS_SUCCESS) { goto done; } if (pstm_cmp(&R->y, modulus) != PSTM_LT) { if ((err = pstm_sub(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done; } } /* Y = Y * Y */ if ((err = pstm_sqr_comba(pool, &R->y, &R->y, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T2 = Y * Y */ if ((err = pstm_sqr_comba(pool, &R->y, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* T2 = T2/2 */ if (pstm_isodd(&t2)) { if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; } } if ((err = pstm_div_2(&t2, &t2)) != PS_SUCCESS) { goto done; } /* Y = Y * X */ if ((err = pstm_mul_comba(pool, &R->y, &R->x, &R->y, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = T1 * T1 */ if ((err = pstm_sqr_comba(pool, &t1, &R->x, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &R->x, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* X = X - Y */ if ((err = pstm_sub(&R->x, &R->y, &R->x)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&R->x, 0) == PSTM_LT) { if ((err = pstm_add(&R->x, modulus, &R->x)) != PS_SUCCESS) { goto done; } } /* X = X - Y */ if ((err = pstm_sub(&R->x, &R->y, &R->x)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&R->x, 0) == PSTM_LT) { if ((err = pstm_add(&R->x, modulus, &R->x)) != PS_SUCCESS) { goto done; } } /* Y = Y - X */ if ((err = pstm_sub(&R->y, &R->x, &R->y)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&R->y, 0) == PSTM_LT) { if ((err = pstm_add(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done; } } /* Y = Y * T1 */ if ((err = pstm_mul_comba(pool, &R->y, &t1, &R->y, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* Y = Y - T2 */ if ((err = pstm_sub(&R->y, &t2, &R->y)) != PS_SUCCESS) { goto done; } if (pstm_cmp_d(&R->y, 0) == PSTM_LT) { if ((err = pstm_add(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done; } } err = PS_SUCCESS; done: pstm_clear_multi(&t1, &t2, NULL, NULL, NULL, NULL, NULL, NULL); if (paD) { psFree(paD, pool); } return err; } /******************************************************************************/ /** Allocate a new ECC point. @return A newly allocated point or NULL on error */ psEccPoint_t *eccNewPoint(psPool_t *pool, short size) { psEccPoint_t *p = NULL; p = psMalloc(pool, sizeof(psEccPoint_t)); if (p == NULL) { return NULL; } p->pool = pool; if (size == 0) { if (pstm_init(pool, &p->x) != PSTM_OKAY) { goto ERR; } if (pstm_init(pool, &p->y) != PSTM_OKAY) { goto ERR_X; } if (pstm_init(pool, &p->z) != PSTM_OKAY) { goto ERR_Y; } } else { if (pstm_init_size(pool, &p->x, size) != PSTM_OKAY) { goto ERR; } if (pstm_init_size(pool, &p->y, size) != PSTM_OKAY) { goto ERR_X; } if (pstm_init_size(pool, &p->z, size) != PSTM_OKAY) { goto ERR_Y; } } return p; ERR_Y: pstm_clear(&p->y); ERR_X: pstm_clear(&p->x); ERR: psFree(p, pool); return NULL; } /** Free an ECC point from memory. @param p The point to free */ void eccFreePoint(psEccPoint_t *p) { if (p != NULL) { pstm_clear(&p->x); pstm_clear(&p->y); pstm_clear(&p->z); psFree(p, p->pool); } } /** Map a projective jacbobian point back to affine space @param[in,out] P [in/out] The point to map @param[in] modulus The modulus of the field the ECC curve is in @param[in] mp The "b" value from montgomery_setup() @return PS_SUCCESS on success */ int32_t eccMap(psPool_t *pool, psEccPoint_t *P, const pstm_int *modulus, const pstm_digit *mp) { pstm_int t1, t2; pstm_digit *paD; int32 err; uint32 paDlen; if (pstm_init_size(pool, &t1, P->x.alloc) < 0) { return PS_MEM_FAIL; } if (pstm_init_size(pool, &t2, P->x.alloc) < 0) { pstm_clear(&t1); return PS_MEM_FAIL; } /* Pre-allocated digit. Used for mul, sqr, AND reduce */ paDlen = (modulus->used * 2 + 1) * sizeof(pstm_digit); if ((paD = psMalloc(pool, paDlen)) == NULL) { err = PS_MEM_FAIL; goto done; } /* first map z back to normal */ if ((err = pstm_montgomery_reduce(pool, &P->z, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } /* get 1/z */ if ((err = pstm_invmod(pool, &P->z, modulus, &t1)) != PS_SUCCESS) { goto done; } /* get 1/z^2 and 1/z^3 */ if ((err = pstm_sqr_comba(pool, &t1, &t2, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_mod(pool, &t2, modulus, &t2)) != PS_SUCCESS) { goto done; } if ((err = pstm_mul_comba(pool, &t1, &t2, &t1, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_mod(pool, &t1, modulus, &t1)) != PS_SUCCESS) { goto done; } /* multiply against x/y */ if ((err = pstm_mul_comba(pool, &P->x, &t2, &P->x, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &P->x, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_mul_comba(pool, &P->y, &t1, &P->y, paD, paDlen)) != PS_SUCCESS) { goto done; } if ((err = pstm_montgomery_reduce(pool, &P->y, modulus, *mp, paD, paDlen)) != PS_SUCCESS) { goto done; } pstm_set(&P->z, 1); err = PS_SUCCESS; done: pstm_clear_multi(&t1, &t2, NULL, NULL, NULL, NULL, NULL, NULL); if (paD) { psFree(paD, pool); } return err; } #endif /* USE_MATRIX_ECC */