/* -*-c-*-
*
- * $Id: mpmont.c,v 1.5 1999/11/22 13:58:40 mdw Exp $
+ * $Id$
*
* Montgomery reduction
*
* (c) 1999 Straylight/Edgeware
*/
-/*----- Licensing notice --------------------------------------------------*
+/*----- Licensing notice --------------------------------------------------*
*
* This file is part of Catacomb.
*
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* Catacomb 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 Library General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Library General Public
* License along with Catacomb; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: mpmont.c,v $
- * Revision 1.5 1999/11/22 13:58:40 mdw
- * Add an option to disable Montgomery reduction, so that performance
- * comparisons can be done.
- *
- * Revision 1.4 1999/11/21 12:27:06 mdw
- * Remove a division from the Montgomery setup by calculating
- * %$R^2 \bmod m$% first and then %$R \bmod m$% by Montgomery reduction of
- * %$R^2$%.
- *
- * Revision 1.3 1999/11/21 11:35:10 mdw
- * Performance improvement: use @mp_sqr@ and @mpmont_reduce@ instead of
- * @mpmont_mul@ for squaring in exponentiation.
- *
- * Revision 1.2 1999/11/19 13:17:26 mdw
- * Add extra interface to exponentiation which returns a Montgomerized
- * result.
- *
- * Revision 1.1 1999/11/17 18:02:16 mdw
- * New multiprecision integer arithmetic suite.
- *
- */
-
/*----- Header files ------------------------------------------------------*/
#include "mp.h"
/* #define MPMONT_DISABLE */
-/*----- Main code ---------------------------------------------------------*/
+/*----- Reduction and multiplication --------------------------------------*/
/* --- @mpmont_create@ --- *
*
* Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
* @mp *m@ = modulus to use
*
- * Returns: ---
+ * Returns: Zero on success, nonzero on error.
*
* Use: Initializes a Montgomery reduction context ready for use.
+ * The argument @m@ must be a positive odd integer.
*/
#ifdef MPMONT_DISABLE
-void mpmont_create(mpmont *mm, mp *m)
+int mpmont_create(mpmont *mm, mp *m)
{
mp_shrink(m);
mm->m = MP_COPY(m);
mm->r = MP_ONE;
mm->r2 = MP_ONE;
+ mm->mi = MP_ONE;
+ return (0);
}
#else
-void mpmont_create(mpmont *mm, mp *m)
+int mpmont_create(mpmont *mm, mp *m)
{
+ size_t n = MP_LEN(m);
+ mp *r2 = mp_new(2 * n + 1, 0);
+ mp r;
+
/* --- Take a copy of the modulus --- */
- mp_shrink(m);
+ if (!MP_POSP(m) || !MP_ODDP(m))
+ return (-1);
mm->m = MP_COPY(m);
- /* --- Find the magic value @mi@ --- *
- *
- * This is a slightly grungy way of solving the problem, but it does work.
- */
+ /* --- Determine %$R^2$% --- */
- {
- mpw av[2] = { 0, 1 };
- mp a, b;
- mp *i;
- mpw mi;
+ mm->n = n;
+ MPX_ZERO(r2->v, r2->vl - 1);
+ r2->vl[-1] = 1;
- mp_build(&a, av, av + 2);
- mp_build(&b, m->v, m->v + 1);
- mp_gcd(0, 0, &i, &a, &b);
- mi = i->v[0];
- if (!(i->f & MP_NEG))
- mi = MPW(-mi);
- mm->mi = mi;
- MP_DROP(i);
- }
+ /* --- Find the magic value @mi@ --- */
+
+ mp_build(&r, r2->v + n, r2->vl);
+ mm->mi = mp_modinv(MP_NEW, m, &r);
+ mm->mi = mp_sub(mm->mi, &r, mm->mi);
/* --- Discover the values %$R \bmod m$% and %$R^2 \bmod m$% --- */
- {
- size_t l = MP_LEN(m);
- mp *r = mp_create(2 * l + 1);
-
- mm->shift = l * MPW_BITS;
- MPX_ZERO(r->v, r->vl - 1);
- r->vl[-1] = 1;
- mm->r2 = MP_NEW;
- mp_div(0, &mm->r2, r, m);
- mm->r = mpmont_reduce(mm, MP_NEW, mm->r2);
- MP_DROP(r);
- }
+ mm->r2 = MP_NEW;
+ mp_div(0, &mm->r2, r2, m);
+ mm->r = mpmont_reduce(mm, MP_NEW, mm->r2);
+ MP_DROP(r2);
+ return (0);
}
#endif
MP_DROP(mm->m);
MP_DROP(mm->r);
MP_DROP(mm->r2);
+ MP_DROP(mm->mi);
}
/* --- @mpmont_reduce@ --- *
*
* Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
* @mp *d@ = destination
- * @const mp *a@ = source, assumed positive
+ * @mp *a@ = source, assumed positive
*
* Returns: Result, %$a R^{-1} \bmod m$%.
*/
#ifdef MPMONT_DISABLE
-mp *mpmont_reduce(mpmont *mm, mp *d, const mp *a)
+mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
{
mp_div(0, &d, a, mm->m);
return (d);
#else
-mp *mpmont_reduce(mpmont *mm, mp *d, const mp *a)
+mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
{
- mpw *dv, *dvl;
- const mpw *mv, *mvl;
- size_t n;
-
- /* --- Initial conditioning of the arguments --- */
+ size_t n = mm->n;
+
+ /* --- Check for serious Karatsuba reduction --- */
+
+ if (n > MPK_THRESH * 3) {
+ mp al;
+ mpw *vl;
+ mp *u;
+
+ if (MP_LEN(a) >= n)
+ vl = a->v + n;
+ else
+ vl = a->vl;
+ mp_build(&al, a->v, vl);
+ u = mp_mul(MP_NEW, &al, mm->mi);
+ if (MP_LEN(u) > n)
+ u->vl = u->v + n;
+ u = mp_mul(u, u, mm->m);
+ d = mp_add(d, a, u);
+ mp_drop(u);
+ }
- n = MP_LEN(mm->m);
+ /* --- Otherwise do it the hard way --- */
- if (d == a)
- MP_MODIFY(d, 2 * n + 1);
else {
- MP_MODIFY(d, 2 * n + 1);
- memcpy(d->v, a->v, MPWS(MP_LEN(a)));
- memset(d->v + MP_LEN(a), 0, MPWS(MP_LEN(d) - MP_LEN(a)));
- }
-
- dv = d->v; dvl = d->vl;
- mv = mm->m->v; mvl = mm->m->vl;
+ mpw *dv, *dvl;
+ mpw *mv, *mvl;
+ mpw mi;
+ size_t k = n;
+
+ /* --- Initial conditioning of the arguments --- */
- /* --- Let's go to work --- */
+ a = MP_COPY(a);
+ if (d)
+ MP_DROP(d);
+ d = a;
+ MP_DEST(d, 2 * n + 1, a->f);
- while (n--) {
- mpw u = MPW(*dv * mm->mi);
- MPX_UMLAN(dv, dvl, mv, mvl, u);
- dv++;
+ dv = d->v; dvl = d->vl;
+ mv = mm->m->v; mvl = mm->m->vl;
+
+ /* --- Let's go to work --- */
+
+ mi = mm->mi->v[0];
+ while (k--) {
+ mpw u = MPW(*dv * mi);
+ MPX_UMLAN(dv, dvl, mv, mvl, u);
+ dv++;
+ }
}
- /* --- Done --- */
+ /* --- Wrap everything up --- */
- memmove(d->v, dv, MPWS(dvl - dv));
- d->vl -= dv - d->v;
+ memmove(d->v, d->v + n, MPWS(MP_LEN(d) - n));
+ d->vl -= n;
+ if (MPX_UCMP(d->v, d->vl, >=, mm->m->v, mm->m->vl))
+ mpx_usub(d->v, d->vl, d->v, d->vl, mm->m->v, mm->m->vl);
+ if (d->f & MP_NEG) {
+ mpx_usub(d->v, d->vl, mm->m->v, mm->m->vl, d->v, d->vl);
+ d->f &= ~MP_NEG;
+ }
MP_SHRINK(d);
- d->f = a->f & MP_BURN;
- if (MP_CMP(d, >=, mm->m))
- d = mp_sub(d, d, mm->m);
return (d);
}
*
* Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
* @mp *d@ = destination
- * @const mp *a, *b@ = sources, assumed positive
+ * @mp *a, *b@ = sources, assumed positive
*
* Returns: Result, %$a b R^{-1} \bmod m$%.
*/
#ifdef MPMONT_DISABLE
-mp *mpmont_mul(mpmont *mm, mp *d, const mp *a, const mp *b)
+mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
{
d = mp_mul(d, a, b);
mp_div(0, &d, d, mm->m);
#else
-mp *mpmont_mul(mpmont *mm, mp *d, const mp *a, const mp *b)
+mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
{
- mpw *dv, *dvl;
- const mpw *av, *avl;
- const mpw *bv, *bvl;
- const mpw *mv, *mvl;
- mpw y;
- size_t n, i;
+ if (mm->n > MPK_THRESH * 3) {
+ d = mp_mul(d, a, b);
+ d = mpmont_reduce(mm, d, d);
+ } else {
+ mpw *dv, *dvl;
+ mpw *av, *avl;
+ mpw *bv, *bvl;
+ mpw *mv, *mvl;
+ mpw y;
+ size_t n, i;
+ mpw mi;
- /* --- Initial conditioning of the arguments --- */
+ /* --- Initial conditioning of the arguments --- */
- if (MP_LEN(a) > MP_LEN(b)) {
- const mp *t = a; a = b; b = t;
- }
- n = MP_LEN(mm->m);
-
- MP_MODIFY(d, 2 * n + 1);
- dv = d->v; dvl = d->vl;
- MPX_ZERO(dv, dvl);
- av = a->v; avl = a->vl;
- bv = b->v; bvl = b->vl;
- mv = mm->m->v; mvl = mm->m->vl;
- y = *bv;
-
- /* --- Montgomery multiplication phase --- */
-
- i = 0;
- while (i < n && av < avl) {
- mpw x = *av++;
- mpw u = MPW((*dv + x * y) * mm->mi);
- MPX_UMLAN(dv, dvl, bv, bvl, x);
- MPX_UMLAN(dv, dvl, mv, mvl, u);
- dv++;
- i++;
- }
+ if (MP_LEN(a) > MP_LEN(b)) {
+ mp *t = a; a = b; b = t;
+ }
+ n = MP_LEN(mm->m);
+
+ a = MP_COPY(a);
+ b = MP_COPY(b);
+ MP_DEST(d, 2 * n + 1, a->f | b->f | MP_UNDEF);
+ dv = d->v; dvl = d->vl;
+ MPX_ZERO(dv, dvl);
+ av = a->v; avl = a->vl;
+ bv = b->v; bvl = b->vl;
+ mv = mm->m->v; mvl = mm->m->vl;
+ y = *bv;
+
+ /* --- Montgomery multiplication phase --- */
+
+ i = 0;
+ mi = mm->mi->v[0];
+ while (i < n && av < avl) {
+ mpw x = *av++;
+ mpw u = MPW((*dv + x * y) * mi);
+ MPX_UMLAN(dv, dvl, bv, bvl, x);
+ MPX_UMLAN(dv, dvl, mv, mvl, u);
+ dv++;
+ i++;
+ }
- /* --- Simpler Montgomery reduction phase --- */
+ /* --- Simpler Montgomery reduction phase --- */
- while (i < n) {
- mpw u = MPW(*dv * mm->mi);
- MPX_UMLAN(dv, dvl, mv, mvl, u);
- dv++;
- i++;
- }
+ while (i < n) {
+ mpw u = MPW(*dv * mi);
+ MPX_UMLAN(dv, dvl, mv, mvl, u);
+ dv++;
+ i++;
+ }
- /* --- Done --- */
+ /* --- Done --- */
+
+ memmove(d->v, dv, MPWS(dvl - dv));
+ d->vl -= dv - d->v;
+ if (MPX_UCMP(d->v, d->vl, >=, mm->m->v, mm->m->vl))
+ mpx_usub(d->v, d->vl, d->v, d->vl, mm->m->v, mm->m->vl);
+ if ((a->f ^ b->f) & MP_NEG)
+ mpx_usub(d->v, d->vl, mm->m->v, mm->m->vl, d->v, d->vl);
+ MP_SHRINK(d);
+ d->f = (a->f | b->f) & MP_BURN;
+ MP_DROP(a);
+ MP_DROP(b);
+ }
- memmove(d->v, dv, MPWS(dvl - dv));
- d->vl -= dv - d->v;
- MP_SHRINK(d);
- d->f = (a->f | b->f) & MP_BURN;
- if (MP_CMP(d, >=, mm->m))
- d = mp_sub(d, d, mm->m);
return (d);
}
#endif
-/* --- @mpmont_expr@ --- *
- *
- * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
- * @const mp *a@ = base
- * @const mp *e@ = exponent
- *
- * Returns: Result, %$a^e R \bmod m$%.
- */
-
-mp *mpmont_expr(mpmont *mm, const mp *a, const mp *e)
-{
- mpscan sc;
- mp *ar = mpmont_mul(mm, MP_NEW, a, mm->r2);
- mp *d = MP_COPY(mm->r);
- mp *spare = MP_NEW;
-
- mp_scan(&sc, e);
-
- if (MP_STEP(&sc)) {
- size_t sq = 0;
- for (;;) {
- mp *dd;
- if (MP_BIT(&sc)) {
- while (sq) {
- dd = mp_sqr(spare, ar);
- dd = mpmont_reduce(mm, dd, dd);
- spare = ar; ar = dd;
- sq--;
- }
- dd = mpmont_mul(mm, spare, d, ar);
- spare = d; d = dd;
- }
- sq++;
- if (!MP_STEP(&sc))
- break;
- }
- }
- MP_DROP(ar);
- if (spare != MP_NEW)
- MP_DROP(spare);
- return (d);
-}
-
-/* --- @mpmont_exp@ --- *
- *
- * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
- * @const mp *a@ = base
- * @const mp *e@ = exponent
- *
- * Returns: Result, %$a^e \bmod m$%.
- */
-
-mp *mpmont_exp(mpmont *mm, const mp *a, const mp *e)
-{
- mp *d = mpmont_expr(mm, a, e);
- d = mpmont_reduce(mm, d, d);
- return (d);
-}
-
/*----- Test rig ----------------------------------------------------------*/
#ifdef TEST_RIG
mpmont_create(&mm, m);
- if (mm.mi != mi->v[0]) {
+ if (mm.mi->v[0] != mi->v[0]) {
fprintf(stderr, "\n*** bad mi: found %lu, expected %lu",
- (unsigned long)mm.mi, (unsigned long)mi->v[0]);
+ (unsigned long)mm.mi->v[0], (unsigned long)mi->v[0]);
fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
fputc('\n', stderr);
ok = 0;
}
- if (MP_CMP(mm.r, !=, r)) {
+ if (!MP_EQ(mm.r, r)) {
fputs("\n*** bad r", stderr);
fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
fputs("\nexpected ", stderr); mp_writefile(r, stderr, 10);
- fputs("\n found ", stderr); mp_writefile(mm.r, stderr, 10);
+ fputs("\n found ", stderr); mp_writefile(mm.r, stderr, 10);
fputc('\n', stderr);
ok = 0;
}
- if (MP_CMP(mm.r2, !=, r2)) {
+ if (!MP_EQ(mm.r2, r2)) {
fputs("\n*** bad r2", stderr);
fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
fputs("\nexpected ", stderr); mp_writefile(r2, stderr, 10);
- fputs("\n found ", stderr); mp_writefile(mm.r2, stderr, 10);
+ fputs("\n found ", stderr); mp_writefile(mm.r2, stderr, 10);
fputc('\n', stderr);
ok = 0;
}
MP_DROP(r);
MP_DROP(r2);
mpmont_destroy(&mm);
+ assert(mparena_count(MPARENA_GLOBAL) == 0);
return (ok);
}
mp *qr = mp_mul(MP_NEW, a, b);
mp_div(0, &qr, qr, m);
- if (MP_CMP(qr, !=, r)) {
+ if (!MP_EQ(qr, r)) {
fputs("\n*** classical modmul failed", stderr);
fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
mp *br = mpmont_mul(&mm, MP_NEW, b, mm.r2);
mp *mr = mpmont_mul(&mm, MP_NEW, ar, br);
mr = mpmont_reduce(&mm, mr, mr);
- if (MP_CMP(mr, !=, r)) {
+ if (!MP_EQ(mr, r)) {
fputs("\n*** montgomery modmul failed", stderr);
fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
MP_DROP(b);
MP_DROP(r);
mpmont_destroy(&mm);
+ assert(mparena_count(MPARENA_GLOBAL) == 0);
return ok;
}
-static int texp(dstr *v)
-{
- mp *m = *(mp **)v[0].buf;
- mp *a = *(mp **)v[1].buf;
- mp *b = *(mp **)v[2].buf;
- mp *r = *(mp **)v[3].buf;
- mp *mr;
- int ok = 1;
-
- mpmont mm;
- mpmont_create(&mm, m);
-
- mr = mpmont_exp(&mm, a, b);
-
- if (MP_CMP(mr, !=, r)) {
- fputs("\n*** montgomery modexp failed", stderr);
- fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
- fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
- fputs("\n e = ", stderr); mp_writefile(b, stderr, 10);
- fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
- fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
- fputc('\n', stderr);
- ok = 0;
- }
-
- MP_DROP(m);
- MP_DROP(a);
- MP_DROP(b);
- MP_DROP(r);
- MP_DROP(mr);
- mpmont_destroy(&mm);
- return ok;
-}
-
-
static test_chunk tests[] = {
- { "create", tcreate, { &type_mp, &type_mp, &type_mp, &type_mp } },
- { "mul", tmul, { &type_mp, &type_mp, &type_mp, &type_mp } },
- { "exp", texp, { &type_mp, &type_mp, &type_mp, &type_mp } },
+ { "create", tcreate, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
+ { "mul", tmul, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
{ 0, 0, { 0 } },
};