Blum-Blum-Shub generator, and Blum-Goldwasser encryption.
[u/mdw/catacomb] / bbs-jump.c
diff --git a/bbs-jump.c b/bbs-jump.c
new file mode 100644 (file)
index 0000000..629871d
--- /dev/null
@@ -0,0 +1,292 @@
+/* -*-c-*-
+ *
+ * $Id: bbs-jump.c,v 1.1 1999/12/10 23:14:59 mdw Exp $
+ *
+ * Jumping around a BBS sequence
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * 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: bbs-jump.c,v $
+ * Revision 1.1  1999/12/10 23:14:59  mdw
+ * Blum-Blum-Shub generator, and Blum-Goldwasser encryption.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "bbs.h"
+#include "mp.h"
+#include "mpbarrett.h"
+#include "mpcrt.h"
+#include "mpint.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @jump@ --- *
+ *
+ * Arguments:  @bbs *b@ = pointer to BBS generator context
+ *             @bbs_params *bp@ = pointer to BBS modulus factors
+ *             @unsigned long n@ = number of steps to move
+ *             @mp *px@ = exponent mod @p@ for a one-step jump
+ *             @mp *qx@ = exponent mod @q@ for a one-step jump
+ *
+ * Returns:    ---
+ *
+ * Use:                Jumps a BBS context a certain number of places (assuming the
+ *             arguments are right).
+ *
+ *             Let the BBS modulus be %$n = pq$% and the current residue be
+ *             %$x$%.  Then the computations performed are:
+ *
+ *               * Calculate %$x_p = x \bmod p$% and %$x_q = x \bmod q$%.
+ *
+ *               * Determine %$e_p = px^n \bmod (p - 1)$% and similarly
+ *                 %$e_q = qx^n \bmod (p - 1)$%.
+ *
+ *               * Calculate %$x_p' = x_p^{e_p} \bmod p$% and
+ *                 %$x_q' = x_q^{e_q} \bmod q$%.
+ *
+ *               * Combine %$x_p'$% and %$x_q'$% using the Chinese Remainder
+ *                 Theorem.
+ *
+ *             If you want to step the generator forwards, simply set
+ *             %$px = qx = 2$%.  If you want to step backwards, make
+ *             %$px = (p + 1)/4$% and %$qx = (q + 1)/4%$.  Note that, if
+ *             %$x$% is a quadratic residue mod $%p$%, then
+ *
+ *             %$(x^2) ^ {(p + 1)/4}$%
+ *               %${} = x^{(p + 1)/2}$%
+ *               %${} = x \cdot x^{(p - 1)/2}$%
+ *               %${} = x$%
+ *
+ *             Simple, no?  (Note that the division works because
+ *             %$p \equiv 3 \pmod 4$%.)
+ */
+
+static void jump(bbs *b, bbs_params *bp, unsigned long n,
+                mp *px, mp *qx)
+{
+  mp *ep, *eq;
+  mp *v[2] = { MP_NEW, MP_NEW };
+
+  /* --- First work out the exponents --- */
+
+  {
+    mpbarrett mb;
+    mp *m;
+    mp *e;
+
+    e = mp_fromulong(MP_NEW, n);
+    m = mp_sub(MP_NEW, bp->p, MP_ONE);
+    mpbarrett_create(&mb, m);
+    ep = mpbarrett_exp(&mb, MP_NEW, px, e);
+    mpbarrett_destroy(&mb);
+    if (qx == px)
+      eq = MP_COPY(ep);
+    else {
+      m = mp_sub(m, bp->q, MP_ONE);
+      mpbarrett_create(&mb, m);
+      eq = mpbarrett_exp(&mb, MP_NEW, qx, e);
+      mpbarrett_destroy(&mb);
+    }
+
+    mp_drop(m);
+    mp_drop(e);
+  }
+
+  /* --- Now calculate the residues of @x@ --- */
+
+  mp_div(0, &v[0], b->x, bp->p);
+  mp_div(0, &v[1], b->x, bp->q);
+
+  /* --- Exponentiate --- */
+
+  {
+    mpbarrett mb;
+
+    mpbarrett_create(&mb, bp->p);
+    v[0] = mpbarrett_exp(&mb, v[0], v[0], ep);
+    mpbarrett_destroy(&mb);
+
+    mpbarrett_create(&mb, bp->q);
+    v[1] = mpbarrett_exp(&mb, v[1], v[1], eq);
+    mpbarrett_destroy(&mb);
+
+    mp_drop(ep);
+    mp_drop(eq);
+  }
+
+  /* --- Sort out the result using the Chinese Remainder Theorem --- */
+
+  {
+    mpcrt_mod mv[2];
+    mpcrt c;
+    int i;
+
+    mv[0].m = MP_COPY(bp->p);
+    mv[1].m = MP_COPY(bp->q);
+    for (i = 0; i < 2; i++)
+      mv[i].n = mv[i].ni = mv[i].nni = MP_NEW;
+    mpcrt_create(&c, mv, 2, b->mb.m);
+    b->x = mpcrt_solve(&c, b->x, v);
+    mpcrt_destroy(&c);
+  }
+
+  /* --- Tidy away --- */
+
+  mp_drop(v[0]);
+  mp_drop(v[1]);
+  b->r = b->x->v[0];
+  b->b = b->k;
+}
+
+/* --- @bbs_ff@ --- *
+ *
+ * Arguments:  @bbs *b@ = pointer to a BBS generator state
+ *             @bbs_params *bp@ = pointer to BBS modulus factors
+ *             @unsigned long n@ = number of steps to make
+ *
+ * Returns:    ---
+ *
+ * Use:                `Fast-forwards' a Blum-Blum-Shub generator by @n@ steps.
+ *             Requires the factorization of the Blum modulus to do this
+ *             efficiently.
+ */
+
+void bbs_ff(bbs *b, bbs_params *bp, unsigned long n)
+{
+  jump(b, bp, n, MP_TWO, MP_TWO);
+}
+
+/* --- @bbs_rew@ --- *
+ *
+ * Arguments:  @bbs *b@ = pointer to a BBS generator state
+ *             @bbs_params *bp@ = pointer to BBS modulus factors
+ *             @unsigned long n@ = number of steps to make
+ *
+ * Returns:    ---
+ *
+ * Use:                `Rewinds' a Blum-Blum-Shub generator by @n@ steps.
+ *             Requires the factorization of the Blum modulus to do this
+ *             at all.
+ */
+
+void bbs_rew(bbs *b, bbs_params *bp, unsigned long n)
+{
+  mp *px = mp_lsr(MP_NEW, bp->p, 2);
+  mp *qx = mp_lsr(MP_NEW, bp->q, 2);
+  px = mp_add(px, px, MP_ONE);
+  qx = mp_add(qx, qx, MP_ONE);
+  jump(b, bp, n, px, qx);
+  mp_drop(px);
+  mp_drop(qx);
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+static int verify(dstr *v)
+{
+  bbs_params bp;
+  bbs b;
+  mp *x;
+  unsigned long n;
+  int ok = 1;
+  uint32 p, q, r;
+  int i;
+
+  bp.p = *(mp **)v[0].buf;
+  bp.q = *(mp **)v[1].buf;
+  bp.n = mp_mul(MP_NEW, bp.p, bp.q);
+  x = *(mp **)v[2].buf;
+  n = *(unsigned long *)v[3].buf;
+
+  bbs_create(&b, bp.n, x);
+  p = bbs_bits(&b, 32);
+
+  bbs_seed(&b, x);
+  for (i = 0; i < n; i++)
+    bbs_step(&b);
+  q = bbs_bits(&b, 32);
+  bbs_wrap(&b);
+
+  bbs_rew(&b, &bp, n + (32 + b.k - 1) / b.k);
+  r = bbs_bits(&b, 32);
+
+  if (r != p) {
+    fputs("\n*** bbs rewind failure\n", stderr);
+    fputs("p = ", stderr); mp_writefile(bp.p, stderr, 10); fputc('\n', stderr);
+    fputs("q = ", stderr); mp_writefile(bp.q, stderr, 10); fputc('\n', stderr);
+    fputs("n = ", stderr); mp_writefile(bp.n, stderr, 10); fputc('\n', stderr);
+    fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
+    fprintf(stderr, "stepped %lu back\n", n + (32 + b.k - 1) / b.k);
+    fprintf(stderr, "expected output = %08lx, found %08lx\n",
+           (unsigned long)p, (unsigned long)r);
+    ok = 0;
+  }
+
+  bbs_seed(&b, x);
+  bbs_ff(&b, &bp, n);
+  r = bbs_bits(&b, 32);
+
+  if (q != r) {
+    fputs("\n*** bbs fastforward failure\n", stderr);
+    fputs("p = ", stderr); mp_writefile(bp.p, stderr, 10); fputc('\n', stderr);
+    fputs("q = ", stderr); mp_writefile(bp.q, stderr, 10); fputc('\n', stderr);
+    fputs("n = ", stderr); mp_writefile(bp.n, stderr, 10); fputc('\n', stderr);
+    fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
+    fprintf(stderr, "stepped %lu back\n", n + (32 + b.k - 1) / b.k);
+    fprintf(stderr, "expected output = %08lx, found %08lx\n",
+           (unsigned long)q, (unsigned long)r);
+    ok = 0;
+  }
+
+  bbs_destroy(&b);
+  mp_drop(bp.p);
+  mp_drop(bp.q);
+  mp_drop(bp.n);
+  mp_drop(x);
+
+  assert(mparena_count(MPARENA_GLOBAL) == 0);
+  return (ok);
+}
+
+static test_chunk tests[] = {
+  { "bbs-jump", verify, { &type_mp, &type_mp, &type_mp, &type_ulong, 0 } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  sub_init();
+  test_run(argc, argv, tests, SRCDIR "/tests/bbs");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/