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/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"

View file

@ -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)
}

View file

@ -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
}