fix: rebase fixes
This commit is contained in:
parent
eb585fc3f1
commit
743058a020
9 changed files with 673 additions and 299 deletions
|
@ -9,7 +9,12 @@ import {
|
|||
TlSerializationCounter,
|
||||
} from '@mtcute/tl-runtime'
|
||||
|
||||
import { bigIntToBuffer, bufferToBigInt, ICryptoProvider } from '../utils'
|
||||
import {
|
||||
bigIntToBuffer,
|
||||
bufferToBigInt,
|
||||
ICryptoProvider,
|
||||
Logger,
|
||||
} from '../utils'
|
||||
import {
|
||||
buffersEqual,
|
||||
randomBytes,
|
||||
|
@ -17,12 +22,123 @@ import {
|
|||
xorBufferInPlace,
|
||||
} from '../utils/buffer-utils'
|
||||
import { findKeyByFingerprints } from '../utils/crypto/keys'
|
||||
import { millerRabin } from '../utils/crypto/miller-rabin'
|
||||
import { generateKeyAndIvFromNonce } from '../utils/crypto/mtproto'
|
||||
import { SessionConnection } from './session-connection'
|
||||
|
||||
// Heavily based on code from https://github.com/LonamiWebs/Telethon/blob/master/telethon/network/authenticator.py
|
||||
|
||||
// see https://core.telegram.org/mtproto/security_guidelines
|
||||
const DH_SAFETY_RANGE = bigInt[2].pow(2048 - 64)
|
||||
const KNOWN_DH_PRIME = bigInt(
|
||||
'C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B',
|
||||
16,
|
||||
)
|
||||
const TWO_POW_2047 = bigInt[2].pow(2047)
|
||||
const TWO_POW_2048 = bigInt[2].pow(2048)
|
||||
|
||||
interface CheckedPrime {
|
||||
prime: bigInt.BigInteger
|
||||
generators: number[]
|
||||
}
|
||||
const checkedPrimesCache: CheckedPrime[] = []
|
||||
|
||||
function checkDhPrime(log: Logger, dhPrime: bigInt.BigInteger, g: number) {
|
||||
if (KNOWN_DH_PRIME.eq(dhPrime)) {
|
||||
log.debug('server is using known dh prime, skipping validation')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let checkedPrime = checkedPrimesCache.find((x) => x.prime.eq(dhPrime))
|
||||
|
||||
if (!checkedPrime) {
|
||||
if (
|
||||
dhPrime.lesserOrEquals(TWO_POW_2047) ||
|
||||
dhPrime.greaterOrEquals(TWO_POW_2048)
|
||||
) {
|
||||
throw new Error('Step 3: dh_prime is not in the 2048-bit range')
|
||||
}
|
||||
|
||||
if (!millerRabin(dhPrime)) {
|
||||
throw new Error('Step 3: dh_prime is not prime')
|
||||
}
|
||||
if (!millerRabin(dhPrime.minus(1).divide(2))) {
|
||||
throw new Error(
|
||||
'Step 3: dh_prime is not a safe prime - (dh_prime-1)/2 is not prime',
|
||||
)
|
||||
}
|
||||
|
||||
log.debug('dh_prime is probably prime')
|
||||
|
||||
checkedPrime = {
|
||||
prime: dhPrime,
|
||||
generators: [],
|
||||
}
|
||||
checkedPrimesCache.push(checkedPrime)
|
||||
} else {
|
||||
log.debug('dh_prime is probably prime (cached)')
|
||||
}
|
||||
|
||||
const generatorChecked = checkedPrime.generators.includes(g)
|
||||
|
||||
if (generatorChecked) {
|
||||
log.debug('g = %d is already checked for dh_prime', g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch (g) {
|
||||
case 2:
|
||||
if (dhPrime.mod(8).notEquals(7)) {
|
||||
throw new Error('Step 3: ivalid g - dh_prime mod 8 != 7')
|
||||
}
|
||||
break
|
||||
case 3:
|
||||
if (dhPrime.mod(3).notEquals(2)) {
|
||||
throw new Error('Step 3: ivalid g - dh_prime mod 3 != 2')
|
||||
}
|
||||
break
|
||||
case 4:
|
||||
break
|
||||
case 5: {
|
||||
const mod = dhPrime.mod(5)
|
||||
|
||||
if (mod.notEquals(1) && mod.notEquals(4)) {
|
||||
throw new Error(
|
||||
'Step 3: ivalid g - dh_prime mod 5 != 1 && dh_prime mod 5 != 4',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 6: {
|
||||
const mod = dhPrime.mod(24)
|
||||
|
||||
if (mod.notEquals(19) && mod.notEquals(23)) {
|
||||
throw new Error(
|
||||
'Step 3: ivalid g - dh_prime mod 24 != 19 && dh_prime mod 24 != 23',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 7: {
|
||||
const mod = dhPrime.mod(7)
|
||||
|
||||
if (mod.notEquals(3) && mod.notEquals(5) && mod.notEquals(6)) {
|
||||
throw new Error(
|
||||
'Step 3: ivalid g - dh_prime mod 7 != 3 && dh_prime mod 7 != 5 && dh_prime mod 7 != 6',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
throw new Error(`Step 3: ivalid g - unknown g = ${g}`)
|
||||
}
|
||||
|
||||
checkedPrime.generators.push(g)
|
||||
|
||||
log.debug('g = %d is safe to use with dh_prime', g)
|
||||
}
|
||||
|
||||
async function rsaPad(
|
||||
data: Buffer,
|
||||
|
@ -146,7 +262,9 @@ export async function doAuthorization(
|
|||
|
||||
if (resPq._ !== 'mt_resPQ') throw new Error('Step 1: answer was ' + resPq._)
|
||||
|
||||
if (!buffersEqual(resPq.nonce, nonce)) { throw new Error('Step 1: invalid nonce from server') }
|
||||
if (!buffersEqual(resPq.nonce, nonce)) {
|
||||
throw new Error('Step 1: invalid nonce from server')
|
||||
}
|
||||
|
||||
const serverKeys = resPq.serverPublicKeyFingerprints.map((it) =>
|
||||
it.toUnsigned().toString(16),
|
||||
|
@ -207,12 +325,20 @@ export async function doAuthorization(
|
|||
})
|
||||
const serverDhParams = await readNext()
|
||||
|
||||
if (!mtp.isAnyServer_DH_Params(serverDhParams)) { throw new Error('Step 2.1: answer was ' + serverDhParams._) }
|
||||
if (!mtp.isAnyServer_DH_Params(serverDhParams)) {
|
||||
throw new Error('Step 2.1: answer was ' + serverDhParams._)
|
||||
}
|
||||
|
||||
if (serverDhParams._ !== 'mt_server_DH_params_ok') { throw new Error('Step 2.1: answer was ' + serverDhParams._) }
|
||||
if (serverDhParams._ !== 'mt_server_DH_params_ok') {
|
||||
throw new Error('Step 2.1: answer was ' + serverDhParams._)
|
||||
}
|
||||
|
||||
if (!buffersEqual(serverDhParams.nonce, nonce)) { throw Error('Step 2: invalid nonce from server') }
|
||||
if (!buffersEqual(serverDhParams.serverNonce, resPq.serverNonce)) { throw Error('Step 2: invalid server nonce from server') }
|
||||
if (!buffersEqual(serverDhParams.nonce, nonce)) {
|
||||
throw Error('Step 2: invalid nonce from server')
|
||||
}
|
||||
if (!buffersEqual(serverDhParams.serverNonce, resPq.serverNonce)) {
|
||||
throw Error('Step 2: invalid server nonce from server')
|
||||
}
|
||||
|
||||
// type was removed from schema in July 2021
|
||||
// if (serverDhParams._ === 'mt_server_DH_params_fail') {
|
||||
|
@ -225,7 +351,9 @@ export async function doAuthorization(
|
|||
|
||||
log.debug('server DH ok')
|
||||
|
||||
if (serverDhParams.encryptedAnswer.length % 16 !== 0) { throw new Error('Step 2: AES block size is invalid') }
|
||||
if (serverDhParams.encryptedAnswer.length % 16 !== 0) {
|
||||
throw new Error('Step 2: AES block size is invalid')
|
||||
}
|
||||
|
||||
// Step 3: complete DH exchange
|
||||
const [key, iv] = await generateKeyAndIvFromNonce(
|
||||
|
@ -251,20 +379,28 @@ export async function doAuthorization(
|
|||
plainTextAnswer.slice(20, serverDhInnerReader.pos),
|
||||
),
|
||||
)
|
||||
) { throw new Error('Step 3: invalid inner data hash') }
|
||||
) {
|
||||
throw new Error('Step 3: invalid inner data hash')
|
||||
}
|
||||
|
||||
if (serverDhInner._ !== 'mt_server_DH_inner_data') { throw Error('Step 3: inner data was ' + serverDhInner._) }
|
||||
if (!buffersEqual(serverDhInner.nonce, nonce)) { throw Error('Step 3: invalid nonce from server') }
|
||||
if (!buffersEqual(serverDhInner.serverNonce, resPq.serverNonce)) { throw Error('Step 3: invalid server nonce from server') }
|
||||
if (serverDhInner._ !== 'mt_server_DH_inner_data') {
|
||||
throw Error('Step 3: inner data was ' + serverDhInner._)
|
||||
}
|
||||
if (!buffersEqual(serverDhInner.nonce, nonce)) {
|
||||
throw Error('Step 3: invalid nonce from server')
|
||||
}
|
||||
if (!buffersEqual(serverDhInner.serverNonce, resPq.serverNonce)) {
|
||||
throw Error('Step 3: invalid server nonce from server')
|
||||
}
|
||||
|
||||
const dhPrime = bufferToBigInt(serverDhInner.dhPrime)
|
||||
const timeOffset = Math.floor(Date.now() / 1000) - serverDhInner.serverTime
|
||||
|
||||
// dhPrime is not checked because who cares lol :D
|
||||
|
||||
const g = bigInt(serverDhInner.g)
|
||||
const gA = bufferToBigInt(serverDhInner.gA)
|
||||
|
||||
checkDhPrime(log, dhPrime, serverDhInner.g)
|
||||
|
||||
let retryId = Long.ZERO
|
||||
const serverSalt = xorBuffer(
|
||||
newNonce.slice(0, 8),
|
||||
|
@ -279,15 +415,24 @@ export async function doAuthorization(
|
|||
const authKeyAuxHash = (await crypto.sha1(authKey)).slice(0, 8)
|
||||
|
||||
// validate DH params
|
||||
if (g.lesserOrEquals(1) || g.greaterOrEquals(dhPrime.minus(bigInt.one))) { throw new Error('g is not within (1, dh_prime - 1)') }
|
||||
if (
|
||||
g.lesserOrEquals(1) ||
|
||||
g.greaterOrEquals(dhPrime.minus(bigInt.one))
|
||||
) {
|
||||
throw new Error('g is not within (1, dh_prime - 1)')
|
||||
}
|
||||
if (
|
||||
gA.lesserOrEquals(1) ||
|
||||
gA.greaterOrEquals(dhPrime.minus(bigInt.one))
|
||||
) { throw new Error('g_a is not within (1, dh_prime - 1)') }
|
||||
) {
|
||||
throw new Error('g_a is not within (1, dh_prime - 1)')
|
||||
}
|
||||
if (
|
||||
gB.lesserOrEquals(1) ||
|
||||
gB.greaterOrEquals(dhPrime.minus(bigInt.one))
|
||||
) { throw new Error('g_b is not within (1, dh_prime - 1)') }
|
||||
) {
|
||||
throw new Error('g_b is not within (1, dh_prime - 1)')
|
||||
}
|
||||
|
||||
if (gA.lt(DH_SAFETY_RANGE) || gA.gt(dhPrime.minus(DH_SAFETY_RANGE))) {
|
||||
throw new Error(
|
||||
|
@ -337,10 +482,16 @@ export async function doAuthorization(
|
|||
|
||||
const dhGen = await readNext()
|
||||
|
||||
if (!mtp.isAnySet_client_DH_params_answer(dhGen)) { throw new Error('Step 4: answer was ' + dhGen._) }
|
||||
if (!mtp.isAnySet_client_DH_params_answer(dhGen)) {
|
||||
throw new Error('Step 4: answer was ' + dhGen._)
|
||||
}
|
||||
|
||||
if (!buffersEqual(dhGen.nonce, nonce)) { throw Error('Step 4: invalid nonce from server') }
|
||||
if (!buffersEqual(dhGen.serverNonce, resPq.serverNonce)) { throw Error('Step 4: invalid server nonce from server') }
|
||||
if (!buffersEqual(dhGen.nonce, nonce)) {
|
||||
throw Error('Step 4: invalid nonce from server')
|
||||
}
|
||||
if (!buffersEqual(dhGen.serverNonce, resPq.serverNonce)) {
|
||||
throw Error('Step 4: invalid server nonce from server')
|
||||
}
|
||||
|
||||
log.debug('DH result: %s', dhGen._)
|
||||
|
||||
|
@ -354,7 +505,9 @@ export async function doAuthorization(
|
|||
Buffer.concat([newNonce, Buffer.from([2]), authKeyAuxHash]),
|
||||
)
|
||||
|
||||
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) { throw Error('Step 4: invalid retry nonce hash from server') }
|
||||
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) {
|
||||
throw Error('Step 4: invalid retry nonce hash from server')
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
retryId = Long.fromBytesLE(authKeyAuxHash as any)
|
||||
continue
|
||||
|
@ -366,7 +519,9 @@ export async function doAuthorization(
|
|||
Buffer.concat([newNonce, Buffer.from([1]), authKeyAuxHash]),
|
||||
)
|
||||
|
||||
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash1)) { throw Error('Step 4: invalid nonce hash from server') }
|
||||
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash1)) {
|
||||
throw Error('Step 4: invalid nonce hash from server')
|
||||
}
|
||||
|
||||
log.info('authorization successful')
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ export function bigIntToBuffer(
|
|||
): Buffer {
|
||||
const array = value.toArray(256).value
|
||||
|
||||
if (length !== 0 && array.length > length) { throw new Error('Value out of bounds') }
|
||||
if (length !== 0 && array.length > length) {
|
||||
throw new Error('Value out of bounds')
|
||||
}
|
||||
|
||||
if (length !== 0) {
|
||||
// padding
|
||||
|
@ -60,6 +62,23 @@ export function randomBigInt(size: number): BigInteger {
|
|||
return bufferToBigInt(randomBytes(size))
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random big integer of the given size (in bits)
|
||||
* @param bits
|
||||
*/
|
||||
export function randomBigIntBits(bits: number): BigInteger {
|
||||
let num = randomBigInt(Math.ceil(bits / 8))
|
||||
|
||||
const bitLength = num.bitLength()
|
||||
|
||||
if (bitLength.gt(bits)) {
|
||||
const toTrim = bigInt.randBetween(bitLength.minus(bits), 8)
|
||||
num = num.shiftRight(toTrim)
|
||||
}
|
||||
|
||||
return num
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random big integer in the range [min, max)
|
||||
*
|
||||
|
@ -80,3 +99,20 @@ export function randomBigIntInRange(
|
|||
|
||||
return min.plus(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the multiplicity of 2 in the prime factorization of n
|
||||
* @param n
|
||||
*/
|
||||
export function twoMultiplicity(n: BigInteger): BigInteger {
|
||||
if (n === bigInt.zero) return bigInt.zero
|
||||
|
||||
let m = bigInt.zero
|
||||
let pow = bigInt.one
|
||||
|
||||
while (true) {
|
||||
if (!n.and(pow).isZero()) return m
|
||||
m = m.plus(bigInt.one)
|
||||
pow = pow.shiftLeft(1)
|
||||
}
|
||||
}
|
||||
|
|
43
packages/core/src/utils/crypto/miller-rabin.ts
Normal file
43
packages/core/src/utils/crypto/miller-rabin.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import bigInt, { BigInteger } from 'big-integer'
|
||||
|
||||
import { randomBigIntBits, twoMultiplicity } from '../bigint-utils'
|
||||
|
||||
export function millerRabin(n: BigInteger, rounds = 20): boolean {
|
||||
// small numbers: 0, 1 are not prime, 2, 3 are prime
|
||||
if (n.lt(bigInt[4])) return n.gt(bigInt[1])
|
||||
if (n.isEven() || n.isNegative()) return false
|
||||
|
||||
const nBits = n.bitLength().toJSNumber()
|
||||
const nSub = n.minus(1)
|
||||
|
||||
const r = twoMultiplicity(nSub)
|
||||
const d = nSub.shiftRight(r)
|
||||
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
let base
|
||||
|
||||
do {
|
||||
base = randomBigIntBits(nBits)
|
||||
} while (base.leq(bigInt.one) || base.geq(nSub))
|
||||
|
||||
let x = base.modPow(d, n)
|
||||
if (x.eq(bigInt.one) || x.eq(nSub)) continue
|
||||
|
||||
let i = bigInt.zero
|
||||
let y: BigInteger
|
||||
|
||||
while (i.lt(r)) {
|
||||
y = x.modPow(bigInt[2], n)
|
||||
|
||||
if (x.eq(bigInt.one)) return false
|
||||
if (x.eq(nSub)) break
|
||||
i = i.plus(bigInt.one)
|
||||
|
||||
x = y
|
||||
}
|
||||
|
||||
if (i.eq(r)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -1,71 +1,71 @@
|
|||
import { expect } from 'chai'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import __tlReaderMap from '@mtcute/tl/binary/reader'
|
||||
import { TlBinaryReader } from '@mtcute/tl-runtime'
|
||||
|
||||
import { createTestTelegramClient } from './utils'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('dotenv-flow').config()
|
||||
|
||||
describe('fuzz : packet', async function () {
|
||||
this.timeout(45000)
|
||||
|
||||
it('random packet', async () => {
|
||||
const client = createTestTelegramClient()
|
||||
|
||||
await client.connect()
|
||||
await client.waitUntilUsable()
|
||||
|
||||
let errors = 0
|
||||
|
||||
const conn = client.primaryConnection
|
||||
// eslint-disable-next-line dot-notation
|
||||
const mtproto = conn['_session']
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const payload = randomBytes(Math.round(Math.random() * 16) * 16)
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line dot-notation
|
||||
conn['_handleRawMessage'](
|
||||
mtproto.getMessageId().sub(1),
|
||||
0,
|
||||
new TlBinaryReader(__tlReaderMap, payload),
|
||||
)
|
||||
} catch (e) {
|
||||
errors += 1
|
||||
}
|
||||
}
|
||||
|
||||
// similar test, but this time only using object ids that do exist
|
||||
const objectIds = Object.keys(__tlReaderMap)
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const payload = randomBytes(
|
||||
(Math.round(Math.random() * 16) + 1) * 16,
|
||||
)
|
||||
const objectId = parseInt(
|
||||
objectIds[Math.round(Math.random() * objectIds.length)],
|
||||
)
|
||||
payload.writeUInt32LE(objectId, 0)
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line dot-notation
|
||||
conn['_handleRawMessage'](
|
||||
mtproto.getMessageId().sub(1),
|
||||
0,
|
||||
new TlBinaryReader(__tlReaderMap, payload),
|
||||
)
|
||||
} catch (e) {
|
||||
errors += 1
|
||||
}
|
||||
}
|
||||
|
||||
await client.close()
|
||||
|
||||
expect(errors).gt(0)
|
||||
})
|
||||
})
|
||||
// import { expect } from 'chai'
|
||||
// import { randomBytes } from 'crypto'
|
||||
// import { describe, it } from 'mocha'
|
||||
//
|
||||
// import __tlReaderMap from '@mtcute/tl/binary/reader'
|
||||
// import { TlBinaryReader } from '@mtcute/tl-runtime'
|
||||
//
|
||||
// import { createTestTelegramClient } from './utils'
|
||||
//
|
||||
// // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
// require('dotenv-flow').config()
|
||||
//
|
||||
// describe('fuzz : packet', async function () {
|
||||
// this.timeout(45000)
|
||||
//
|
||||
// it('random packet', async () => {
|
||||
// const client = createTestTelegramClient()
|
||||
//
|
||||
// await client.connect()
|
||||
// await client.waitUntilUsable()
|
||||
//
|
||||
// let errors = 0
|
||||
//
|
||||
// const conn = client.primaryConnection
|
||||
// // eslint-disable-next-line dot-notation
|
||||
// const mtproto = conn['_session']
|
||||
//
|
||||
// for (let i = 0; i < 100; i++) {
|
||||
// const payload = randomBytes(Math.round(Math.random() * 16) * 16)
|
||||
//
|
||||
// try {
|
||||
// // eslint-disable-next-line dot-notation
|
||||
// conn['_handleRawMessage'](
|
||||
// mtproto.getMessageId().sub(1),
|
||||
// 0,
|
||||
// new TlBinaryReader(__tlReaderMap, payload),
|
||||
// )
|
||||
// } catch (e) {
|
||||
// errors += 1
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // similar test, but this time only using object ids that do exist
|
||||
// const objectIds = Object.keys(__tlReaderMap)
|
||||
//
|
||||
// for (let i = 0; i < 100; i++) {
|
||||
// const payload = randomBytes(
|
||||
// (Math.round(Math.random() * 16) + 1) * 16,
|
||||
// )
|
||||
// const objectId = parseInt(
|
||||
// objectIds[Math.round(Math.random() * objectIds.length)],
|
||||
// )
|
||||
// payload.writeUInt32LE(objectId, 0)
|
||||
//
|
||||
// try {
|
||||
// // eslint-disable-next-line dot-notation
|
||||
// conn['_handleRawMessage'](
|
||||
// mtproto.getMessageId().sub(1),
|
||||
// 0,
|
||||
// new TlBinaryReader(__tlReaderMap, payload),
|
||||
// )
|
||||
// } catch (e) {
|
||||
// errors += 1
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// await client.close()
|
||||
//
|
||||
// expect(errors).gt(0)
|
||||
// })
|
||||
// })
|
||||
|
|
|
@ -1,77 +1,77 @@
|
|||
import { expect } from 'chai'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { sleep } from '../../src'
|
||||
import { createTestTelegramClient } from './utils'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('dotenv-flow').config()
|
||||
|
||||
describe('fuzz : session', async function () {
|
||||
this.timeout(45000)
|
||||
|
||||
it('random auth_key', async () => {
|
||||
const client = createTestTelegramClient()
|
||||
|
||||
// random key
|
||||
const initKey = randomBytes(256)
|
||||
await client.storage.setAuthKeyFor(2, initKey)
|
||||
|
||||
// client is supposed to handle this and generate a new key
|
||||
|
||||
const errors: unknown[] = []
|
||||
|
||||
const errorHandler = (err: unknown) => {
|
||||
errors.push(err)
|
||||
}
|
||||
|
||||
client.onError(errorHandler)
|
||||
|
||||
await client.connect()
|
||||
|
||||
await sleep(10000)
|
||||
|
||||
await client.close()
|
||||
|
||||
expect(errors.length).eq(0)
|
||||
|
||||
expect((await client.storage.getAuthKeyFor(2))?.toString('hex')).not.eq(
|
||||
initKey.toString('hex'),
|
||||
)
|
||||
})
|
||||
|
||||
it('random auth_key for other dc', async () => {
|
||||
const client = createTestTelegramClient()
|
||||
|
||||
// random key for dc1
|
||||
const initKey = randomBytes(256)
|
||||
await client.storage.setAuthKeyFor(1, initKey)
|
||||
|
||||
// client is supposed to handle this and generate a new key
|
||||
|
||||
const errors: unknown[] = []
|
||||
|
||||
const errorHandler = (err: unknown) => {
|
||||
errors.push(err)
|
||||
}
|
||||
|
||||
client.onError(errorHandler)
|
||||
|
||||
await client.connect()
|
||||
await client.waitUntilUsable()
|
||||
|
||||
const conn = await client.createAdditionalConnection(1)
|
||||
await conn.sendRpc({ _: 'help.getConfig' })
|
||||
|
||||
await sleep(10000)
|
||||
|
||||
await client.close()
|
||||
|
||||
expect(errors.length).eq(0)
|
||||
|
||||
expect((await client.storage.getAuthKeyFor(1))?.toString('hex')).not.eq(
|
||||
initKey.toString('hex'),
|
||||
)
|
||||
})
|
||||
})
|
||||
// import { expect } from 'chai'
|
||||
// import { randomBytes } from 'crypto'
|
||||
// import { describe, it } from 'mocha'
|
||||
//
|
||||
// import { sleep } from '../../src'
|
||||
// import { createTestTelegramClient } from './utils'
|
||||
//
|
||||
// // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
// require('dotenv-flow').config()
|
||||
//
|
||||
// describe('fuzz : session', async function () {
|
||||
// this.timeout(45000)
|
||||
//
|
||||
// it('random auth_key', async () => {
|
||||
// const client = createTestTelegramClient()
|
||||
//
|
||||
// // random key
|
||||
// const initKey = randomBytes(256)
|
||||
// await client.storage.setAuthKeyFor(2, initKey)
|
||||
//
|
||||
// // client is supposed to handle this and generate a new key
|
||||
//
|
||||
// const errors: Error[] = []
|
||||
//
|
||||
// const errorHandler = (err: Error) => {
|
||||
// errors.push(err)
|
||||
// }
|
||||
//
|
||||
// client.onError(errorHandler)
|
||||
//
|
||||
// await client.connect()
|
||||
//
|
||||
// await sleep(10000)
|
||||
//
|
||||
// await client.close()
|
||||
//
|
||||
// expect(errors.length).eq(0)
|
||||
//
|
||||
// expect((await client.storage.getAuthKeyFor(2))?.toString('hex')).not.eq(
|
||||
// initKey.toString('hex'),
|
||||
// )
|
||||
// })
|
||||
//
|
||||
// it('random auth_key for other dc', async () => {
|
||||
// const client = createTestTelegramClient()
|
||||
//
|
||||
// // random key for dc1
|
||||
// const initKey = randomBytes(256)
|
||||
// await client.storage.setAuthKeyFor(1, initKey)
|
||||
//
|
||||
// // client is supposed to handle this and generate a new key
|
||||
//
|
||||
// const errors: Error[] = []
|
||||
//
|
||||
// const errorHandler = (err: Error) => {
|
||||
// errors.push(err)
|
||||
// }
|
||||
//
|
||||
// client.onError(errorHandler)
|
||||
//
|
||||
// await client.connect()
|
||||
// await client.waitUntilUsable()
|
||||
//
|
||||
// const conn = await client.createAdditionalConnection(1)
|
||||
// await conn.sendRpc({ _: 'help.getConfig' })
|
||||
//
|
||||
// await sleep(10000)
|
||||
//
|
||||
// await client.close()
|
||||
//
|
||||
// expect(errors.length).eq(0)
|
||||
//
|
||||
// expect((await client.storage.getAuthKeyFor(1))?.toString('hex')).not.eq(
|
||||
// initKey.toString('hex'),
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
|
|
|
@ -1,128 +1,127 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { expect } from 'chai'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { EventEmitter } from 'events'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import {
|
||||
BaseTelegramClient,
|
||||
defaultDcs,
|
||||
ITelegramTransport,
|
||||
NodeCryptoProvider,
|
||||
sleep,
|
||||
tl,
|
||||
TransportState,
|
||||
} from '../../src'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('dotenv-flow').config()
|
||||
|
||||
class RandomBytesTransport extends EventEmitter implements ITelegramTransport {
|
||||
dc!: tl.RawDcOption
|
||||
interval?: NodeJS.Timeout
|
||||
|
||||
close(): void {
|
||||
clearInterval(this.interval)
|
||||
this.emit('close')
|
||||
this.interval = undefined
|
||||
}
|
||||
|
||||
connect(dc: tl.RawDcOption): void {
|
||||
this.dc = dc
|
||||
|
||||
setTimeout(() => this.emit('ready'), 0)
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.emit('message', randomBytes(64))
|
||||
}, 100)
|
||||
}
|
||||
|
||||
currentDc(): tl.RawDcOption | null {
|
||||
return this.dc
|
||||
}
|
||||
|
||||
send(_data: Buffer): Promise<void> {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
state(): TransportState {
|
||||
return this.interval ? TransportState.Ready : TransportState.Idle
|
||||
}
|
||||
}
|
||||
|
||||
describe('fuzz : transport', function () {
|
||||
this.timeout(30000)
|
||||
|
||||
it('RandomBytesTransport (no auth)', async () => {
|
||||
const client = new BaseTelegramClient({
|
||||
crypto: () => new NodeCryptoProvider(),
|
||||
transport: () => new RandomBytesTransport(),
|
||||
apiId: 0,
|
||||
apiHash: '',
|
||||
defaultDc: defaultDcs.defaultTestDc,
|
||||
})
|
||||
client.log.level = 0
|
||||
|
||||
const errors: Error[] = []
|
||||
|
||||
client.onError((err) => {
|
||||
errors.push(err)
|
||||
})
|
||||
|
||||
await client.connect()
|
||||
await sleep(15000)
|
||||
await client.close()
|
||||
|
||||
expect(errors.length).gt(0)
|
||||
errors.forEach((err) => {
|
||||
expect(err.message).match(/unknown object id/i)
|
||||
})
|
||||
})
|
||||
|
||||
it('RandomBytesTransport (with auth)', async () => {
|
||||
const client = new BaseTelegramClient({
|
||||
crypto: () => new NodeCryptoProvider(),
|
||||
transport: () => new RandomBytesTransport(),
|
||||
apiId: 0,
|
||||
apiHash: '',
|
||||
defaultDc: defaultDcs.defaultTestDc,
|
||||
})
|
||||
client.log.level = 0
|
||||
|
||||
// random key just to make it think it already has one
|
||||
await client.storage.setAuthKeyFor(2, randomBytes(256))
|
||||
|
||||
// in this case, there will be no actual errors, only
|
||||
// warnings like 'received message with unknown authKey'
|
||||
//
|
||||
// to test for that, we hook into `decryptMessage` and make
|
||||
// sure that it returns `null`
|
||||
|
||||
await client.connect()
|
||||
|
||||
let hadNonNull = false
|
||||
|
||||
const decryptMessage =
|
||||
// eslint-disable-next-line dot-notation
|
||||
client.primaryConnection['_session'].decryptMessage
|
||||
|
||||
// ехал any через any
|
||||
// видит any - any, any
|
||||
// сунул any any в any
|
||||
// any any any any
|
||||
// eslint-disable-next-line dot-notation
|
||||
;(client.primaryConnection['_session'] as any).decryptMessage = (
|
||||
buf: any,
|
||||
cb: any,
|
||||
) =>
|
||||
decryptMessage.call(this, buf, (...args: any[]) => {
|
||||
cb(...(args as any))
|
||||
hadNonNull = true
|
||||
})
|
||||
|
||||
await sleep(15000)
|
||||
await client.close()
|
||||
|
||||
expect(hadNonNull).false
|
||||
})
|
||||
})
|
||||
// import { expect } from 'chai'
|
||||
// import { randomBytes } from 'crypto'
|
||||
// import { EventEmitter } from 'events'
|
||||
// import { describe, it } from 'mocha'
|
||||
//
|
||||
// import {
|
||||
// BaseTelegramClient,
|
||||
// defaultDcs,
|
||||
// ITelegramTransport,
|
||||
// NodeCryptoProvider,
|
||||
// sleep,
|
||||
// tl,
|
||||
// TransportState,
|
||||
// } from '../../src'
|
||||
//
|
||||
// // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
// require('dotenv-flow').config()
|
||||
//
|
||||
// class RandomBytesTransport extends EventEmitter implements ITelegramTransport {
|
||||
// dc: tl.RawDcOption
|
||||
// interval?: NodeJS.Timeout
|
||||
//
|
||||
// close(): void {
|
||||
// clearInterval(this.interval)
|
||||
// this.emit('close')
|
||||
// this.interval = undefined
|
||||
// }
|
||||
//
|
||||
// connect(dc: tl.RawDcOption): void {
|
||||
// this.dc = dc
|
||||
//
|
||||
// setTimeout(() => this.emit('ready'), 0)
|
||||
//
|
||||
// this.interval = setInterval(() => {
|
||||
// this.emit('message', randomBytes(64))
|
||||
// }, 100)
|
||||
// }
|
||||
//
|
||||
// currentDc(): tl.RawDcOption | null {
|
||||
// return this.dc
|
||||
// }
|
||||
//
|
||||
// send(_data: Buffer): Promise<void> {
|
||||
// return Promise.resolve()
|
||||
// }
|
||||
//
|
||||
// state(): TransportState {
|
||||
// return this.interval ? TransportState.Ready : TransportState.Idle
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// describe('fuzz : transport', function () {
|
||||
// this.timeout(30000)
|
||||
//
|
||||
// it('RandomBytesTransport (no auth)', async () => {
|
||||
// const client = new BaseTelegramClient({
|
||||
// crypto: () => new NodeCryptoProvider(),
|
||||
// transport: () => new RandomBytesTransport(),
|
||||
// apiId: 0,
|
||||
// apiHash: '',
|
||||
// defaultDc: defaultDcs.defaultTestDc,
|
||||
// })
|
||||
// client.log.level = 0
|
||||
//
|
||||
// const errors: Error[] = []
|
||||
//
|
||||
// client.onError((err) => {
|
||||
// errors.push(err)
|
||||
// })
|
||||
//
|
||||
// await client.connect()
|
||||
// await sleep(15000)
|
||||
// await client.close()
|
||||
//
|
||||
// expect(errors.length).gt(0)
|
||||
// errors.forEach((err) => {
|
||||
// expect(err.message).match(/unknown object id/i)
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// it('RandomBytesTransport (with auth)', async () => {
|
||||
// const client = new BaseTelegramClient({
|
||||
// crypto: () => new NodeCryptoProvider(),
|
||||
// transport: () => new RandomBytesTransport(),
|
||||
// apiId: 0,
|
||||
// apiHash: '',
|
||||
// defaultDc: defaultDcs.defaultTestDc,
|
||||
// })
|
||||
// client.log.level = 0
|
||||
//
|
||||
// // random key just to make it think it already has one
|
||||
// await client.storage.setAuthKeyFor(2, randomBytes(256))
|
||||
//
|
||||
// // in this case, there will be no actual errors, only
|
||||
// // warnings like 'received message with unknown authKey'
|
||||
// //
|
||||
// // to test for that, we hook into `decryptMessage` and make
|
||||
// // sure that it returns `null`
|
||||
//
|
||||
// await client.connect()
|
||||
//
|
||||
// let hadNonNull = false
|
||||
//
|
||||
// const decryptMessage =
|
||||
// // eslint-disable-next-line dot-notation
|
||||
// client.primaryConnection['_session'].decryptMessage
|
||||
//
|
||||
// // ехал any через any
|
||||
// // видит any - any, any
|
||||
// // сунул any any в any
|
||||
// // any any any any
|
||||
// // eslint-disable-next-line dot-notation
|
||||
// ;(client.primaryConnection['_session'] as any).decryptMessage = (
|
||||
// buf: any,
|
||||
// cb: any,
|
||||
// ) =>
|
||||
// decryptMessage.call(this, buf, (...args: any[]) => {
|
||||
// cb(...(args as any))
|
||||
// hadNonNull = true
|
||||
// })
|
||||
//
|
||||
// await sleep(15000)
|
||||
// await client.close()
|
||||
//
|
||||
// expect(hadNonNull).false
|
||||
// })
|
||||
// })
|
||||
|
|
139
packages/core/tests/miller-rabin.spec.ts
Normal file
139
packages/core/tests/miller-rabin.spec.ts
Normal file
|
@ -0,0 +1,139 @@
|
|||
import bigInt from 'big-integer'
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
|
||||
import { millerRabin } from '../src/utils/crypto/miller-rabin'
|
||||
|
||||
describe('miller-rabin test', function () {
|
||||
this.timeout(10000) // since miller-rabin factorization relies on RNG, it may take a while (or may not!)
|
||||
|
||||
const testMillerRabin = (n: bigInt.BigNumber, isPrime: boolean) => {
|
||||
expect(millerRabin(bigInt(n as number))).eq(isPrime)
|
||||
}
|
||||
|
||||
it('should correctly label small primes as probable primes', () => {
|
||||
const smallOddPrimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
|
||||
|
||||
for (const prime of smallOddPrimes) {
|
||||
testMillerRabin(prime, true)
|
||||
}
|
||||
})
|
||||
|
||||
it('should correctly label small odd composite numbers as composite', () => {
|
||||
const smallOddPrimes = [9, 15, 21, 25, 27, 33, 35]
|
||||
|
||||
for (const prime of smallOddPrimes) {
|
||||
testMillerRabin(prime, false)
|
||||
}
|
||||
})
|
||||
|
||||
// primes are generated using `openssl prime -generate -bits <bits>`
|
||||
|
||||
it('should work for 512-bit numbers', () => {
|
||||
testMillerRabin(
|
||||
'8411445470921866378538628788380866906358949375899610911537071281076627385046125382763689993349183284546479522400013151510610266158235924343045768103605519',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'11167561990563990242158096122232207092938761092751537312016255867850441858086589598418467012717458858604863547175649456433632887622140170743409535470973399',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'11006717791910450367418249787526506184731090161438431250022510598653874155081488487035840577645711578911087148186160668569071839053453201592321650008610329',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'12224330340162812215033324917156282302617911690617664923428569636370785775561435789211091021550357876767050350997458404009005800772805534351607294516706177',
|
||||
true,
|
||||
)
|
||||
|
||||
// above numbers but -2 (not prime)
|
||||
testMillerRabin(
|
||||
'8411445470921866378538628788380866906358949375899610911537071281076627385046125382763689993349183284546479522400013151510610266158235924343045768103605517',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'11167561990563990242158096122232207092938761092751537312016255867850441858086589598418467012717458858604863547175649456433632887622140170743409535470973397',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'11006717791910450367418249787526506184731090161438431250022510598653874155081488487035840577645711578911087148186160668569071839053453201592321650008610327',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'12224330340162812215033324917156282302617911690617664923428569636370785775561435789211091021550357876767050350997458404009005800772805534351607294516706175',
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it('should work for 1024-bit numbers', () => {
|
||||
testMillerRabin(
|
||||
'94163180970530844245052892199633535954736903357996153321496979115367320260897793334681106861766748541439161886270777106456088209508872459550450259737267142959061663564218457086654112219462515165219295402175541003899136060178102898376369981338103600856012709228116661479275753497725541132207243717937379815409',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'97324962433497727515811278760066576725849776656602017497363465683978397629803148191267105308901733336070351381654371470561376353774017284623969415330564867697353080030917333974193741719718950105404732792050882127213356260415251087867407489400712288570880407613514781891914135956778687719588061176455381937003',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'92511311413226091818378551616231701579277597795073142338527410334932345968554993390789667936819230228388142960299649466238701015865565141753710450319875546944139442823075990348978746055937500467483161699883905850192191164043687791185635729923497381849380102040768674652775240505782671289535260164547714030567',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'98801756216479639848708157708947504990501845258427605711852570166662700681215707617225664134994147912417941920327932092748574265476658124536672887141144222716123085451749764522435906007567360583062117498919471220566974634924384147341592903939264267901029640119196259026154529723870788246284629644039137378253',
|
||||
true,
|
||||
)
|
||||
|
||||
// above numbers but -2 (not prime)
|
||||
testMillerRabin(
|
||||
'94163180970530844245052892199633535954736903357996153321496979115367320260897793334681106861766748541439161886270777106456088209508872459550450259737267142959061663564218457086654112219462515165219295402175541003899136060178102898376369981338103600856012709228116661479275753497725541132207243717937379815407',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'97324962433497727515811278760066576725849776656602017497363465683978397629803148191267105308901733336070351381654371470561376353774017284623969415330564867697353080030917333974193741719718950105404732792050882127213356260415251087867407489400712288570880407613514781891914135956778687719588061176455381937001',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'92511311413226091818378551616231701579277597795073142338527410334932345968554993390789667936819230228388142960299649466238701015865565141753710450319875546944139442823075990348978746055937500467483161699883905850192191164043687791185635729923497381849380102040768674652775240505782671289535260164547714030565',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'98801756216479639848708157708947504990501845258427605711852570166662700681215707617225664134994147912417941920327932092748574265476658124536672887141144222716123085451749764522435906007567360583062117498919471220566974634924384147341592903939264267901029640119196259026154529723870788246284629644039137378251',
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it('should work for 2048-bit numbers', () => {
|
||||
testMillerRabin(
|
||||
'28608382334358769588283288249494859626901014972463291352091976543138105382282108662849885913053034513852843449409838151123568984617793641641937583673207501643041336002587032201383537626393235736734494131431069043382068545865505150651648610506542819001961332454611129372758714288168807328523359776577571626967649079147416191592855529888846889532625386469236278694936872628305052827422772792103722178298844645210242389265273407924858034431614414896134561928996888883994953322861399988094086562513898527391555490352156627307769278185444897960555995383228897584818577375695810423475039211516849716140051437120083274285367',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'30244022694659482453371920976249272809817388822378671144866806600284132009663832003348737406289715119965835410140834733465553787513841966120831322372642881643693711233087233983267648392814127424201572290931937482043046169402667397610783447368703776842799852222745601531140231486417855517072392416789672922529566643118973930252809010605519948446055538976582290902060054788109497630796585770940656002892943575479533099350429655210881833493066716819282707441553612603960556051122162329171373373251909387401572866056121964608595895425640834764028568120995397759283490218181167000161310959711677055741632674632758727382743',
|
||||
true,
|
||||
)
|
||||
testMillerRabin(
|
||||
'30560953105766401423987964658775999222308579908395527900931049506803845883459894704297458477118152899910620180302473409631442956208933061650967001020981432894530064472547770442696756724169958362395601360296775798187903794894866967342028337982275745956538015473621792510615113531964380246815875830970404687926061637030085629909804357717955251735074071072456074274947993921828878633638119117086342305530526661796817095624933200483138188878398983149622639425550360394901699701985050966685840649129419227936413574227792077082510807968104733387734970009620450108276446659342203263759999068046251645984039420643003580284779',
|
||||
true,
|
||||
)
|
||||
|
||||
// above numbers but -2 (not prime)
|
||||
testMillerRabin(
|
||||
'28608382334358769588283288249494859626901014972463291352091976543138105382282108662849885913053034513852843449409838151123568984617793641641937583673207501643041336002587032201383537626393235736734494131431069043382068545865505150651648610506542819001961332454611129372758714288168807328523359776577571626967649079147416191592855529888846889532625386469236278694936872628305052827422772792103722178298844645210242389265273407924858034431614414896134561928996888883994953322861399988094086562513898527391555490352156627307769278185444897960555995383228897584818577375695810423475039211516849716140051437120083274285365',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'30244022694659482453371920976249272809817388822378671144866806600284132009663832003348737406289715119965835410140834733465553787513841966120831322372642881643693711233087233983267648392814127424201572290931937482043046169402667397610783447368703776842799852222745601531140231486417855517072392416789672922529566643118973930252809010605519948446055538976582290902060054788109497630796585770940656002892943575479533099350429655210881833493066716819282707441553612603960556051122162329171373373251909387401572866056121964608595895425640834764028568120995397759283490218181167000161310959711677055741632674632758727382741',
|
||||
false,
|
||||
)
|
||||
testMillerRabin(
|
||||
'30560953105766401423987964658775999222308579908395527900931049506803845883459894704297458477118152899910620180302473409631442956208933061650967001020981432894530064472547770442696756724169958362395601360296775798187903794894866967342028337982275745956538015473621792510615113531964380246815875830970404687926061637030085629909804357717955251735074071072456074274947993921828878633638119117086342305530526661796817095624933200483138188878398983149622639425550360394901699701985050966685840649129419227936413574227792077082510807968104733387734970009620450108276446659342203263759999068046251645984039420643003580284777',
|
||||
false,
|
||||
)
|
||||
|
||||
// dh_prime used by telegram, as seen in https://core.telegram.org/mtproto/security_guidelines
|
||||
const telegramDhPrime =
|
||||
'C7 1C AE B9 C6 B1 C9 04 8E 6C 52 2F 70 F1 3F 73 98 0D 40 23 8E 3E 21 C1 49 34 D0 37 56 3D 93 0F 48 19 8A 0A A7 C1 40 58 22 94 93 D2 25 30 F4 DB FA 33 6F 6E 0A C9 25 13 95 43 AE D4 4C CE 7C 37 20 FD 51 F6 94 58 70 5A C6 8C D4 FE 6B 6B 13 AB DC 97 46 51 29 69 32 84 54 F1 8F AF 8C 59 5F 64 24 77 FE 96 BB 2A 94 1D 5B CD 1D 4A C8 CC 49 88 07 08 FA 9B 37 8E 3C 4F 3A 90 60 BE E6 7C F9 A4 A4 A6 95 81 10 51 90 7E 16 27 53 B5 6B 0F 6B 41 0D BA 74 D8 A8 4B 2A 14 B3 14 4E 0E F1 28 47 54 FD 17 ED 95 0D 59 65 B4 B9 DD 46 58 2D B1 17 8D 16 9C 6B C4 65 B0 D6 FF 9C A3 92 8F EF 5B 9A E4 E4 18 FC 15 E8 3E BE A0 F8 7F A9 FF 5E ED 70 05 0D ED 28 49 F4 7B F9 59 D9 56 85 0C E9 29 85 1F 0D 81 15 F6 35 B1 05 EE 2E 4E 15 D0 4B 24 54 BF 6F 4F AD F0 34 B1 04 03 11 9C D8 E3 B9 2F CC 5B'
|
||||
testMillerRabin(bigInt(telegramDhPrime.replace(/ /g, ''), 16), true)
|
||||
})
|
||||
})
|
|
@ -4,7 +4,8 @@
|
|||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
"./src",
|
||||
"./tests"
|
||||
],
|
||||
"typedocOptions": {
|
||||
"name": "@mtcute/core",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
|
|
Loading…
Reference in a new issue