diff --git a/.cirrus.yml b/.cirrus.yml
index 506a860336..7a16cc698d 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,7 +1,8 @@
 env:
   WIDEMUL: auto
   STATICPRECOMPUTATION: yes
-  ECMULTGENPRECISION: auto
+  ECMULTGENBLOCKS: auto
+  ECMULTGENTEETH: auto
   ASM: no
   BUILD: check
   WITH_VALGRIND: yes
@@ -73,8 +74,9 @@ task:
         EXPERIMENTAL: yes
         SCHNORRSIG: yes
         CTIMETEST: no
-    - env: { ECMULTGENPRECISION: 2 }
-    - env: { ECMULTGENPRECISION: 8 }
+    - env: { ECMULTGENBLOCKS: 256, ECMULTGENTEETH: 1 }
+    - env: { ECMULTGENBLOCKS: 43, ECMULTGENTEETH: 6, STATICPRECOMPUTATION: no }
+    - env: { ECMULTGENBLOCKS: 1, ECMULTGENTEETH: 1, STATICPRECOMPUTATION: no }
     - env:
         RUN_VALGRIND: yes
         ASM: x86_64
diff --git a/README.md b/README.md
index 197a56fff8..2acc4f9f96 100644
--- a/README.md
+++ b/README.md
@@ -49,13 +49,11 @@ Implementation details
   * Use a much larger window for multiples of G, using precomputed multiples.
   * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
   * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
-* Point multiplication for signing
-  * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
+* Point multiplication for signing using Mike Hamburg's signed-digit multi-comb method (see https://eprint.iacr.org/2012/309, section 3.3)
   * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
     * Access the table with branch-free conditional moves so memory access is uniform.
     * No data-dependent branches
   * Optional runtime blinding which attempts to frustrate differential power analysis.
-  * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally.
 
 Build steps
 -----------
diff --git a/ci/cirrus.sh b/ci/cirrus.sh
index f26ca98d1d..679dbdfebf 100755
--- a/ci/cirrus.sh
+++ b/ci/cirrus.sh
@@ -15,7 +15,7 @@ valgrind --version || true
 ./configure \
     --enable-experimental="$EXPERIMENTAL" \
     --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
-    --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
+    --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-blocks="$ECMULTGENBLOCKS" --with-ecmult-gen-teeth="$ECMULTGENTEETH" \
     --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
     --enable-module-schnorrsig="$SCHNORRSIG" \
     --with-valgrind="$WITH_VALGRIND" \
diff --git a/configure.ac b/configure.ac
index e84005edf4..236109fd83 100644
--- a/configure.ac
+++ b/configure.ac
@@ -170,13 +170,21 @@ AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
 )],
 [req_ecmult_window=$withval], [req_ecmult_window=auto])
 
-AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision=2|4|8|auto],
-[Precision bits to tune the precomputed table size for signing.]
-[The size of the table is 32kB for 2 bits, 64kB for 4 bits, 512kB for 8 bits of precision.]
-[A larger table size usually results in possible faster signing.]
+AC_ARG_WITH([ecmult-gen-blocks], [AS_HELP_STRING([--with-ecmult-gen-blocks=BLOCKS|auto],
+[The number of blocks to use in the multi-comb multiplication algorithm, in the range [1..256].]
+[Larger values result in possibly better performance at the cost of a linearly larger precomputed table.]
+[There must exist a multiple of BLOCKS*TEETH that is between 256 and 288, inclusive.]
 ["auto" is a reasonable setting for desktop machines (currently 4). [default=auto]]
 )],
-[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
+[req_ecmult_gen_blocks=$withval], [req_ecmult_gen_blocks=auto])
+
+AC_ARG_WITH([ecmult-gen-teeth], [AS_HELP_STRING([--with-ecmult-gen-teeth=TEETH|auto],
+[The number of teeth to use in the multi-comb multiplication algorithm, in the range [1..8].]
+[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
+[There must exist a multiple of BLOCKS*TEETH that is between 256 and 288, inclusive.]
+["auto" is a reasonable setting for desktop machines (currently 5). [default=auto]]
+)],
+[req_ecmult_gen_teeth=$withval], [req_ecmult_gen_teeth=auto])
 
 AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
 [Build with extra checks for running inside Valgrind [default=auto]]
@@ -296,19 +304,45 @@ case $set_ecmult_window in
   ;;
 esac
 
-# Set ecmult gen precision
-if test x"$req_ecmult_gen_precision" = x"auto"; then
-  set_ecmult_gen_precision=4
+# Set ecmult gen blocks
+if test x"$req_ecmult_gen_blocks" = x"auto"; then
+  set_ecmult_gen_blocks=4
 else
-  set_ecmult_gen_precision=$req_ecmult_gen_precision
+  set_ecmult_gen_blocks=$req_ecmult_gen_blocks
 fi
+error_gen_blocks=['option to --with-ecmult-gen-blocks not an integer in range [1..256] or "auto"']
+case $set_ecmult_gen_blocks in
+''|*[[!0-9]]*)
+  # no valid integer
+  AC_MSG_ERROR($error_gen_blocks)
+  ;;
+*)
+  if test "$set_ecmult_gen_blocks" -lt 1 -o "$set_ecmult_gen_blocks" -gt 256 ; then
+    # not in range
+    AC_MSG_ERROR($error_gen_blocks)
+  fi
+  AC_DEFINE_UNQUOTED(COMB_BLOCKS, $set_ecmult_gen_blocks, [Set number of blocks in ecmult_gen precomputation])
+  ;;
+esac
 
-case $set_ecmult_gen_precision in
-2|4|8)
-  AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits])
+#set ecmult gen teeth
+if test x"$req_ecmult_gen_teeth" = x"auto"; then
+  set_ecmult_gen_teeth=5
+else
+  set_ecmult_gen_teeth=$req_ecmult_gen_teeth
+fi
+error_gen_teeth=['option to --with-ecmult-gen-teeth not an integer in range [1..8] or "auto"']
+case $set_ecmult_gen_teeth in
+''|*[[!0-9]]*)
+  # no valid integer
+  AC_MSG_ERROR($error_gen_teeth)
   ;;
 *)
-  AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"'])
+  if test "$set_ecmult_gen_teeth" -lt 1 -o "$set_ecmult_gen_teeth" -gt 8 ; then
+    # not in range
+    AC_MSG_ERROR($error_gen_teeth)
+  fi
+  AC_DEFINE_UNQUOTED(COMB_TEETH, $set_ecmult_gen_teeth, [Set number of teeth in ecmult_gen precomputation])
   ;;
 esac
 
@@ -515,7 +549,8 @@ echo "  module schnorrsig       = $enable_module_schnorrsig"
 echo
 echo "  asm                     = $set_asm"
 echo "  ecmult window size      = $set_ecmult_window"
-echo "  ecmult gen prec. bits   = $set_ecmult_gen_precision"
+echo "  ecmult gen blocks       = $set_ecmult_gen_blocks"
+echo "  ecmult gen teeth        = $set_ecmult_gen_teeth"
 # Hide test-only options unless they're used.
 if test x"$set_widemul" != xauto; then
 echo "  wide multiplication     = $set_widemul"
diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h
index 539618dcbb..c9445751e3 100644
--- a/src/ecmult_gen.h
+++ b/src/ecmult_gen.h
@@ -10,27 +10,91 @@
 #include "scalar.h"
 #include "group.h"
 
-#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8
-#  error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8."
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#if defined(EXHAUSTIVE_TEST_ORDER)
+
+  /* We need to control these values for exhaustive tests because
+   * the tables cannot have infinities in them (secp256k1_ge_storage
+   * doesn't support infinities) */
+#undef COMB_BLOCKS
+#undef COMB_TEETH
+#undef COMB_SPACING
+
+#  if EXHAUSTIVE_TEST_ORDER > 32
+#    define COMB_BLOCKS 52
+#    define COMB_TEETH 5
+#  elif EXHAUSTIVE_TEST_ORDER > 16
+#    define COMB_BLOCKS 64
+#    define COMB_TEETH 4
+#  elif EXHAUSTIVE_TEST_ORDER > 8
+#    define COMB_BLOCKS 86
+#    define COMB_TEETH 3
+#  elif EXHAUSTIVE_TEST_ORDER > 4
+#    define COMB_BLOCKS 128
+#    define COMB_TEETH 2
+#  else
+#    define COMB_BLOCKS 256
+#    define COMB_TEETH 1
+#  endif
+
+#  define COMB_SPACING 1
+
+#else
+
+  /* COMB_BLOCKS, COMB_TEETH, COMB_SPACING must all be positive and the product of the three (COMB_BITS)
+   * must evaluate to a value in the range [256, 288]. The resulting memory usage for precomputation
+   * will be COMB_POINTS_TOTAL * sizeof(secp256k1_ge_storage). */
+#  ifndef COMB_BLOCKS
+#    define COMB_BLOCKS 4
+#  endif
+#  ifndef COMB_TEETH
+#    define COMB_TEETH 5
+#  endif
+#  ifndef COMB_SPACING
+#    define COMB_SPACING ((COMB_BLOCKS * COMB_TEETH + 255) / (COMB_BLOCKS * COMB_TEETH))
+#  endif
+
+#endif
+
+#if !(1 <= COMB_BLOCKS && COMB_BLOCKS <= 256)
+#  error "COMB_BLOCKS must be in the range [1, 256]"
+#endif
+#if !(1 <= COMB_TEETH && COMB_TEETH <= 8)
+#  error "COMB_TEETH must be in the range [1, 8]"
+#endif
+#if !(1 <= COMB_SPACING && COMB_SPACING <= 256)
+#  error "COMB_SPACING must be in the range [1, 256]"
+#endif
+
+/* The remaining COMB_* parameters are derived values, don't modify these. */
+#define COMB_BITS (COMB_BLOCKS * COMB_TEETH * COMB_SPACING)
+#define COMB_GROUPED ((COMB_SPACING == 1) && ((32 % COMB_TEETH) == 0))
+#define COMB_OFFSET (COMB_BITS == 256)
+#define COMB_POINTS (1 << (COMB_TEETH - 1))
+#define COMB_POINTS_TOTAL (COMB_BLOCKS * COMB_POINTS)
+#define COMB_MASK (COMB_POINTS - 1)
+
+#if !(256 <= COMB_BITS && COMB_BITS <= 288)
+#  error "COMB_BITS must be in the range [256, 288]"
 #endif
-#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS
-#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B)
-#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B)
 
 typedef struct {
-    /* For accelerating the computation of a*G:
-     * To harden against timing attacks, use the following mechanism:
-     * * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1).
-     * * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where:
-     *   * U_i = U * 2^i, for i=0 ... PREC_N-2
-     *   * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1
-     *   where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0.
-     * For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is
-     * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1).
-     * None of the resulting prec group elements have a known scalar, and neither do any of
-     * the intermediate sums while computing a*G.
+    /* Precomputation data for the signed-digit multi-comb algorithm as described in section 3.3 of:
+     *     "Fast and compact elliptic-curve cryptography", Mike Hamburg
+     *         (https://eprint.iacr.org/2012/309)
      */
-    secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */
+    secp256k1_ge_storage (*prec)[COMB_BLOCKS][COMB_POINTS];
+#if COMB_OFFSET
+    /* Signed recoding of a 256-bit scalar must be at least 257 bits, with the top bit always 1. We
+     * support a 256-bit comb over a 257-bit recoding by pre-adding an 'offset' value to the context's
+     * 'initial' value, to account for the high 1 bit. Note that the 'offset' is calculated to allow
+     * for the (COMB_SPACING - 1) doublings in the _ecmult_gen ladder.
+     */
+    secp256k1_ge offset;
+#endif
     secp256k1_scalar blind;
     secp256k1_gej initial;
 } secp256k1_ecmult_gen_context;
diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h
index 384a67faed..728a8945ea 100644
--- a/src/ecmult_gen_impl.h
+++ b/src/ecmult_gen_impl.h
@@ -28,10 +28,9 @@ static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx)
 
 static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) {
 #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
-    secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G];
-    secp256k1_gej gj;
-    secp256k1_gej nums_gej;
-    int i, j;
+    secp256k1_ge prec[COMB_POINTS_TOTAL + COMB_OFFSET];
+    secp256k1_gej u, sum;
+    int block, index, spacing, stride, tooth;
     size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
     void* const base = *prealloc;
 #endif
@@ -40,63 +39,60 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
         return;
     }
 #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
-    ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
+    ctx->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
 
     /* get the generator */
-    secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
-
-    /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */
-    {
-        static const unsigned char nums_b32[33] = "The scalar for this x is unknown";
-        secp256k1_fe nums_x;
-        secp256k1_ge nums_ge;
-        int r;
-        r = secp256k1_fe_set_b32(&nums_x, nums_b32);
-        (void)r;
-        VERIFY_CHECK(r);
-        r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0);
-        (void)r;
-        VERIFY_CHECK(r);
-        secp256k1_gej_set_ge(&nums_gej, &nums_ge);
-        /* Add G to make the bits in x uniformly distributed. */
-        secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL);
-    }
+    secp256k1_gej_set_ge(&u, &secp256k1_ge_const_g);
 
     /* compute prec. */
     {
-        secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */
-        secp256k1_gej gbase;
-        secp256k1_gej numsbase;
-        gbase = gj; /* PREC_G^j * G */
-        numsbase = nums_gej; /* 2^j * nums. */
-        for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
-            /* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */
-            precj[j*ECMULT_GEN_PREC_G] = numsbase;
-            for (i = 1; i < ECMULT_GEN_PREC_G; i++) {
-                secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL);
-            }
-            /* Multiply gbase by PREC_G. */
-            for (i = 0; i < ECMULT_GEN_PREC_B; i++) {
-                secp256k1_gej_double_var(&gbase, &gbase, NULL);
+        secp256k1_gej ds[COMB_TEETH];
+        secp256k1_gej vs[COMB_POINTS_TOTAL + COMB_OFFSET];
+        int vs_pos = 0;
+
+        for (block = 0; block < COMB_BLOCKS; ++block) {
+            secp256k1_gej_set_infinity(&sum);
+            for (tooth = 0; tooth < COMB_TEETH; ++tooth) {
+                secp256k1_gej_add_var(&sum, &sum, &u, NULL);
+                secp256k1_gej_double_var(&u, &u, NULL);
+                ds[tooth] = u;
+                if (block + tooth != COMB_BLOCKS + COMB_TEETH - 2) {
+                    for (spacing = 1; spacing < COMB_SPACING; ++spacing) {
+                        secp256k1_gej_double_var(&u, &u, NULL);
+                    }
+                }
             }
-            /* Multiply numbase by 2. */
-            secp256k1_gej_double_var(&numsbase, &numsbase, NULL);
-            if (j == ECMULT_GEN_PREC_N - 2) {
-                /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
-                secp256k1_gej_neg(&numsbase, &numsbase);
-                secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
+            secp256k1_gej_neg(&vs[vs_pos++], &sum);
+            for (tooth = 0; tooth < (COMB_TEETH - 1); ++tooth) {
+                stride = 1 << tooth;
+                for (index = 0; index < stride; ++index, ++vs_pos) {
+                    secp256k1_gej_add_var(&vs[vs_pos], &vs[vs_pos - stride], &ds[tooth], NULL);
+                }
             }
         }
-        secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G);
+        VERIFY_CHECK(vs_pos == COMB_POINTS_TOTAL);
+#if COMB_OFFSET
+        vs[COMB_POINTS_TOTAL] = ds[COMB_TEETH - 1];
+#endif
+        secp256k1_ge_set_all_gej_var(prec, vs, COMB_POINTS_TOTAL + COMB_OFFSET);
     }
-    for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
-        for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
-            secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]);
+
+    for (block = 0; block < COMB_BLOCKS; ++block) {
+        for (index = 0; index < COMB_POINTS; ++index) {
+            secp256k1_ge_to_storage(&(*ctx->prec)[block][index], &prec[block * COMB_POINTS + index]);
         }
     }
+
+#if COMB_OFFSET
+    ctx->offset = prec[COMB_POINTS_TOTAL];
+#endif
+
 #else
     (void)prealloc;
-    ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context;
+    ctx->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])secp256k1_ecmult_gen_ctx_prec;
+#if COMB_OFFSET
+    secp256k1_ge_from_storage(&ctx->offset, &secp256k1_ecmult_gen_ctx_offset);
+#endif
 #endif
     secp256k1_ecmult_gen_blind(ctx, NULL);
 }
@@ -109,14 +105,19 @@ static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_co
 #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
     if (src->prec != NULL) {
         /* We cast to void* first to suppress a -Wcast-align warning. */
-        dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
+        dst->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
     }
-#else
-    (void)dst, (void)src;
+#if COMB_OFFSET
+    dst->offset = src->offset;
 #endif
+#endif
+    (void)dst, (void)src;
 }
 
 static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
+#if COMB_OFFSET
+    secp256k1_ge_clear(&ctx->offset);
+#endif
     secp256k1_scalar_clear(&ctx->blind);
     secp256k1_gej_clear(&ctx->initial);
     ctx->prec = NULL;
@@ -126,38 +127,86 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
     secp256k1_ge add;
     secp256k1_ge_storage adds;
     secp256k1_scalar gnb;
-    int bits;
-    int i, j;
+    uint32_t bits;
+
+    uint32_t abs, bit_pos, block, comb_off, index, sign;
+#if !COMB_GROUPED
+    uint32_t bit, tooth;
+#endif
+    uint32_t recoded[9];
+    secp256k1_fe neg;
+
     memset(&adds, 0, sizeof(adds));
     *r = ctx->initial;
+
     /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
     secp256k1_scalar_add(&gnb, gn, &ctx->blind);
-    add.infinity = 0;
-    for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
-        bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B);
-        for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
-            /** This uses a conditional move to avoid any secret data in array indexes.
-             *   _Any_ use of secret indexes has been demonstrated to result in timing
-             *   sidechannels, even when the cache-line access patterns are uniform.
-             *  See also:
-             *   "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe
-             *    (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and
-             *   "Cache Attacks and Countermeasures: the Case of AES", RSA 2006,
-             *    by Dag Arne Osvik, Adi Shamir, and Eran Tromer
-             *    (https://www.tau.ac.il/~tromer/papers/cache.pdf)
-             */
-            secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits);
+    secp256k1_scalar_signed_recoding(recoded, &gnb, COMB_BITS + COMB_OFFSET);
+
+    comb_off = COMB_SPACING - 1;
+    for (;;) {
+        bit_pos = comb_off;
+        for (block = 0; block < COMB_BLOCKS; ++block) {
+#if COMB_GROUPED
+            bits = recoded[bit_pos >> 5] >> (bit_pos & 0x1F);
+            bit_pos += COMB_TEETH;
+#else
+            bits = 0;
+            for (tooth = 0; tooth < COMB_TEETH; ++tooth) {
+                bit = recoded[bit_pos >> 5] >> (bit_pos & 0x1F);
+                bits &= ~(1 << tooth);
+                bits ^= bit << tooth;
+                bit_pos += COMB_SPACING;
+            }
+#endif
+
+            sign = (bits >> (COMB_TEETH - 1)) & 1;
+            abs = (bits ^ -sign) & COMB_MASK;
+
+            VERIFY_CHECK(sign == 0 || sign == 1);
+            VERIFY_CHECK(abs < COMB_POINTS);
+
+            for (index = 0; index < COMB_POINTS; ++index) {
+                /** This uses a conditional move to avoid any secret data in array indexes.
+                 *   _Any_ use of secret indexes has been demonstrated to result in timing
+                 *   sidechannels, even when the cache-line access patterns are uniform.
+                 *  See also:
+                 *   "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe
+                 *    (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and
+                 *   "Cache Attacks and Countermeasures: the Case of AES", RSA 2006,
+                 *    by Dag Arne Osvik, Adi Shamir, and Eran Tromer
+                 *    (http://www.tau.ac.il/~tromer/papers/cache.pdf)
+                 */
+                secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[block][index], index == abs);
+            }
+
+            secp256k1_ge_from_storage(&add, &adds);
+            secp256k1_fe_negate(&neg, &add.y, 1);
+            secp256k1_fe_cmov(&add.y, &neg, sign);
+
+            secp256k1_gej_add_ge(r, r, &add);
         }
-        secp256k1_ge_from_storage(&add, &adds);
-        secp256k1_gej_add_ge(r, r, &add);
+
+        if (comb_off-- == 0) {
+            break;
+        }
+
+        secp256k1_gej_double(r, r);
     }
+
+    secp256k1_fe_clear(&neg);
+    memset(recoded, 0, sizeof(recoded));
+    abs = 0;
+    sign = 0;
     bits = 0;
     secp256k1_ge_clear(&add);
+    memset(&adds, 0, sizeof(adds));
     secp256k1_scalar_clear(&gnb);
 }
 
 /* Setup blinding values for secp256k1_ecmult_gen. */
 static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) {
+    int spacing;
     secp256k1_scalar b;
     secp256k1_gej gb;
     secp256k1_fe s;
@@ -170,6 +219,12 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
         secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g);
         secp256k1_gej_neg(&ctx->initial, &ctx->initial);
         secp256k1_scalar_set_int(&ctx->blind, 1);
+        for (spacing = 1; spacing < COMB_SPACING; ++spacing) {
+            secp256k1_scalar_add(&ctx->blind, &ctx->blind, &ctx->blind);
+        }
+#if COMB_OFFSET
+        secp256k1_gej_add_ge(&ctx->initial, &ctx->initial, &ctx->offset);
+#endif
     }
     /* The prior blinding value (if not reset) is chained forward by including it in the hash. */
     secp256k1_scalar_get_b32(nonce32, &ctx->blind);
@@ -201,6 +256,12 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
     secp256k1_scalar_negate(&b, &b);
     ctx->blind = b;
     ctx->initial = gb;
+    for (spacing = 1; spacing < COMB_SPACING; ++spacing) {
+        secp256k1_scalar_add(&ctx->blind, &ctx->blind, &ctx->blind);
+    }
+#if COMB_OFFSET
+    secp256k1_gej_add_ge(&ctx->initial, &ctx->initial, &ctx->offset);
+#endif
     secp256k1_scalar_clear(&b);
     secp256k1_gej_clear(&gb);
 }
diff --git a/src/gen_context.c b/src/gen_context.c
index 05e7dee175..6abcc9df2b 100644
--- a/src/gen_context.c
+++ b/src/gen_context.c
@@ -37,6 +37,13 @@ int main(int argc, char **argv) {
     int inner;
     int outer;
     FILE* fp;
+    const char *SC_FORMAT = "    SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)";
+
+    const int blocks = COMB_BLOCKS;
+    const int points = COMB_POINTS;
+#if COMB_OFFSET
+    secp256k1_ge_storage offset;
+#endif
 
     (void)argc;
     (void)argv;
@@ -51,26 +58,34 @@ int main(int argc, char **argv) {
     fprintf(fp, "#define SECP256K1_ECMULT_STATIC_CONTEXT_H\n");
     fprintf(fp, "#include \"src/group.h\"\n");
     fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
-    fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
-    fprintf(fp, "   #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build.\n");
+    fprintf(fp, "#if COMB_BLOCKS != %i || COMB_TEETH != %i || COMB_SPACING != %i\n", COMB_BLOCKS, COMB_TEETH, COMB_SPACING);
+    fprintf(fp, "   #error configuration mismatch, invalid COMB_BLOCKS, COMB_TEETH, or COMB_SPACING. Try deleting ecmult_static_context.h before the build.\n");
     fprintf(fp, "#endif\n");
-    fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = {\n");
 
     base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE);
     prealloc = base;
     secp256k1_ecmult_gen_context_init(&ctx);
     secp256k1_ecmult_gen_context_build(&ctx, &prealloc);
-    for(outer = 0; outer != ECMULT_GEN_PREC_N; outer++) {
+
+#if COMB_OFFSET
+    secp256k1_ge_to_storage(&offset, &ctx.offset);
+    fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_gen_ctx_offset =\n");
+    fprintf(fp, SC_FORMAT, SECP256K1_GE_STORAGE_CONST_GET(offset));
+    fprintf(fp, ";\n");
+#endif
+
+    fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_gen_ctx_prec[%i][%i] = {\n", blocks, points);
+    for(outer = 0; outer != blocks; outer++) {
         fprintf(fp,"{\n");
-        for(inner = 0; inner != ECMULT_GEN_PREC_G; inner++) {
-            fprintf(fp,"    SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner]));
-            if (inner != ECMULT_GEN_PREC_G - 1) {
+        for(inner = 0; inner != points; inner++) {
+            fprintf(fp, SC_FORMAT, SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner]));
+            if (inner != (points - 1)) {
                 fprintf(fp,",\n");
             } else {
                 fprintf(fp,"\n");
             }
         }
-        if (outer != ECMULT_GEN_PREC_N - 1) {
+        if (outer != (blocks - 1)) {
             fprintf(fp,"},\n");
         } else {
             fprintf(fp,"}\n");
diff --git a/src/scalar.h b/src/scalar.h
index aaaa3d8827..a9dd5ca139 100644
--- a/src/scalar.h
+++ b/src/scalar.h
@@ -102,4 +102,6 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_
 /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time.  Both *r and *a must be initialized.*/
 static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag);
 
+static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits);
+
 #endif /* SECP256K1_SCALAR_H */
diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h
index a1def26fca..88cd85a9d1 100644
--- a/src/scalar_4x64_impl.h
+++ b/src/scalar_4x64_impl.h
@@ -865,4 +865,39 @@ SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a)
     return !(a->d[0] & 1);
 }
 
+SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) {
+    uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3], a4;
+    uint64_t mask = (a0 & 1ULL) - 1ULL;
+    uint128_t c;
+
+    VERIFY_CHECK(256 < bits && bits <= 288);
+
+    c  = (uint128_t)a0 + (SECP256K1_N_0 & mask);
+    a0 = (uint64_t)c; c >>= 64;
+    c += (uint128_t)a1 + (SECP256K1_N_1 & mask);
+    a1 = (uint64_t)c; c >>= 64;
+    c += (uint128_t)a2 + (SECP256K1_N_2 & mask);
+    a2 = (uint64_t)c; c >>= 64;
+    c += (uint128_t)a3 + (SECP256K1_N_3 & mask);
+    a3 = (uint64_t)c; c >>= 64;
+    a4 = (uint64_t)c;
+    VERIFY_CHECK(a0 & 1ULL);
+    VERIFY_CHECK(a4 < 2ULL);
+
+    a0 = (a0 >> 1) | (a1 << 63);
+    a1 = (a1 >> 1) | (a2 << 63);
+    a2 = (a2 >> 1) | (a3 << 63);
+    a3 = (a3 >> 1) | (a4 << 63);
+
+    r[0] = (uint32_t)a0;
+    r[1] = (uint32_t)(a0 >> 32);
+    r[2] = (uint32_t)a1;
+    r[3] = (uint32_t)(a1 >> 32);
+    r[4] = (uint32_t)a2;
+    r[5] = (uint32_t)(a2 >> 32);
+    r[6] = (uint32_t)a3;
+    r[7] = (uint32_t)(a3 >> 32);
+    r[8] = 1UL << (bits - 257);
+}
+
 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h
index 62c7ae7156..6dcdcbc7d1 100644
--- a/src/scalar_8x32_impl.h
+++ b/src/scalar_8x32_impl.h
@@ -732,4 +732,43 @@ SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a)
     return !(a->d[0] & 1);
 }
 
+SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) {
+    uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3],
+             a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7], a8;
+    uint32_t mask = (a0 & 1UL) - 1UL;
+    uint64_t c;
+
+    VERIFY_CHECK(256 < bits && bits <= 288);
+
+    c  = (uint64_t)a0 + (SECP256K1_N_0 & mask);
+    a0 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a1 + (SECP256K1_N_1 & mask);
+    a1 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a2 + (SECP256K1_N_2 & mask);
+    a2 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a3 + (SECP256K1_N_3 & mask);
+    a3 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a4 + (SECP256K1_N_4 & mask);
+    a4 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a5 + (SECP256K1_N_5 & mask);
+    a5 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a6 + (SECP256K1_N_6 & mask);
+    a6 = (uint32_t)c; c >>= 32;
+    c += (uint64_t)a7 + (SECP256K1_N_7 & mask);
+    a7 = (uint32_t)c; c >>= 32;
+    a8 = (uint32_t)c;
+    VERIFY_CHECK(a0 & 1UL);
+    VERIFY_CHECK(a8 < 2UL);
+
+    r[0] = (a0 >> 1) | (a1 << 31);
+    r[1] = (a1 >> 1) | (a2 << 31);
+    r[2] = (a2 >> 1) | (a3 << 31);
+    r[3] = (a3 >> 1) | (a4 << 31);
+    r[4] = (a4 >> 1) | (a5 << 31);
+    r[5] = (a5 >> 1) | (a6 << 31);
+    r[6] = (a6 >> 1) | (a7 << 31);
+    r[7] = (a7 >> 1) | (a8 << 31);
+    r[8] = 1UL << (bits - 257);
+}
+
 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h
index 7176f0b2ca..d2f2ae81ee 100644
--- a/src/scalar_low_impl.h
+++ b/src/scalar_low_impl.h
@@ -136,4 +136,28 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
     secp256k1_scalar_inverse(r, x);
 }
 
+SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) {
+    uint32_t a0 = *a, a1;
+    uint32_t mask = (a0 & 1UL) - 1UL;
+    uint64_t c;
+
+    VERIFY_CHECK(256 < bits && bits <= 288);
+
+    c  = (uint64_t)a0 + (EXHAUSTIVE_TEST_ORDER & mask);
+    a0 = (uint32_t)c; c >>= 32;
+    a1 = (uint32_t)c;
+    VERIFY_CHECK(a0 & 1UL);
+    VERIFY_CHECK(a1 < 2UL);
+
+    r[0] = (a0 >> 1) | (a1 << 31);
+    r[1] = 0;
+    r[2] = 0;
+    r[3] = 0;
+    r[4] = 0;
+    r[5] = 0;
+    r[6] = 0;
+    r[7] = 0;
+    r[8] = 1UL << (bits - 257);
+}
+
 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */