wasmcurves
Version:
elliptic curves implementations in wasm
155 lines (136 loc) • 3.74 kB
JavaScript
// Many of these utilities are from the `big-integer` library,
// but adjusted to only work with native BigInt type
// Ref https://github.com/peterolson/BigInteger.js/blob/e5d2154d3c417069c51e7116bafc3b91d0b9fe41/BigInteger.js
// Originally licensed The Unlicense
function compare(a, b) {
return a === b ? 0 : a > b ? 1 : -1;
}
function square(n) {
return n * n;
}
function isOdd(n) {
return n % 2n !== 0n;
}
function isEven(n) {
return n % 2n === 0n;
}
function isNegative(n) {
return n < 0n;
}
function isPositive(n) {
return n > 0n;
}
function bitLength(n) {
if (isNegative(n)) {
return n.toString(2).length - 1; // discard the - sign
} else {
return n.toString(2).length;
}
}
function abs(n) {
return n < 0n ? -n : n;
}
function isUnit(n) {
return abs(n) === 1n;
}
function modInv(a, n) {
var t = 0n, newT = 1n, r = n, newR = abs(a), q, lastT, lastR;
while (newR !== 0n) {
q = r / newR;
lastT = t;
lastR = r;
t = newT;
r = newR;
newT = lastT - (q * newT);
newR = lastR - (q * newR);
}
if (!isUnit(r)) throw new Error(a.toString() + " and " + n.toString() + " are not co-prime");
if (compare(t, 0n) === -1) {
t = t + n;
}
if (isNegative(a)) {
return -t;
}
return t;
}
function modPow(n, exp, mod) {
if (mod === 0n) throw new Error("Cannot take modPow with modulus 0");
var r = 1n,
base = n % mod;
if (isNegative(exp)) {
exp = exp * -1n;
base = modInv(base, mod);
}
while (isPositive(exp)) {
if (base === 0n) return 0n;
if (isOdd(exp)) r = r * base % mod;
exp = exp / 2n;
base = square(base) % mod;
}
return r;
}
function compareAbs(a, b) {
a = a >= 0n ? a : -a;
b = b >= 0n ? b : -b;
return a === b ? 0 : a > b ? 1 : -1;
}
function isDivisibleBy(a, n) {
if (n === 0n) return false;
if (isUnit(n)) return true;
if (compareAbs(n, 2n) === 0) return isEven(a);
return a % n === 0n;
}
function isBasicPrime(v) {
var n = abs(v);
if (isUnit(n)) return false;
if (n === 2n || n === 3n || n === 5n) return true;
if (isEven(n) || isDivisibleBy(n, 3n) || isDivisibleBy(n, 5n)) return false;
if (n < 49n) return true;
// we don't know if it's prime: let the other functions figure it out
}
function prev(n) {
return n - 1n;
}
function millerRabinTest(n, a) {
var nPrev = prev(n),
b = nPrev,
r = 0,
d, i, x;
while (isEven(b)) b = b / 2n, r++;
next: for (i = 0; i < a.length; i++) {
if (n < a[i]) continue;
x = modPow(BigInt(a[i]), b, n);
if (isUnit(x) || x === nPrev) continue;
for (d = r - 1; d != 0; d--) {
x = square(x) % n;
if (isUnit(x)) return false;
if (x === nPrev) continue next;
}
return false;
}
return true;
}
function isPrime(p) {
var isPrime = isBasicPrime(p);
if (isPrime !== undefined) return isPrime;
var n = abs(p);
var bits = bitLength(n);
if (bits <= 64)
return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]);
var logN = Math.log(2) * Number(bits);
var t = Math.ceil(logN);
for (var a = [], i = 0; i < t; i++) {
a.push(BigInt(i + 2));
}
return millerRabinTest(n, a);
}
module.exports.bitLength = bitLength;
module.exports.isOdd = isOdd;
module.exports.isNegative = isNegative;
module.exports.abs = abs;
module.exports.isUnit = isUnit;
module.exports.compare = compare;
module.exports.modInv = modInv;
module.exports.modPow = modPow;
module.exports.isPrime = isPrime;
module.exports.square = square;