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/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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue