Selaa lähdekoodia

GramJS: Remove Node `crypto` dependency

painor 5 vuotta sitten
vanhempi
commit
99aa117944

+ 18 - 25
src/lib/gramjs/Helpers.js

@@ -1,4 +1,4 @@
-const crypto = require('crypto')
+const crypto = require('./crypto/crypto')
 const BigInt = require('big-integer')
 
 
@@ -102,7 +102,7 @@ function bigIntMod(n,m) {
  * @returns {Buffer}
  */
 function generateRandomBytes(count) {
-    return crypto.randomBytes(count)
+    return Buffer.from(crypto.randomBytes(count))
 }
 
 
@@ -114,40 +114,33 @@ function generateRandomBytes(count) {
  * @returns {{iv: Buffer, key: Buffer}}
  */
 
-function calcKey(sharedKey, msgKey, client) {
+async function calcKey(sharedKey, msgKey, client) {
     const x = client === true ? 0 : 8
-    const sha1a = sha1(Buffer.concat([ msgKey, sharedKey.slice(x, x + 32) ]))
-    const sha1b = sha1(
-        Buffer.concat([ sharedKey.slice(x + 32, x + 48), msgKey, sharedKey.slice(x + 48, x + 64) ]),
-    )
-    const sha1c = sha1(Buffer.concat([ sharedKey.slice(x + 64, x + 96), msgKey ]))
-    const sha1d = sha1(Buffer.concat([ msgKey, sharedKey.slice(x + 96, x + 128) ]))
+    const [sha1a, sha1b, sha1c, sha1d] = await Promise.all([
+        sha1(Buffer.concat([ msgKey, sharedKey.slice(x, x + 32) ])),
+        sha1(Buffer.concat([ sharedKey.slice(x + 32, x + 48), msgKey, sharedKey.slice(x + 48, x + 64) ])),
+        sha1(Buffer.concat([ sharedKey.slice(x + 64, x + 96), msgKey ])),
+        sha1(Buffer.concat([ msgKey, sharedKey.slice(x + 96, x + 128) ]))
+    ])
     const key = Buffer.concat([ sha1a.slice(0, 8), sha1b.slice(8, 20), sha1c.slice(4, 16) ])
     const iv = Buffer.concat([ sha1a.slice(8, 20), sha1b.slice(0, 8), sha1c.slice(16, 20), sha1d.slice(0, 8) ])
     return { key, iv }
 }
 
-/**
- * Calculates the message key from the given data
- * @param data
- * @returns {Buffer}
- */
-function calcMsgKey(data) {
-    return sha1(data).slice(4, 20)
-}
-
 /**
  * Generates the key data corresponding to the given nonces
  * @param serverNonce
  * @param newNonce
  * @returns {{key: Buffer, iv: Buffer}}
  */
-function generateKeyDataFromNonce(serverNonce, newNonce) {
+async function generateKeyDataFromNonce(serverNonce, newNonce) {
     serverNonce = readBufferFromBigInt(serverNonce, 16, true, true)
     newNonce = readBufferFromBigInt(newNonce, 32, true, true)
-    const hash1 = sha1(Buffer.concat([ newNonce, serverNonce ]))
-    const hash2 = sha1(Buffer.concat([ serverNonce, newNonce ]))
-    const hash3 = sha1(Buffer.concat([ newNonce, newNonce ]))
+    const [hash1, hash2, hash3] = await Promise.all([
+        sha1(Buffer.concat([ newNonce, serverNonce ])),
+        sha1(Buffer.concat([ serverNonce, newNonce ])),
+        sha1(Buffer.concat([ newNonce, newNonce ]))
+    ])
     const keyBuffer = Buffer.concat([ hash1, hash2.slice(0, 12) ])
     const ivBuffer = Buffer.concat([ hash2.slice(12, 20), hash3, newNonce.slice(0, 4) ])
     return { key: keyBuffer, iv: ivBuffer }
@@ -156,7 +149,7 @@ function generateKeyDataFromNonce(serverNonce, newNonce) {
 /**
  * Calculates the SHA1 digest for the given data
  * @param data
- * @returns {Buffer}
+ * @returns {Promise}
  */
 function sha1(data) {
     const shaSum = crypto.createHash('sha1')
@@ -164,10 +157,11 @@ function sha1(data) {
     return shaSum.digest()
 }
 
+
 /**
  * Calculates the SHA256 digest for the given data
  * @param data
- * @returns {Buffer}
+ * @returns {Promise}
  */
 function sha256(data) {
     const shaSum = crypto.createHash('sha256')
@@ -288,7 +282,6 @@ module.exports = {
     crc32,
     generateRandomBytes,
     calcKey,
-    calcMsgKey,
     generateKeyDataFromNonce,
     sha1,
     sha256,

+ 26 - 19
src/lib/gramjs/Password.js

@@ -2,7 +2,7 @@ const Factorizator = require('./crypto/Factorizator')
 const { constructors } = require('./tl')
 const { readBigIntFromBuffer, readBufferFromBigInt, sha256, bigIntMod, modExp,
     generateRandomBytes } = require('./Helpers')
-const crypto = require('crypto')
+const crypto = require('./crypto/crypto')
 const SIZE_FOR_HASH = 256
 
 /**
@@ -148,8 +148,9 @@ function xor(a, b) {
  * @param iterations{number}
  * @returns {*}
  */
+
 function pbkdf2sha512(password, salt, iterations) {
-    return crypto.pbkdf2Sync(password, salt, iterations, 64, 'sha512')
+    return crypto.pbkdf2(password, salt, iterations, 64, 'sha512')
 }
 
 /**
@@ -158,10 +159,10 @@ function pbkdf2sha512(password, salt, iterations) {
  * @param password
  * @returns {Buffer|*}
  */
-function computeHash(algo, password) {
-    const hash1 = sha256(Buffer.concat([ algo.salt1, Buffer.from(password, 'utf-8'), algo.salt1 ]))
-    const hash2 = sha256(Buffer.concat([ algo.salt2, hash1, algo.salt2 ]))
-    const hash3 = pbkdf2sha512(hash2, algo.salt1, 100000)
+async function computeHash(algo, password) {
+    const hash1 = await sha256(Buffer.concat([ algo.salt1, Buffer.from(password, 'utf-8'), algo.salt1 ]))
+    const hash2 = await sha256(Buffer.concat([ algo.salt2, hash1, algo.salt2 ]))
+    const hash3 = await pbkdf2sha512(hash2, algo.salt1, 100000)
     return sha256(Buffer.concat([ algo.salt2, hash3, algo.salt2 ]))
 }
 
@@ -170,7 +171,7 @@ function computeHash(algo, password) {
  * @param algo {constructors.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow}
  * @param password
  */
-function computeDigest(algo, password) {
+async function computeDigest(algo, password) {
     try {
         checkPrimeAndGood(algo.p, algo.g)
     } catch (e) {
@@ -178,7 +179,7 @@ function computeDigest(algo, password) {
     }
 
     const value = modExp(BigInt(algo.g),
-        readBigIntFromBuffer(computeHash(algo, password), false),
+        readBigIntFromBuffer(await computeHash(algo, password), false),
         readBigIntFromBuffer(algo.p, false))
     return bigNumForHash(value)
 }
@@ -188,13 +189,13 @@ function computeDigest(algo, password) {
  * @param request {constructors.account.Password}
  * @param password {string}
  */
-function computeCheck(request, password) {
+async function computeCheck(request, password) {
     const algo = request.currentAlgo
     if (!(algo instanceof constructors.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)) {
-        throw new Error(`Unsupported password algorithm ${algo.constructor.name}`)
+        throw new Error(`Unsupported password algorithm ${algo.className}`)
     }
 
-    const pwHash = computeHash(algo, password)
+    const pwHash = await computeHash(algo, password)
     const p = readBigIntFromBuffer(algo.p, false)
     const g = algo.g
     const B = readBigIntFromBuffer(request.srp_B, false)
@@ -211,9 +212,9 @@ function computeCheck(request, password) {
     const gForHash = bigNumForHash(g)
     const bForHash = numBytesForHash(request.srp_B)
     const gX = modExp(BigInt(g), x, p)
-    const k = readBigIntFromBuffer(sha256(Buffer.concat([ pForHash, gForHash ])), false)
+    const k = readBigIntFromBuffer(await sha256(Buffer.concat([ pForHash, gForHash ])), false)
     const kgX = bigIntMod(k.multiply(gX),p)
-    const generateAndCheckRandom = () => {
+    const generateAndCheckRandom =async () => {
         const randomSize = 256
         // eslint-disable-next-line no-constant-condition
         while (true) {
@@ -222,14 +223,14 @@ function computeCheck(request, password) {
             const A = modExp(BigInt(g), a, p)
             if (isGoodModExpFirst(A, p)) {
                 const aForHash = bigNumForHash(A)
-                const u = readBigIntFromBuffer(sha256(Buffer.concat([ aForHash, bForHash ])), false)
+                const u = readBigIntFromBuffer(await sha256(Buffer.concat([ aForHash, bForHash ])), false)
                 if (u.greater(BigInt(0))) {
                     return [ a, aForHash, u ]
                 }
             }
         }
     }
-    const [ a, aForHash, u ] = generateAndCheckRandom()
+    const [ a, aForHash, u ] =await  generateAndCheckRandom()
     const gB = bigIntMod(B.subtract(kgX),p)
     if (!isGoodModExpFirst(gB, p)) {
         throw new Error('bad gB')
@@ -238,11 +239,17 @@ function computeCheck(request, password) {
     const ux = u.multiply(x)
     const aUx = a.add(ux)
     const S = modExp(gB, aUx, p)
-    const K = sha256(bigNumForHash(S))
-    const M1 = sha256(Buffer.concat([
-        xor(sha256(pForHash), sha256(gForHash)),
+    const [K, pSha ,gSha, salt1Sha, salt2Sha] = await Promise.all([
+        sha256(bigNumForHash(S)),
+        sha256(pForHash),
+        sha256(gForHash),
         sha256(algo.salt1),
-        sha256(algo.salt2),
+        sha256(algo.salt2)
+    ])
+    const M1 = await sha256(Buffer.concat([
+        xor(pSha,gSha),
+        salt1Sha,
+        salt2Sha,
         aForHash,
         bForHash,
         K,

+ 21 - 14
src/lib/gramjs/client/TelegramClient.js

@@ -2,7 +2,7 @@ const Logger = require('../extensions/Logger')
 const { sleep } = require('../Helpers')
 const errors = require('../errors')
 const MemorySession = require('../sessions/Memory')
-const { addKey } = require('../crypto/RSA')
+const { init: initRSA } = require('../crypto/RSA')
 const Helpers = require('../Helpers')
 const { BinaryWriter } = require('../extensions')
 const utils = require('../Utils')
@@ -116,16 +116,6 @@ class TelegramClient {
         }
         // These will be set later
         this._config = null
-        this._sender = new MTProtoSender(this.session.authKey, {
-            logger: this._log,
-            retries: this._connectionRetries,
-            delay: this._retryDelay,
-            autoReconnect: this._autoReconnect,
-            connectTimeout: this._timeout,
-            authKeyCallback: this._authKeyCallback.bind(this),
-            updateCallback: this._handleUpdate.bind(this),
-
-        })
         this.phoneCodeHashes = []
         this._borrowedSenderPromises = {}
     }
@@ -140,6 +130,18 @@ class TelegramClient {
      * @returns {Promise<void>}
      */
     async connect() {
+        await initRSA()
+        await this.session.load()
+        this._sender = new MTProtoSender(this.session.authKey, {
+            logger: this._log,
+            retries: this._connectionRetries,
+            delay: this._retryDelay,
+            autoReconnect: this._autoReconnect,
+            connectTimeout: this._timeout,
+            authKeyCallback: this._authKeyCallback.bind(this),
+            updateCallback: this._handleUpdate.bind(this),
+
+        })
         const connection = new this._connection(this.session.serverAddress
             , this.session.port, this.session.dcId, this._log)
         if (!await this._sender.connect(connection)) {
@@ -201,7 +203,7 @@ class TelegramClient {
         this.session.setDC(newDc, DC.ipAddress, DC.port)
         // authKey's are associated with a server, which has now changed
         // so it's not valid anymore. Set to None to force recreating it.
-        this._sender.authKey.key = null
+        await this._sender.authKey.setKey(null)
         this.session.authKey = null
         await this.session.save()
         await this.disconnect()
@@ -821,10 +823,9 @@ class TelegramClient {
             }
             if (typeof args.password == 'function') {
                 try {
-                    const pass = await args.password()
                     me = await this.signIn({
                         phone: args.phone,
-                        password: pass,
+                        password: args.password,
                     })
                 } catch (e) {
                     console.log(e)
@@ -869,9 +870,15 @@ class TelegramClient {
         } else if (args.password) {
             for (let i = 0; i < 5; i++) {
                 try {
+<<<<<<< HEAD
                     const pwd = await this.invoke(new requests.account.GetPasswordRequest())
                     result = await this.invoke(new requests.auth.CheckPasswordRequest({
                         password: computeCheck(pwd, args.password),
+=======
+                    const pwd = await this.invoke(new requests.account.GetPassword())
+                    result = await this.invoke(new requests.auth.CheckPassword({
+                        password: await computeCheck(pwd, await args.password()),
+>>>>>>> 783ae306... GramJS: Remove Node `crypto` dependency
                     }))
                     break
                 } catch (err) {

+ 0 - 18
src/lib/gramjs/crypto/AESCTR.js

@@ -1,18 +0,0 @@
-const crypto = require('crypto')
-
-class AESModeCTR {
-    constructor(key, iv) {
-        if (!(key instanceof Buffer) || !(iv instanceof Buffer) || iv.length !== 16) {
-            throw new Error('Key and iv need to be a buffer')
-        }
-        this.cipher = crypto.createCipheriv('AES-256-CTR', key, iv)
-    }
-
-    encrypt(data) {
-        return this.cipher.update(data)
-    }
-
-
-}
-
-module.exports = AESModeCTR

+ 8 - 11
src/lib/gramjs/crypto/AuthKey.js

@@ -1,13 +1,10 @@
 const { sha1, readBufferFromBigInt, readBigIntFromBuffer } = require('../Helpers')
 const BinaryReader = require('../extensions/BinaryReader')
-const { sleep } = require("../Helpers")
+const { sleep } = require('../Helpers')
 
 class AuthKey {
-    constructor(data) {
-        this.key = data
-    }
 
-    set key(value) {
+    async setKey(value) {
         if (!value) {
             this._key = this.auxHash = this.keyId = null
             return
@@ -19,19 +16,19 @@ class AuthKey {
             return
         }
         this._key = value
-        const reader = new BinaryReader(sha1(this._key))
+        const reader = new BinaryReader(Buffer.from(await sha1(this._key)))
         this.auxHash = reader.readLong(false)
         reader.read(4)
         this.keyId = reader.readLong(false)
     }
 
     async waitForKey() {
-        while (!this.key) {
+        while (!this.keyId) {
             await sleep(20)
         }
     }
 
-    get key() {
+    getKey() {
         return this._key
     }
 
@@ -43,7 +40,7 @@ class AuthKey {
      * @param number
      * @returns {bigint}
      */
-    calcNewNonceHash(newNonce, number) {
+    async calcNewNonceHash(newNonce, number) {
         newNonce = readBufferFromBigInt(newNonce, 32, true, true)
         const n = Buffer.alloc(1)
         n.writeUInt8(number, 0)
@@ -51,12 +48,12 @@ class AuthKey {
             Buffer.concat([n, readBufferFromBigInt(this.auxHash, 8, true)])])
 
         // Calculates the message key from the given data
-        const shaData = sha1(data).slice(4, 20)
+        const shaData = (await sha1(data)).slice(4, 20)
         return readBigIntFromBuffer(shaData, true, true)
     }
 
     equals(other) {
-        return other instanceof this.constructor && other.key === this._key
+        return other instanceof this.constructor && other.getKey() === this._key
     }
 }
 

+ 17 - 0
src/lib/gramjs/crypto/CTR.js

@@ -0,0 +1,17 @@
+const crypto = require('./crypto')
+
+class CTR {
+    constructor(key, iv) {
+        if (!Buffer.isBuffer(key) || !Buffer.isBuffer(iv) || iv.length !== 16) {
+            throw new Error('Key and iv need to be a buffer')
+        }
+
+        this.cipher = crypto.createCipheriv('AES-256-CTR', key, iv)
+    }
+
+    encrypt(data) {
+        return Buffer.from(this.cipher.update(data))
+    }
+}
+
+module.exports = CTR

+ 9 - 8
src/lib/gramjs/crypto/AES.js → src/lib/gramjs/crypto/IGE.js

@@ -1,7 +1,7 @@
-const crypto = require('crypto')
+const crypto = require('./crypto')
 const { generateRandomBytes } = require('../Helpers')
 
-class AES {
+class IGE {
     /**
      * Decrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector
      * @param cipherText {Buffer}
@@ -15,9 +15,10 @@ class AES {
         let iv2 = iv.slice(Math.floor(iv.length / 2))
         let plainText = []
         const aes = crypto.createDecipheriv('AES-256-ECB', key, Buffer.alloc(0))
-        aes.setAutoPadding(true)
+        //aes.setAutoPadding(true)
         const blocksCount = Math.floor(cipherText.length / 16)
-        const cipherTextBlock = Buffer.alloc(16).fill(0)
+        const cipherTextBlock = Buffer.alloc(16)
+            .fill(0)
 
         for (let blockIndex = 0; blockIndex < blocksCount; blockIndex++) {
             for (let i = 0; i < 16; i++) {
@@ -25,7 +26,7 @@ class AES {
             }
             //This might be a bug in the crypto module
             aes.update(cipherTextBlock)
-            const plainTextBlock = aes.update(cipherTextBlock)
+            const plainTextBlock =Buffer.from(aes.update(cipherTextBlock))
 
             for (let i = 0; i < 16; i++) {
                 plainTextBlock[i] ^= iv1[i]
@@ -55,7 +56,7 @@ class AES {
         let iv1 = iv.slice(0, Math.floor(iv.length / 2))
         let iv2 = iv.slice(Math.floor(iv.length / 2))
         const aes = crypto.createCipheriv('AES-256-ECB', key, Buffer.alloc(0))
-        aes.setAutoPadding(true)
+        //aes.setAutoPadding(true)
         let cipherText = Buffer.alloc(0)
         const blockCount = Math.floor(plainText.length / 16)
 
@@ -65,7 +66,7 @@ class AES {
             for (let i = 0; i < 16; i++) {
                 plainTextBlock[i] ^= iv1[i]
             }
-            const cipherTextBlock = aes.update(plainTextBlock)
+            const cipherTextBlock = Buffer.from(aes.update(plainTextBlock))
 
             for (let i = 0; i < 16; i++) {
                 cipherTextBlock[i] ^= iv2[i]
@@ -79,4 +80,4 @@ class AES {
     }
 }
 
-module.exports = AES
+module.exports = IGE

+ 15 - 9
src/lib/gramjs/crypto/RSA.js

@@ -10,12 +10,12 @@ const BigInt = require('big-integer')
  * @returns {bigInt.BigInteger|*} its 8-bytes-long fingerprint
  * @private
  */
-function _computeFingerprint(key) {
+async function _computeFingerprint(key) {
 
     const n = serializeBytes(getByteArray(key.n))
     const e = serializeBytes(getByteArray(key.e))
     // Telegram uses the last 8 bytes as the fingerprint
-    const sh = sha1(Buffer.concat([n, e]))
+    const sh = await sha1(Buffer.concat([n, e]))
     return readBigIntFromBuffer(sh.slice(-8), true, true)
 }
 
@@ -23,8 +23,8 @@ function _computeFingerprint(key) {
  * Adds a new public key to be used when encrypting new data is needed
  * @param pub {{n:BigInt,e:BigInt}}
  */
-function addKey(pub) {
-    _serverKeys[_computeFingerprint(pub)] = pub
+async function addKey(pub) {
+    _serverKeys[await _computeFingerprint(pub)] = pub
 }
 
 /**
@@ -35,7 +35,7 @@ function addKey(pub) {
  * @param data the data to be encrypted.
  * @returns {Buffer|*|undefined} the cipher text, or None if no key matching this fingerprint is found.
  */
-function encrypt(fingerprint, data) {
+async function encrypt(fingerprint, data) {
     const key = _serverKeys[fingerprint]
     if (!key) {
         return undefined
@@ -43,7 +43,8 @@ function encrypt(fingerprint, data) {
 
     // len(sha1.digest) is always 20, so we're left with 255 - 20 - x padding
     const rand = generateRandomBytes(235 - data.length)
-    const toEncrypt = Buffer.concat([sha1(data), data, rand])
+
+    const toEncrypt = Buffer.concat([await sha1(data), data, rand])
 
     // rsa module rsa.encrypt adds 11 bits for padding which we don't want
     // rsa module uses rsa.transform.bytes2int(to_encrypt), easier way:
@@ -66,9 +67,14 @@ const publicKeys = [{
     'n': BigInt('24573455207957565047870011785254215390918912369814947541785386299516827003508659346069416840622922416779652050319196701077275060353178142796963682024347858398319926119639265555410256455471016400261630917813337515247954638555325280392998950756512879748873422896798579889820248358636937659872379948616822902110696986481638776226860777480684653756042166610633513404129518040549077551227082262066602286208338952016035637334787564972991208252928951876463555456715923743181359826124083963758009484867346318483872552977652588089928761806897223231500970500186019991032176060579816348322451864584743414550721639495547636008351'),
     'e': 65537
 }]
-for (const pub of publicKeys) {
-    addKey(pub)
+
+async function init() {
+    await Promise.all(publicKeys.map(addKey));
 }
 
-module.exports = { encrypt, addKey }
+module.exports = {
+    encrypt,
+    addKey,
+    init
+}
 

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 60 - 0
src/lib/gramjs/crypto/crypto.js


+ 11 - 9
src/lib/gramjs/network/Authenticator.js

@@ -1,5 +1,5 @@
 const BigInt = require('big-integer')
-const AES = require('../crypto/AES')
+const IGE = require('../crypto/IGE')
 const AuthKey = require('../crypto/AuthKey')
 const Factorizator = require('../crypto/Factorizator')
 const RSA = require('../crypto/RSA')
@@ -55,7 +55,7 @@ async function doAuthentication(sender, log) {
     let cipherText = null
     let targetFingerprint = null
     for (const fingerprint of resPQ.serverPublicKeyFingerprints) {
-        cipherText = RSA.encrypt(fingerprint.toString(), pqInnerData.getBytes())
+        cipherText = await RSA.encrypt(fingerprint.toString(), pqInnerData.getBytes())
         if (cipherText !== null && cipherText !== undefined) {
             targetFingerprint = fingerprint
             break
@@ -87,7 +87,7 @@ async function doAuthentication(sender, log) {
     }
 
     if (serverDhParams instanceof constructors.ServerDHParamsFail) {
-        const sh = Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32, true, true).slice(4, 20))
+        const sh = await Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32, true, true).slice(4, 20))
         const nnh = Helpers.readBigIntFromBuffer(sh, true, true)
         if (serverDhParams.newNonceHash.neq(nnh)) {
             throw new SecurityError('Step 2 invalid DH fail nonce from server')
@@ -100,12 +100,12 @@ async function doAuthentication(sender, log) {
     log.debug('Starting authKey generation step 3')
 
     // Step 3 sending: Complete DH Exchange
-    const { key, iv } = Helpers.generateKeyDataFromNonce(resPQ.serverNonce, newNonce)
+    const { key, iv } = await Helpers.generateKeyDataFromNonce(resPQ.serverNonce, newNonce)
     if (serverDhParams.encryptedAnswer.length % 16 !== 0) {
         // See PR#453
         throw new SecurityError('Step 3 AES block size mismatch')
     }
-    const plainTextAnswer = AES.decryptIge(serverDhParams.encryptedAnswer, key, iv)
+    const plainTextAnswer = IGE.decryptIge(serverDhParams.encryptedAnswer, key, iv)
     const reader = new BinaryReader(plainTextAnswer)
     reader.read(20) // hash sum
     const serverDhInner = reader.tgReadObject()
@@ -134,9 +134,9 @@ async function doAuthentication(sender, log) {
         gB: Helpers.getByteArray(gb, false),
     }).getBytes()
 
-    const clientDdhInnerHashed = Buffer.concat([Helpers.sha1(clientDhInner), clientDhInner])
+    const clientDdhInnerHashed = Buffer.concat([await Helpers.sha1(clientDhInner), clientDhInner])
     // Encryption
-    const clientDhEncrypted = AES.encryptIge(clientDdhInnerHashed, key, iv)
+    const clientDhEncrypted = IGE.encryptIge(clientDdhInnerHashed, key, iv)
     const dhGen = await sender.send(
         new requests.SetClientDHParamsRequest({
             nonce: resPQ.nonce,
@@ -155,10 +155,12 @@ async function doAuthentication(sender, log) {
     if (dhGen.serverNonce.neq(resPQ.serverNonce)) {
         throw new SecurityError(`Step 3 invalid ${name} server nonce from server`)
     }
-    const authKey = new AuthKey(Helpers.getByteArray(gab))
+    const authKey = new AuthKey()
+    await authKey.setKey(Helpers.getByteArray(gab))
+
     const nonceNumber = 1 + nonceTypes.indexOf(dhGen.constructor)
 
-    const newNonceHash = authKey.calcNewNonceHash(newNonce, nonceNumber)
+    const newNonceHash = await authKey.calcNewNonceHash(newNonce, nonceNumber)
     const dhHash = dhGen[`newNonceHash${nonceNumber}`]
 
     if (dhHash.neq(newNonceHash)) {

+ 5 - 3
src/lib/gramjs/network/MTProtoSender.js

@@ -91,7 +91,7 @@ class MTProtoSender {
         /**
          * Preserving the references of the AuthKey and state is important
          */
-        this.authKey = authKey || new AuthKey(null)
+        this.authKey = authKey || new AuthKey()
         this._state = new MTProtoState(this.authKey, this._log)
 
         /**
@@ -223,7 +223,8 @@ class MTProtoSender {
             this._log.debug('New auth_key attempt ...')
             const res = await doAuthentication(plain, this._log)
             this._log.debug('Generated new auth_key successfully')
-            this.authKey.key = res.authKey
+            await this.authKey.setKey(res.authKey)
+
             this._state.time_offset = res.timeOffset
 
             /**
@@ -360,7 +361,8 @@ class MTProtoSender {
                     // return
                 } else if (e instanceof InvalidBufferError) {
                     this._log.info('Broken authorization key; resetting')
-                    this.authKey.key = null
+                    await this.authKey.setKey(null)
+
                     if (this._authKeyCallback) {
                         await this._authKeyCallback(null)
                     }

+ 12 - 11
src/lib/gramjs/network/MTProtoState.js

@@ -1,5 +1,5 @@
 const Helpers = require('../Helpers')
-const AES = require('../crypto/AES')
+const IGE = require('../crypto/IGE')
 const BinaryReader = require('../extensions/BinaryReader')
 const GZIPPacked = require('../tl/core/GZIPPacked')
 const { TLMessage } = require('../tl/core')
@@ -71,11 +71,12 @@ class MTProtoState {
      * @param client
      * @returns {{iv: Buffer, key: Buffer}}
      */
-    _calcKey(authKey, msgKey, client) {
+    async _calcKey(authKey, msgKey, client) {
         const x = client === true ? 0 : 8
-        const sha256a = Helpers.sha256(Buffer.concat([msgKey, authKey.slice(x, x + 36)]))
-        const sha256b = Helpers.sha256(Buffer.concat([authKey.slice(x + 40, x + 76), msgKey]))
-
+        const [sha256a , sha256b] = await Promise.all([
+            Helpers.sha256(Buffer.concat([msgKey, authKey.slice(x, x + 36)])),
+            Helpers.sha256(Buffer.concat([authKey.slice(x + 40, x + 76), msgKey]))
+        ])
         const key = Buffer.concat([sha256a.slice(0, 8), sha256b.slice(8, 24), sha256a.slice(24, 32)])
         const iv = Buffer.concat([sha256b.slice(0, 8), sha256a.slice(8, 24), sha256b.slice(24, 32)])
         return { key, iv }
@@ -121,14 +122,14 @@ class MTProtoState {
         const padding = Helpers.generateRandomBytes(Helpers.mod(-(data.length + 12), 16) + 12)
         // Being substr(what, offset, length); x = 0 for client
         // "msg_key_large = SHA256(substr(auth_key, 88+x, 32) + pt + padding)"
-        const msgKeyLarge = Helpers.sha256(Buffer.concat([this.authKey.key.slice(88, 88 + 32), data, padding]))
+        const msgKeyLarge = await Helpers.sha256(Buffer.concat([this.authKey.getKey().slice(88, 88 + 32), data, padding]))
         // "msg_key = substr (msg_key_large, 8, 16)"
         const msgKey = msgKeyLarge.slice(8, 24)
 
-        const { iv, key } = this._calcKey(this.authKey.key, msgKey, true)
+        const { iv, key } = await this._calcKey(this.authKey.getKey(), msgKey, true)
 
         const keyId = Helpers.readBufferFromBigInt(this.authKey.keyId, 8)
-        return Buffer.concat([keyId, msgKey, AES.encryptIge(Buffer.concat([data, padding]), key, iv)])
+        return Buffer.concat([keyId, msgKey, IGE.encryptIge(Buffer.concat([data, padding]), key, iv)])
     }
 
     /**
@@ -147,13 +148,13 @@ class MTProtoState {
         }
 
         const msgKey = body.slice(8, 24)
-        const { iv, key } = this._calcKey(this.authKey.key, msgKey, false)
-        body = AES.decryptIge(body.slice(24), key, iv)
+        const { iv, key } = await this._calcKey(this.authKey.getKey(), msgKey, false)
+        body = IGE.decryptIge(body.slice(24), key, iv)
 
         // https://core.telegram.org/mtproto/security_guidelines
         // Sections "checking sha256 hash" and "message length"
 
-        const ourKey = Helpers.sha256(Buffer.concat([this.authKey.key.slice(96, 96 + 32), body]))
+        const ourKey = await Helpers.sha256(Buffer.concat([this.authKey.getKey().slice(96, 96 + 32), body]))
 
         if (!msgKey.equals(ourKey.slice(8, 24))) {
             throw new SecurityError('Received msg_key doesn\'t match with expected one')

+ 4 - 3
src/lib/gramjs/network/connection/TCPObfuscated.js

@@ -1,7 +1,7 @@
 const { generateRandomBytes } = require('../../Helpers')
 const { ObfuscatedConnection } = require('./Connection')
 const { AbridgedPacketCodec } = require('./TCPAbridged')
-const AESModeCTR = require('../../crypto/AESCTR')
+const CTR = require('../../crypto/CTR')
 
 class ObfuscatedIO {
     header = null
@@ -45,8 +45,8 @@ class ObfuscatedIO {
         const encryptIv = Buffer.from(random.slice(40, 56))
         const decryptKey = Buffer.from(randomReversed.slice(0, 32))
         const decryptIv = Buffer.from(randomReversed.slice(32, 48))
-        const encryptor = new AESModeCTR(encryptKey, encryptIv)
-        const decryptor = new AESModeCTR(decryptKey, decryptIv)
+        const encryptor = new CTR(encryptKey, encryptIv)
+        const decryptor = new CTR(decryptKey, decryptIv)
 
         random = Buffer.concat([
             Buffer.from(random.slice(0, 56)), packetCodec.obfuscateTag, Buffer.from(random.slice(60)),
@@ -75,3 +75,4 @@ class ConnectionTCPObfuscated extends ObfuscatedConnection {
 module.exports = {
     ConnectionTCPObfuscated,
 }
+

+ 4 - 0
src/lib/gramjs/sessions/Memory.js

@@ -65,6 +65,10 @@ class MemorySession extends Session {
     save() {
     }
 
+    async load() {
+
+    }
+
     delete() {
     }
 

+ 87 - 0
src/lib/gramjs/sessions/StringSession.js

@@ -0,0 +1,87 @@
+const MemorySession = require('./Memory')
+const AuthKey = require('../crypto/AuthKey')
+const BinaryReader = require('../extensions/BinaryReader')
+const CURRENT_VERSION = '1'
+
+
+class StringSession extends MemorySession {
+    /**
+     * This session file can be easily saved and loaded as a string. According
+     * to the initial design, it contains only the data that is necessary for
+     * successful connection and authentication, so takeout ID is not stored.
+
+     * It is thought to be used where you don't want to create any on-disk
+     * files but would still like to be able to save and load existing sessions
+     * by other means.
+
+     * You can use custom `encode` and `decode` functions, if present:
+
+     * `encode` definition must be ``function encode(value: Buffer) -> string:``.
+     * `decode` definition must be ``function decode(value: string) -> Buffer:``.
+     * @param session {string|null}
+     */
+    constructor(session = null) {
+        super()
+        if (session) {
+            if (session[0] !== CURRENT_VERSION) {
+                throw new Error('Not a valid string')
+            }
+            session = session.slice(1)
+            const r = StringSession.decode(session)
+            const reader = new BinaryReader(r)
+            this._dcId = reader.read(1)
+                .readUInt8(0)
+            const serverAddressLen = reader.read(2)
+                .readInt16BE(0)
+            this._serverAddress = String(reader.read(serverAddressLen))
+            this._port = reader.read(2)
+                .readInt16BE(0)
+            this._key = reader.read(-1)
+        }
+    }
+
+    /**
+     * @param x {Buffer}
+     * @returns {string}
+     */
+    static encode(x) {
+        return x.toString('base64')
+    }
+
+    /**
+     * @param x {string}
+     * @returns {Buffer}
+     */
+    static decode(x) {
+        return Buffer.from(x, 'base64')
+    }
+
+    async load() {
+        if (this._key) {
+            this._authKey = new AuthKey()
+            await this._authKey.setKey(this._key)
+        }
+    }
+
+    save() {
+        if (!this.authKey) {
+            return ''
+        }
+        const dcBuffer = Buffer.from([this.dcId])
+        const addressBuffer = Buffer.from(this.serverAddress)
+        const addressLengthBuffer = Buffer.alloc(2)
+        addressLengthBuffer.writeInt16BE(addressBuffer.length, 0)
+        const portBuffer = Buffer.alloc(2)
+        portBuffer.writeInt16BE(this.port, 0)
+
+        return CURRENT_VERSION + StringSession.encode(Buffer.concat([
+            dcBuffer,
+            addressLengthBuffer,
+            addressBuffer,
+            portBuffer,
+            this.authKey.getKey(),
+        ]))
+    }
+}
+
+module.exports = StringSession

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä