refactor(core): moved to PollardRhoBrent for PQ factorization (thanks @mytecor!)

also removed leemon from deps since it's no longer used
This commit is contained in:
teidesu 2021-09-23 23:11:43 +03:00
parent 49eda8f0e3
commit a834fbfa8d
3 changed files with 75 additions and 109 deletions

View file

@ -21,7 +21,6 @@
"@types/node": "^15.12.1", "@types/node": "^15.12.1",
"@types/events": "^3.0.0", "@types/events": "^3.0.0",
"@mtcute/tl": "~1.131.0", "@mtcute/tl": "~1.131.0",
"leemon": "6.2.0",
"pako": "2.0.2", "pako": "2.0.2",
"big-integer": "1.6.48", "big-integer": "1.6.48",
"events": "3.2.0" "events": "3.2.0"

View file

@ -73,3 +73,15 @@ export function bufferToBigInt(
export function randomBigInt(size: number): BigInteger { export function randomBigInt(size: number): BigInteger {
return bufferToBigInt(randomBytes(size)) 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)
}

View file

@ -1,116 +1,71 @@
import { getRandomInt } from '../misc-utils' import { bigIntToBuffer, bufferToBigInt, randomBigIntInRange } from '../bigint-utils'
import bigInt, { BigInteger } from 'big-integer'
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')
}
export function factorizePQSync(pq: Buffer): [Buffer, Buffer] { export function factorizePQSync(pq: Buffer): [Buffer, Buffer] {
return leemonPqFactorizationSync( const pq_ = bufferToBigInt(pq)
str2bigInt(pq.toString('hex'), 16, Math.ceil(64 / bpe) + 1)
) const n = PollardRhoBrent(pq_)
const m = pq_.divide(n)
let p, q
if (n.lt(m)) {
p = n
q = m
} else {
p = m
q = n
}
return [
bigIntToBuffer(p),
bigIntToBuffer(q),
]
} }
// TODO: maybe move this to CryptoProvider to allow delegating computations to a worker? function PollardRhoBrent(n: BigInteger): BigInteger {
// returns tuple of [P, Q] if (n.isEven()) return bigInt[2]
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)
for (i = 0; i < 3; i++) { let y = randomBigIntInRange(n.minus(1))
q = (getRandomInt(128) & 15) + 17 const c = randomBigIntInRange(n.minus(1))
copyInt_(x, getRandomInt(1000000000) + 1) const m = randomBigIntInRange(n.minus(1))
copy_(y, x) let g = bigInt.one
lim = 1 << (i + 18) let r = bigInt.one
let q = bigInt.one
for (j = 1; j < lim; j++) { let ys: BigInteger, x: BigInteger
// 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)) { while (g.eq(bigInt.one)) {
if (b[0] & 1) { x = y
add_(c, a) for (let i = 0; r.geq(i); i++) y = y.multiply(y).mod(n).plus(c).mod(n)
if (greater(c, what)) { // y = ((y * y) % n + c) % n
sub_(c, what)
} let k = bigInt.zero
} while (k.lt(r) && g.eq(1)) {
add_(a, a) ys = y
if (greater(a, what)) { for (
sub_(a, what) let i = bigInt.zero;
} i.lt(bigInt.min(m, r.minus(k)));
rightShift_(b, 1) 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
} }
copy_(x, c) g = bigInt.gcd(q, n)
if (greater(x, y)) { k = k.plus(m)
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) r = r.multiply(bigInt[2])
if (greater(g, x)) {
P = x
Q = g
} else {
P = g
Q = x
} }
// console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10)) 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 [leemonBigintToBytes(P), leemonBigintToBytes(Q)] return g
} }