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:
parent
49eda8f0e3
commit
a834fbfa8d
3 changed files with 75 additions and 109 deletions
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue