inthash
Version:
Efficient integer hashing library using Knuth's multiplicative method for Javascript and Typescript, perfect for obfuscating sequential numbers.
104 lines (103 loc) • 2.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPrimeMillerRabin = isPrimeMillerRabin;
exports.randomPrime = randomPrime;
function modPow(x, y, p) {
x = x % p;
let result = 1n;
while (y > 0n) {
if (y & 1n) {
result = (result * x) % p;
}
y = y / 2n;
x = (x * x) % p;
}
return result;
}
function randomBigInt(range) {
const n = range.toString(2).length;
if (n === 1) {
return 0n;
}
const bytes = Math.ceil(n / 8);
const mask = (1n << BigInt(n)) - 1n;
let result;
do {
const buf = new Uint8Array(bytes);
crypto.getRandomValues(buf);
result = 0n;
for (let i = 0; i < bytes; i++) {
result = (result << 8n) | BigInt(buf[i]);
}
result = result & mask;
} while (result >= range);
return result;
}
// https://github.com/openssl/openssl/blob/4cedf30e995f9789cf6bb103e248d33285a84067/crypto/bn/bn_prime.c#L337
function isPrimeMillerRabin(w, iterations) {
if (w === 2n) {
return true;
}
if (w < 2n || w % 2n === 0n) {
return false;
}
const w1 = w - 1n;
const w3 = w - 3n;
let a = 1;
let m = w1;
// (Step 1) Calculate largest integer 'a' such that 2^a divides w-1
// (Step 2) m = (w-1) / 2^a
while (m % 2n === 0n) {
a++;
m /= 2n;
}
// TODO montgomery
iterations = iterations ?? (w.toString(2).length > 2048 ? 128 : 64);
// (Step 4)
outer_loop: for (let i = 0; i < iterations; i++) {
// (Step 4.1) obtain a Random string of bits b where 1 < b < w-1 */
const b = randomBigInt(w3) + 2n;
// (Step 4.5) z = b^m mod w
// in openssl, Montgomery modular multiplication (TODO)
let z = modPow(b, m, w);
/* (Step 4.6) if (z = 1 or z = w-1) */
if (z === 1n || z === w1) {
continue outer_loop;
}
/* (Step 4.7) for j = 1 to a-1 */
for (let j = 1; j < a; j++) {
// (Step 4.7.1 - 4.7.2) x = z, z = x^2 mod w
z = modPow(z, 2n, w);
// (Step 4.7.3)
if (z === w1) {
continue outer_loop;
}
// (Step 4.7.4)
if (z === 1n) {
return false;
}
}
return false;
}
return true;
}
function randomOdd(bits) {
let result = 1n;
for (let i = 2; i < bits; i++) {
result = (result << 1n) | (Math.random() < 0.5 ? 1n : 0n);
}
return (result << 1n) | 1n;
}
function randomPrime(bits) {
if (bits < 2) {
return 1n;
}
let result = randomOdd(bits);
while (!isPrimeMillerRabin(result)) {
result += 2n;
if (result.toString(2).length > bits) {
result = randomOdd(bits);
}
}
return result;
}