diff --git a/packages/core/package.json b/packages/core/package.json index 509937fc..c626d827 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,7 +21,6 @@ "@types/node": "^15.12.1", "@types/events": "^3.0.0", "@mtcute/tl": "~1.131.0", - "leemon": "6.2.0", "pako": "2.0.2", "big-integer": "1.6.48", "events": "3.2.0" diff --git a/packages/core/src/utils/bigint-utils.ts b/packages/core/src/utils/bigint-utils.ts index eaa24a7f..9940110d 100644 --- a/packages/core/src/utils/bigint-utils.ts +++ b/packages/core/src/utils/bigint-utils.ts @@ -73,3 +73,15 @@ export function bufferToBigInt( export function randomBigInt(size: number): BigInteger { return bufferToBigInt(randomBytes(size)) } + +export function randomBigIntInRange(max: BigInteger, min = bigInt.one): BigInteger { + const interval = max.minus(min) + if (interval.isNegative()) throw new Error('expected min < max') + + const byteSize = interval.bitLength().divide(8).toJSNumber() + + let result = randomBigInt(byteSize) + while (result.gt(interval)) result = result.minus(interval) + + return min.plus(result) +} diff --git a/packages/core/src/utils/crypto/factorization.ts b/packages/core/src/utils/crypto/factorization.ts index f4aa2287..d87cdb17 100644 --- a/packages/core/src/utils/crypto/factorization.ts +++ b/packages/core/src/utils/crypto/factorization.ts @@ -1,116 +1,71 @@ -import { getRandomInt } from '../misc-utils' - -const { - eGCD_, - greater, - divide_, - str2bigInt, - equalsInt, - isZero, - bigInt2str, - copy_, - copyInt_, - rightShift_, - sub_, - add_, - one, - bpe, -} = require('leemon') - -type leemonBigint = number[] - -function leemonBigintToBytes(val: leemonBigint): Buffer { - return Buffer.from(bigInt2str(val, 16), 'hex') -} +import { bigIntToBuffer, bufferToBigInt, randomBigIntInRange } from '../bigint-utils' +import bigInt, { BigInteger } from 'big-integer' export function factorizePQSync(pq: Buffer): [Buffer, Buffer] { - return leemonPqFactorizationSync( - str2bigInt(pq.toString('hex'), 16, Math.ceil(64 / bpe) + 1) - ) -} + const pq_ = bufferToBigInt(pq) -// TODO: maybe move this to CryptoProvider to allow delegating computations to a worker? -// returns tuple of [P, Q] -function leemonPqFactorizationSync(what: leemonBigint): [Buffer, Buffer] { - // i honestly have no idea where this code originates and how it works, - // but this is some ancient magic kind of stuff. - // i'm guessing it's from webogram (https://github.com/zhukov/webogram/blob/master/app/js/lib/bin_utils.js) - // but i'm not sure if this is its real origin - const minBits = 64 - const minLen = Math.ceil(minBits / bpe) + 1 - let it = 0 - let i, q - let j, lim - let P - let Q - const a = new Array(minLen) - const b = new Array(minLen) - const c = new Array(minLen) - const g = new Array(minLen) - const z = new Array(minLen) - const x = new Array(minLen) - const y = new Array(minLen) + const n = PollardRhoBrent(pq_) + const m = pq_.divide(n) - for (i = 0; i < 3; i++) { - q = (getRandomInt(128) & 15) + 17 - copyInt_(x, getRandomInt(1000000000) + 1) - copy_(y, x) - lim = 1 << (i + 18) - - for (j = 1; j < lim; j++) { - // idk why is this needed, but i'd rather not touch :shrug: - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ++it - copy_(a, x) - copy_(b, x) - copyInt_(c, q) - - while (!isZero(b)) { - if (b[0] & 1) { - add_(c, a) - if (greater(c, what)) { - sub_(c, what) - } - } - add_(a, a) - if (greater(a, what)) { - sub_(a, what) - } - rightShift_(b, 1) - } - - copy_(x, c) - if (greater(x, y)) { - copy_(z, x) - sub_(z, y) - } else { - copy_(z, y) - sub_(z, x) - } - eGCD_(z, what, g, a, b) - if (!equalsInt(g, 1)) { - break - } - if ((j & (j - 1)) == 0) { - copy_(y, x) - } - } - if (greater(g, one)) { - break - } - } - - divide_(what, g, x, y) - - if (greater(g, x)) { - P = x - Q = g + let p, q + if (n.lt(m)) { + p = n + q = m } else { - P = g - Q = x + p = m + q = n } - // console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10)) - - return [leemonBigintToBytes(P), leemonBigintToBytes(Q)] + return [ + bigIntToBuffer(p), + bigIntToBuffer(q), + ] +} + +function PollardRhoBrent(n: BigInteger): BigInteger { + if (n.isEven()) return bigInt[2] + + let y = randomBigIntInRange(n.minus(1)) + const c = randomBigIntInRange(n.minus(1)) + const m = randomBigIntInRange(n.minus(1)) + let g = bigInt.one + let r = bigInt.one + let q = bigInt.one + + let ys: BigInteger, x: BigInteger + + while (g.eq(bigInt.one)) { + x = y + for (let i = 0; r.geq(i); i++) y = y.multiply(y).mod(n).plus(c).mod(n) + // y = ((y * y) % n + c) % n + + let k = bigInt.zero + while (k.lt(r) && g.eq(1)) { + ys = y + for ( + let i = bigInt.zero; + i.lt(bigInt.min(m, r.minus(k))); + i = i.plus(bigInt.one) + ) { + y = y.multiply(y).mod(n).plus(c).mod(n) + q = q.multiply(x.minus(y).abs()).mod(n) + // y = (y * y % n + c) % n + // q = q * abs(x - y) % n + } + + g = bigInt.gcd(q, n) + k = k.plus(m) + } + + r = r.multiply(bigInt[2]) + } + + if (g.eq(n)) + do { + ys = ys!.multiply(ys!).mod(n).plus(c).mod(n) + // ys = ((ys * ys) % n + c) % n + g = bigInt.gcd(x!.minus(ys), n) + } while (g.leq(bigInt.one)) + + return g }