├── debug.js ├── src ├── index.js ├── Utils │ ├── index.js │ ├── aesIGEMode.js │ ├── utils.js │ └── bigNumbers.js ├── TL │ ├── index.js │ └── TLSerialization.js ├── Services │ ├── index.js │ ├── DateTimeService.js │ ├── ErrorResponse.js │ └── LogService.js ├── Mtp │ ├── index.js │ ├── MtpDcConfigurator.js │ ├── MtpTimeManager.js │ ├── MtpRsaKeysManager.js │ ├── MtpProxy.js │ ├── MtpAuthorizer.js │ └── MtpNetworker.js ├── network.js └── state.js ├── .eslintrc ├── .babelrc ├── webpack.config.js ├── package.json ├── README.md └── test └── library.spec.js /debug.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('./test/library.spec.js'); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { MtpProxy } from './Mtp/index' 2 | 3 | export default MtpProxy 4 | -------------------------------------------------------------------------------- /src/Utils/index.js: -------------------------------------------------------------------------------- 1 | import * as utils from './utils' 2 | import * as BigNumbers from './bigNumbers' 3 | 4 | export default { ...utils, ...BigNumbers } -------------------------------------------------------------------------------- /src/TL/index.js: -------------------------------------------------------------------------------- 1 | import { TLSerialization, TLDeserialization } from './TLSerialization' 2 | 3 | export { 4 | TLSerialization, 5 | TLDeserialization 6 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "globals": {}, 7 | "parser": "babel-eslint", 8 | "plugins": [], 9 | "rules": {} 10 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env" 4 | ], 5 | "plugins": [ 6 | "babel-plugin-add-module-exports", 7 | "transform-object-rest-spread" 8 | ], 9 | "sourceMaps": true, 10 | "retainLines": true 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/index.js: -------------------------------------------------------------------------------- 1 | import * as LogService from './LogService' 2 | import * as DateTimeService from './DateTimeService' 3 | import ErrorResponse from './ErrorResponse' 4 | 5 | export default { LogService, DateTimeService, ErrorResponse } -------------------------------------------------------------------------------- /src/Mtp/index.js: -------------------------------------------------------------------------------- 1 | import * as MtpDcConfigurator from './MtpDcConfigurator' 2 | import * as MtpRsaKeysManager from './MtpRsaKeysManager' 3 | import * as MtpTimeManager from './MtpTimeManager' 4 | import * as MtpAuthorizer from './MtpAuthorizer' 5 | import * as MtpNetworker from './MtpNetworker' 6 | import * as MtpProxy from './MtpProxy' 7 | 8 | export { 9 | MtpDcConfigurator, 10 | MtpRsaKeysManager, 11 | MtpTimeManager, 12 | MtpAuthorizer, 13 | MtpNetworker, 14 | MtpProxy 15 | } 16 | -------------------------------------------------------------------------------- /src/Mtp/MtpDcConfigurator.js: -------------------------------------------------------------------------------- 1 | import Config from '../config.js' 2 | import { getState } from '../state.js' 3 | 4 | export function chooseServer(dcId) { 5 | let dcConfig = getState().dc_options 6 | 7 | // Excluding ipV6 8 | dcConfig = dcConfig.filter(dc => !dc.pFlags.ipv6) 9 | 10 | const dcOption = dcConfig.find(dc => dc.id == dcId) 11 | 12 | if (!dcOption) { 13 | throw new Error(`Could not find dc with id = ${dcId}`) 14 | } 15 | 16 | return `http://${dcOption.ip_address}:${dcOption.port}/${Config.Modes.test ? 'apiw_test1' : 'apiw1'}` 17 | } 18 | -------------------------------------------------------------------------------- /src/network.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export function toArrayBuffer(buf) { 4 | var ab = new ArrayBuffer(buf.length) 5 | var view = new Uint8Array(ab) 6 | for (var i = 0; i < buf.length; ++i) { 7 | view[i] = buf[i] 8 | } 9 | return ab 10 | } 11 | 12 | export const networkRequest = (url, requestData) => { 13 | return axios({ 14 | method: 'POST', 15 | url: url, 16 | data: requestData, 17 | responseType: 'arraybuffer', 18 | transformRequest: null, 19 | transformResponse: (data) => toArrayBuffer(data) 20 | }) 21 | } -------------------------------------------------------------------------------- /src/Services/DateTimeService.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | 3 | const DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS' 4 | 5 | export const getDefaultDateTimeFormat = () => DATETIME_FORMAT 6 | 7 | export const dateStrToDMY = (dateObject, format = 'DD.MM.YYYY') => { 8 | if (!dateObject) { 9 | throw new Error('Empty dateObject') 10 | } 11 | 12 | const date = moment(dateObject, format) 13 | 14 | return { 15 | day: date.date(), 16 | month: date.month() + 1, 17 | year: date.year() 18 | } 19 | } 20 | 21 | export const now = () => moment().format(DATETIME_FORMAT) 22 | 23 | export const DMYToMomentDate = (date) => { 24 | return moment({ day: date.day, month: date.month - 1, year: date.year }) 25 | } -------------------------------------------------------------------------------- /src/Services/ErrorResponse.js: -------------------------------------------------------------------------------- 1 | class ErrorResponse { 2 | constructor(obj) { 3 | this.dataObject = {} 4 | 5 | if (!obj) { 6 | return 7 | } 8 | 9 | if (obj instanceof Error) { 10 | Object.getOwnPropertyNames(obj).forEach((key) => this.dataObject[key] = obj[key]) 11 | return 12 | } 13 | 14 | this.dataObject = obj 15 | } 16 | 17 | toJSON() { 18 | return this.dataObject 19 | } 20 | 21 | toString() { 22 | /// Circular Reference Exception 23 | let cache = [] 24 | return JSON.stringify(this.dataObject, (key, value) => { 25 | if (typeof value === 'object' && value !== null) { 26 | if (cache.indexOf(value) !== -1) { 27 | return 28 | } 29 | cache.push(value) 30 | } 31 | return value 32 | }, 2) 33 | } 34 | 35 | } 36 | 37 | export default ErrorResponse -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; 3 | const path = require('path'); 4 | const env = require('yargs').argv.env; // use --env with webpack 2 5 | 6 | let libraryName = 'telejs'; 7 | 8 | let plugins = [], outputFile; 9 | 10 | if (env === 'build') { 11 | plugins.push(new UglifyJsPlugin({ minimize: true })); 12 | outputFile = libraryName + '.min.js'; 13 | } else { 14 | outputFile = libraryName + '.js'; 15 | } 16 | 17 | const config = { 18 | entry: __dirname + '/src/index.js', 19 | devtool: 'source-map', 20 | output: { 21 | path: __dirname + '/lib', 22 | filename: outputFile, 23 | library: libraryName, 24 | libraryTarget: 'umd', 25 | umdNamedDefine: true 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | test: /(\.jsx|\.js)$/, 31 | loader: 'babel-loader', 32 | exclude: /(node_modules|bower_components)/ 33 | }, 34 | { 35 | test: /(\.jsx|\.js)$/, 36 | loader: 'eslint-loader', 37 | exclude: /node_modules/ 38 | } 39 | ] 40 | }, 41 | externals: [ 42 | "axios", 43 | "crypto-js", 44 | "rusha" 45 | ], 46 | resolve: { 47 | modules: [path.resolve('./node_modules'), path.resolve('./src')], 48 | extensions: ['.json', '.js'] 49 | }, 50 | plugins: plugins 51 | }; 52 | 53 | module.exports = config; 54 | -------------------------------------------------------------------------------- /src/Mtp/MtpTimeManager.js: -------------------------------------------------------------------------------- 1 | import { tsNow, dT, nextRandomInt, longFromInts } from '../Utils' 2 | import { getState, setState } from '../state' 3 | import { LogService, ErrorResponse } from '../Services' 4 | 5 | let lastMessageID = [0, 0] 6 | let timeOffset = 0 7 | 8 | const to = getState().server_time_offset 9 | 10 | if (to) { 11 | timeOffset = to 12 | } 13 | 14 | function generateMessageID() { 15 | var timeTicks = tsNow(), 16 | timeSec = Math.floor(timeTicks / 1000) + timeOffset, 17 | timeMSec = timeTicks % 1000, 18 | random = nextRandomInt(0xFFFF) 19 | 20 | var messageID = [timeSec, (timeMSec << 21) | (random << 3) | 4] 21 | if (lastMessageID[0] > messageID[0] || 22 | lastMessageID[0] == messageID[0] && lastMessageID[1] >= messageID[1]) { 23 | messageID = [lastMessageID[0], lastMessageID[1] + 4] 24 | } 25 | 26 | lastMessageID = messageID 27 | 28 | return longFromInts(messageID[0], messageID[1]) 29 | } 30 | 31 | export function applyServerTime(serverTime, localTime) { 32 | const newTimeOffset = serverTime - Math.floor((localTime || tsNow()) / 1000) 33 | const changed = Math.abs(timeOffset - newTimeOffset) > 10 34 | 35 | setState({ server_time_offset: newTimeOffset }) 36 | 37 | lastMessageID = [0, 0] 38 | timeOffset = newTimeOffset 39 | 40 | return changed 41 | } 42 | 43 | export const generateID = generateMessageID 44 | 45 | 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telejs", 3 | "version": "0.1.7", 4 | "description": "JavaScript library to work with Telegram API", 5 | "homepage": "https://github.com/RD17/TeleJS", 6 | "main": "lib/telejs.min.js", 7 | "scripts": { 8 | "build": "webpack --env dev && webpack --env build", 9 | "dev": "webpack --progress --colors --watch --env dev", 10 | "test": "mocha --require babel-core/register --colors ./test/*.spec.js", 11 | "test:watch": "mocha --require babel-core/register --colors -w ./test/*.spec.js" 12 | }, 13 | "keywords": [ 14 | "telegram" 15 | ], 16 | "author": "RD17 LLC", 17 | "babel": { 18 | "presets": [ 19 | "env" 20 | ], 21 | "plugins": [ 22 | "babel-plugin-add-module-exports", 23 | "transform-object-rest-spread" 24 | ], 25 | "sourceMaps": true, 26 | "retainLines": true 27 | }, 28 | "license": "MIT", 29 | "devDependencies": { 30 | "babel-cli": "^6.26.0", 31 | "babel-core": "^6.26.0", 32 | "babel-eslint": "^8.0.3", 33 | "babel-loader": "^7.1.2", 34 | "babel-plugin-add-module-exports": "^0.2.1", 35 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 36 | "babel-preset-env": "^1.6.1", 37 | "chai": "^4.1.2", 38 | "eslint": "^4.13.1", 39 | "eslint-loader": "^1.9.0", 40 | "mocha": "^4.0.1", 41 | "webpack": "^3.10.0", 42 | "yargs": "^10.0.3" 43 | }, 44 | "dependencies": { 45 | "axios": "^0.17.1", 46 | "crypto-js": "^3.1.9-1", 47 | "moment": "^2.22.1", 48 | "rusha": "^0.8.9" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TeleJS - Telegram API implementation for JS 2 | 3 | 🚩 TeleJS is a pure JavaScript implementation of Telegram MTP protocol 4 | 5 | 🌟 If you ❤️ library, please star it! 🌟 6 | 7 | ## Installing 8 | 9 | `yarn add telejs` 10 | 11 | ## Initializing MtpProxy 12 | 13 | The main MTP wrapper class is `MtpProxy`, since you do not want to login into Telegram each time you run your app, you should implement two fucntions for saving and restoring the state of `MtpProxy` and pass them to `MtpProxy.init` method: 14 | 15 | ``` 16 | import fs from 'fs' 17 | import { MtpProxy } from 'telejs' 18 | 19 | MtpProxy.init( 20 | (state) => new Promise((resolve, reject) => { 21 | fs.writeFile('state.json', state, 'utf8', (err) => { 22 | if (err) { 23 | reject(err) 24 | return 25 | } 26 | resolve() 27 | }) 28 | }), 29 | () => new Promise((resolve, reject) => { 30 | fs.readFile('state.json', 'utf8', (err, data) => { 31 | if (err) { 32 | reject(err) 33 | return 34 | } 35 | resolve(data) 36 | }) 37 | }) 38 | , 'verbose') 39 | ``` 40 | 41 | After `MtpProxy.init` is called, your `MtpProxy` is ready to work. 42 | 43 | ## Signing in 44 | 45 | ``` 46 | MtpProxy.signInUser('YOUR PHONE NUMBER HERE', codeInputPromise) 47 | .then(res => console.log(res)) 48 | .catch((err) => console.error(err)) 49 | ``` 50 | 51 | `codeInputPromise` - Promise that should return the auth code received from Telegram 52 | 53 | ## Calling Telegram API methods 54 | 55 | Please refer to [official Telegram API methods list](https://core.telegram.org/methods) to find available methods. 56 | 57 | Here is an example of calling `messages.sendMessage` method: 58 | 59 | ``` 60 | MtpProxy.mtpInvokeApi('messages.sendMessage', 61 | { 62 | "random_id": Math.floor(Math.random() * 5000), 63 | "peer": { "_": "inputPeerUser", "user_id": 'USER ID TO SEND MESSAGE TO', "access_hash": "ACCESS HASH" }, 64 | "message": `How are you doing?` 65 | } 66 | ) 67 | .then(res => console.log('sent')) 68 | .catch(err => console.error(err)) 69 | ``` 70 | -------------------------------------------------------------------------------- /src/Services/LogService.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | import * as DateTimeService from './DateTimeService' 3 | 4 | const LEVELS = { 5 | debug: { 6 | level: 0, 7 | description: 'debug' 8 | }, 9 | verbose: { 10 | level: 1, 11 | description: 'verbose' 12 | }, 13 | info: { 14 | level: 2, 15 | description: 'info' 16 | }, 17 | error: { 18 | level: 3, 19 | description: 'error' 20 | } 21 | } 22 | const DATETIME_FORMAT = DateTimeService.getDefaultDateTimeFormat() 23 | 24 | let sourceName = null 25 | let minLevel = null 26 | 27 | export const init = (loggingSourceName, minLoggingLevel) => { 28 | if (!minLoggingLevel) { 29 | throw new Error(`Please specify minimal logging level, levels available: ${Object.keys(LEVELS)}`) 30 | } 31 | if (!loggingSourceName) { 32 | throw new Error(`Please specify source name`) 33 | } 34 | sourceName = loggingSourceName 35 | minLevel = LEVELS[minLoggingLevel] 36 | if (!minLevel) { 37 | throw new Error(`Could not find logging level ${minLoggingLevel}, levels available: ${Object.keys(LEVELS)}`) 38 | } 39 | } 40 | 41 | export const logDebug = (message) => { 42 | if (LEVELS.debug.level >= minLevel.level) { 43 | logMessage(LEVELS.debug, message) 44 | } 45 | } 46 | export const logVerbose = (message) => { 47 | if (LEVELS.verbose.level >= minLevel.level) { 48 | logMessage(LEVELS.verbose, message) 49 | } 50 | } 51 | export const logInfo = (message) => { 52 | if (LEVELS.info.level >= minLevel.level) { 53 | logMessage(LEVELS.info, message) 54 | } 55 | } 56 | export const logError = (message) => { 57 | if (LEVELS.error.level >= minLevel.level) { 58 | logMessage(LEVELS.error, message) 59 | } 60 | } 61 | 62 | const logMessage = (level, message) => { 63 | if (level == LEVELS.error) { 64 | console.error(composeMessage(level.description, message)) 65 | return 66 | } 67 | 68 | console.log(composeMessage(level.description, message)) 69 | } 70 | 71 | const composeMessage = (levelDescription, message) => { 72 | if (!sourceName) { 73 | throw new Error('LogService is not initialized!') 74 | } 75 | 76 | return `${moment().format(DATETIME_FORMAT)}: [${sourceName}] [${levelDescription}] ${message}` 77 | } -------------------------------------------------------------------------------- /src/Utils/aesIGEMode.js: -------------------------------------------------------------------------------- 1 | import * as CryptoJS from 'crypto-js' 2 | 3 | /** 4 | * Abstract base IGE mode. 5 | */ 6 | let IGE = CryptoJS.mode.IGE = CryptoJS.lib.BlockCipherMode.extend(); 7 | 8 | /** 9 | * IGE encryptor. 10 | */ 11 | IGE.Encryptor = IGE.extend({ 12 | /** 13 | * Processes the data block at offset. 14 | * 15 | * @param {Array} words The data words to operate on. 16 | * @param {number} offset The offset where the block starts. 17 | * 18 | * @example 19 | * 20 | * mode.processBlock(data.words, offset); 21 | */ 22 | processBlock: function (words, offset) { 23 | // Shortcuts 24 | let cipher = this._cipher; 25 | let blockSize = cipher.blockSize; 26 | 27 | if (this._ivp === undefined) { 28 | this._ivp = this._iv.slice(0, blockSize); 29 | this._iv2p = this._iv.slice(blockSize, blockSize + blockSize); 30 | } 31 | 32 | 33 | // Remember this block to use with next block 34 | let nextIv2p = words.slice(offset, offset + blockSize); 35 | 36 | // XOR with previous ciphertext 37 | xorBlock(words, this._ivp, offset, blockSize); 38 | 39 | // Block cipher 40 | cipher.encryptBlock(words, offset); 41 | 42 | // XOR with previous plaintext 43 | xorBlock(words, this._iv2p, offset, blockSize); 44 | 45 | this._ivp = words.slice(offset, offset + blockSize); 46 | this._iv2p = nextIv2p; 47 | } 48 | }); 49 | 50 | /** 51 | * IGE decryptor. 52 | */ 53 | IGE.Decryptor = IGE.extend({ 54 | /** 55 | * Processes the data block at offset. 56 | * 57 | * @param {Array} words The data words to operate on. 58 | * @param {number} offset The offset where the block starts. 59 | * 60 | * @example 61 | * 62 | * mode.processBlock(data.words, offset); 63 | */ 64 | processBlock: function (words, offset) { 65 | // Shortcuts 66 | let cipher = this._cipher; 67 | let blockSize = cipher.blockSize; 68 | 69 | if (this._ivp === undefined) { 70 | this._ivp = this._iv.slice(0, blockSize); 71 | this._iv2p = this._iv.slice(blockSize, 2 * blockSize); 72 | } 73 | 74 | // Remember this block to use with next block 75 | let nextIvp = words.slice(offset, offset + blockSize); 76 | 77 | // XOR with previous ciphertext 78 | xorBlock(words, this._iv2p, offset, blockSize); 79 | 80 | // Block cipher 81 | cipher.decryptBlock(words, offset); 82 | 83 | // XOR with previous plaintext 84 | xorBlock(words, this._ivp, offset, blockSize); 85 | 86 | this._ivp = nextIvp; 87 | this._iv2p = words.slice(offset, offset + blockSize); 88 | } 89 | }); 90 | 91 | function xorBlock(words, block, offset, blockSize) { 92 | for (let i = 0; i < blockSize; i++) { 93 | words[offset + i] ^= block[i]; 94 | } 95 | } -------------------------------------------------------------------------------- /test/library.spec.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import chai from 'chai' 3 | import assert from 'assert' 4 | import { MtpDcConfigurator, MtpRsaKeysManager, MtpTimeManager, MtpAuthorizer, MtpProxy } from '../src/Mtp/index' 5 | import readline from 'readline' 6 | 7 | const expect = chai.expect 8 | 9 | MtpProxy.init( 10 | (state) => new Promise((resolve, reject) => { 11 | fs.writeFile('state.json', state, 'utf8', (err) => { 12 | if (err) { 13 | reject(err) 14 | return 15 | } 16 | resolve() 17 | }) 18 | }), 19 | () => new Promise((resolve, reject) => { 20 | fs.readFile('state.json', 'utf8', (err, data) => { 21 | if (err) { 22 | reject(err) 23 | return 24 | } 25 | resolve(data) 26 | }) 27 | }) 28 | , 'info') 29 | 30 | describe('DH test', function () { 31 | this.timeout(20000) 32 | this.slow(10000) 33 | 34 | it('should initialize DH params', (done) => { 35 | MtpAuthorizer.auth(2) 36 | .then((res) => done()) 37 | .catch((err) => done(err)) 38 | }) 39 | }) 40 | 41 | describe('Test MtpNetworker', function () { 42 | this.timeout(20000) 43 | this.slow(10000) 44 | 45 | it('Should return MtpNetworker', (done) => { 46 | MtpProxy.mtpGetNetworker(2) 47 | .then((res) => done()) 48 | .catch((err) => done(new Error(err))) 49 | }) 50 | }) 51 | 52 | describe('Test MtpProxy', function () { 53 | this.timeout(0) 54 | this.slow(10000) 55 | 56 | it('Should return dc config', (done) => { 57 | MtpProxy.mtpInvokeApi("help.getConfig", {}) 58 | .then((res) => { 59 | expect(res.dc_options).to.be.a('array') 60 | done() 61 | }) 62 | .catch((err) => done(new Error(err))) 63 | }) 64 | 65 | it('Should sign in user', (done) => { 66 | const rl = readline.createInterface({ 67 | input: process.stdin, 68 | output: process.stdout 69 | }) 70 | 71 | const codeInputPromise = () => new Promise((resolve, reject) => { 72 | rl.question('Code? ', (code) => { 73 | resolve(code) 74 | rl.close() 75 | }) 76 | }) 77 | 78 | MtpProxy.signInUser('YOUR PHONE NUMBER HERE', codeInputPromise) 79 | .then(res => { 80 | done() 81 | }) 82 | .catch((err) => done(new Error(err))) 83 | }) 84 | 85 | it('should send test message', (done) => { 86 | const sendMsg = () => { 87 | console.log('sending') 88 | MtpProxy.mtpInvokeApi('messages.sendMessage', 89 | { 90 | "random_id": Math.floor(Math.random() * 5000), 91 | "peer": { "_": "inputPeerUser", "user_id": 'USER ID TO SEND MESSAGE TO', "access_hash": "ACCESS HASH" }, 92 | "message": `How are you doing? ${new Date().getMinutes()}` 93 | } 94 | ) 95 | .then(res => { console.log('sent') }) 96 | .catch(err => done(new Error(err))) 97 | } 98 | 99 | sendMsg() 100 | setInterval(() => sendMsg(), 5 * 60 * 1000) 101 | }) 102 | }) -------------------------------------------------------------------------------- /src/state.js: -------------------------------------------------------------------------------- 1 | import Config from './config.js' 2 | import { LogService, ErrorResponse } from './Services' 3 | 4 | const defaultState = { 5 | current_dc_id: 2, 6 | prev_dc_id: undefined, 7 | dc_options: Config.Modes.test 8 | ? [ 9 | { 10 | "_": "dcOption", 11 | "pFlags": {}, 12 | "flags": 0, 13 | "id": 1, 14 | "ip_address": "149.154.175.10", 15 | "port": 80 16 | }, 17 | { 18 | "_": "dcOption", 19 | "pFlags": {}, 20 | "flags": 0, 21 | "id": 2, 22 | "ip_address": "149.154.167.40", 23 | "port": 80 24 | }, 25 | { 26 | "_": "dcOption", 27 | "pFlags": {}, 28 | "flags": 0, 29 | "id": 2, 30 | "ip_address": "149.154.175.117", 31 | "port": 80 32 | }, 33 | ] 34 | : [ 35 | { 36 | "_": "dcOption", 37 | "pFlags": {}, 38 | "flags": 0, 39 | "id": 1, 40 | "ip_address": "149.154.175.50", 41 | "port": 443 42 | }, 43 | { 44 | "_": "dcOption", 45 | "pFlags": { 46 | "ipv6": true 47 | }, 48 | "flags": 1, 49 | "id": 1, 50 | "ip_address": "2001:0b28:f23d:f001:0000:0000:0000:000a", 51 | "port": 443 52 | }, 53 | { 54 | "_": "dcOption", 55 | "pFlags": {}, 56 | "flags": 0, 57 | "id": 2, 58 | "ip_address": "149.154.167.51", 59 | "port": 443 60 | }, 61 | { 62 | "_": "dcOption", 63 | "pFlags": { 64 | "static": true 65 | }, 66 | "flags": 16, 67 | "id": 2, 68 | "ip_address": "149.154.167.51", 69 | "port": 443 70 | }, 71 | { 72 | "_": "dcOption", 73 | "pFlags": { 74 | "ipv6": true 75 | }, 76 | "flags": 1, 77 | "id": 2, 78 | "ip_address": "2001:067c:04e8:f002:0000:0000:0000:000a", 79 | "port": 443 80 | }, 81 | { 82 | "_": "dcOption", 83 | "pFlags": {}, 84 | "flags": 0, 85 | "id": 3, 86 | "ip_address": "149.154.175.100", 87 | "port": 443 88 | }, 89 | { 90 | "_": "dcOption", 91 | "pFlags": { 92 | "ipv6": true 93 | }, 94 | "flags": 1, 95 | "id": 3, 96 | "ip_address": "2001:0b28:f23d:f003:0000:0000:0000:000a", 97 | "port": 443 98 | }, 99 | { 100 | "_": "dcOption", 101 | "pFlags": {}, 102 | "flags": 0, 103 | "id": 4, 104 | "ip_address": "149.154.167.92", 105 | "port": 443 106 | }, 107 | { 108 | "_": "dcOption", 109 | "pFlags": { 110 | "ipv6": true 111 | }, 112 | "flags": 1, 113 | "id": 4, 114 | "ip_address": "2001:067c:04e8:f004:0000:0000:0000:000a", 115 | "port": 443 116 | }, 117 | { 118 | "_": "dcOption", 119 | "pFlags": { 120 | "media_only": true 121 | }, 122 | "flags": 2, 123 | "id": 4, 124 | "ip_address": "149.154.165.120", 125 | "port": 443 126 | }, 127 | { 128 | "_": "dcOption", 129 | "pFlags": {}, 130 | "flags": 0, 131 | "id": 5, 132 | "ip_address": "91.108.56.170", 133 | "port": 443 134 | }, 135 | { 136 | "_": "dcOption", 137 | "pFlags": { 138 | "ipv6": true 139 | }, 140 | "flags": 1, 141 | "id": 5, 142 | "ip_address": "2001:0b28:f23f:f005:0000:0000:0000:000a", 143 | "port": 443 144 | } 145 | ], 146 | networkers: [ 147 | ] 148 | } 149 | 150 | let state = { ...defaultState } 151 | 152 | let dump = undefined 153 | let load = undefined 154 | 155 | export const initState = (dumpState, loadState) => new Promise((resolve, reject) => { 156 | dump = dumpState 157 | load = loadState 158 | 159 | LogService.logInfo(`[state] init`) 160 | 161 | if (load) { 162 | load() 163 | .then(stateContents => { 164 | state = JSON.parse(stateContents) 165 | resolve() 166 | }) 167 | .catch(err => { 168 | LogService.logError(`[state] ${new ErrorResponse(err)}`) 169 | 170 | reject() 171 | }) 172 | return 173 | } 174 | 175 | resolve() 176 | }) 177 | 178 | export const getState = () => { 179 | return state 180 | } 181 | export const setState = (newState) => { 182 | LogService.logVerbose(`[state] SetState()`) 183 | 184 | state = { ...state, ...newState } 185 | if (dump) { 186 | dump(JSON.stringify(state)) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Mtp/MtpRsaKeysManager.js: -------------------------------------------------------------------------------- 1 | import { TLSerialization } from '../TL' 2 | import { bytesToHex, bytesFromHex, sha1BytesSync, bigStringInt } from '../Utils' 3 | 4 | /** 5 | * Server public key, obtained from here: https://core.telegram.org/api/obtaining_api_id 6 | * 7 | * 8 | * -----BEGIN RSA PUBLIC KEY----- 9 | * MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 10 | * lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS 11 | * an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw 12 | * Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ 13 | * 8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n 14 | * Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB 15 | * -----END RSA PUBLIC KEY----- 16 | * 17 | * -----BEGIN PUBLIC KEY----- 18 | * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAruw2yP/BCcsJliRoW5eB 19 | * VBVle9dtjJw+OYED160Wybum9SXtBBLXriwt4rROd9csv0t0OHCaTmRqBcQ0J8fx 20 | * hN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvd 21 | * l84Kd9ORYjDEAyFnEA7dD556OptgLQQ2e2iVNq8NZLYTzLp5YpOdO1doK+ttrltg 22 | * gTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnSLj16yE5HvJQn0CNpRdENvRUXe6tBP78O 23 | * 39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wFXGF710w9lwCGNbmNxNYhtIkdqfsEcwR5 24 | * JwIDAQAB 25 | * -----END PUBLIC KEY----- 26 | * 27 | * -----BEGIN PUBLIC KEY----- 28 | * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvfLHfYH2r9R70w8prHbl 29 | * Wt/nDkh+XkgpflqQVcnAfSuTtO05lNPspQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOO 30 | * KPi0OfJXoRVylFzAQG/j83u5K3kRLbae7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ 31 | * 3TDS2pQOCtovG4eDl9wacrXOJTG2990VjgnIKNA0UMoP+KF03qzryqIt3oTvZq03 32 | * DyWdGK+AZjgBLaDKSnC6qD2cFY81UryRWOab8zKkWAnhw2kFpcqhI0jdV5QaSCEx 33 | * vnsjVaX0Y1N0870931/5Jb9ICe4nweZ9kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV 34 | * /wIDAQAB 35 | * -----END PUBLIC KEY----- 36 | * 37 | * -----BEGIN PUBLIC KEY----- 38 | * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ditzm+mPND6xkhzwFI 39 | * z6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGrzqTDHkO30R8VeRM/Kz2f4nR05GIFiITl 40 | * 4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+th6knSU0yLtNKuQVP6voMrnt9MV1X92L 41 | * GZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvSUwwc+yi1/gGaybwlzZwqXYoPOhwMebzK 42 | * Uk0xW14htcJrRrq+PXXQbRzTMynseCoPIoke0dtCodbA3qQxQovE16q9zz4Otv2k 43 | * 4j63cz53J+mhkVWAeWxVGI0lltJmWtEYK6er8VqqWot3nqmWMXogrgRLggv/Nbbo 44 | * oQIDAQAB 45 | * -----END PUBLIC KEY----- 46 | * 47 | * -----BEGIN PUBLIC KEY----- 48 | * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q0 49 | * 5shjg8/4p6047bn6/m8yPy1RBsvIyvuDuGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xb 50 | * nfxL5BXHplJhMtADXKM9bWB11PU1Eioc3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA 51 | * 9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvifRLJbY08/Gp66KpQvy7g8w7VB8wlgePe 52 | * xW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqePji9NP3tJUFQjcECqcm0yV7/2d0t/pbC 53 | * m+ZH1sadZspQCEPPrtbkQBlvHb4OLiIWPGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6M 54 | * AQIDAQAB 55 | * -----END PUBLIC KEY----- 56 | * 57 | * Bytes can be got via 58 | * $ openssl rsa -pubin -in key.pub -text -noout 59 | */ 60 | 61 | const publisKeysHex = [ 62 | { 63 | modulus: 'c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f', 64 | exponent: '010001' 65 | }, 66 | { 67 | modulus: 'aeec36c8ffc109cb099624685b97815415657bd76d8c9c3e398103d7ad16c9bba6f525ed0412d7ae2c2de2b44e77d72cbf4b7438709a4e646a05c43427c7f184debf72947519680e651500890c6832796dd11f772c25ff8f576755afe055b0a3752c696eb7d8da0d8be1faf38c9bdd97ce0a77d3916230c4032167100edd0f9e7a3a9b602d04367b689536af0d64b613ccba7962939d3b57682beb6dae5b608130b2e52aca78ba023cf6ce806b1dc49c72cf928a7199d22e3d7ac84e47bc9427d0236945d10dbd15177bab413fbf0edfda09f014c7a7da088dde9759702ca760af2b8e4e97cc055c617bd74c3d97008635b98dc4d621b4891da9fb0473047927', 68 | exponent: '010001' 69 | }, 70 | { 71 | modulus: 'bdf2c77d81f6afd47bd30f29ac76e55adfe70e487e5e48297e5a9055c9c07d2b93b4ed3994d3eca5098bf18d978d54f8b7c713eb10247607e69af9ef44f38e28f8b439f257a11572945cc0406fe3f37bb92b79112db69eedf2dc71584a661638ea5becb9e23585074b80d57d9f5710dd30d2da940e0ada2f1b878397dc1a72b5ce2531b6f7dd158e09c828d03450ca0ff8a174deacebcaa22dde84ef66ad370f259d18af806638012da0ca4a70baa83d9c158f3552bc9158e69bf332a45809e1c36905a5caa12348dd57941a482131be7b2355a5f4635374f3bd3ddf5ff925bf4809ee27c1e67d9120c5fe08a9de458b1b4a3c5d0a428437f2beca81f4e2d5ff', 72 | exponent: '010001' 73 | }, 74 | { 75 | modulus: 'b3f762b739be98f343eb1921cf0148cfa27ff7af02b6471213fed9daa0098976e667750324f1abcea4c31e43b7d11f1579133f2b3d9fe27474e462058884e5e1b123be9cbbc6a443b2925c08520e7325e6f1a6d50e117eb61ea49d2534c8bb4d2ae4153fabe832b9edf4c5755fdd8b19940b81d1d96cf433d19e6a22968a85dc80f0312f596bd2530c1cfb28b5fe019ac9bc25cd9c2a5d8a0f3a1c0c79bcca524d315b5e21b5c26b46babe3d75d06d1cd33329ec782a0f22891ed1db42a1d6c0dea431428bc4d7aabdcf3e0eb6fda4e23eb7733e7727e9a1915580796c55188d2596d2665ad1182ba7abf15aaa5a8b779ea996317a20ae044b820bff35b6e8a1', 76 | exponent: '010001' 77 | }, 78 | { 79 | modulus: 'be6a71558ee577ff03023cfa17aab4e6c86383cff8a7ad38edb9fafe6f323f2d5106cbc8cafb83b869cffd1ccf121cd743d509e589e68765c96601e813dc5b9dfc4be415c7a6526132d0035ca33d6d6075d4f535122a1cdfe017041f1088d1419f65c8e5490ee613e16dbf662698c0f54870f0475fa893fc41eb55b08ff1ac211bc045ded31be27d12c96d8d3cfc6a7ae8aa50bf2ee0f30ed507cc2581e3dec56de94f5dc0a7abee0be990b893f2887bd2c6310a1e0a9e3e38bd34fded2541508dc102a9c9b4c95effd9dd2dfe96c29be647d6c69d66ca500843cfaed6e440196f1dbe0e2e22163c61ca48c79116fa77216726749a976a1c4b0944b5121e8c01', 80 | exponent: '010001' 81 | } 82 | ] 83 | 84 | var publicKeysParsed = {} 85 | var prepared = false 86 | 87 | function prepareRsaKeys() { 88 | if (prepared) { 89 | return 90 | } 91 | 92 | for (var i = 0; i < publisKeysHex.length; i++) { 93 | var keyParsed = publisKeysHex[i] 94 | 95 | var RSAPublicKey = new TLSerialization() 96 | RSAPublicKey.storeBytes(bytesFromHex(keyParsed.modulus), 'n') 97 | RSAPublicKey.storeBytes(bytesFromHex(keyParsed.exponent), 'e') 98 | 99 | var buffer = RSAPublicKey.getBuffer() 100 | 101 | var fingerprintBytes = sha1BytesSync(buffer).slice(-8) 102 | fingerprintBytes.reverse() 103 | 104 | publicKeysParsed[bytesToHex(fingerprintBytes)] = { 105 | modulus: keyParsed.modulus, 106 | exponent: keyParsed.exponent 107 | } 108 | } 109 | 110 | prepared = true 111 | } 112 | 113 | function selectRsaKeyByFingerPrint(fingerprints) { 114 | prepareRsaKeys() 115 | 116 | var fingerprintHex, foundKey, i 117 | for (i = 0; i < fingerprints.length; i++) { 118 | fingerprintHex = bigStringInt(fingerprints[i]).toString(16) 119 | if (foundKey = publicKeysParsed[fingerprintHex]) { 120 | return { ...foundKey, fingerprint: fingerprints[i] } 121 | } 122 | } 123 | 124 | return false 125 | } 126 | 127 | export const prepare = prepareRsaKeys 128 | export const select = selectRsaKeyByFingerPrint 129 | 130 | -------------------------------------------------------------------------------- /src/Mtp/MtpProxy.js: -------------------------------------------------------------------------------- 1 | import * as MtpAuthorizer from './MtpAuthorizer' 2 | import * as MtpNetworker from './MtpNetworker' 3 | import { getState, setState, initState } from '../state' 4 | import { dT, tsNow } from '../Utils' 5 | import { LogService, ErrorResponse } from '../Services' 6 | import { name } from '../../package.json' 7 | 8 | let isInitialized = false 9 | 10 | export const init = (dumpState, loadState, loggingLevel = 'verbose') => new Promise((resolve, reject) => { 11 | LogService.init(name, loggingLevel) 12 | 13 | initState(dumpState, loadState) 14 | .then(() => { 15 | isInitialized = true 16 | resolve() 17 | }) 18 | .catch(reject) 19 | }) 20 | 21 | export const mtpGetNetworker = (dcId, options = {}) => new Promise((resolve, reject) => { 22 | if (!isInitialized) { 23 | reject(new Error('Not initialized!')) 24 | return 25 | } 26 | 27 | if (!dcId) { 28 | reject(new Error('Please specify dcId')) 29 | return 30 | } 31 | 32 | const networkerDataFromState = getState().networkers.find(nw => nw.id == dcId) 33 | if (networkerDataFromState) { 34 | const networker = MtpNetworker.getNetworker(dcId, networkerDataFromState.auth.authKey, networkerDataFromState.auth.serverSalt, options) 35 | resolve(networker) 36 | return 37 | } 38 | 39 | MtpAuthorizer.auth(dcId) 40 | .then((auth) => { 41 | const networker = MtpNetworker.getNetworker(dcId, auth.authKey, auth.serverSalt, options) 42 | setState({ networkers: [...getState().networkers, { id: dcId, auth: auth }] }) 43 | resolve(networker) 44 | }) 45 | .catch((error) => reject(error)) 46 | }) 47 | 48 | export const signUpUser = (phoneNumber, firstName, lastName, codeInputPromise) => new Promise((resolve, reject) => { 49 | if (!isInitialized) { 50 | reject(new Error('Not initialized!')) 51 | return 52 | } 53 | 54 | const sendCodeCallParams = { 55 | flags: 0, 56 | allow_flashcall: null, 57 | phone_number: phoneNumber, 58 | current_number: null, 59 | api_id: 2496, 60 | api_hash: '8da85b0d5bfe62527e5b244c209159c3' 61 | } 62 | 63 | mtpInvokeApi("auth.sendCode", sendCodeCallParams) 64 | .then(res => res.phone_code_hash) 65 | .then(phone_code_hash => { 66 | return codeInputPromise() 67 | .then((code) => { 68 | return { code, phone_code_hash } 69 | }) 70 | }) 71 | .then(({ code, phone_code_hash }) => { 72 | const signUpCallParams = { 73 | phone_number: phoneNumber, 74 | phone_code_hash: phone_code_hash, 75 | phone_code: code, 76 | first_name: firstName, 77 | last_name: lastName 78 | } 79 | 80 | return mtpInvokeApi("auth.signUp", signUpCallParams) 81 | }) 82 | .then(resolve) 83 | .catch(reject) 84 | }) 85 | 86 | export const signInUser = (phoneNumber, codeInputPromise) => new Promise((resolve, reject) => { 87 | if (!isInitialized) { 88 | reject(new Error('Not initialized!')) 89 | return 90 | } 91 | 92 | const sendCodeCallParams = { 93 | sms_type: 5, 94 | phone_number: phoneNumber, 95 | api_id: 2496, 96 | api_hash: '8da85b0d5bfe62527e5b244c209159c3' 97 | } 98 | 99 | mtpInvokeApi("auth.sendCode", sendCodeCallParams) 100 | .then(res => res.phone_code_hash) 101 | .then(phone_code_hash => { 102 | return codeInputPromise() 103 | .then((code) => { 104 | return { code, phone_code_hash } 105 | }) 106 | }) 107 | .then(({ code, phone_code_hash }) => { 108 | const signInCallParams = { 109 | phone_number: phoneNumber, 110 | phone_code_hash: phone_code_hash, 111 | phone_code: code 112 | } 113 | 114 | return mtpInvokeApi("auth.signIn", signInCallParams) 115 | }) 116 | .then(resolve) 117 | .catch(reject) 118 | }) 119 | 120 | export const mtpInvokeApi = (method, params, options = {}) => new Promise((resolve, reject) => { 121 | if (!isInitialized) { 122 | reject(new Error('Not initialized!')) 123 | return 124 | } 125 | 126 | LogService.logVerbose(`[MtpProxy] mtpInvokeApi() ${JSON.stringify(method, 0, 2)} ${JSON.stringify(params, 0, 2)} ${JSON.stringify(options, 0, 2)}`) 127 | 128 | const rejectPromise = function (error) { 129 | if (!error) { 130 | error = { type: 'ERROR_EMPTY' } 131 | } else if (typeof (error) !== 'object') { 132 | error = { message: error } 133 | } 134 | 135 | if (error.code == 406) { 136 | error.handled = true 137 | } 138 | 139 | reject(error) 140 | } 141 | 142 | const requestPromise = function (networker) { 143 | let dcId = networker.getDcId() 144 | let prevDcId = getState().prev_dc_id 145 | 146 | return networker.wrapApiCall(method, params, options) 147 | .then(resolve) 148 | .catch(error => { 149 | LogService.logError(`[MtpProxy] networker.wrapApiCall() ${new ErrorResponse(error)} ${prevDcId} ${dcId}`) 150 | 151 | if (error.code == 401 && (!prevDcId || (dcId == prevDcId))) { 152 | rejectPromise(error) 153 | } 154 | else if (error.code == 401 && prevDcId && dcId != prevDcId) { 155 | mtpInvokeApi('auth.exportAuthorization', { dc_id: dcId }, { dcId: prevDcId, noErrorBox: true }) 156 | .then((exportedAuth) => 157 | mtpInvokeApi('auth.importAuthorization', { id: exportedAuth.id, bytes: exportedAuth.bytes }, { dcId: dcId, noErrorBox: true }) 158 | ) 159 | .then(() => mtpInvokeApi(method, params, options)) 160 | .then(resolve) 161 | .catch(rejectPromise) 162 | } 163 | else if (error.code == 303) { 164 | var newDcID = error.type.match(/^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_)(\d+)/)[2] 165 | if (newDcID != dcId) { 166 | setState({ prev_dc_id: dcId, current_dc_id: newDcID }) 167 | 168 | mtpInvokeApi(method, params, options) 169 | .then(resolve) 170 | .catch(rejectPromise) 171 | } 172 | } 173 | else if (!options.rawError && error.code == 420) { 174 | const waitTime = error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 10 175 | if (waitTime > (options.timeout || 60)) { 176 | rejectPromise(error) 177 | return 178 | } 179 | 180 | setTimeout(function () { 181 | requestPromise(networker) 182 | }, waitTime * 1000) 183 | } 184 | else if (!options.rawError && (error.code == 500 || error.type == 'MSG_WAIT_FAILED')) { 185 | const now = tsNow() 186 | if (options.stopTime) { 187 | if (now >= options.stopTime) { 188 | rejectPromise(error) 189 | return 190 | } 191 | } else { 192 | options.stopTime = now + (options.timeout !== undefined ? options.timeout : 10) * 1000 193 | } 194 | options.waitTime = options.waitTime ? Math.min(60, options.waitTime * 1.5) : 1 195 | setTimeout(function () { 196 | requestPromise(networker) 197 | }, options.waitTime * 1000) 198 | } else { 199 | rejectPromise(error) 200 | } 201 | }) 202 | } 203 | 204 | const currrentDcId = options.dcId || getState().current_dc_id 205 | mtpGetNetworker(currrentDcId, options) 206 | .then(requestPromise) 207 | .catch(rejectPromise) 208 | }) -------------------------------------------------------------------------------- /src/Mtp/MtpAuthorizer.js: -------------------------------------------------------------------------------- 1 | //import requestLib from 'request-promise-native' 2 | import { TLSerialization, TLDeserialization } from '../TL' 3 | import { MtpTimeManager, MtpDcConfigurator, MtpRsaKeysManager } from '../Mtp' 4 | import { 5 | BigInteger, nextRandomInt, dT, bytesToHex, bytesCmp, pqPrimeFactorization, SecureRandom, sha1BytesSync, rsaEncrypt, 6 | tsNow, aesDecryptSync, aesEncryptSync, bytesToArrayBuffer, bytesFromHex, bytesModPow, bytesXor 7 | } from '../Utils' 8 | import { LogService, ErrorResponse } from '../Services' 9 | import { networkRequest } from '../network' 10 | 11 | const mtpSendPlainRequest = (dcID, requestBuffer) => new Promise((resolve, reject) => { 12 | var requestLength = requestBuffer.byteLength, 13 | requestArray = new Int32Array(requestBuffer) 14 | 15 | var header = new TLSerialization() 16 | header.storeLongP(0, 0, 'auth_key_id') // Auth key 17 | header.storeLong(MtpTimeManager.generateID(), 'msg_id') // Msg_id 18 | header.storeInt(requestLength, 'request_length') 19 | 20 | var headerBuffer = header.getBuffer(), 21 | headerArray = new Int32Array(headerBuffer) 22 | var headerLength = headerBuffer.byteLength 23 | 24 | var resultBuffer = new ArrayBuffer(headerLength + requestLength), 25 | resultArray = new Int32Array(resultBuffer) 26 | 27 | resultArray.set(headerArray) 28 | resultArray.set(requestArray, headerArray.length) 29 | 30 | var requestData = resultArray 31 | var url = MtpDcConfigurator.chooseServer(dcID) 32 | var baseError = { code: 406, type: 'NETWORK_BAD_RESPONSE', url: url } 33 | 34 | networkRequest(url, resultArray).then((result) => { 35 | if (!result.data || !result.data.byteLength) { 36 | reject(baseError) 37 | } 38 | 39 | const deserializer = new TLDeserialization(result.data, { mtproto: true }) 40 | const auth_key_id = deserializer.fetchLong('auth_key_id') 41 | const msg_id = deserializer.fetchLong('msg_id') 42 | const msg_len = deserializer.fetchInt('msg_len') 43 | 44 | resolve(deserializer) 45 | }) 46 | .catch(err => reject({ ...baseError, originalError: err })) 47 | }) 48 | 49 | function mtpSendReqPQ(auth) { 50 | var deferred = auth.deferred 51 | 52 | var request = new TLSerialization({ mtproto: true }) 53 | 54 | request.storeMethod('req_pq', { nonce: auth.nonce }) 55 | 56 | LogService.logVerbose(`[MtpAuthorizer] mtpSendReqPQ() Send req_pq ${bytesToHex(auth.nonce)}`) 57 | 58 | mtpSendPlainRequest(auth.dcID, request.getBuffer()) 59 | .then((deserializer) => { 60 | 61 | var response = deserializer.fetchObject('ResPQ') 62 | 63 | if (response._ != 'resPQ') { 64 | throw new Error('[MT] resPQ response invalid: ' + response._) 65 | } 66 | 67 | if (!bytesCmp(auth.nonce, response.nonce)) { 68 | throw new Error('[MT] resPQ nonce mismatch') 69 | } 70 | 71 | auth.serverNonce = response.server_nonce 72 | auth.pq = response.pq 73 | auth.fingerprints = response.server_public_key_fingerprints 74 | 75 | LogService.logVerbose(`[MtpAuthorizer] mtpSendReqPQ() Got ResPQ ${bytesToHex(auth.serverNonce)} ${bytesToHex(auth.pq)} ${auth.fingerprints}`) 76 | 77 | auth.publicKey = MtpRsaKeysManager.select(auth.fingerprints) 78 | 79 | if (!auth.publicKey) { 80 | throw new Error('[MT] No public key found') 81 | } 82 | 83 | LogService.logVerbose(`[MtpAuthorizer] mtpSendReqPQ() 'PQ factorization start ${auth.pq}`) 84 | 85 | const pAndQ = pqPrimeFactorization(auth.pq) 86 | 87 | if (!pAndQ) { 88 | throw new Error('Error factorizing p and q') 89 | } 90 | 91 | auth.p = pAndQ[0] 92 | auth.q = pAndQ[1] 93 | 94 | LogService.logVerbose(`[MtpAuthorizer] mtpSendReqPQ() 'PQ factorization done ${pAndQ[2]}`) 95 | 96 | mtpSendReqDhParams(auth) 97 | }) 98 | .catch(err => { 99 | LogService.logError(`[MtpAuthorizer] mtpSendReqPQ() ${new ErrorResponse(err)}`) 100 | deferred.reject(err) 101 | }) 102 | 103 | MtpRsaKeysManager.prepare() 104 | } 105 | 106 | function mtpSendReqDhParams(auth) { 107 | var deferred = auth.deferred 108 | 109 | auth.newNonce = new Array(32) 110 | new SecureRandom().nextBytes(auth.newNonce) 111 | 112 | var data = new TLSerialization({ mtproto: true }) 113 | data.storeObject({ 114 | _: 'p_q_inner_data', 115 | pq: auth.pq, 116 | p: auth.p, 117 | q: auth.q, 118 | nonce: auth.nonce, 119 | server_nonce: auth.serverNonce, 120 | new_nonce: auth.newNonce 121 | }, 'P_Q_inner_data', 'DECRYPTED_DATA') 122 | 123 | var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes()) 124 | 125 | var request = new TLSerialization({ mtproto: true }) 126 | request.storeMethod('req_DH_params', { 127 | nonce: auth.nonce, 128 | server_nonce: auth.serverNonce, 129 | p: auth.p, 130 | q: auth.q, 131 | public_key_fingerprint: auth.publicKey.fingerprint, 132 | encrypted_data: rsaEncrypt(auth.publicKey, dataWithHash) 133 | }) 134 | 135 | LogService.logVerbose(`[MtpAuthorizer] mtpSendReqDhParams()`) 136 | 137 | mtpSendPlainRequest(auth.dcID, request.getBuffer()).then(function (deserializer) { 138 | var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE') 139 | 140 | if (response._ != 'server_DH_params_fail' && response._ != 'server_DH_params_ok') { 141 | deferred.reject(new Error('[MT] Server_DH_Params response invalid: ' + response._)) 142 | return false 143 | } 144 | 145 | if (!bytesCmp(auth.nonce, response.nonce)) { 146 | deferred.reject(new Error('[MT] Server_DH_Params nonce mismatch')) 147 | return false 148 | } 149 | 150 | if (!bytesCmp(auth.serverNonce, response.server_nonce)) { 151 | deferred.reject(new Error('[MT] Server_DH_Params server_nonce mismatch')) 152 | return false 153 | } 154 | 155 | if (response._ == 'server_DH_params_fail') { 156 | var newNonceHash = sha1BytesSync(auth.newNonce).slice(-16) 157 | if (!bytesCmp(newNonceHash, response.new_nonce_hash)) { 158 | deferred.reject(new Error('[MT] server_DH_params_fail new_nonce_hash mismatch')) 159 | return false 160 | } 161 | deferred.reject(new Error('[MT] server_DH_params_fail')) 162 | return false 163 | } 164 | 165 | try { 166 | mtpDecryptServerDhDataAnswer(auth, response.encrypted_answer) 167 | } catch (e) { 168 | deferred.reject(e) 169 | return false 170 | } 171 | 172 | mtpSendSetClientDhParams(auth) 173 | }, function (error) { 174 | deferred.reject(error) 175 | }) 176 | } 177 | 178 | function mtpDecryptServerDhDataAnswer(auth, encryptedAnswer) { 179 | auth.localTime = tsNow() 180 | 181 | auth.tmpAesKey = sha1BytesSync(auth.newNonce.concat(auth.serverNonce)).concat(sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(0, 12)) 182 | auth.tmpAesIv = sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(12).concat(sha1BytesSync([].concat(auth.newNonce, auth.newNonce)), auth.newNonce.slice(0, 4)) 183 | 184 | var answerWithHash = aesDecryptSync(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv) 185 | 186 | var hash = answerWithHash.slice(0, 20) 187 | var answerWithPadding = answerWithHash.slice(20) 188 | var buffer = bytesToArrayBuffer(answerWithPadding) 189 | 190 | var deserializer = new TLDeserialization(buffer, { mtproto: true }) 191 | var response = deserializer.fetchObject('Server_DH_inner_data') 192 | 193 | if (response._ != 'server_DH_inner_data') { 194 | throw new Error('[MT] server_DH_inner_data response invalid: ' + constructor) 195 | } 196 | 197 | if (!bytesCmp(auth.nonce, response.nonce)) { 198 | throw new Error('[MT] server_DH_inner_data nonce mismatch') 199 | } 200 | 201 | if (!bytesCmp(auth.serverNonce, response.server_nonce)) { 202 | throw new Error('[MT] server_DH_inner_data serverNonce mismatch') 203 | } 204 | 205 | LogService.logVerbose(`[MtpAuthorizer] mtpDecryptServerDhDataAnswer() Done decrypting answer`) 206 | 207 | auth.g = response.g 208 | auth.dhPrime = response.dh_prime 209 | auth.gA = response.g_a 210 | auth.serverTime = response.server_time 211 | auth.retry = 0 212 | 213 | mtpVerifyDhParams(auth.g, auth.dhPrime, auth.gA) 214 | 215 | var offset = deserializer.getOffset() 216 | 217 | if (!bytesCmp(hash, sha1BytesSync(answerWithPadding.slice(0, offset)))) { 218 | throw new Error('[MT] server_DH_inner_data SHA1-hash mismatch') 219 | } 220 | 221 | 222 | MtpTimeManager.applyServerTime(auth.serverTime, auth.localTime) 223 | } 224 | 225 | function mtpVerifyDhParams(g, dhPrime, gA) { 226 | LogService.logVerbose(`[MtpAuthorizer] mtpVerifyDhParams() Verifying DH params`) 227 | 228 | var dhPrimeHex = bytesToHex(dhPrime) 229 | if (g != 3 || 230 | dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') { 231 | // The verified value is from https://core.telegram.org/mtproto/security_guidelines 232 | throw new Error('[MT] DH params are not verified: unknown dhPrime') 233 | } 234 | 235 | LogService.logVerbose(`[MtpAuthorizer] mtpVerifyDhParams() dhPrime cmp OK`) 236 | 237 | var gABigInt = new BigInteger(bytesToHex(gA), 16) 238 | var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16) 239 | 240 | if (gABigInt.compareTo(BigInteger.ONE) <= 0) { 241 | throw new Error('[MT] DH params are not verified: gA <= 1') 242 | } 243 | 244 | if (gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) { 245 | throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1') 246 | } 247 | 248 | LogService.logVerbose(`[MtpAuthorizer] mtpVerifyDhParams() 1 < gA < dhPrime-1 OK`) 249 | 250 | var two = new BigInteger(null) 251 | two.fromInt(2) 252 | var twoPow = two.pow(2048 - 64) 253 | 254 | if (gABigInt.compareTo(twoPow) < 0) { 255 | throw new Error('[MT] DH params are not verified: gA < 2^{2048-64}') 256 | } 257 | if (gABigInt.compareTo(dhPrimeBigInt.subtract(twoPow)) >= 0) { 258 | throw new Error('[MT] DH params are not verified: gA > dhPrime - 2^{2048-64}') 259 | } 260 | 261 | LogService.logVerbose(`[MtpAuthorizer] mtpVerifyDhParams() 2^{2048-64} < gA < dhPrime-2^{2048-64} OK`) 262 | 263 | return true 264 | } 265 | 266 | function mtpSendSetClientDhParams(auth) { 267 | var deferred = auth.deferred 268 | var gBytes = bytesFromHex(auth.g.toString(16)) 269 | 270 | auth.b = new Array(256) 271 | new SecureRandom().nextBytes(auth.b) 272 | 273 | let gB = bytesModPow(gBytes, auth.b, auth.dhPrime) 274 | var data = new TLSerialization({ mtproto: true }) 275 | data.storeObject({ 276 | _: 'client_DH_inner_data', 277 | nonce: auth.nonce, 278 | server_nonce: auth.serverNonce, 279 | retry_id: [0, auth.retry++], 280 | g_b: gB 281 | }, 'Client_DH_Inner_Data') 282 | 283 | var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes()) 284 | 285 | var encryptedData = aesEncryptSync(dataWithHash, auth.tmpAesKey, auth.tmpAesIv) 286 | 287 | var request = new TLSerialization({ mtproto: true }) 288 | request.storeMethod('set_client_DH_params', { 289 | nonce: auth.nonce, 290 | server_nonce: auth.serverNonce, 291 | encrypted_data: encryptedData 292 | }) 293 | 294 | LogService.logVerbose(`[MtpAuthorizer] mtpSendSetClientDhParams() Send set_client_DH_params`) 295 | 296 | mtpSendPlainRequest(auth.dcID, request.getBuffer()).then(function (deserializer) { 297 | var response = deserializer.fetchObject('Set_client_DH_params_answer') 298 | 299 | if (response._ != 'dh_gen_ok' && response._ != 'dh_gen_retry' && response._ != 'dh_gen_fail') { 300 | deferred.reject(new Error('[MT] Set_client_DH_params_answer response invalid: ' + response._)) 301 | return false 302 | } 303 | 304 | if (!bytesCmp(auth.nonce, response.nonce)) { 305 | deferred.reject(new Error('[MT] Set_client_DH_params_answer nonce mismatch')) 306 | return false 307 | } 308 | 309 | if (!bytesCmp(auth.serverNonce, response.server_nonce)) { 310 | deferred.reject(new Error('[MT] Set_client_DH_params_answer server_nonce mismatch')) 311 | return false 312 | } 313 | 314 | let authKey = bytesModPow(auth.gA, auth.b, auth.dhPrime) 315 | var authKeyHash = sha1BytesSync(authKey), 316 | authKeyAux = authKeyHash.slice(0, 8), 317 | authKeyID = authKeyHash.slice(-8) 318 | 319 | LogService.logVerbose(`[MtpAuthorizer] mtpSendSetClientDhParams() Got Set_client_DH_params_answer ${response._}`) 320 | 321 | switch (response._) { 322 | case 'dh_gen_ok': 323 | var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16) 324 | 325 | if (!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { 326 | deferred.reject(new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch')) 327 | return false 328 | } 329 | 330 | var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)) 331 | 332 | auth.authKeyID = authKeyID 333 | auth.authKey = authKey 334 | auth.serverSalt = serverSalt 335 | 336 | deferred.resolve(auth) 337 | break 338 | 339 | case 'dh_gen_retry': 340 | var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16) 341 | if (!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { 342 | deferred.reject(new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch')) 343 | return false 344 | } 345 | 346 | return mtpSendSetClientDhParams(auth) 347 | 348 | case 'dh_gen_fail': 349 | var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16) 350 | if (!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { 351 | deferred.reject(new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch')) 352 | return false 353 | } 354 | 355 | deferred.reject(new Error('[MT] Set_client_DH_params_answer fail')) 356 | return false 357 | } 358 | }, function (error) { 359 | deferred.reject(error) 360 | }) 361 | } 362 | 363 | const cached = {} 364 | 365 | const mtpAuth = (dcID) => new Promise((resolve, reject) => { 366 | if (cached[dcID] !== undefined) { 367 | return cached[dcID] 368 | } 369 | 370 | var nonce = [] 371 | for (var i = 0; i < 16; i++) { 372 | nonce.push(nextRandomInt(0xFF)) 373 | } 374 | 375 | if (!MtpDcConfigurator.chooseServer(dcID)) { 376 | throw new Error('[MT] No server found for dc ' + dcID) 377 | } 378 | 379 | var auth = { 380 | dcID: dcID, 381 | nonce: nonce, 382 | deferred: { 383 | resolve: (obj) => resolve(obj), reject: (err) => reject(err) 384 | } 385 | } 386 | 387 | mtpSendReqPQ(auth) 388 | }) 389 | 390 | export const auth = mtpAuth -------------------------------------------------------------------------------- /src/Utils/utils.js: -------------------------------------------------------------------------------- 1 | import Rusha from 'rusha' 2 | import { BigInteger, SecureRandom } from './bigNumbers' 3 | import { str2bigInt, bigInt2str, getBpe, getOne, copyInt_, copy_, add_, rightShift_, sub_, eGCD_, divide_, equalsInt, greater, isZero, powMod } from './leemonBigInt' 4 | import CryptoJS from 'crypto-js' 5 | import * as IGE from './aesIGEMode' 6 | import { LogService, ErrorResponse } from '../Services' 7 | import zlib from 'zlib' 8 | 9 | const _logTimer = (new Date()).getTime() 10 | 11 | 12 | export function dT() { 13 | return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']' 14 | } 15 | 16 | export function tsNow(seconds) { 17 | var t = +new Date() 18 | return seconds ? Math.floor(t / 1000) : t 19 | } 20 | 21 | export function bigint(num) { 22 | return new BigInteger(num.toString(16), 16) 23 | } 24 | 25 | export function bigStringInt(strNum) { 26 | return new BigInteger(strNum, 10) 27 | } 28 | 29 | export function dHexDump(bytes) { 30 | var arr = [] 31 | for (var i = 0; i < bytes.length; i++) { 32 | if (i && !(i % 2)) { 33 | if (!(i % 16)) { 34 | arr.push('\n') 35 | } else if (!(i % 4)) { 36 | arr.push(' ') 37 | } else { 38 | arr.push(' ') 39 | } 40 | } 41 | arr.push((bytes[i] < 16 ? '0' : '') + bytes[i].toString(16)) 42 | } 43 | } 44 | 45 | export function bytesToHex(bytes) { 46 | bytes = bytes || [] 47 | var arr = [] 48 | for (var i = 0; i < bytes.length; i++) { 49 | arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16)) 50 | } 51 | return arr.join('') 52 | } 53 | 54 | export function bytesFromHex(hexString) { 55 | var len = hexString.length, 56 | i 57 | var start = 0 58 | var bytes = [] 59 | 60 | if (hexString.length % 2) { 61 | bytes.push(parseInt(hexString.charAt(0), 16)) 62 | start++ 63 | } 64 | 65 | for (i = start; i < len; i += 2) { 66 | bytes.push(parseInt(hexString.substr(i, 2), 16)) 67 | } 68 | 69 | return bytes 70 | } 71 | 72 | export function bytesToBase64(bytes) { 73 | var mod3 74 | var result = '' 75 | 76 | for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { 77 | mod3 = nIdx % 3 78 | nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24) 79 | if (mod3 === 2 || nLen - nIdx === 1) { 80 | result += String.fromCharCode( 81 | uint6ToBase64(nUint24 >>> 18 & 63), 82 | uint6ToBase64(nUint24 >>> 12 & 63), 83 | uint6ToBase64(nUint24 >>> 6 & 63), 84 | uint6ToBase64(nUint24 & 63) 85 | ) 86 | nUint24 = 0 87 | } 88 | } 89 | 90 | return result.replace(/A(?=A$|$)/g, '=') 91 | } 92 | 93 | export function uint6ToBase64(nUint6) { 94 | return nUint6 < 26 95 | ? nUint6 + 65 96 | : nUint6 < 52 97 | ? nUint6 + 71 98 | : nUint6 < 62 99 | ? nUint6 - 4 100 | : nUint6 === 62 101 | ? 43 102 | : nUint6 === 63 103 | ? 47 104 | : 65 105 | } 106 | 107 | export function base64ToBlob(base64str, mimeType) { 108 | var sliceSize = 1024 109 | var byteCharacters = atob(base64str) 110 | var bytesLength = byteCharacters.length 111 | var slicesCount = Math.ceil(bytesLength / sliceSize) 112 | var byteArrays = new Array(slicesCount) 113 | 114 | for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { 115 | var begin = sliceIndex * sliceSize 116 | var end = Math.min(begin + sliceSize, bytesLength) 117 | 118 | var bytes = new Array(end - begin) 119 | for (var offset = begin, i = 0; offset < end; ++i, ++offset) { 120 | bytes[i] = byteCharacters[offset].charCodeAt(0) 121 | } 122 | byteArrays[sliceIndex] = new Uint8Array(bytes) 123 | } 124 | 125 | return blobConstruct(byteArrays, mimeType) 126 | } 127 | 128 | export function dataUrlToBlob(url) { 129 | var urlParts = url.split(',') 130 | var base64str = urlParts[1] 131 | var mimeType = urlParts[0].split(':')[1].split(';')[0] 132 | var blob = base64ToBlob(base64str, mimeType) 133 | return blob 134 | } 135 | 136 | export function blobConstruct(blobParts, mimeType) { 137 | var blob 138 | var safeMimeType = blobSafeMimeType(mimeType) 139 | try { 140 | blob = new Blob(blobParts, { type: safeMimeType }) 141 | } catch (e) { 142 | var bb = new BlobBuilder 143 | angular.forEach(blobParts, function (blobPart) { 144 | bb.append(blobPart) 145 | }) 146 | blob = bb.getBlob(safeMimeType) 147 | } 148 | return blob 149 | } 150 | 151 | export function blobSafeMimeType(mimeType) { 152 | if ([ 153 | 'image/jpeg', 154 | 'image/png', 155 | 'image/gif', 156 | 'image/webp', 157 | 'image/bmp', 158 | 'video/mp4', 159 | 'video/webm', 160 | 'video/quicktime', 161 | 'audio/ogg', 162 | 'audio/mpeg', 163 | 'audio/mp4', 164 | ].indexOf(mimeType) == -1) { 165 | return 'application/octet-stream' 166 | } 167 | return mimeType 168 | } 169 | 170 | export function bytesCmp(bytes1, bytes2) { 171 | var len = bytes1.length 172 | if (len != bytes2.length) { 173 | return false 174 | } 175 | 176 | for (var i = 0; i < len; i++) { 177 | if (bytes1[i] != bytes2[i]) { 178 | return false 179 | } 180 | } 181 | return true 182 | } 183 | 184 | export function bytesXor(bytes1, bytes2) { 185 | var len = bytes1.length 186 | var bytes = [] 187 | 188 | for (var i = 0; i < len; ++i) { 189 | bytes[i] = bytes1[i] ^ bytes2[i] 190 | } 191 | 192 | return bytes 193 | } 194 | 195 | export function bytesToWords(bytes) { 196 | if (bytes instanceof ArrayBuffer) { 197 | bytes = new Uint8Array(bytes) 198 | } 199 | var len = bytes.length 200 | var words = [] 201 | var i 202 | for (i = 0; i < len; i++) { 203 | words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8) 204 | } 205 | 206 | return new CryptoJS.lib.WordArray.init(words, len) 207 | } 208 | 209 | export function bytesFromWords(wordArray) { 210 | var words = wordArray.words 211 | var sigBytes = wordArray.sigBytes 212 | var bytes = [] 213 | 214 | for (var i = 0; i < sigBytes; i++) { 215 | bytes.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff) 216 | } 217 | 218 | return bytes 219 | } 220 | 221 | export function bytesFromBigInt(bigInt, len) { 222 | var bytes = bigInt.toByteArray() 223 | 224 | if (len && bytes.length < len) { 225 | var padding = [] 226 | for (var i = 0, needPadding = len - bytes.length; i < needPadding; i++) { 227 | padding[i] = 0 228 | } 229 | if (bytes instanceof ArrayBuffer) { 230 | bytes = bufferConcat(padding, bytes) 231 | } else { 232 | bytes = padding.concat(bytes) 233 | } 234 | } else { 235 | while (!bytes[0] && (!len || bytes.length > len)) { 236 | bytes = bytes.slice(1) 237 | } 238 | } 239 | 240 | return bytes 241 | } 242 | 243 | export function bytesFromLeemonBigInt(bigInt, len) { 244 | var str = bigInt2str(bigInt, 16) 245 | return bytesFromHex(str) 246 | } 247 | 248 | export function bytesToArrayBuffer(b) { 249 | return (new Uint8Array(b)).buffer 250 | } 251 | 252 | export function convertToArrayBuffer(bytes) { 253 | // Be careful with converting subarrays!! 254 | if (bytes instanceof ArrayBuffer) { 255 | return bytes 256 | } 257 | if (bytes.buffer !== undefined && 258 | bytes.buffer.byteLength == bytes.length * bytes.BYTES_PER_ELEMENT) { 259 | return bytes.buffer 260 | } 261 | return bytesToArrayBuffer(bytes) 262 | } 263 | 264 | export function convertToUint8Array(bytes) { 265 | if (bytes.buffer !== undefined) { 266 | return bytes 267 | } 268 | return new Uint8Array(bytes) 269 | } 270 | 271 | export function convertToByteArray(bytes) { 272 | if (Array.isArray(bytes)) { 273 | return bytes 274 | } 275 | bytes = convertToUint8Array(bytes) 276 | var newBytes = [] 277 | for (var i = 0, len = bytes.length; i < len; i++) { 278 | newBytes.push(bytes[i]) 279 | } 280 | return newBytes 281 | } 282 | 283 | export function bytesFromArrayBuffer(buffer) { 284 | var len = buffer.byteLength 285 | var byteView = new Uint8Array(buffer) 286 | var bytes = [] 287 | 288 | for (var i = 0; i < len; ++i) { 289 | bytes[i] = byteView[i] 290 | } 291 | 292 | return bytes 293 | } 294 | 295 | export function bufferConcat(buffer1, buffer2) { 296 | var l1 = buffer1.byteLength || buffer1.length 297 | var l2 = buffer2.byteLength || buffer2.length 298 | var tmp = new Uint8Array(l1 + l2) 299 | tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0) 300 | tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1) 301 | 302 | return tmp.buffer 303 | } 304 | 305 | export function longToInts(sLong) { 306 | var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000)) 307 | 308 | return [divRem[0].intValue(), divRem[1].intValue()] 309 | } 310 | 311 | export function longToBytes(sLong) { 312 | return bytesFromWords({ words: longToInts(sLong), sigBytes: 8 }).reverse() 313 | } 314 | 315 | export function longFromInts(high, low) { 316 | return bigint(high).shiftLeft(32).add(bigint(low)).toString(10) 317 | } 318 | 319 | export function intToUint(val) { 320 | val = parseInt(val) 321 | if (val < 0) { 322 | val = val + 4294967296 323 | } 324 | return val 325 | } 326 | 327 | export function uintToInt(val) { 328 | if (val > 2147483647) { 329 | val = val - 4294967296 330 | } 331 | return val 332 | } 333 | 334 | export function sha1HashSync(bytes) { 335 | const rushaInstance = new Rusha(1024 * 1024) 336 | var hashBytes = rushaInstance.rawDigest(bytes).buffer 337 | return hashBytes 338 | } 339 | 340 | export function sha1BytesSync(bytes) { 341 | return bytesFromArrayBuffer(sha1HashSync(bytes)) 342 | } 343 | 344 | export function sha256HashSync(bytes) { 345 | var hashWords = CryptoJS.SHA256(bytesToWords(bytes)) 346 | var hashBytes = bytesFromWords(hashWords) 347 | return hashBytes 348 | } 349 | 350 | export function rsaEncrypt(publicKey, bytes) { 351 | bytes = addPadding(bytes, 255) 352 | var N = new BigInteger(publicKey.modulus, 16) 353 | var E = new BigInteger(publicKey.exponent, 16) 354 | var X = new BigInteger(bytes) 355 | var encryptedBigInt = X.modPowInt(E, N), 356 | encryptedBytes = bytesFromBigInt(encryptedBigInt, 256) 357 | return encryptedBytes 358 | } 359 | 360 | export function addPadding(bytes, blockSize, zeroes) { 361 | blockSize = blockSize || 16 362 | var len = bytes.byteLength || bytes.length 363 | var needPadding = blockSize - (len % blockSize) 364 | if (needPadding > 0 && needPadding < blockSize) { 365 | var padding = new Array(needPadding) 366 | if (zeroes) { 367 | for (var i = 0; i < needPadding; i++) { 368 | padding[i] = 0 369 | } 370 | } else { 371 | (new SecureRandom()).nextBytes(padding) 372 | } 373 | 374 | if (bytes instanceof ArrayBuffer) { 375 | bytes = bufferConcat(bytes, padding) 376 | } else { 377 | bytes = bytes.concat(padding) 378 | } 379 | } 380 | 381 | return bytes 382 | } 383 | 384 | export function aesEncryptSync(bytes, keyBytes, ivBytes) { 385 | var len = bytes.byteLength || bytes.length 386 | 387 | bytes = addPadding(bytes) 388 | 389 | var encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), { 390 | iv: bytesToWords(ivBytes), 391 | padding: CryptoJS.pad.NoPadding, 392 | mode: CryptoJS.mode.IGE 393 | }).ciphertext 394 | 395 | var encryptedBytes = bytesFromWords(encryptedWords) 396 | 397 | return encryptedBytes 398 | } 399 | 400 | export function aesDecryptSync(encryptedBytes, keyBytes, ivBytes) { 401 | 402 | var decryptedWords = CryptoJS.AES.decrypt({ ciphertext: bytesToWords(encryptedBytes) }, bytesToWords(keyBytes), { 403 | iv: bytesToWords(ivBytes), 404 | padding: CryptoJS.pad.NoPadding, 405 | mode: CryptoJS.mode.IGE 406 | }) 407 | 408 | var bytes = bytesFromWords(decryptedWords) 409 | 410 | return bytes 411 | } 412 | 413 | export function toArrayBuffer(buf) { 414 | var ab = new ArrayBuffer(buf.length) 415 | var view = new Uint8Array(ab) 416 | for (var i = 0; i < buf.length; ++i) { 417 | view[i] = buf[i] 418 | } 419 | return ab 420 | } 421 | 422 | export function gzipUncompress(bytes) { 423 | var result = zlib.gunzipSync(bytes) 424 | return toArrayBuffer(result) 425 | } 426 | 427 | export function nextRandomInt(maxValue) { 428 | return Math.floor(Math.random() * maxValue) 429 | } 430 | 431 | export function pqPrimeFactorization(pqBytes) { 432 | var what = new BigInteger(pqBytes) 433 | var result = false 434 | 435 | try { 436 | result = pqPrimeLeemon(str2bigInt(what.toString(16), 16, Math.ceil(64 / getBpe()) + 1)) 437 | } catch (e) { 438 | LogService.logError(`[utils] pqPrimeFactorization() pqPrimeLeemon() ${new ErrorResponse(e)}`) 439 | } 440 | 441 | if (result === false && what.bitLength() <= 64) { 442 | try { 443 | result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16)) 444 | } catch (e) { 445 | LogService.logError(`[utils] pqPrimeFactorization() pqPrimeLong() ${new ErrorResponse(e)}`) 446 | } 447 | } 448 | 449 | if (result === false) { 450 | result = pqPrimeBigInteger(what) 451 | } 452 | 453 | return result 454 | } 455 | 456 | export function pqPrimeBigInteger(what) { 457 | var it = 0, 458 | g 459 | for (var i = 0; i < 3; i++) { 460 | var q = (nextRandomInt(128) & 15) + 17 461 | var x = bigint(nextRandomInt(1000000000) + 1) 462 | var y = x.clone() 463 | var lim = 1 << (i + 18) 464 | 465 | for (var j = 1; j < lim; j++) { 466 | ++it 467 | var a = x.clone() 468 | var b = x.clone() 469 | var c = bigint(q) 470 | 471 | while (!b.equals(BigInteger.ZERO)) { 472 | if (!b.and(BigInteger.ONE).equals(BigInteger.ZERO)) { 473 | c = c.add(a) 474 | if (c.compareTo(what) > 0) { 475 | c = c.subtract(what) 476 | } 477 | } 478 | a = a.add(a) 479 | if (a.compareTo(what) > 0) { 480 | a = a.subtract(what) 481 | } 482 | b = b.shiftRight(1) 483 | } 484 | 485 | x = c.clone() 486 | var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y) 487 | g = z.gcd(what) 488 | if (!g.equals(BigInteger.ONE)) { 489 | break 490 | } 491 | if ((j & (j - 1)) == 0) { 492 | y = x.clone() 493 | } 494 | } 495 | if (g.compareTo(BigInteger.ONE) > 0) { 496 | break 497 | } 498 | } 499 | 500 | var f = what.divide(g), P, Q 501 | 502 | if (g.compareTo(f) > 0) { 503 | P = f 504 | Q = g 505 | } else { 506 | P = g 507 | Q = f 508 | } 509 | 510 | return [bytesFromBigInt(P), bytesFromBigInt(Q), it] 511 | } 512 | 513 | export function gcdLong(a, b) { 514 | while (a.notEquals(goog.math.Long.ZERO) && b.notEquals(goog.math.Long.ZERO)) { 515 | while (b.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) { 516 | b = b.shiftRight(1) 517 | } 518 | while (a.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) { 519 | a = a.shiftRight(1) 520 | } 521 | if (a.compare(b) > 0) { 522 | a = a.subtract(b) 523 | } else { 524 | b = b.subtract(a) 525 | } 526 | } 527 | return b.equals(goog.math.Long.ZERO) ? a : b 528 | } 529 | 530 | export function pqPrimeLong(what) { 531 | var it = 0, 532 | g 533 | for (var i = 0; i < 3; i++) { 534 | var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17) 535 | var x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1) 536 | var y = x 537 | var lim = 1 << (i + 18) 538 | 539 | for (var j = 1; j < lim; j++) { 540 | ++it 541 | var a = x 542 | var b = x 543 | var c = q 544 | 545 | while (b.notEquals(goog.math.Long.ZERO)) { 546 | if (b.and(goog.math.Long.ONE).notEquals(goog.math.Long.ZERO)) { 547 | c = c.add(a) 548 | if (c.compare(what) > 0) { 549 | c = c.subtract(what) 550 | } 551 | } 552 | a = a.add(a) 553 | if (a.compare(what) > 0) { 554 | a = a.subtract(what) 555 | } 556 | b = b.shiftRight(1) 557 | } 558 | 559 | x = c 560 | var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y) 561 | g = gcdLong(z, what) 562 | if (g.notEquals(goog.math.Long.ONE)) { 563 | break 564 | } 565 | if ((j & (j - 1)) == 0) { 566 | y = x 567 | } 568 | } 569 | if (g.compare(goog.math.Long.ONE) > 0) { 570 | break 571 | } 572 | } 573 | 574 | var f = what.div(g), P, Q 575 | 576 | if (g.compare(f) > 0) { 577 | P = f 578 | Q = g 579 | } else { 580 | P = g 581 | Q = f 582 | } 583 | 584 | return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it] 585 | } 586 | 587 | export function pqPrimeLeemon(what) { 588 | var minBits = 64 589 | var minLen = Math.ceil(minBits / getBpe()) + 1 590 | var it = 0 591 | var i, q 592 | var j, lim 593 | var g, P 594 | var Q 595 | var a = new Array(minLen) 596 | var b = new Array(minLen) 597 | var c = new Array(minLen) 598 | var g = new Array(minLen) 599 | var z = new Array(minLen) 600 | var x = new Array(minLen) 601 | var y = new Array(minLen) 602 | 603 | for (i = 0; i < 3; i++) { 604 | q = (nextRandomInt(128) & 15) + 17 605 | copyInt_(x, nextRandomInt(1000000000) + 1) 606 | copy_(y, x) 607 | lim = 1 << (i + 18) 608 | 609 | for (j = 1; j < lim; j++) { 610 | ++it 611 | copy_(a, x) 612 | copy_(b, x) 613 | copyInt_(c, q) 614 | 615 | while (!isZero(b)) { 616 | if (b[0] & 1) { 617 | add_(c, a) 618 | if (greater(c, what)) { 619 | sub_(c, what) 620 | } 621 | } 622 | add_(a, a) 623 | if (greater(a, what)) { 624 | sub_(a, what) 625 | } 626 | rightShift_(b, 1) 627 | } 628 | 629 | copy_(x, c) 630 | if (greater(x, y)) { 631 | copy_(z, x) 632 | sub_(z, y) 633 | } else { 634 | copy_(z, y) 635 | sub_(z, x) 636 | } 637 | eGCD_(z, what, g, a, b) 638 | if (!equalsInt(g, 1)) { 639 | break 640 | } 641 | if ((j & (j - 1)) == 0) { 642 | copy_(y, x) 643 | } 644 | } 645 | if (greater(g, getOne())) { 646 | break 647 | } 648 | } 649 | 650 | divide_(what, g, x, y) 651 | 652 | if (greater(g, x)) { 653 | P = x 654 | Q = g 655 | } else { 656 | P = g 657 | Q = x 658 | } 659 | 660 | return [bytesFromLeemonBigInt(P), bytesFromLeemonBigInt(Q), it] 661 | } 662 | 663 | export function bytesModPow(x, y, m) { 664 | try { 665 | var xBigInt = str2bigInt(bytesToHex(x), 16) 666 | var yBigInt = str2bigInt(bytesToHex(y), 16) 667 | var mBigInt = str2bigInt(bytesToHex(m), 16) 668 | var resBigInt = powMod(xBigInt, yBigInt, mBigInt) 669 | 670 | return bytesFromHex(bigInt2str(resBigInt, 16)) 671 | } catch (e) { 672 | LogService.logError(`[utils] bytesModPow() ${new ErrorResponse(e)}`) 673 | } 674 | 675 | return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256) 676 | } 677 | -------------------------------------------------------------------------------- /src/TL/TLSerialization.js: -------------------------------------------------------------------------------- 1 | import { intToUint, bytesToHex, bigStringInt, bigint, uintToInt, gzipUncompress, bytesToArrayBuffer } from '../Utils' 2 | import Config from '../config' 3 | import { bytesFromArrayBuffer } from '../Utils/utils' 4 | import { LogService, ErrorResponse } from '../Services' 5 | 6 | export function TLSerialization(options) { 7 | options = options || {} 8 | this.maxLength = options.startMaxLength || 2048 // 2Kb 9 | this.offset = 0 // in bytes 10 | 11 | this.createBuffer() 12 | 13 | this.mtproto = options.mtproto || false 14 | return this 15 | } 16 | 17 | TLSerialization.prototype.createBuffer = function () { 18 | this.buffer = new ArrayBuffer(this.maxLength) 19 | this.intView = new Int32Array(this.buffer) 20 | this.byteView = new Uint8Array(this.buffer) 21 | } 22 | 23 | TLSerialization.prototype.getArray = function () { 24 | var resultBuffer = new ArrayBuffer(this.offset) 25 | var resultArray = new Int32Array(resultBuffer) 26 | 27 | resultArray.set(this.intView.subarray(0, this.offset / 4)) 28 | 29 | return resultArray 30 | } 31 | 32 | TLSerialization.prototype.getBuffer = function () { 33 | return this.getArray().buffer 34 | } 35 | 36 | TLSerialization.prototype.getBytes = function (typed) { 37 | if (typed) { 38 | var resultBuffer = new ArrayBuffer(this.offset) 39 | var resultArray = new Uint8Array(resultBuffer) 40 | 41 | resultArray.set(this.byteView.subarray(0, this.offset)) 42 | 43 | return resultArray 44 | } 45 | 46 | var bytes = [] 47 | for (var i = 0; i < this.offset; i++) { 48 | bytes.push(this.byteView[i]) 49 | } 50 | return bytes 51 | } 52 | 53 | TLSerialization.prototype.checkLength = function (needBytes) { 54 | if (this.offset + needBytes < this.maxLength) { 55 | return 56 | } 57 | 58 | LogService.logDebug(`[TLSerialization] checkLength() ${this.offset} ${needBytes} ${this.maxLength}`) 59 | 60 | this.maxLength = Math.ceil(Math.max(this.maxLength * 2, this.offset + needBytes + 16) / 4) * 4 61 | var previousBuffer = this.buffer 62 | var previousArray = new Int32Array(previousBuffer) 63 | 64 | this.createBuffer() 65 | 66 | new Int32Array(this.buffer).set(previousArray) 67 | } 68 | 69 | TLSerialization.prototype.writeInt = function (i, field) { 70 | LogService.logDebug(`[TLSerialization] writeInt() ${i} ${i} ${field}`) 71 | 72 | this.checkLength(4) 73 | this.intView[this.offset / 4] = i 74 | this.offset += 4 75 | } 76 | 77 | TLSerialization.prototype.storeInt = function (i, field) { 78 | this.writeInt(i, (field || '') + ':int') 79 | } 80 | 81 | TLSerialization.prototype.storeBool = function (i, field) { 82 | if (i) { 83 | this.writeInt(0x997275b5, (field || '') + ':bool') 84 | } else { 85 | this.writeInt(0xbc799737, (field || '') + ':bool') 86 | } 87 | } 88 | 89 | TLSerialization.prototype.storeLongP = function (iHigh, iLow, field) { 90 | this.writeInt(iLow, (field || '') + ':long[low]') 91 | this.writeInt(iHigh, (field || '') + ':long[high]') 92 | } 93 | 94 | TLSerialization.prototype.storeLong = function (sLong, field) { 95 | if (Array.isArray(sLong)) { 96 | if (sLong.length == 2) { 97 | return this.storeLongP(sLong[0], sLong[1], field) 98 | } else { 99 | return this.storeIntBytes(sLong, 64, field) 100 | } 101 | } 102 | 103 | if (typeof sLong != 'string') { 104 | sLong = sLong ? sLong.toString() : '0' 105 | } 106 | var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000)) 107 | 108 | this.writeInt(intToUint(divRem[1].intValue()), (field || '') + ':long[low]') 109 | this.writeInt(intToUint(divRem[0].intValue()), (field || '') + ':long[high]') 110 | } 111 | 112 | TLSerialization.prototype.storeDouble = function (f, field) { 113 | var buffer = new ArrayBuffer(8) 114 | var intView = new Int32Array(buffer) 115 | var doubleView = new Float64Array(buffer) 116 | 117 | doubleView[0] = f 118 | 119 | this.writeInt(intView[0], (field || '') + ':double[low]') 120 | this.writeInt(intView[1], (field || '') + ':double[high]') 121 | } 122 | 123 | TLSerialization.prototype.storeString = function (s, field) { 124 | LogService.logDebug(`[TLSerialization] storeString() ${s} ${(field || '') + ':string'}`) 125 | 126 | if (s === undefined) { 127 | s = '' 128 | } 129 | var sUTF8 = unescape(encodeURIComponent(s)) 130 | 131 | this.checkLength(sUTF8.length + 8) 132 | 133 | var len = sUTF8.length 134 | if (len <= 253) { 135 | this.byteView[this.offset++] = len 136 | } else { 137 | this.byteView[this.offset++] = 254 138 | this.byteView[this.offset++] = len & 0xFF 139 | this.byteView[this.offset++] = (len & 0xFF00) >> 8 140 | this.byteView[this.offset++] = (len & 0xFF0000) >> 16 141 | } 142 | for (var i = 0; i < len; i++) { 143 | this.byteView[this.offset++] = sUTF8.charCodeAt(i) 144 | } 145 | 146 | // Padding 147 | while (this.offset % 4) { 148 | this.byteView[this.offset++] = 0 149 | } 150 | } 151 | 152 | TLSerialization.prototype.storeBytes = function (bytes, field) { 153 | if (bytes instanceof ArrayBuffer) { 154 | bytes = new Uint8Array(bytes) 155 | } 156 | else if (bytes === undefined) { 157 | bytes = [] 158 | } 159 | 160 | LogService.logDebug(`[TLSerialization] storeBytes() ${bytesToHex(bytes)} ${(field || '') + ':bytes'}`) 161 | 162 | var len = bytes.byteLength || bytes.length 163 | this.checkLength(len + 8) 164 | if (len <= 253) { 165 | this.byteView[this.offset++] = len 166 | } else { 167 | this.byteView[this.offset++] = 254 168 | this.byteView[this.offset++] = len & 0xFF 169 | this.byteView[this.offset++] = (len & 0xFF00) >> 8 170 | this.byteView[this.offset++] = (len & 0xFF0000) >> 16 171 | } 172 | 173 | this.byteView.set(bytes, this.offset) 174 | this.offset += len 175 | 176 | // Padding 177 | while (this.offset % 4) { 178 | this.byteView[this.offset++] = 0 179 | } 180 | } 181 | 182 | TLSerialization.prototype.storeIntBytes = function (bytes, bits, field) { 183 | if (bytes instanceof ArrayBuffer) { 184 | bytes = new Uint8Array(bytes) 185 | } 186 | var len = bytes.length 187 | if ((bits % 32) || (len * 8) != bits) { 188 | throw new Error('Invalid bits: ' + bits + ', ' + bytes.length) 189 | } 190 | 191 | LogService.logDebug(`[TLSerialization] storeIntBytes() ${bytesToHex(bytes)} ${(field || '') + ':int' + bits}`) 192 | 193 | this.checkLength(len) 194 | 195 | this.byteView.set(bytes, this.offset) 196 | this.offset += len 197 | } 198 | 199 | TLSerialization.prototype.storeRawBytes = function (bytes, field) { 200 | if (bytes instanceof ArrayBuffer) { 201 | bytes = new Uint8Array(bytes) 202 | } 203 | var len = bytes.length 204 | 205 | LogService.logDebug(`[TLSerialization] storeRawBytes() ${bytesToHex(bytes)} ${(field || '')}`) 206 | 207 | this.checkLength(len) 208 | 209 | this.byteView.set(bytes, this.offset) 210 | this.offset += len 211 | } 212 | 213 | TLSerialization.prototype.storeMethod = function (methodName, params) { 214 | var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API 215 | var methodData = false, 216 | i 217 | 218 | for (i = 0; i < schema.methods.length; i++) { 219 | if (schema.methods[i].method == methodName) { 220 | methodData = schema.methods[i] 221 | break 222 | } 223 | } 224 | if (!methodData) { 225 | throw new Error('No method ' + methodName + ' found') 226 | } 227 | 228 | this.storeInt(intToUint(methodData.id), methodName + '[id]') 229 | 230 | var param, type 231 | var i, condType 232 | var fieldBit 233 | var len = methodData.params.length 234 | for (i = 0; i < len; i++) { 235 | param = methodData.params[i] 236 | type = param.type 237 | if (type.indexOf('?') !== -1) { 238 | condType = type.split('?') 239 | fieldBit = condType[0].split('.') 240 | if (!(params[fieldBit[0]] & (1 << fieldBit[1]))) { 241 | continue 242 | } 243 | type = condType[1] 244 | } 245 | 246 | this.storeObject(params[param.name], type, methodName + '[' + param.name + ']') 247 | } 248 | 249 | return methodData.type 250 | } 251 | 252 | TLSerialization.prototype.storeObject = function (obj, type, field) { 253 | switch (type) { 254 | case '#': 255 | case 'int': 256 | return this.storeInt(obj, field) 257 | case 'long': 258 | return this.storeLong(obj, field) 259 | case 'int128': 260 | return this.storeIntBytes(obj, 128, field) 261 | case 'int256': 262 | return this.storeIntBytes(obj, 256, field) 263 | case 'int512': 264 | return this.storeIntBytes(obj, 512, field) 265 | case 'string': 266 | return this.storeString(obj, field) 267 | case 'bytes': 268 | return this.storeBytes(obj, field) 269 | case 'double': 270 | return this.storeDouble(obj, field) 271 | case 'Bool': 272 | return this.storeBool(obj, field) 273 | case 'true': 274 | return 275 | } 276 | 277 | if (Array.isArray(obj)) { 278 | if (type.substr(0, 6) == 'Vector') { 279 | this.writeInt(0x1cb5c415, field + '[id]') 280 | } 281 | else if (type.substr(0, 6) != 'vector') { 282 | throw new Error('Invalid vector type ' + type) 283 | } 284 | var itemType = type.substr(7, type.length - 8); // for "Vector" 285 | this.writeInt(obj.length, field + '[count]') 286 | for (var i = 0; i < obj.length; i++) { 287 | this.storeObject(obj[i], itemType, field + '[' + i + ']') 288 | } 289 | return true 290 | } 291 | else if (type.substr(0, 6).toLowerCase() == 'vector') { 292 | throw new Error(`Invalid vector object "${type}"`) 293 | } 294 | 295 | if (typeof (obj) !== 'object') { 296 | throw new Error('Invalid object for type ' + type) 297 | } 298 | 299 | var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API 300 | var predicate = obj['_'] 301 | var isBare = false 302 | var constructorData = false, 303 | i 304 | 305 | if (isBare = (type.charAt(0) == '%')) { 306 | type = type.substr(1) 307 | } 308 | 309 | for (i = 0; i < schema.constructors.length; i++) { 310 | if (schema.constructors[i].predicate == predicate) { 311 | constructorData = schema.constructors[i] 312 | break 313 | } 314 | } 315 | if (!constructorData) { 316 | throw new Error('No predicate ' + predicate + ' found') 317 | } 318 | 319 | if (predicate == type) { 320 | isBare = true 321 | } 322 | 323 | if (!isBare) { 324 | this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]') 325 | } 326 | 327 | var param, type 328 | var i, condType 329 | var fieldBit 330 | var len = constructorData.params.length 331 | for (i = 0; i < len; i++) { 332 | param = constructorData.params[i] 333 | type = param.type 334 | if (type.indexOf('?') !== -1) { 335 | condType = type.split('?') 336 | fieldBit = condType[0].split('.') 337 | if (!(obj[fieldBit[0]] & (1 << fieldBit[1]))) { 338 | continue 339 | } 340 | type = condType[1] 341 | } 342 | 343 | this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']') 344 | } 345 | 346 | return constructorData.type 347 | } 348 | 349 | export function TLDeserialization(buffer, options) { 350 | options = options || {} 351 | 352 | this.offset = 0 // in bytes 353 | this.override = options.override || {} 354 | 355 | this.buffer = buffer 356 | this.intView = new Uint32Array(this.buffer) 357 | this.byteView = new Uint8Array(this.buffer) 358 | 359 | this.mtproto = options.mtproto || false 360 | return this 361 | } 362 | 363 | TLDeserialization.prototype.readInt = function (field) { 364 | if (this.offset >= this.intView.length * 4) { 365 | throw new Error('Nothing to fetch: ' + field) 366 | } 367 | var i = this.intView[this.offset / 4] 368 | 369 | LogService.logDebug(`[TLSerialization] readInt() ${i.toString(16)} ${i} ${field}`) 370 | 371 | this.offset += 4 372 | 373 | return i 374 | } 375 | 376 | TLDeserialization.prototype.fetchInt = function (field) { 377 | return this.readInt((field || '') + ':int') 378 | } 379 | 380 | TLDeserialization.prototype.fetchDouble = function (field) { 381 | var buffer = new ArrayBuffer(8) 382 | var intView = new Int32Array(buffer) 383 | var doubleView = new Float64Array(buffer) 384 | 385 | intView[0] = this.readInt((field || '') + ':double[low]'), 386 | intView[1] = this.readInt((field || '') + ':double[high]') 387 | 388 | return doubleView[0] 389 | } 390 | 391 | TLDeserialization.prototype.fetchLong = function (field) { 392 | var iLow = this.readInt((field || '') + ':long[low]') 393 | var iHigh = this.readInt((field || '') + ':long[high]') 394 | var longDec = bigint(iHigh).shiftLeft(32).add(bigint(iLow)).toString() 395 | 396 | return longDec 397 | } 398 | 399 | TLDeserialization.prototype.fetchBool = function (field) { 400 | var i = this.readInt((field || '') + ':bool') 401 | if (i == 0x997275b5) { 402 | return true 403 | } else if (i == 0xbc799737) { 404 | return false 405 | } 406 | 407 | this.offset -= 4 408 | return this.fetchObject('Object', field) 409 | } 410 | 411 | TLDeserialization.prototype.fetchString = function (field) { 412 | var len = this.byteView[this.offset++] 413 | 414 | if (len == 254) { 415 | var len = this.byteView[this.offset++] | 416 | (this.byteView[this.offset++] << 8) | 417 | (this.byteView[this.offset++] << 16) 418 | } 419 | 420 | var sUTF8 = '' 421 | for (var i = 0; i < len; i++) { 422 | sUTF8 += String.fromCharCode(this.byteView[this.offset++]) 423 | } 424 | 425 | // Padding 426 | while (this.offset % 4) { 427 | this.offset++ 428 | } 429 | 430 | try { 431 | var s = decodeURIComponent(escape(sUTF8)) 432 | } catch (e) { 433 | var s = sUTF8 434 | } 435 | 436 | LogService.logDebug(`[TLSerialization] fetchString() ${s} ${(field || '') + ':string'}`) 437 | 438 | return s 439 | } 440 | 441 | TLDeserialization.prototype.fetchBytes = function (field) { 442 | var len = this.byteView[this.offset++] 443 | 444 | if (len == 254) { 445 | len = this.byteView[this.offset++] | 446 | (this.byteView[this.offset++] << 8) | 447 | (this.byteView[this.offset++] << 16) 448 | } 449 | 450 | var bytes = this.byteView.subarray(this.offset, this.offset + len) 451 | this.offset += len 452 | 453 | // Padding 454 | while (this.offset % 4) { 455 | this.offset++ 456 | } 457 | 458 | LogService.logDebug(`[TLSerialization] fetchBytes() ${bytesToHex(bytes)} ${(field || '') + ':bytes'}`) 459 | 460 | return bytes 461 | } 462 | 463 | TLDeserialization.prototype.fetchIntBytes = function (bits, typed, field) { 464 | if (bits % 32) { 465 | throw new Error('Invalid bits: ' + bits) 466 | } 467 | 468 | var len = bits / 8 469 | if (typed) { 470 | var result = this.byteView.subarray(this.offset, this.offset + len) 471 | this.offset += len 472 | return result 473 | } 474 | 475 | var bytes = [] 476 | for (var i = 0; i < len; i++) { 477 | bytes.push(this.byteView[this.offset++]) 478 | } 479 | 480 | LogService.logDebug(`[TLSerialization] fetchIntBytes() ${bytesToHex(bytes)} ${(field || '') + ':int' + bits}`) 481 | 482 | return bytes 483 | } 484 | 485 | TLDeserialization.prototype.fetchRawBytes = function (len, typed, field) { 486 | if (len === false) { 487 | len = this.readInt((field || '') + '_length') 488 | if (len > this.byteView.byteLength) { 489 | throw new Error('Invalid raw bytes length: ' + len + ', buffer len: ' + this.byteView.byteLength) 490 | } 491 | } 492 | 493 | if (typed) { 494 | var bytes = new Uint8Array(len) 495 | bytes.set(this.byteView.subarray(this.offset, this.offset + len)) 496 | this.offset += len 497 | return bytes 498 | } 499 | 500 | var bytes = [] 501 | for (var i = 0; i < len; i++) { 502 | bytes.push(this.byteView[this.offset++]) 503 | } 504 | 505 | LogService.logDebug(`[TLSerialization] fetchRawBytes() ${bytesToHex(bytes)} ${(field || '')}`) 506 | 507 | return bytes 508 | } 509 | 510 | TLDeserialization.prototype.fetchObject = function (type, field) { 511 | switch (type) { 512 | case '#': 513 | case 'int': 514 | return this.fetchInt(field) 515 | case 'long': 516 | return this.fetchLong(field) 517 | case 'int128': 518 | return this.fetchIntBytes(128, false, field) 519 | case 'int256': 520 | return this.fetchIntBytes(256, false, field) 521 | case 'int512': 522 | return this.fetchIntBytes(512, false, field) 523 | case 'string': 524 | return this.fetchString(field) 525 | case 'bytes': 526 | return this.fetchBytes(field) 527 | case 'double': 528 | return this.fetchDouble(field) 529 | case 'Bool': 530 | return this.fetchBool(field) 531 | case 'true': 532 | return true 533 | } 534 | 535 | field = field || type || 'Object' 536 | 537 | if (type.substr(0, 6) == 'Vector' || type.substr(0, 6) == 'vector') { 538 | 539 | if (type.charAt(0) == 'V') { 540 | var constructor = this.readInt(field + '[id]') 541 | var constructorCmp = uintToInt(constructor) 542 | 543 | if (constructorCmp == 0x3072cfa1) { // Gzip packed 544 | var compressed = this.fetchBytes(field + '[packed_string]') 545 | var buffer = gzipUncompress(compressed) 546 | var newDeserializer = (new TLDeserialization(buffer)) 547 | 548 | return newDeserializer.fetchObject(type, field) 549 | } 550 | if (constructorCmp != 0x1cb5c415) { 551 | throw new Error('Invalid vector constructor ' + constructor) 552 | } 553 | } 554 | var len = this.readInt(field + '[count]') 555 | var result = [] 556 | if (len > 0) { 557 | var itemType = type.substr(7, type.length - 8); // for "Vector" 558 | for (var i = 0; i < len; i++) { 559 | result.push(this.fetchObject(itemType, field + '[' + i + ']')) 560 | } 561 | } 562 | 563 | return result 564 | } 565 | 566 | var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API 567 | 568 | var predicate = false 569 | var constructorData = false 570 | 571 | if (type.charAt(0) == '%') { 572 | var checkType = type.substr(1) 573 | for (var i = 0; i < schema.constructors.length; i++) { 574 | if (schema.constructors[i].type == checkType) { 575 | constructorData = schema.constructors[i] 576 | break 577 | } 578 | } 579 | if (!constructorData) { 580 | throw new Error('Constructor not found for type: ' + type) 581 | } 582 | } 583 | else if (type.charAt(0) >= 97 && type.charAt(0) <= 122) { 584 | for (var i = 0; i < schema.constructors.length; i++) { 585 | if (schema.constructors[i].predicate == type) { 586 | constructorData = schema.constructors[i] 587 | break 588 | } 589 | } 590 | if (!constructorData) { 591 | throw new Error('Constructor not found for predicate: ' + type) 592 | } 593 | } else { 594 | var constructor = this.readInt(field + '[id]') 595 | var constructorCmp = uintToInt(constructor) 596 | 597 | if (constructorCmp == 0x3072cfa1) { // Gzip packed 598 | var compressed = this.fetchBytes(field + '[packed_string]') 599 | var buffer = gzipUncompress(Buffer.from(compressed)) 600 | var newDeserializer = (new TLDeserialization(buffer)) 601 | 602 | return newDeserializer.fetchObject(type, field) 603 | } 604 | 605 | var index = schema.constructorsIndex 606 | if (!index) { 607 | schema.constructorsIndex = index = {} 608 | for (var i = 0; i < schema.constructors.length; i++) { 609 | index[schema.constructors[i].id] = i 610 | } 611 | } 612 | var i = index[constructorCmp] 613 | if (i) { 614 | constructorData = schema.constructors[i] 615 | } 616 | 617 | var fallback = false 618 | if (!constructorData && this.mtproto) { 619 | var schemaFallback = Config.Schema.API 620 | for (i = 0; i < schemaFallback.constructors.length; i++) { 621 | if (schemaFallback.constructors[i].id == constructorCmp) { 622 | constructorData = schemaFallback.constructors[i] 623 | 624 | delete this.mtproto 625 | fallback = true 626 | break 627 | } 628 | } 629 | } 630 | 631 | if (!constructorData) { 632 | throw new Error('MTProto Constructor not found: ' + constructor + ' ' + this.fetchInt() + ' ' + this.fetchInt()) 633 | } 634 | } 635 | 636 | 637 | predicate = constructorData.predicate 638 | 639 | var result = { '_': predicate } 640 | var overrideKey = (this.mtproto ? 'mt_' : '') + predicate 641 | var self = this 642 | 643 | if (this.override[overrideKey]) { 644 | this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']']) 645 | } else { 646 | var i, param 647 | var type, isCond 648 | var condType, fieldBit 649 | var value 650 | var len = constructorData.params.length 651 | for (i = 0; i < len; i++) { 652 | param = constructorData.params[i] 653 | type = param.type 654 | if (type == '#' && result.pFlags === undefined) { 655 | result.pFlags = {} 656 | } 657 | if (isCond = (type.indexOf('?') !== -1)) { 658 | condType = type.split('?') 659 | fieldBit = condType[0].split('.') 660 | if (!(result[fieldBit[0]] & (1 << fieldBit[1]))) { 661 | continue 662 | } 663 | type = condType[1] 664 | } 665 | 666 | value = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']') 667 | 668 | if (isCond && type === 'true') { 669 | result.pFlags[param.name] = value 670 | } else { 671 | result[param.name] = value 672 | } 673 | } 674 | } 675 | 676 | if (fallback) { 677 | this.mtproto = true 678 | } 679 | 680 | return result 681 | } 682 | 683 | TLDeserialization.prototype.getOffset = function () { 684 | return this.offset 685 | } 686 | 687 | TLDeserialization.prototype.fetchEnd = function () { 688 | if (this.offset != this.byteView.length) { 689 | throw new Error('Fetch end with non-empty buffer') 690 | } 691 | return true 692 | } 693 | -------------------------------------------------------------------------------- /src/Mtp/MtpNetworker.js: -------------------------------------------------------------------------------- 1 | import Config from '../config.js' 2 | import { MtpTimeManager, MtpDcConfigurator, MtpRsaKeysManager } from '../Mtp' 3 | import { 4 | SecureRandom, convertToUint8Array, convertToArrayBuffer, 5 | sha1BytesSync, dT, tsNow, nextRandomInt, bufferConcat, sha256HashSync, aesEncryptSync, 6 | bytesCmp, aesDecryptSync, bytesToArrayBuffer, bytesToHex, longToBytes, uintToInt 7 | } from '../Utils' 8 | import { TLSerialization, TLDeserialization } from '../TL' 9 | import { networkRequest } from '../network' 10 | import { setState, getState } from '../state' 11 | import { LogService, ErrorResponse } from '../Services' 12 | 13 | var updatesProcessor 14 | var iii = 0, 15 | offline 16 | var offlineInited = false 17 | var akStopped = false 18 | 19 | function MtpNetworker(dcID, authKey, serverSalt, options) { 20 | options = options || {} 21 | 22 | this.dcID = dcID 23 | this.iii = iii++ 24 | 25 | this.authKey = authKey 26 | this.authKeyUint8 = convertToUint8Array(authKey) 27 | this.authKeyBuffer = convertToArrayBuffer(authKey) 28 | this.authKeyID = sha1BytesSync(authKey).slice(-8) 29 | 30 | this.serverSalt = serverSalt 31 | 32 | this.upload = options.fileUpload || options.fileDownload || false 33 | 34 | this.updateSessionId() 35 | 36 | this.lastServerMessages = [] 37 | 38 | this.currentRequests = 0 39 | this.checkConnectionPeriod = 0 40 | 41 | this.sentMessages = {} 42 | this.clientMessages = [] 43 | 44 | this.pendingMessages = {} 45 | this.pendingAcks = [] 46 | this.pendingResends = [] 47 | this.connectionInited = false 48 | 49 | this.pendingTimeouts = [] 50 | 51 | //this.longPollInt = $interval(this.checkLongPoll.bind(this), 10000) 52 | /* 53 | this.checkLongPoll() 54 | 55 | if (!offlineInited) { 56 | offlineInited = true 57 | $rootScope.offline = true 58 | $rootScope.offlineConnecting = true 59 | } 60 | 61 | if (Config.Navigator.mobile) { 62 | this.setupMobileSleep() 63 | } 64 | */ 65 | } 66 | 67 | MtpNetworker.prototype.getDcId = function () { return this.dcID } 68 | 69 | MtpNetworker.prototype.updateSessionId = function () { 70 | this.seqNo = 0 71 | this.prevSessionID = this.sessionID 72 | this.sessionID = new Array(8) 73 | new SecureRandom().nextBytes(this.sessionID) 74 | } 75 | 76 | MtpNetworker.prototype.setupMobileSleep = function () { 77 | var self = this 78 | /* 79 | $rootScope.$watch('idle.isIDLE', function (isIDLE) { 80 | if (isIDLE) { 81 | self.sleepAfter = tsNow() + 30000 82 | } else { 83 | delete self.sleepAfter 84 | self.checkLongPoll() 85 | } 86 | }) 87 | 88 | $rootScope.$on('push_received', function () { 89 | LogService.logVerbose(`[MtpNetworker] setupMobileSleep() push_received`) 90 | if (self.sleepAfter) { 91 | self.sleepAfter = tsNow() + 30000 92 | self.checkLongPoll() 93 | } 94 | }) 95 | */ 96 | } 97 | 98 | MtpNetworker.prototype.updateSentMessage = function (sentMessageID) { 99 | var sentMessage = this.sentMessages[sentMessageID] 100 | if (!sentMessage) { 101 | return false 102 | } 103 | var self = this 104 | if (sentMessage.container) { 105 | var newInner = [] 106 | sentMessage.inner.forEach(function (innerSentMessageID) { 107 | var innerSentMessage = self.updateSentMessage(innerSentMessageID) 108 | if (innerSentMessage) { 109 | newInner.push(innerSentMessage.msg_id) 110 | } 111 | }) 112 | sentMessage.inner = newInner 113 | } 114 | 115 | sentMessage.msg_id = MtpTimeManager.generateID() 116 | sentMessage.seq_no = this.generateSeqNo( 117 | sentMessage.notContentRelated || 118 | sentMessage.container 119 | ) 120 | this.sentMessages[sentMessage.msg_id] = sentMessage 121 | delete self.sentMessages[sentMessageID] 122 | 123 | return sentMessage 124 | } 125 | 126 | MtpNetworker.prototype.generateSeqNo = function (notContentRelated) { 127 | var seqNo = this.seqNo * 2 128 | 129 | if (!notContentRelated) { 130 | seqNo++ 131 | this.seqNo++ 132 | } 133 | 134 | return seqNo 135 | } 136 | 137 | MtpNetworker.prototype.wrapMtpCall = function (method, params, options) { 138 | var serializer = new TLSerialization({ mtproto: true }) 139 | 140 | serializer.storeMethod(method, params) 141 | 142 | var messageID = MtpTimeManager.generateID() 143 | var seqNo = this.generateSeqNo() 144 | var message = { 145 | msg_id: messageID, 146 | seq_no: seqNo, 147 | body: serializer.getBytes() 148 | } 149 | 150 | LogService.logVerbose(`[MtpNetworker] wrapMtpCall() ${method} ${JSON.stringify(params, 0, 2)}`) 151 | 152 | return this.pushMessage(message, options) 153 | } 154 | 155 | MtpNetworker.prototype.wrapMtpMessage = function (object, options) { 156 | options = options || {} 157 | 158 | var serializer = new TLSerialization({ mtproto: true }) 159 | serializer.storeObject(object, 'Object') 160 | 161 | const messageID = MtpTimeManager.generateID() 162 | const seqNo = this.generateSeqNo(options.notContentRelated) 163 | const message = { 164 | msg_id: messageID, 165 | seq_no: seqNo, 166 | body: serializer.getBytes() 167 | } 168 | 169 | LogService.logVerbose(`[MtpNetworker] wrapMtpMessage ${JSON.stringify(object, 0, 2)} ${messageID} ${seqNo}`) 170 | 171 | return this.pushMessage(message, options) 172 | } 173 | 174 | MtpNetworker.prototype.wrapApiCall = function (method, params, options) { 175 | return new Promise((resolve, reject) => { 176 | const serializer = new TLSerialization(options) 177 | 178 | if (!this.connectionInited) { 179 | serializer.storeInt(0xda9b0d0d, 'invokeWithLayer') 180 | serializer.storeInt(Config.Schema.API.layer, 'layer') 181 | serializer.storeInt(0x69796de9, 'initConnection') 182 | serializer.storeInt(Config.App.id, 'api_id') 183 | serializer.storeString('Unknown UserAgent', 'device_model') 184 | serializer.storeString('Unknown Platform', 'system_version') 185 | serializer.storeString(Config.App.version, 'app_version') 186 | serializer.storeString('en', 'lang_code') 187 | } 188 | 189 | if (options.afterMessageID) { 190 | serializer.storeInt(0xcb9f372d, 'invokeAfterMsg') 191 | serializer.storeLong(options.afterMessageID, 'msg_id') 192 | } 193 | 194 | options.resultType = serializer.storeMethod(method, params) 195 | 196 | const messageID = MtpTimeManager.generateID() 197 | const seqNo = this.generateSeqNo() 198 | const message = { 199 | msg_id: messageID, 200 | seq_no: seqNo, 201 | body: serializer.getBytes(true), 202 | isAPI: true 203 | } 204 | 205 | LogService.logVerbose(`[MtpNetworker] wrapApiCall() ${method} ${JSON.stringify(params, 0, 2)} ${messageID} ${seqNo} ${JSON.stringify(options, 0, 2)}`) 206 | 207 | this.pushMessage(message, options) 208 | .then(res => resolve(res)) 209 | .catch(err => reject(err)) 210 | }) 211 | } 212 | 213 | MtpNetworker.prototype.checkLongPoll = function (force) { 214 | var isClean = this.cleanupSent() 215 | if (this.longPollPending && tsNow() < this.longPollPending || 216 | this.offline || 217 | akStopped) { 218 | return false 219 | } 220 | var self = this 221 | Storage.get('dc').then(function (baseDcID) { 222 | if (isClean && ( 223 | baseDcID != self.dcID || 224 | self.upload || 225 | self.sleepAfter && tsNow() > self.sleepAfter 226 | )) { 227 | return 228 | } 229 | self.sendLongPoll() 230 | }) 231 | } 232 | 233 | MtpNetworker.prototype.sendLongPoll = function () { 234 | var maxWait = 25000 235 | var self = this 236 | 237 | this.longPollPending = tsNow() + maxWait 238 | 239 | this.wrapMtpCall('http_wait', { 240 | max_delay: 500, 241 | wait_after: 150, 242 | max_wait: maxWait 243 | }, { 244 | noResponse: true, 245 | longPoll: true 246 | }).then(function () { 247 | delete self.longPollPending 248 | self.checkLongPoll.bind(self)() 249 | }, function (error) { 250 | LogService.logError(`[MtpNetworker] sendLongPoll() ${new ErrorResponse(error)}`) 251 | }) 252 | } 253 | 254 | MtpNetworker.prototype.pushMessage = function (message, options = {}) { 255 | return new Promise((resolve, reject) => { 256 | this.sentMessages[message.msg_id] = { ...message, ...options, deferred: { resolve, reject } } 257 | this.pendingMessages[message.msg_id] = 0 258 | 259 | if (!options || !options.noShedule) { 260 | this.sheduleRequest() 261 | } 262 | if (typeof options === 'object') { 263 | options.messageID = message.msg_id 264 | } 265 | }) 266 | } 267 | 268 | MtpNetworker.prototype.pushResend = function (messageID, delay) { 269 | var value = delay ? tsNow() + delay : 0 270 | var sentMessage = this.sentMessages[messageID] 271 | if (sentMessage.container) { 272 | for (var i = 0; i < sentMessage.inner.length; i++) { 273 | this.pendingMessages[sentMessage.inner[i]] = value 274 | } 275 | } else { 276 | this.pendingMessages[messageID] = value 277 | } 278 | 279 | this.sheduleRequest(delay) 280 | } 281 | 282 | MtpNetworker.prototype.getMsgKey = function (dataWithPadding, isOut) { 283 | var authKey = this.authKeyUint8 284 | var x = isOut ? 0 : 8 285 | var msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding) 286 | const msgKeyLarge = sha256HashSync(msgKeyLargePlain) 287 | var msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24) 288 | return msgKey 289 | } 290 | 291 | MtpNetworker.prototype.getAesKeyIv = function (msgKey, isOut) { 292 | var authKey = this.authKeyUint8 293 | var x = isOut ? 0 : 8 294 | var sha2aText = new Uint8Array(52) 295 | var sha2bText = new Uint8Array(52) 296 | var promises = {} 297 | 298 | sha2aText.set(msgKey, 0) 299 | sha2aText.set(authKey.subarray(x, x + 36), 16) 300 | let result_sha2a = sha256HashSync(sha2aText) 301 | 302 | sha2bText.set(authKey.subarray(40 + x, 40 + x + 36), 0) 303 | sha2bText.set(msgKey, 36) 304 | let result_sha2b = sha256HashSync(sha2bText) 305 | 306 | var aesKey = new Uint8Array(32) 307 | var aesIv = new Uint8Array(32) 308 | var sha2a = new Uint8Array(result_sha2a) 309 | var sha2b = new Uint8Array(result_sha2b) 310 | 311 | aesKey.set(sha2a.subarray(0, 8)) 312 | aesKey.set(sha2b.subarray(8, 24), 8) 313 | aesKey.set(sha2a.subarray(24, 32), 24) 314 | 315 | aesIv.set(sha2b.subarray(0, 8)) 316 | aesIv.set(sha2a.subarray(8, 24), 8) 317 | aesIv.set(sha2b.subarray(24, 32), 24) 318 | 319 | return [aesKey, aesIv] 320 | } 321 | 322 | MtpNetworker.prototype.checkConnection = function (event) { 323 | $rootScope.offlineConnecting = true 324 | 325 | LogService.logVerbose(`[MtpNetworker] checkConnection()`) 326 | //$timeout.cancel(this.checkConnectionPromise) 327 | 328 | var serializer = new TLSerialization({ mtproto: true }) 329 | var pingID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] 330 | 331 | serializer.storeMethod('ping', { ping_id: pingID }) 332 | 333 | var pingMessage = { 334 | msg_id: MtpTimeManager.generateID(), 335 | seq_no: this.generateSeqNo(true), 336 | body: serializer.getBytes() 337 | } 338 | 339 | var self = this 340 | this.sendEncryptedRequest(pingMessage, { timeout: 15000 }) 341 | .then(function (result) { 342 | /*delete $rootScope.offlineConnecting 343 | self.toggleOffline(false) 344 | */ 345 | }) 346 | .catch(function () { 347 | LogService.logVerbose(`[MtpNetworker] checkConnection() Delay ${self.checkConnectionPeriod * 1000}`) 348 | //self.checkConnectionPromise = $timeout(self.checkConnection.bind(self), parseInt(self.checkConnectionPeriod * 1000)) 349 | self.checkConnectionPeriod = Math.min(60, self.checkConnectionPeriod * 1.5) 350 | /* 351 | $timeout(function () { 352 | delete $rootScope.offlineConnecting 353 | }, 1000) 354 | */ 355 | }) 356 | } 357 | 358 | MtpNetworker.prototype.toggleOffline = function (enabled) { 359 | LogService.logVerbose(`[MtpNetworker] toggleOffline() ${enabled}`) 360 | 361 | if (this.offline !== undefined && this.offline == enabled) { 362 | return false 363 | } 364 | 365 | this.offline = enabled 366 | //$rootScope.offline = enabled 367 | //$rootScope.offlineConnecting = false 368 | 369 | /* 370 | 371 | if (this.offline) { 372 | $timeout.cancel(this.nextReqPromise) 373 | delete this.nextReq 374 | 375 | if (this.checkConnectionPeriod < 1.5) { 376 | this.checkConnectionPeriod = 0 377 | } 378 | 379 | this.checkConnectionPromise = $timeout(this.checkConnection.bind(this), parseInt(this.checkConnectionPeriod * 1000)) 380 | this.checkConnectionPeriod = Math.min(30, (1 + this.checkConnectionPeriod) * 1.5) 381 | 382 | this.onOnlineCb = this.checkConnection.bind(this) 383 | } else { 384 | delete this.longPollPending 385 | this.checkLongPoll() 386 | this.sheduleRequest() 387 | 388 | $timeout.cancel(this.checkConnectionPromise) 389 | } 390 | */ 391 | } 392 | 393 | MtpNetworker.prototype.performSheduledRequest = function () { 394 | LogService.logVerbose(`[MtpNetworker] performSheduledRequest()`) 395 | 396 | if (this.offline || akStopped) { 397 | LogService.logVerbose(`[MtpNetworker] performSheduledRequest() Cancel sheduled`) 398 | return false 399 | } 400 | 401 | delete this.nextReq 402 | 403 | if (this.pendingAcks.length) { 404 | var ackMsgIDs = [] 405 | for (var i = 0; i < this.pendingAcks.length; i++) { 406 | ackMsgIDs.push(this.pendingAcks[i]) 407 | } 408 | this.wrapMtpMessage({ _: 'msgs_ack', msg_ids: ackMsgIDs }, { notContentRelated: true, noShedule: true }) 409 | } 410 | 411 | if (this.pendingResends.length) { 412 | var resendMsgIDs = [] 413 | var resendOpts = { noShedule: true, notContentRelated: true } 414 | for (var i = 0; i < this.pendingResends.length; i++) { 415 | resendMsgIDs.push(this.pendingResends[i]) 416 | } 417 | this.wrapMtpMessage({ _: 'msg_resend_req', msg_ids: resendMsgIDs }, resendOpts) 418 | this.lastResendReq = { req_msg_id: resendOpts.messageID, resend_msg_ids: resendMsgIDs } 419 | } 420 | 421 | let messages = [] 422 | let message 423 | let messagesByteLen = 0 424 | let currentTime = tsNow() 425 | let hasApiCall = false 426 | let hasHttpWait = false 427 | let lengthOverflow = false 428 | let singlesCount = 0 429 | let self = this 430 | 431 | Object.keys(this.pendingMessages).forEach((messageID) => { 432 | const value = this.pendingMessages[messageID] 433 | 434 | if (!value || value >= currentTime) { 435 | if (message = self.sentMessages[messageID]) { 436 | var messageByteLength = (message.body.byteLength || message.body.length) + 32 437 | if (!message.notContentRelated && 438 | lengthOverflow) { 439 | return 440 | } 441 | if (!message.notContentRelated && 442 | messagesByteLen && 443 | messagesByteLen + messageByteLength > 655360) { // 640 Kb 444 | lengthOverflow = true 445 | return 446 | } 447 | if (message.singleInRequest) { 448 | singlesCount++ 449 | if (singlesCount > 1) { 450 | return 451 | } 452 | } 453 | messages.push(message) 454 | messagesByteLen += messageByteLength 455 | if (message.isAPI) { 456 | hasApiCall = true 457 | } 458 | else if (message.longPoll) { 459 | hasHttpWait = true 460 | } 461 | } 462 | delete self.pendingMessages[messageID] 463 | } 464 | }) 465 | 466 | if (hasApiCall && !hasHttpWait) { 467 | var serializer = new TLSerialization({ mtproto: true }) 468 | serializer.storeMethod('http_wait', { 469 | max_delay: 500, 470 | wait_after: 150, 471 | max_wait: 3000 472 | }) 473 | messages.push({ 474 | msg_id: MtpTimeManager.generateID(), 475 | seq_no: this.generateSeqNo(), 476 | body: serializer.getBytes() 477 | }) 478 | } 479 | 480 | if (!messages.length) { 481 | return 482 | } 483 | 484 | var noResponseMsgs = [] 485 | 486 | if (messages.length > 1) { 487 | var container = new TLSerialization({ mtproto: true, startMaxLength: messagesByteLen + 64 }) 488 | container.storeInt(0x73f1f8dc, 'CONTAINER[id]') 489 | container.storeInt(messages.length, 'CONTAINER[count]') 490 | var onloads = [] 491 | var innerMessages = [] 492 | for (var i = 0; i < messages.length; i++) { 493 | container.storeLong(messages[i].msg_id, 'CONTAINER[' + i + '][msg_id]') 494 | innerMessages.push(messages[i].msg_id) 495 | container.storeInt(messages[i].seq_no, 'CONTAINER[' + i + '][seq_no]') 496 | container.storeInt(messages[i].body.length, 'CONTAINER[' + i + '][bytes]') 497 | container.storeRawBytes(messages[i].body, 'CONTAINER[' + i + '][body]') 498 | if (messages[i].noResponse) { 499 | noResponseMsgs.push(messages[i].msg_id) 500 | } 501 | } 502 | 503 | var containerSentMessage = { 504 | msg_id: MtpTimeManager.generateID(), 505 | seq_no: this.generateSeqNo(true), 506 | container: true, 507 | inner: innerMessages 508 | } 509 | 510 | message = { body: container.getBytes(true), ...containerSentMessage } 511 | 512 | this.sentMessages[message.msg_id] = containerSentMessage 513 | 514 | LogService.logVerbose(`[MtpNetworker] performSheduledRequest() Container ${JSON.stringify(innerMessages, 0, 2)} ${message.msg_id} ${message.seq_no}`) 515 | } else { 516 | if (message.noResponse) { 517 | noResponseMsgs.push(message.msg_id) 518 | } 519 | this.sentMessages[message.msg_id] = message 520 | } 521 | 522 | this.pendingAcks = [] 523 | 524 | LogService.logVerbose(`[MtpNetworker] performSheduledRequest() sendEncryptedRequest(${JSON.stringify(message, 0, 2)})`) 525 | 526 | this.sendEncryptedRequest(message).then(function (result) { 527 | self.toggleOffline(false) 528 | 529 | const response = self.parseResponse(result.data) 530 | 531 | LogService.logVerbose(`[MtpNetworker] performSheduledRequest() sendEncryptedRequest() Server response ${self.dcID} ${JSON.stringify(response, 0, 2)}`) 532 | 533 | self.processMessage(response.response, response.messageID, response.sessionID) 534 | 535 | noResponseMsgs.forEach(function (msgID) { 536 | if (self.sentMessages[msgID]) { 537 | var deferred = self.sentMessages[msgID].deferred 538 | delete self.sentMessages[msgID] 539 | deferred.resolve() 540 | } 541 | }) 542 | 543 | //self.checkLongPoll() 544 | 545 | self.checkConnectionPeriod = Math.max(1.1, Math.sqrt(self.checkConnectionPeriod)) 546 | 547 | }) 548 | .catch(error => { 549 | if (message.container) { 550 | message.inner.forEach(function (msgID) { 551 | self.pendingMessages[msgID] = 0 552 | }) 553 | delete self.sentMessages[message.msg_id] 554 | } else { 555 | self.pendingMessages[message.msg_id] = 0 556 | } 557 | 558 | noResponseMsgs.forEach(function (msgID) { 559 | if (self.sentMessages[msgID]) { 560 | var deferred = self.sentMessages[msgID].deferred 561 | delete self.sentMessages[msgID] 562 | delete self.pendingMessages[msgID] 563 | deferred.reject() 564 | } 565 | }) 566 | 567 | self.toggleOffline(true) 568 | }) 569 | 570 | /* 571 | if (lengthOverflow || singlesCount > 1) { 572 | this.sheduleRequest() 573 | } 574 | */ 575 | } 576 | 577 | MtpNetworker.prototype.getEncryptedMessage = function (dataWithPadding) { 578 | var self = this 579 | const msgKey = self.getMsgKey(dataWithPadding, true) 580 | const keyIv = self.getAesKeyIv(msgKey, true) 581 | const encryptedBytes = aesEncryptSync(dataWithPadding, keyIv[0], keyIv[1]) 582 | return { 583 | bytes: encryptedBytes, 584 | msgKey: msgKey 585 | } 586 | } 587 | 588 | MtpNetworker.prototype.getDecryptedMessage = function (msgKey, encryptedData) { 589 | const keyIv = this.getAesKeyIv(msgKey, false) 590 | return aesDecryptSync(encryptedData, keyIv[0], keyIv[1]) 591 | } 592 | 593 | MtpNetworker.prototype.sendEncryptedRequest = function (message, options = {}) { 594 | return new Promise((resolve, reject) => { 595 | var self = this 596 | options = options || {} 597 | var data = new TLSerialization({ startMaxLength: message.body.length + 2048 }) 598 | 599 | data.storeIntBytes(this.serverSalt, 64, 'salt') 600 | data.storeIntBytes(this.sessionID, 64, 'session_id') 601 | 602 | data.storeLong(message.msg_id, 'message_id') 603 | data.storeInt(message.seq_no, 'seq_no') 604 | 605 | data.storeInt(message.body.length, 'message_data_length') 606 | data.storeRawBytes(message.body, 'message_data') 607 | 608 | var dataBuffer = data.getBuffer() 609 | 610 | var paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5)) 611 | var padding = new Array(paddingLength) 612 | new SecureRandom().nextBytes(padding) 613 | 614 | var dataWithPadding = bufferConcat(dataBuffer, padding) 615 | 616 | const encryptedResult = this.getEncryptedMessage(dataWithPadding) 617 | 618 | var request = new TLSerialization({ startMaxLength: encryptedResult.bytes.byteLength + 256 }) 619 | request.storeIntBytes(self.authKeyID, 64, 'auth_key_id') 620 | request.storeIntBytes(encryptedResult.msgKey, 128, 'msg_key') 621 | request.storeRawBytes(encryptedResult.bytes, 'encrypted_data') 622 | 623 | var requestData = request.getArray() 624 | 625 | var requestPromise 626 | var url = MtpDcConfigurator.chooseServer(self.dcID) 627 | var baseError = { code: 406, type: 'NETWORK_BAD_RESPONSE', url: url } 628 | 629 | networkRequest(url, requestData).then( 630 | function (result) { 631 | if (!result.data || !result.data.byteLength) { 632 | reject(baseError) 633 | } 634 | resolve(result) 635 | }) 636 | .catch(error => { 637 | if (!error.message && !error.type) { 638 | error = { ...baseError, type: 'NETWORK_BAD_REQUEST', originalError: error } 639 | } 640 | LogService.logError(`[MtpNetworker] sendEncryptedRequest() ${error.message}`) 641 | reject(error) 642 | }) 643 | }) 644 | } 645 | 646 | export function toArrayBuffer(arr) { 647 | var ab = new ArrayBuffer(arr.length) 648 | var view = new Uint8Array(ab) 649 | for (var i = 0; i < arr.length; ++i) { 650 | view[i] = arr[i] 651 | } 652 | return ab 653 | } 654 | 655 | MtpNetworker.prototype.parseResponse = function (responseBuffer) { 656 | var self = this 657 | var deserializer = new TLDeserialization(responseBuffer) 658 | 659 | var authKeyID = deserializer.fetchIntBytes(64, false, 'auth_key_id') 660 | 661 | if (!bytesCmp(authKeyID, this.authKeyID)) { 662 | throw new Error('[MT] Invalid server auth_key_id: ' + bytesToHex(authKeyID)) 663 | } 664 | var msgKey = deserializer.fetchIntBytes(128, true, 'msg_key') 665 | var encryptedData = deserializer.fetchRawBytes(responseBuffer.byteLength - deserializer.getOffset(), true, 'encrypted_data') 666 | 667 | const dataWithPadding = toArrayBuffer(self.getDecryptedMessage(msgKey, encryptedData)) 668 | const calcMsgKey = self.getMsgKey(dataWithPadding, false) 669 | if (!bytesCmp(msgKey, calcMsgKey)) { 670 | LogService.logError(`[MtpNetworker] parseResponse() server msgKey mismatch: ${bytesFromArrayBuffer(calcMsgKey)}`) 671 | throw new Error('[MT] server msgKey mismatch') 672 | } 673 | 674 | var deserializer = new TLDeserialization(dataWithPadding, { mtproto: true }) 675 | 676 | var salt = deserializer.fetchIntBytes(64, false, 'salt') 677 | var sessionID = deserializer.fetchIntBytes(64, false, 'session_id') 678 | var messageID = deserializer.fetchLong('message_id') 679 | 680 | if (!bytesCmp(sessionID, self.sessionID) && 681 | (!self.prevSessionID || !bytesCmp(sessionID, self.prevSessionID))) { 682 | LogService.logError(`[MtpNetworker] parseResponse() Invalid server session_id: ${bytesToHex(sessionID)}`) 683 | throw new Error('[MT] Invalid server session_id: ' + bytesToHex(sessionID)) 684 | } 685 | 686 | var seqNo = deserializer.fetchInt('seq_no') 687 | var totalLength = dataWithPadding.bytesLength 688 | 689 | var messageBodyLength = deserializer.fetchInt('message_data[length]') 690 | var offset = deserializer.getOffset() 691 | 692 | if ((messageBodyLength % 4 !== 0) || (messageBodyLength > totalLength - offset)) { 693 | throw new Error('[MT] Invalid body length: ' + messageBodyLength) 694 | } 695 | var messageBody = deserializer.fetchRawBytes(messageBodyLength, true, 'message_data') 696 | 697 | var offset = deserializer.getOffset() 698 | var paddingLength = totalLength - offset 699 | if (paddingLength < 12 || paddingLength > 1024) { 700 | throw new Error('[MT] Invalid padding length: ' + paddingLength) 701 | } 702 | 703 | var buffer = bytesToArrayBuffer(messageBody) 704 | var deserializerOptions = { 705 | mtproto: true, 706 | override: { 707 | mt_message: function (result, field) { 708 | result.msg_id = this.fetchLong(field + '[msg_id]') 709 | result.seqno = this.fetchInt(field + '[seqno]') 710 | result.bytes = this.fetchInt(field + '[bytes]') 711 | 712 | var offset = this.getOffset() 713 | 714 | try { 715 | result.body = this.fetchObject('Object', field + '[body]') 716 | } catch (e) { 717 | LogService.logError(`[MtpNetworker] parseResponse() parse error: ${new ErrorResponse(e)}`) 718 | result.body = { _: 'parse_error', error: e } 719 | } 720 | if (this.offset != offset + result.bytes) { 721 | this.offset = offset + result.bytes 722 | } 723 | }, 724 | mt_rpc_result: function (result, field) { 725 | result.req_msg_id = this.fetchLong(field + '[req_msg_id]') 726 | 727 | var sentMessage = self.sentMessages[result.req_msg_id] 728 | var type = sentMessage && sentMessage.resultType || 'Object' 729 | 730 | if (result.req_msg_id && !sentMessage) { 731 | return 732 | } 733 | result.result = this.fetchObject(type, field + '[result]') 734 | } 735 | } 736 | } 737 | var deserializer = new TLDeserialization(buffer, deserializerOptions) 738 | var response = deserializer.fetchObject('', 'INPUT') 739 | 740 | return { 741 | response: response, 742 | messageID: messageID, 743 | sessionID: sessionID, 744 | seqNo: seqNo 745 | } 746 | } 747 | 748 | MtpNetworker.prototype.applyServerSalt = function (newServerSalt) { 749 | var serverSalt = longToBytes(newServerSalt) 750 | 751 | const newNetworkers = getState().networkers 752 | let networker = newNetworkers.find(nw => nw.id == this.dcID) 753 | if (!networker) { 754 | throw new Error(`Networker with dcID = ${thid.dcID} not found in the state`) 755 | } 756 | 757 | networker.auth.serverSalt = serverSalt 758 | setState({ networkers: newNetworkers }) 759 | this.serverSalt = serverSalt 760 | return true 761 | } 762 | 763 | MtpNetworker.prototype.sheduleRequest = function (delay) { 764 | if (this.offline) { 765 | this.checkConnection('forced shedule') 766 | } 767 | var nextReq = tsNow() + delay 768 | 769 | if (delay && this.nextReq && this.nextReq <= nextReq) { 770 | return false 771 | } 772 | 773 | /* 774 | $timeout.cancel(this.nextReqPromise) 775 | if (delay > 0) { 776 | this.nextReqPromise = $timeout(this.performSheduledRequest.bind(this), delay || 0) 777 | } else { 778 | setZeroTimeout(this.performSheduledRequest.bind(this)) 779 | } 780 | */ 781 | 782 | this.performSheduledRequest.bind(this)() 783 | this.nextReq = nextReq 784 | } 785 | 786 | MtpNetworker.prototype.ackMessage = function (msgID) { 787 | LogService.logVerbose(`[MtpNetworker] ackMessage() ${msgID}`) 788 | this.pendingAcks.push(msgID) 789 | this.sheduleRequest(30000) 790 | } 791 | 792 | MtpNetworker.prototype.reqResendMessage = function (msgID) { 793 | LogService.logVerbose(`[MtpNetworker] reqResendMessage() ${msgID}`) 794 | this.pendingResends.push(msgID) 795 | this.sheduleRequest(100) 796 | } 797 | 798 | MtpNetworker.prototype.cleanupSent = function () { 799 | var self = this 800 | var notEmpty = false 801 | 802 | Object.keys(this.sentMessages).forEach(function (msgID) { 803 | const message = self.sentMessages[msgID] 804 | if (message.notContentRelated && self.pendingMessages[msgID] === undefined) { 805 | delete self.sentMessages[msgID] 806 | } 807 | else if (message.container) { 808 | for (var i = 0; i < message.inner.length; i++) { 809 | if (self.sentMessages[message.inner[i]] !== undefined) { 810 | notEmpty = true 811 | return 812 | } 813 | } 814 | delete self.sentMessages[msgID] 815 | } else { 816 | notEmpty = true 817 | } 818 | }) 819 | 820 | return !notEmpty 821 | } 822 | 823 | MtpNetworker.prototype.processMessageAck = function (messageID) { 824 | var sentMessage = this.sentMessages[messageID] 825 | if (sentMessage && !sentMessage.acked) { 826 | delete sentMessage.body 827 | sentMessage.acked = true 828 | 829 | return true 830 | } 831 | 832 | return false 833 | } 834 | 835 | MtpNetworker.prototype.processError = function (rawError) { 836 | var matches = (rawError.error_message || '').match(/^([A-Z_0-9]+\b)(: (.+))?/) || [] 837 | rawError.error_code = uintToInt(rawError.error_code) 838 | 839 | return { 840 | code: !rawError.error_code || rawError.error_code <= 0 ? 500 : rawError.error_code, 841 | type: matches[1] || 'UNKNOWN', 842 | description: matches[3] || ('CODE#' + rawError.error_code + ' ' + rawError.error_message), 843 | originalError: rawError 844 | } 845 | } 846 | 847 | MtpNetworker.prototype.processMessage = function (message, messageID, sessionID) { 848 | var msgidInt = parseInt(messageID.toString(10).substr(0, -10), 10) 849 | if (msgidInt % 2) { 850 | LogService.logInfo(`[MtpNetworker] processMessage() Server even message id ${messageID}`) 851 | return 852 | } 853 | switch (message._) { 854 | case 'msg_container': 855 | var len = message.messages.length 856 | for (var i = 0; i < len; i++) { 857 | this.processMessage(message.messages[i], message.messages[i].msg_id, sessionID) 858 | } 859 | break 860 | 861 | case 'bad_server_salt': 862 | LogService.logInfo(`[MtpNetworker] processMessage() Bad server salt ${JSON.stringify(message, 0, 2)}`) 863 | var sentMessage = this.sentMessages[message.bad_msg_id] 864 | if (!sentMessage || sentMessage.seq_no != message.bad_msg_seqno) { 865 | LogService.logInfo(`[MtpNetworker] processMessage() Bad server salt for invalid message ${JSON.stringify(message, 0, 2)}`) 866 | throw new Error('[MT] Bad server salt for invalid message') 867 | } 868 | 869 | this.applyServerSalt(message.new_server_salt) 870 | this.pushResend(message.bad_msg_id) 871 | this.ackMessage(messageID) 872 | break 873 | 874 | case 'bad_msg_notification': 875 | LogService.logInfo(`[MtpNetworker] processMessage() Bad msg notification ${JSON.stringify(message, 0, 2)}`) 876 | var sentMessage = this.sentMessages[message.bad_msg_id] 877 | if (!sentMessage || sentMessage.seq_no != message.bad_msg_seqno) { 878 | throw new Error('[MT] Bad msg notification for invalid message') 879 | } 880 | 881 | if (message.error_code == 16 || message.error_code == 17) { 882 | if (MtpTimeManager.applyServerTime( 883 | bigStringInt(messageID).shiftRight(32).toString(10) 884 | )) { 885 | LogService.logInfo(`[MtpNetworker] processMessage() Update session`) 886 | this.updateSessionId() 887 | } 888 | var badMessage = this.updateSentMessage(message.bad_msg_id) 889 | this.pushResend(badMessage.msg_id) 890 | this.ackMessage(messageID) 891 | } 892 | break 893 | 894 | case 'message': 895 | if (this.lastServerMessages.indexOf(messageID) != -1) { 896 | this.ackMessage(messageID) 897 | return 898 | } 899 | this.lastServerMessages.push(messageID) 900 | if (this.lastServerMessages.length > 100) { 901 | this.lastServerMessages.shift() 902 | } 903 | this.processMessage(message.body, message.msg_id, sessionID) 904 | break 905 | 906 | case 'new_session_created': 907 | this.ackMessage(messageID) 908 | 909 | this.processMessageAck(message.first_msg_id) 910 | this.applyServerSalt(message.server_salt) 911 | 912 | var self = this 913 | 914 | const currentDcId = getState().current_dc_id 915 | 916 | if (currentDcId == self.dcID && !self.upload && updatesProcessor) { 917 | updatesProcessor(message, true) 918 | } 919 | 920 | break 921 | 922 | case 'msgs_ack': 923 | for (var i = 0; i < message.msg_ids.length; i++) { 924 | this.processMessageAck(message.msg_ids[i]) 925 | } 926 | break 927 | 928 | case 'msg_detailed_info': 929 | if (!this.sentMessages[message.msg_id]) { 930 | this.ackMessage(message.answer_msg_id) 931 | break 932 | } 933 | case 'msg_new_detailed_info': 934 | if (this.pendingAcks.indexOf(message.answer_msg_id)) { 935 | break 936 | } 937 | this.reqResendMessage(message.answer_msg_id) 938 | break 939 | 940 | case 'msgs_state_info': 941 | this.ackMessage(message.answer_msg_id) 942 | if (this.lastResendReq && this.lastResendReq.req_msg_id == message.req_msg_id && this.pendingResends.length) { 943 | var i, badMsgID, pos 944 | for (i = 0; i < this.lastResendReq.resend_msg_ids.length; i++) { 945 | badMsgID = this.lastResendReq.resend_msg_ids[i] 946 | pos = this.pendingResends.indexOf(badMsgID) 947 | if (pos != -1) { 948 | this.pendingResends.splice(pos, 1) 949 | } 950 | } 951 | } 952 | break 953 | 954 | case 'rpc_result': 955 | this.ackMessage(messageID) 956 | 957 | var sentMessageID = message.req_msg_id 958 | var sentMessage = this.sentMessages[sentMessageID] 959 | 960 | this.processMessageAck(sentMessageID) 961 | if (sentMessage) { 962 | var deferred = sentMessage.deferred 963 | if (message.result._ == 'rpc_error') { 964 | var error = this.processError(message.result) 965 | LogService.logError(`[MtpNetworker] processMessageAck() Rpc error ${new ErrorResponse(error)}`) 966 | if (deferred) { 967 | deferred.reject(error) 968 | } 969 | } else { 970 | if (deferred) { 971 | LogService.logVerbose(`[MtpNetworker] processMessageAck() Rpc response ${JSON.stringify(message.result, 0, 2)}`) 972 | sentMessage.deferred.resolve(message.result) 973 | } 974 | if (sentMessage.isAPI) { 975 | this.connectionInited = true 976 | } 977 | } 978 | 979 | delete this.sentMessages[sentMessageID] 980 | } 981 | break 982 | 983 | default: 984 | this.ackMessage(messageID) 985 | 986 | if (updatesProcessor) { 987 | updatesProcessor(message, true) 988 | } 989 | break 990 | 991 | } 992 | } 993 | 994 | export function startAll() { 995 | if (akStopped) { 996 | akStopped = false 997 | updatesProcessor({ _: 'new_session_created' }, true) 998 | } 999 | } 1000 | 1001 | export function stopAll() { 1002 | akStopped = true 1003 | } 1004 | 1005 | export const getNetworker = function (dcID, authKey, serverSalt, options) { 1006 | return new MtpNetworker(dcID, authKey, serverSalt, options) 1007 | } 1008 | 1009 | export const setUpdatesProcessor = function (callback) { 1010 | updatesProcessor = callback 1011 | } 1012 | -------------------------------------------------------------------------------- /src/Utils/bigNumbers.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2005 Tom Wu 2 | // All Rights Reserved. 3 | // See "LICENSE" for details. 4 | 5 | // Basic JavaScript BN library - subset useful for RSA encryption. 6 | 7 | const navigator = {} 8 | 9 | // Bits per digit 10 | var dbits; 11 | 12 | // JavaScript engine analysis 13 | var canary = 0xdeadbeefcafe; 14 | var j_lm = ((canary&0xffffff)==0xefcafe); 15 | 16 | // (public) Constructor 17 | export function BigInteger(a,b,c) { 18 | if(a != null) 19 | if("number" == typeof a) this.fromNumber(a,b,c); 20 | else if(b == null && "string" != typeof a) this.fromString(a,256); 21 | else this.fromString(a,b); 22 | } 23 | 24 | // return new, unset BigInteger 25 | function nbi() { return new BigInteger(null); } 26 | 27 | // am: Compute w_j += (x*this_i), propagate carries, 28 | // c is initial carry, returns final carry. 29 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue 30 | // We need to select the fastest one that works in this environment. 31 | 32 | // am1: use a single mult and divide to get the high bits, 33 | // max digit bits should be 26 because 34 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53) 35 | function am1(i,x,w,j,c,n) { 36 | while(--n >= 0) { 37 | var v = x*this[i++]+w[j]+c; 38 | c = Math.floor(v/0x4000000); 39 | w[j++] = v&0x3ffffff; 40 | } 41 | return c; 42 | } 43 | // am2 avoids a big mult-and-extract completely. 44 | // Max digit bits should be <= 30 because we do bitwise ops 45 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) 46 | function am2(i,x,w,j,c,n) { 47 | var xl = x&0x7fff, xh = x>>15; 48 | while(--n >= 0) { 49 | var l = this[i]&0x7fff; 50 | var h = this[i++]>>15; 51 | var m = xh*l+h*xl; 52 | l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); 53 | c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); 54 | w[j++] = l&0x3fffffff; 55 | } 56 | return c; 57 | } 58 | // Alternately, set max digit bits to 28 since some 59 | // browsers slow down when dealing with 32-bit numbers. 60 | function am3(i,x,w,j,c,n) { 61 | var xl = x&0x3fff, xh = x>>14; 62 | while(--n >= 0) { 63 | var l = this[i]&0x3fff; 64 | var h = this[i++]>>14; 65 | var m = xh*l+h*xl; 66 | l = xl*l+((m&0x3fff)<<14)+w[j]+c; 67 | c = (l>>28)+(m>>14)+xh*h; 68 | w[j++] = l&0xfffffff; 69 | } 70 | return c; 71 | } 72 | 73 | if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { 74 | BigInteger.prototype.am = am2; 75 | dbits = 30; 76 | } 77 | else if(j_lm && (navigator.appName != "Netscape")) { 78 | BigInteger.prototype.am = am1; 79 | dbits = 26; 80 | } 81 | else { // Mozilla/Netscape seems to prefer am3 82 | BigInteger.prototype.am = am3; 83 | dbits = 28; 84 | } 85 | 86 | BigInteger.prototype.DB = dbits; 87 | BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; 115 | r.t = this.t; 116 | r.s = this.s; 117 | } 118 | 119 | // (protected) set from integer value x, -DV <= x < DV 120 | function bnpFromInt(x) { 121 | this.t = 1; 122 | this.s = (x<0)?-1:0; 123 | if(x > 0) this[0] = x; 124 | else if(x < -1) this[0] = x+this.DV; 125 | else this.t = 0; 126 | } 127 | 128 | // return bigint initialized to value 129 | function nbv(i) { var r = nbi(); r.fromInt(i); return r; } 130 | 131 | // (protected) set from string and radix 132 | function bnpFromString(s,b,signed) { 133 | var k; 134 | if(b == 16) k = 4; 135 | else if(b == 8) k = 3; 136 | else if(b == 256) k = 8; // byte array 137 | else if(b == 2) k = 1; 138 | else if(b == 32) k = 5; 139 | else if(b == 4) k = 2; 140 | else { this.fromRadix(s,b); return; } 141 | this.t = 0; 142 | this.s = 0; 143 | var i = s.length, mi = false, sh = 0; 144 | while(--i >= 0) { 145 | var x = (k==8)?s[i]&0xff:intAt(s,i); 146 | if(x < 0) { 147 | if(s.charAt(i) == "-") mi = true; 148 | continue; 149 | } 150 | mi = false; 151 | if(sh == 0) 152 | this[this.t++] = x; 153 | else if(sh+k > this.DB) { 154 | this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); 156 | } 157 | else 158 | this[this.t-1] |= x<= this.DB) sh -= this.DB; 161 | } 162 | // Disabled due to '-' prefix in toString 163 | if(k == 8 && (s[0]&0x80) != 0 && signed) { 164 | this.s = -1; 165 | if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; 175 | } 176 | 177 | // (public) return string representation in given radix 178 | function bnToString(b) { 179 | if(this.s < 0) return "-"+this.negate().toString(b); 180 | var k; 181 | if(b == 16) k = 4; 182 | else if(b == 8) k = 3; 183 | else if(b == 2) k = 1; 184 | else if(b == 32) k = 5; 185 | else if(b == 4) k = 2; 186 | else return this.toRadix(b); 187 | var km = (1< 0) { 190 | if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } 191 | while(i >= 0) { 192 | if(p < k) { 193 | d = (this[i]&((1<>(p+=this.DB-k); 195 | } 196 | else { 197 | d = (this[i]>>(p-=k))&km; 198 | if(p <= 0) { p += this.DB; --i; } 199 | } 200 | if(d > 0) m = true; 201 | if(m) r += int2char(d); 202 | } 203 | } 204 | return m?r:"0"; 205 | } 206 | 207 | // (public) -this 208 | function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } 209 | 210 | // (public) |this| 211 | function bnAbs() { return (this.s<0)?this.negate():this; } 212 | 213 | // (public) return + if this > a, - if this < a, 0 if equal 214 | function bnCompareTo(a) { 215 | var r = this.s-a.s; 216 | if(r != 0) return r; 217 | var i = this.t; 218 | r = i-a.t; 219 | if(r != 0) return (this.s<0)?-r:r; 220 | while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; 221 | return 0; 222 | } 223 | 224 | // returns bit length of the integer x 225 | function nbits(x) { 226 | var r = 1, t; 227 | if((t=x>>>16) != 0) { x = t; r += 16; } 228 | if((t=x>>8) != 0) { x = t; r += 8; } 229 | if((t=x>>4) != 0) { x = t; r += 4; } 230 | if((t=x>>2) != 0) { x = t; r += 2; } 231 | if((t=x>>1) != 0) { x = t; r += 1; } 232 | return r; 233 | } 234 | 235 | // (public) return the number of bits in "this" 236 | function bnBitLength() { 237 | if(this.t <= 0) return 0; 238 | return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); 239 | } 240 | 241 | // (protected) r = this << n*DB 242 | function bnpDLShiftTo(n,r) { 243 | var i; 244 | for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; 245 | for(i = n-1; i >= 0; --i) r[i] = 0; 246 | r.t = this.t+n; 247 | r.s = this.s; 248 | } 249 | 250 | // (protected) r = this >> n*DB 251 | function bnpDRShiftTo(n,r) { 252 | for(var i = n; i < this.t; ++i) r[i-n] = this[i]; 253 | r.t = Math.max(this.t-n,0); 254 | r.s = this.s; 255 | } 256 | 257 | // (protected) r = this << n 258 | function bnpLShiftTo(n,r) { 259 | var bs = n%this.DB; 260 | var cbs = this.DB-bs; 261 | var bm = (1<= 0; --i) { 264 | r[i+ds+1] = (this[i]>>cbs)|c; 265 | c = (this[i]&bm)<= 0; --i) r[i] = 0; 268 | r[ds] = c; 269 | r.t = this.t+ds+1; 270 | r.s = this.s; 271 | r.clamp(); 272 | } 273 | 274 | // (protected) r = this >> n 275 | function bnpRShiftTo(n,r) { 276 | r.s = this.s; 277 | var ds = Math.floor(n/this.DB); 278 | if(ds >= this.t) { r.t = 0; return; } 279 | var bs = n%this.DB; 280 | var cbs = this.DB-bs; 281 | var bm = (1<>bs; 283 | for(var i = ds+1; i < this.t; ++i) { 284 | r[i-ds-1] |= (this[i]&bm)<>bs; 286 | } 287 | if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; 299 | } 300 | if(a.t < this.t) { 301 | c -= a.s; 302 | while(i < this.t) { 303 | c += this[i]; 304 | r[i++] = c&this.DM; 305 | c >>= this.DB; 306 | } 307 | c += this.s; 308 | } 309 | else { 310 | c += this.s; 311 | while(i < a.t) { 312 | c -= a[i]; 313 | r[i++] = c&this.DM; 314 | c >>= this.DB; 315 | } 316 | c -= a.s; 317 | } 318 | r.s = (c<0)?-1:0; 319 | if(c < -1) r[i++] = this.DV+c; 320 | else if(c > 0) r[i++] = c; 321 | r.t = i; 322 | r.clamp(); 323 | } 324 | 325 | // (protected) r = this * a, r != this,a (HAC 14.12) 326 | // "this" should be the larger one if appropriate. 327 | function bnpMultiplyTo(a,r) { 328 | var x = this.abs(), y = a.abs(); 329 | var i = x.t; 330 | r.t = i+y.t; 331 | while(--i >= 0) r[i] = 0; 332 | for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); 333 | r.s = 0; 334 | r.clamp(); 335 | if(this.s != a.s) BigInteger.ZERO.subTo(r,r); 336 | } 337 | 338 | // (protected) r = this^2, r != this (HAC 14.16) 339 | function bnpSquareTo(r) { 340 | var x = this.abs(); 341 | var i = r.t = 2*x.t; 342 | while(--i >= 0) r[i] = 0; 343 | for(i = 0; i < x.t-1; ++i) { 344 | var c = x.am(i,x[i],r,2*i,0,1); 345 | if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { 346 | r[i+x.t] -= x.DV; 347 | r[i+x.t+1] = 1; 348 | } 349 | } 350 | if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); 351 | r.s = 0; 352 | r.clamp(); 353 | } 354 | 355 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) 356 | // r != q, this != m. q or r may be null. 357 | function bnpDivRemTo(m,q,r) { 358 | var pm = m.abs(); 359 | if(pm.t <= 0) return; 360 | var pt = this.abs(); 361 | if(pt.t < pm.t) { 362 | if(q != null) q.fromInt(0); 363 | if(r != null) this.copyTo(r); 364 | return; 365 | } 366 | if(r == null) r = nbi(); 367 | var y = nbi(), ts = this.s, ms = m.s; 368 | var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus 369 | if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } 370 | else { pm.copyTo(y); pt.copyTo(r); } 371 | var ys = y.t; 372 | var y0 = y[ys-1]; 373 | if(y0 == 0) return; 374 | var yt = y0*(1<1)?y[ys-2]>>this.F2:0); 375 | var d1 = this.FV/yt, d2 = (1<= 0) { 379 | r[r.t++] = 1; 380 | r.subTo(t,r); 381 | } 382 | BigInteger.ONE.dlShiftTo(ys,t); 383 | t.subTo(y,y); // "negative" y so we can replace sub with am later 384 | while(y.t < ys) y[y.t++] = 0; 385 | while(--j >= 0) { 386 | // Estimate quotient digit 387 | var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); 388 | if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out 389 | y.dlShiftTo(j,t); 390 | r.subTo(t,r); 391 | while(r[i] < --qd) r.subTo(t,r); 392 | } 393 | } 394 | if(q != null) { 395 | r.drShiftTo(ys,q); 396 | if(ts != ms) BigInteger.ZERO.subTo(q,q); 397 | } 398 | r.t = ys; 399 | r.clamp(); 400 | if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder 401 | if(ts < 0) BigInteger.ZERO.subTo(r,r); 402 | } 403 | 404 | // (public) this mod a 405 | function bnMod(a) { 406 | var r = nbi(); 407 | this.abs().divRemTo(a,null,r); 408 | if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); 409 | return r; 410 | } 411 | 412 | // Modular reduction using "classic" algorithm 413 | function Classic(m) { this.m = m; } 414 | function cConvert(x) { 415 | if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); 416 | else return x; 417 | } 418 | function cRevert(x) { return x; } 419 | function cReduce(x) { x.divRemTo(this.m,null,x); } 420 | function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 421 | function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 422 | 423 | Classic.prototype.convert = cConvert; 424 | Classic.prototype.revert = cRevert; 425 | Classic.prototype.reduce = cReduce; 426 | Classic.prototype.mulTo = cMulTo; 427 | Classic.prototype.sqrTo = cSqrTo; 428 | 429 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction 430 | // justification: 431 | // xy == 1 (mod m) 432 | // xy = 1+km 433 | // xy(2-xy) = (1+km)(1-km) 434 | // x[y(2-xy)] = 1-k^2m^2 435 | // x[y(2-xy)] == 1 (mod m^2) 436 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 437 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. 438 | // JS multiply "overflows" differently from C/C++, so care is needed here. 439 | function bnpInvDigit() { 440 | if(this.t < 1) return 0; 441 | var x = this[0]; 442 | if((x&1) == 0) return 0; 443 | var y = x&3; // y == 1/x mod 2^2 444 | y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 445 | y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 446 | y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 447 | // last step - calculate inverse mod DV directly; 448 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints 449 | y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits 450 | // we really want the negative inverse, and -DV < y < DV 451 | return (y>0)?this.DV-y:-y; 452 | } 453 | 454 | // Montgomery reduction 455 | function Montgomery(m) { 456 | this.m = m; 457 | this.mp = m.invDigit(); 458 | this.mpl = this.mp&0x7fff; 459 | this.mph = this.mp>>15; 460 | this.um = (1<<(m.DB-15))-1; 461 | this.mt2 = 2*m.t; 462 | } 463 | 464 | // xR mod m 465 | function montConvert(x) { 466 | var r = nbi(); 467 | x.abs().dlShiftTo(this.m.t,r); 468 | r.divRemTo(this.m,null,r); 469 | if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); 470 | return r; 471 | } 472 | 473 | // x/R mod m 474 | function montRevert(x) { 475 | var r = nbi(); 476 | x.copyTo(r); 477 | this.reduce(r); 478 | return r; 479 | } 480 | 481 | // x = x/R mod m (HAC 14.32) 482 | function montReduce(x) { 483 | while(x.t <= this.mt2) // pad x so am has enough room later 484 | x[x.t++] = 0; 485 | for(var i = 0; i < this.m.t; ++i) { 486 | // faster way of calculating u0 = x[i]*mp mod DV 487 | var j = x[i]&0x7fff; 488 | var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; 489 | // use am to combine the multiply-shift-add into one call 490 | j = i+this.m.t; 491 | x[j] += this.m.am(0,u0,x,i,0,this.m.t); 492 | // propagate carry 493 | while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } 494 | } 495 | x.clamp(); 496 | x.drShiftTo(this.m.t,x); 497 | if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 498 | } 499 | 500 | // r = "x^2/R mod m"; x != r 501 | function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 502 | 503 | // r = "xy/R mod m"; x,y != r 504 | function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 505 | 506 | Montgomery.prototype.convert = montConvert; 507 | Montgomery.prototype.revert = montRevert; 508 | Montgomery.prototype.reduce = montReduce; 509 | Montgomery.prototype.mulTo = montMulTo; 510 | Montgomery.prototype.sqrTo = montSqrTo; 511 | 512 | // (protected) true iff this is even 513 | function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } 514 | 515 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) 516 | function bnpExp(e,z) { 517 | if(e > 0xffffffff || e < 1) return BigInteger.ONE; 518 | var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; 519 | g.copyTo(r); 520 | while(--i >= 0) { 521 | z.sqrTo(r,r2); 522 | if((e&(1< 0) z.mulTo(r2,g,r); 523 | else { var t = r; r = r2; r2 = t; } 524 | } 525 | return z.revert(r); 526 | } 527 | 528 | // (public) this^e % m, 0 <= e < 2^32 529 | function bnModPowInt(e,m) { 530 | var z; 531 | if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); 532 | return this.exp(e,z); 533 | } 534 | 535 | // protected 536 | BigInteger.prototype.copyTo = bnpCopyTo; 537 | BigInteger.prototype.fromInt = bnpFromInt; 538 | BigInteger.prototype.fromString = bnpFromString; 539 | BigInteger.prototype.clamp = bnpClamp; 540 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo; 541 | BigInteger.prototype.drShiftTo = bnpDRShiftTo; 542 | BigInteger.prototype.lShiftTo = bnpLShiftTo; 543 | BigInteger.prototype.rShiftTo = bnpRShiftTo; 544 | BigInteger.prototype.subTo = bnpSubTo; 545 | BigInteger.prototype.multiplyTo = bnpMultiplyTo; 546 | BigInteger.prototype.squareTo = bnpSquareTo; 547 | BigInteger.prototype.divRemTo = bnpDivRemTo; 548 | BigInteger.prototype.invDigit = bnpInvDigit; 549 | BigInteger.prototype.isEven = bnpIsEven; 550 | BigInteger.prototype.exp = bnpExp; 551 | 552 | // public 553 | BigInteger.prototype.toString = bnToString; 554 | BigInteger.prototype.negate = bnNegate; 555 | BigInteger.prototype.abs = bnAbs; 556 | BigInteger.prototype.compareTo = bnCompareTo; 557 | BigInteger.prototype.bitLength = bnBitLength; 558 | BigInteger.prototype.mod = bnMod; 559 | BigInteger.prototype.modPowInt = bnModPowInt; 560 | 561 | // "constants" 562 | BigInteger.ZERO = nbv(0); 563 | BigInteger.ONE = nbv(1); 564 | 565 | 566 | 567 | // Copyright (c) 2005-2009 Tom Wu 568 | // All Rights Reserved. 569 | // See "LICENSE" for details. 570 | 571 | // Extended JavaScript BN functions, required for RSA private ops. 572 | 573 | // Version 1.1: new BigInteger("0", 10) returns "proper" zero 574 | // Version 1.2: square() API, isProbablePrime fix 575 | 576 | // (public) 577 | function bnClone() { var r = nbi(); this.copyTo(r); return r; } 578 | 579 | // (public) return value as integer 580 | function bnIntValue() { 581 | if(this.s < 0) { 582 | if(this.t == 1) return this[0]-this.DV; 583 | else if(this.t == 0) return -1; 584 | } 585 | else if(this.t == 1) return this[0]; 586 | else if(this.t == 0) return 0; 587 | // assumes 16 < DB < 32 588 | return ((this[1]&((1<<(32-this.DB))-1))<>24; } 593 | 594 | // (public) return value as short (assumes DB>=16) 595 | function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } 596 | 597 | // (protected) return x s.t. r^x < DV 598 | function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } 599 | 600 | // (public) 0 if this == 0, 1 if this > 0 601 | function bnSigNum() { 602 | if(this.s < 0) return -1; 603 | else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; 604 | else return 1; 605 | } 606 | 607 | // (protected) convert to radix string 608 | function bnpToRadix(b) { 609 | if(b == null) b = 10; 610 | if(this.signum() == 0 || b < 2 || b > 36) return "0"; 611 | var cs = this.chunkSize(b); 612 | var a = Math.pow(b,cs); 613 | var d = nbv(a), y = nbi(), z = nbi(), r = ""; 614 | this.divRemTo(d,y,z); 615 | while(y.signum() > 0) { 616 | r = (a+z.intValue()).toString(b).substr(1) + r; 617 | y.divRemTo(d,y,z); 618 | } 619 | return z.intValue().toString(b) + r; 620 | } 621 | 622 | // (protected) convert from radix string 623 | function bnpFromRadix(s,b) { 624 | this.fromInt(0); 625 | if(b == null) b = 10; 626 | var cs = this.chunkSize(b); 627 | var d = Math.pow(b,cs), mi = false, j = 0, w = 0; 628 | for(var i = 0; i < s.length; ++i) { 629 | var x = intAt(s,i); 630 | if(x < 0) { 631 | if(s.charAt(i) == "-" && this.signum() == 0) mi = true; 632 | continue; 633 | } 634 | w = b*w+x; 635 | if(++j >= cs) { 636 | this.dMultiply(d); 637 | this.dAddOffset(w,0); 638 | j = 0; 639 | w = 0; 640 | } 641 | } 642 | if(j > 0) { 643 | this.dMultiply(Math.pow(b,j)); 644 | this.dAddOffset(w,0); 645 | } 646 | if(mi) BigInteger.ZERO.subTo(this,this); 647 | } 648 | 649 | // (protected) alternate constructor 650 | function bnpFromNumber(a,b,c) { 651 | if("number" == typeof b) { 652 | // new BigInteger(int,int,RNG) 653 | if(a < 2) this.fromInt(1); 654 | else { 655 | this.fromNumber(a,c); 656 | if(!this.testBit(a-1)) // force MSB set 657 | this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); 658 | if(this.isEven()) this.dAddOffset(1,0); // force odd 659 | while(!this.isProbablePrime(b)) { 660 | this.dAddOffset(2,0); 661 | if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); 662 | } 663 | } 664 | } 665 | else { 666 | // new BigInteger(int,RNG) 667 | var x = new Array(), t = a&7; 668 | x.length = (a>>3)+1; 669 | b.nextBytes(x); 670 | if(t > 0) x[0] &= ((1< 0) { 681 | if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) 682 | r[k++] = d|(this.s<<(this.DB-p)); 683 | while(i >= 0) { 684 | if(p < 8) { 685 | d = (this[i]&((1<>(p+=this.DB-8); 687 | } 688 | else { 689 | d = (this[i]>>(p-=8))&0xff; 690 | if(p <= 0) { p += this.DB; --i; } 691 | } 692 | if(signed && (d&0x80) != 0) d |= -256; 693 | if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; 694 | if(k > 0 || d != this.s) r[k++] = d; 695 | } 696 | } 697 | return r; 698 | } 699 | 700 | function bnEquals(a) { return(this.compareTo(a)==0); } 701 | function bnMin(a) { return(this.compareTo(a)<0)?this:a; } 702 | function bnMax(a) { return(this.compareTo(a)>0)?this:a; } 703 | 704 | // (protected) r = this op a (bitwise) 705 | function bnpBitwiseTo(a,op,r) { 706 | var i, f, m = Math.min(a.t,this.t); 707 | for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); 708 | if(a.t < this.t) { 709 | f = a.s&this.DM; 710 | for(i = m; i < this.t; ++i) r[i] = op(this[i],f); 711 | r.t = this.t; 712 | } 713 | else { 714 | f = this.s&this.DM; 715 | for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); 716 | r.t = a.t; 717 | } 718 | r.s = op(this.s,a.s); 719 | r.clamp(); 720 | } 721 | 722 | // (public) this & a 723 | function op_and(x,y) { return x&y; } 724 | function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } 725 | 726 | // (public) this | a 727 | function op_or(x,y) { return x|y; } 728 | function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } 729 | 730 | // (public) this ^ a 731 | function op_xor(x,y) { return x^y; } 732 | function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } 733 | 734 | // (public) this & ~a 735 | function op_andnot(x,y) { return x&~y; } 736 | function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } 737 | 738 | // (public) ~this 739 | function bnNot() { 740 | var r = nbi(); 741 | for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; 742 | r.t = this.t; 743 | r.s = ~this.s; 744 | return r; 745 | } 746 | 747 | // (public) this << n 748 | function bnShiftLeft(n) { 749 | var r = nbi(); 750 | if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); 751 | return r; 752 | } 753 | 754 | // (public) this >> n 755 | function bnShiftRight(n) { 756 | var r = nbi(); 757 | if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); 758 | return r; 759 | } 760 | 761 | // return index of lowest 1-bit in x, x < 2^31 762 | function lbit(x) { 763 | if(x == 0) return -1; 764 | var r = 0; 765 | if((x&0xffff) == 0) { x >>= 16; r += 16; } 766 | if((x&0xff) == 0) { x >>= 8; r += 8; } 767 | if((x&0xf) == 0) { x >>= 4; r += 4; } 768 | if((x&3) == 0) { x >>= 2; r += 2; } 769 | if((x&1) == 0) ++r; 770 | return r; 771 | } 772 | 773 | // (public) returns index of lowest 1-bit (or -1 if none) 774 | function bnGetLowestSetBit() { 775 | for(var i = 0; i < this.t; ++i) 776 | if(this[i] != 0) return i*this.DB+lbit(this[i]); 777 | if(this.s < 0) return this.t*this.DB; 778 | return -1; 779 | } 780 | 781 | // return number of 1 bits in x 782 | function cbit(x) { 783 | var r = 0; 784 | while(x != 0) { x &= x-1; ++r; } 785 | return r; 786 | } 787 | 788 | // (public) return number of set bits 789 | function bnBitCount() { 790 | var r = 0, x = this.s&this.DM; 791 | for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); 792 | return r; 793 | } 794 | 795 | // (public) true iff nth bit is set 796 | function bnTestBit(n) { 797 | var j = Math.floor(n/this.DB); 798 | if(j >= this.t) return(this.s!=0); 799 | return((this[j]&(1<<(n%this.DB)))!=0); 800 | } 801 | 802 | // (protected) this op (1<>= this.DB; 825 | } 826 | if(a.t < this.t) { 827 | c += a.s; 828 | while(i < this.t) { 829 | c += this[i]; 830 | r[i++] = c&this.DM; 831 | c >>= this.DB; 832 | } 833 | c += this.s; 834 | } 835 | else { 836 | c += this.s; 837 | while(i < a.t) { 838 | c += a[i]; 839 | r[i++] = c&this.DM; 840 | c >>= this.DB; 841 | } 842 | c += a.s; 843 | } 844 | r.s = (c<0)?-1:0; 845 | if(c > 0) r[i++] = c; 846 | else if(c < -1) r[i++] = this.DV+c; 847 | r.t = i; 848 | r.clamp(); 849 | } 850 | 851 | // (public) this + a 852 | function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } 853 | 854 | // (public) this - a 855 | function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } 856 | 857 | // (public) this * a 858 | function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } 859 | 860 | // (public) this^2 861 | function bnSquare() { var r = nbi(); this.squareTo(r); return r; } 862 | 863 | // (public) this / a 864 | function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } 865 | 866 | // (public) this % a 867 | function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } 868 | 869 | // (public) [this/a,this%a] 870 | function bnDivideAndRemainder(a) { 871 | var q = nbi(), r = nbi(); 872 | this.divRemTo(a,q,r); 873 | return new Array(q,r); 874 | } 875 | 876 | // (protected) this *= n, this >= 0, 1 < n < DV 877 | function bnpDMultiply(n) { 878 | this[this.t] = this.am(0,n-1,this,0,0,this.t); 879 | ++this.t; 880 | this.clamp(); 881 | } 882 | 883 | // (protected) this += n << w words, this >= 0 884 | function bnpDAddOffset(n,w) { 885 | if(n == 0) return; 886 | while(this.t <= w) this[this.t++] = 0; 887 | this[w] += n; 888 | while(this[w] >= this.DV) { 889 | this[w] -= this.DV; 890 | if(++w >= this.t) this[this.t++] = 0; 891 | ++this[w]; 892 | } 893 | } 894 | 895 | // A "null" reducer 896 | function NullExp() {} 897 | function nNop(x) { return x; } 898 | function nMulTo(x,y,r) { x.multiplyTo(y,r); } 899 | function nSqrTo(x,r) { x.squareTo(r); } 900 | 901 | NullExp.prototype.convert = nNop; 902 | NullExp.prototype.revert = nNop; 903 | NullExp.prototype.mulTo = nMulTo; 904 | NullExp.prototype.sqrTo = nSqrTo; 905 | 906 | // (public) this^e 907 | function bnPow(e) { return this.exp(e,new NullExp()); } 908 | 909 | // (protected) r = lower n words of "this * a", a.t <= n 910 | // "this" should be the larger one if appropriate. 911 | function bnpMultiplyLowerTo(a,n,r) { 912 | var i = Math.min(this.t+a.t,n); 913 | r.s = 0; // assumes a,this >= 0 914 | r.t = i; 915 | while(i > 0) r[--i] = 0; 916 | var j; 917 | for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); 918 | for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); 919 | r.clamp(); 920 | } 921 | 922 | // (protected) r = "this * a" without lower n words, n > 0 923 | // "this" should be the larger one if appropriate. 924 | function bnpMultiplyUpperTo(a,n,r) { 925 | --n; 926 | var i = r.t = this.t+a.t-n; 927 | r.s = 0; // assumes a,this >= 0 928 | while(--i >= 0) r[i] = 0; 929 | for(i = Math.max(n-this.t,0); i < a.t; ++i) 930 | r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); 931 | r.clamp(); 932 | r.drShiftTo(1,r); 933 | } 934 | 935 | // Barrett modular reduction 936 | function Barrett(m) { 937 | // setup Barrett 938 | this.r2 = nbi(); 939 | this.q3 = nbi(); 940 | BigInteger.ONE.dlShiftTo(2*m.t,this.r2); 941 | this.mu = this.r2.divide(m); 942 | this.m = m; 943 | } 944 | 945 | function barrettConvert(x) { 946 | if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); 947 | else if(x.compareTo(this.m) < 0) return x; 948 | else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } 949 | } 950 | 951 | function barrettRevert(x) { return x; } 952 | 953 | // x = x mod m (HAC 14.42) 954 | function barrettReduce(x) { 955 | x.drShiftTo(this.m.t-1,this.r2); 956 | if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } 957 | this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); 958 | this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); 959 | while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); 960 | x.subTo(this.r2,x); 961 | while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 962 | } 963 | 964 | // r = x^2 mod m; x != r 965 | function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 966 | 967 | // r = x*y mod m; x,y != r 968 | function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 969 | 970 | Barrett.prototype.convert = barrettConvert; 971 | Barrett.prototype.revert = barrettRevert; 972 | Barrett.prototype.reduce = barrettReduce; 973 | Barrett.prototype.mulTo = barrettMulTo; 974 | Barrett.prototype.sqrTo = barrettSqrTo; 975 | 976 | // (public) this^e % m (HAC 14.85) 977 | function bnModPow(e,m) { 978 | var i = e.bitLength(), k, r = nbv(1), z; 979 | if(i <= 0) return r; 980 | else if(i < 18) k = 1; 981 | else if(i < 48) k = 3; 982 | else if(i < 144) k = 4; 983 | else if(i < 768) k = 5; 984 | else k = 6; 985 | if(i < 8) 986 | z = new Classic(m); 987 | else if(m.isEven()) 988 | z = new Barrett(m); 989 | else 990 | z = new Montgomery(m); 991 | 992 | // precomputation 993 | var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { 996 | var g2 = nbi(); 997 | z.sqrTo(g[1],g2); 998 | while(n <= km) { 999 | g[n] = nbi(); 1000 | z.mulTo(g2,g[n-2],g[n]); 1001 | n += 2; 1002 | } 1003 | } 1004 | 1005 | var j = e.t-1, w, is1 = true, r2 = nbi(), t; 1006 | i = nbits(e[j])-1; 1007 | while(j >= 0) { 1008 | if(i >= k1) w = (e[j]>>(i-k1))&km; 1009 | else { 1010 | w = (e[j]&((1<<(i+1))-1))<<(k1-i); 1011 | if(j > 0) w |= e[j-1]>>(this.DB+i-k1); 1012 | } 1013 | 1014 | n = k; 1015 | while((w&1) == 0) { w >>= 1; --n; } 1016 | if((i -= n) < 0) { i += this.DB; --j; } 1017 | if(is1) { // ret == 1, don't bother squaring or multiplying it 1018 | g[w].copyTo(r); 1019 | is1 = false; 1020 | } 1021 | else { 1022 | while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } 1023 | if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } 1024 | z.mulTo(r2,g[w],r); 1025 | } 1026 | 1027 | while(j >= 0 && (e[j]&(1< 0) { 1044 | x.rShiftTo(g,x); 1045 | y.rShiftTo(g,y); 1046 | } 1047 | while(x.signum() > 0) { 1048 | if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); 1049 | if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); 1050 | if(x.compareTo(y) >= 0) { 1051 | x.subTo(y,x); 1052 | x.rShiftTo(1,x); 1053 | } 1054 | else { 1055 | y.subTo(x,y); 1056 | y.rShiftTo(1,y); 1057 | } 1058 | } 1059 | if(g > 0) y.lShiftTo(g,y); 1060 | return y; 1061 | } 1062 | 1063 | // (protected) this % n, n < 2^26 1064 | function bnpModInt(n) { 1065 | if(n <= 0) return 0; 1066 | var d = this.DV%n, r = (this.s<0)?n-1:0; 1067 | if(this.t > 0) 1068 | if(d == 0) r = this[0]%n; 1069 | else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; 1070 | return r; 1071 | } 1072 | 1073 | // (public) 1/this % m (HAC 14.61) 1074 | function bnModInverse(m) { 1075 | var ac = m.isEven(); 1076 | if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; 1077 | var u = m.clone(), v = this.clone(); 1078 | var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); 1079 | while(u.signum() != 0) { 1080 | while(u.isEven()) { 1081 | u.rShiftTo(1,u); 1082 | if(ac) { 1083 | if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } 1084 | a.rShiftTo(1,a); 1085 | } 1086 | else if(!b.isEven()) b.subTo(m,b); 1087 | b.rShiftTo(1,b); 1088 | } 1089 | while(v.isEven()) { 1090 | v.rShiftTo(1,v); 1091 | if(ac) { 1092 | if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } 1093 | c.rShiftTo(1,c); 1094 | } 1095 | else if(!d.isEven()) d.subTo(m,d); 1096 | d.rShiftTo(1,d); 1097 | } 1098 | if(u.compareTo(v) >= 0) { 1099 | u.subTo(v,u); 1100 | if(ac) a.subTo(c,a); 1101 | b.subTo(d,b); 1102 | } 1103 | else { 1104 | v.subTo(u,v); 1105 | if(ac) c.subTo(a,c); 1106 | d.subTo(b,d); 1107 | } 1108 | } 1109 | if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; 1110 | if(d.compareTo(m) >= 0) return d.subtract(m); 1111 | if(d.signum() < 0) d.addTo(m,d); else return d; 1112 | if(d.signum() < 0) return d.add(m); else return d; 1113 | } 1114 | 1115 | var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; 1116 | var lplim = (1<<26)/lowprimes[lowprimes.length-1]; 1117 | 1118 | // (public) test primality with certainty >= 1-.5^t 1119 | function bnIsProbablePrime(t) { 1120 | var i, x = this.abs(); 1121 | if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { 1122 | for(i = 0; i < lowprimes.length; ++i) 1123 | if(x[0] == lowprimes[i]) return true; 1124 | return false; 1125 | } 1126 | if(x.isEven()) return false; 1127 | i = 1; 1128 | while(i < lowprimes.length) { 1129 | var m = lowprimes[i], j = i+1; 1130 | while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; 1131 | m = x.modInt(m); 1132 | while(i < j) if(m%lowprimes[i++] == 0) return false; 1133 | } 1134 | return x.millerRabin(t); 1135 | } 1136 | 1137 | // (protected) true if probably prime (HAC 4.24, Miller-Rabin) 1138 | function bnpMillerRabin(t) { 1139 | var n1 = this.subtract(BigInteger.ONE); 1140 | var k = n1.getLowestSetBit(); 1141 | if(k <= 0) return false; 1142 | var r = n1.shiftRight(k); 1143 | t = (t+1)>>1; 1144 | if(t > lowprimes.length) t = lowprimes.length; 1145 | var a = nbi(); 1146 | for(var i = 0; i < t; ++i) { 1147 | //Pick bases at random, instead of starting at 2 1148 | a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); 1149 | var y = a.modPow(r,this); 1150 | if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { 1151 | var j = 1; 1152 | while(j++ < k && y.compareTo(n1) != 0) { 1153 | y = y.modPowInt(2,this); 1154 | if(y.compareTo(BigInteger.ONE) == 0) return false; 1155 | } 1156 | if(y.compareTo(n1) != 0) return false; 1157 | } 1158 | } 1159 | return true; 1160 | } 1161 | 1162 | // protected 1163 | BigInteger.prototype.chunkSize = bnpChunkSize; 1164 | BigInteger.prototype.toRadix = bnpToRadix; 1165 | BigInteger.prototype.fromRadix = bnpFromRadix; 1166 | BigInteger.prototype.fromNumber = bnpFromNumber; 1167 | BigInteger.prototype.bitwiseTo = bnpBitwiseTo; 1168 | BigInteger.prototype.changeBit = bnpChangeBit; 1169 | BigInteger.prototype.addTo = bnpAddTo; 1170 | BigInteger.prototype.dMultiply = bnpDMultiply; 1171 | BigInteger.prototype.dAddOffset = bnpDAddOffset; 1172 | BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; 1173 | BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; 1174 | BigInteger.prototype.modInt = bnpModInt; 1175 | BigInteger.prototype.millerRabin = bnpMillerRabin; 1176 | 1177 | // public 1178 | BigInteger.prototype.clone = bnClone; 1179 | BigInteger.prototype.intValue = bnIntValue; 1180 | BigInteger.prototype.byteValue = bnByteValue; 1181 | BigInteger.prototype.shortValue = bnShortValue; 1182 | BigInteger.prototype.signum = bnSigNum; 1183 | BigInteger.prototype.toByteArray = bnToByteArray; 1184 | BigInteger.prototype.equals = bnEquals; 1185 | BigInteger.prototype.min = bnMin; 1186 | BigInteger.prototype.max = bnMax; 1187 | BigInteger.prototype.and = bnAnd; 1188 | BigInteger.prototype.or = bnOr; 1189 | BigInteger.prototype.xor = bnXor; 1190 | BigInteger.prototype.andNot = bnAndNot; 1191 | BigInteger.prototype.not = bnNot; 1192 | BigInteger.prototype.shiftLeft = bnShiftLeft; 1193 | BigInteger.prototype.shiftRight = bnShiftRight; 1194 | BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; 1195 | BigInteger.prototype.bitCount = bnBitCount; 1196 | BigInteger.prototype.testBit = bnTestBit; 1197 | BigInteger.prototype.setBit = bnSetBit; 1198 | BigInteger.prototype.clearBit = bnClearBit; 1199 | BigInteger.prototype.flipBit = bnFlipBit; 1200 | BigInteger.prototype.add = bnAdd; 1201 | BigInteger.prototype.subtract = bnSubtract; 1202 | BigInteger.prototype.multiply = bnMultiply; 1203 | BigInteger.prototype.divide = bnDivide; 1204 | BigInteger.prototype.remainder = bnRemainder; 1205 | BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; 1206 | BigInteger.prototype.modPow = bnModPow; 1207 | BigInteger.prototype.modInverse = bnModInverse; 1208 | BigInteger.prototype.pow = bnPow; 1209 | BigInteger.prototype.gcd = bnGCD; 1210 | BigInteger.prototype.isProbablePrime = bnIsProbablePrime; 1211 | 1212 | // JSBN-specific extension 1213 | BigInteger.prototype.square = bnSquare; 1214 | 1215 | // BigInteger interfaces not implemented in jsbn: 1216 | 1217 | // BigInteger(int signum, byte[] magnitude) 1218 | // double doubleValue() 1219 | // float floatValue() 1220 | // int hashCode() 1221 | // long longValue() 1222 | // static BigInteger valueOf(long val) 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | // Random number generator - requires a PRNG backend, e.g. prng4.js 1229 | 1230 | // For best results, put code like 1231 | // 1232 | // in your main HTML document. 1233 | 1234 | var rng_state; 1235 | var rng_pool; 1236 | var rng_pptr; 1237 | 1238 | // Mix in a 32-bit integer into the pool 1239 | function rng_seed_int(x) { 1240 | rng_pool[rng_pptr++] ^= x & 255; 1241 | rng_pool[rng_pptr++] ^= (x >> 8) & 255; 1242 | rng_pool[rng_pptr++] ^= (x >> 16) & 255; 1243 | rng_pool[rng_pptr++] ^= (x >> 24) & 255; 1244 | if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; 1245 | } 1246 | 1247 | // Mix in the current time (w/milliseconds) into the pool 1248 | function rng_seed_time() { 1249 | rng_seed_int(new Date().getTime()); 1250 | } 1251 | 1252 | // Initialize the pool with junk if needed. 1253 | if(rng_pool == null) { 1254 | rng_pool = new Array(); 1255 | rng_pptr = 0; 1256 | var global = typeof window !== 'undefined' ? window : this; 1257 | var t; 1258 | if (global && global.crypto && global.crypto.getRandomValues) { 1259 | // Use webcrypto if available 1260 | var ua = new Uint8Array(32); 1261 | global.crypto.getRandomValues(ua); 1262 | for(t = 0; t < 32; ++t) 1263 | rng_pool[rng_pptr++] = ua[t]; 1264 | } 1265 | if(navigator.appName == "Netscape" && navigator.appVersion < "5" && global && global.crypto) { 1266 | // Extract entropy (256 bits) from NS4 RNG if available 1267 | var z = global.crypto.random(32); 1268 | for(t = 0; t < z.length; ++t) 1269 | rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; 1270 | } 1271 | while(rng_pptr < rng_psize) { // extract some randomness from Math.random() 1272 | t = Math.floor(65536 * Math.random()); 1273 | rng_pool[rng_pptr++] = t >>> 8; 1274 | rng_pool[rng_pptr++] = t & 255; 1275 | } 1276 | rng_pptr = 0; 1277 | rng_seed_time(); 1278 | //rng_seed_int(window.screenX); 1279 | //rng_seed_int(window.screenY); 1280 | } 1281 | 1282 | function rng_get_byte() { 1283 | if(rng_state == null) { 1284 | rng_seed_time(); 1285 | rng_state = prng_newstate(); 1286 | rng_state.init(rng_pool); 1287 | for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) 1288 | rng_pool[rng_pptr] = 0; 1289 | rng_pptr = 0; 1290 | //rng_pool = null; 1291 | } 1292 | // TODO: allow reseeding after first request 1293 | return rng_state.next(); 1294 | } 1295 | 1296 | function rng_get_bytes(ba) { 1297 | var i; 1298 | for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); 1299 | } 1300 | 1301 | export function SecureRandom() {} 1302 | 1303 | SecureRandom.prototype.nextBytes = rng_get_bytes; 1304 | 1305 | // prng4.js - uses Arcfour as a PRNG 1306 | 1307 | function Arcfour() { 1308 | this.i = 0; 1309 | this.j = 0; 1310 | this.S = new Array(); 1311 | } 1312 | 1313 | // Initialize arcfour context from key, an array of ints, each from [0..255] 1314 | function ARC4init(key) { 1315 | var i, j, t; 1316 | for(i = 0; i < 256; ++i) 1317 | this.S[i] = i; 1318 | j = 0; 1319 | for(i = 0; i < 256; ++i) { 1320 | j = (j + this.S[i] + key[i % key.length]) & 255; 1321 | t = this.S[i]; 1322 | this.S[i] = this.S[j]; 1323 | this.S[j] = t; 1324 | } 1325 | this.i = 0; 1326 | this.j = 0; 1327 | } 1328 | 1329 | function ARC4next() { 1330 | var t; 1331 | this.i = (this.i + 1) & 255; 1332 | this.j = (this.j + this.S[this.i]) & 255; 1333 | t = this.S[this.i]; 1334 | this.S[this.i] = this.S[this.j]; 1335 | this.S[this.j] = t; 1336 | return this.S[(t + this.S[this.i]) & 255]; 1337 | } 1338 | 1339 | Arcfour.prototype.init = ARC4init; 1340 | Arcfour.prototype.next = ARC4next; 1341 | 1342 | // Plug in your RNG constructor here 1343 | function prng_newstate() { 1344 | return new Arcfour(); 1345 | } 1346 | 1347 | // Pool size must be a multiple of 4 and greater than 32. 1348 | // An array of bytes the size of the pool will be passed to init() 1349 | var rng_psize = 256; 1350 | --------------------------------------------------------------------------------