Search code examples
node.jstypescriptcryptojsbigint

Typescript bigint exceeds (non-specified) maximum; `RangeError: Maximum BigInt size exceeded`


For safe prime p, prime q = (p - 1)/2, and generator g = 2, we have a distinct sequence (mod p):

g^0,g^1,...,g^q-1

Then the sequence repeats,

g^q (mod p) = g^0 (mod p)

The largest necessary bigint is g^q-1, but can't be computed, npx ts-node Test.ts:

const crypto = require('crypto');

const dh = crypto.createDiffieHellman(1024);
const p = BigInt(`0x${dh.getPrime().toString('hex')}`);
const g = BigInt(`0x${dh.getGenerator().toString('hex')}`);
const q = (p - 1n)/2n;

console.log(g ** 0n % p)
console.log(g ** 1n % p)
console.log(g ** (q-1n) % p)

As expect, 1n and 2n are output, then:

    console.log(g ** (q-1n) % p)
                 ^
RangeError: Maximum BigInt size exceeded

What's going wrong?


Solution

  • g ** (q-1n) % p is too big because the reduction mod p is done only at the end and g ** (q-1n) is indeed too big for the standard representation as a BigInt: it has more hex digits than there are atoms in the universe.

    This "intermediate overflow" would not happen if you reduced mod p after each multiplication (this does not change the result):

    function powermod(base, exp, p) {
      var result = 1n;
      while (exp !== 0n) {
        if (exp % 2n === 1n) result = result * base % p;
        base = base * base % p;
        exp >>= 1n;
      }
      return result;
    }
    console.log(powermod(g, q-1n, p));
    

    (This is not necessarily the fastest way to compute the power, see , section 4.6.3.)

    powermod is a ternary operation which cannot be expressed in terms of the binary operations ** and %, unless a Javascript engine is able to recognize an expression like base ** exp % p and treat it as ternary. I don't know if such Javascript engines exist. But there are npm packages for "finite fields" that implement this operation.