|
|
|
@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
|
|
|
|
|
import { TLSerialization, TLDeserialization } from "./tl_utils"; |
|
|
|
|
import dcConfigurator from "./dcConfigurator"; |
|
|
|
|
import { dT, bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; |
|
|
|
|
import { bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; |
|
|
|
|
import rsaKeysManager from "./rsaKeysManager"; |
|
|
|
|
import timeManager from "./timeManager"; |
|
|
|
|
|
|
|
|
@ -9,6 +9,8 @@ import { BigInteger } from "jsbn";
@@ -9,6 +9,8 @@ import { BigInteger } from "jsbn";
|
|
|
|
|
|
|
|
|
|
import CryptoWorker from "../crypto/cryptoworker"; |
|
|
|
|
|
|
|
|
|
import { logger, LogLevels } from "../polyfill"; |
|
|
|
|
|
|
|
|
|
/* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse(); |
|
|
|
|
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse(); |
|
|
|
|
let fResult: any = new Uint8Array(bytesFromHex('000000000000000001b473a0661b285e480000006324160514e4cd29c585f44e91a5fa110d7297b5c0c4134c84893db5715ecd56af5ed618082182053cc5de91cd00000015c4b51c02000000a5b7f709355fc30b216be86c022bb4c3')); |
|
|
|
@ -59,7 +61,10 @@ export class Authorizer {
@@ -59,7 +61,10 @@ export class Authorizer {
|
|
|
|
|
[dcID: number]: Promise<AuthOptions> |
|
|
|
|
} = {}; |
|
|
|
|
|
|
|
|
|
private log: ReturnType<typeof logger>; |
|
|
|
|
|
|
|
|
|
constructor() { |
|
|
|
|
this.log = logger(`AUTHORIZER`/* , LogLevels.error | LogLevels.log */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public mtpSendPlainRequest(dcID: number, requestArray: Uint8Array) { |
|
|
|
@ -94,10 +99,10 @@ export class Authorizer {
@@ -94,10 +99,10 @@ export class Authorizer {
|
|
|
|
|
transport: transport |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'mtpSendPlainRequest: creating requestPromise'); |
|
|
|
|
this.log('mtpSendPlainRequest: creating requestPromise'); |
|
|
|
|
|
|
|
|
|
return transport.send(resultArray).then(result => { |
|
|
|
|
console.log(dT(), 'mtpSendPlainRequest: in good sector', result); |
|
|
|
|
this.log('mtpSendPlainRequest: in good sector', result); |
|
|
|
|
|
|
|
|
|
if(!result || !result.byteLength) { |
|
|
|
|
return Promise.reject(baseError); |
|
|
|
@ -109,17 +114,17 @@ export class Authorizer {
@@ -109,17 +114,17 @@ export class Authorizer {
|
|
|
|
|
|
|
|
|
|
let deserializer = new TLDeserialization(result, {mtproto: true}); |
|
|
|
|
let auth_key_id = deserializer.fetchLong('auth_key_id'); |
|
|
|
|
if(auth_key_id != 0) console.error('auth_key_id != 0', auth_key_id); |
|
|
|
|
if(auth_key_id != 0) this.log.error('auth_key_id != 0', auth_key_id); |
|
|
|
|
|
|
|
|
|
let msg_id = deserializer.fetchLong('msg_id'); |
|
|
|
|
if(msg_id == 0) console.error('msg_id == 0', msg_id); |
|
|
|
|
if(msg_id == 0) this.log.error('msg_id == 0', msg_id); |
|
|
|
|
|
|
|
|
|
let msg_len = deserializer.fetchInt('msg_len'); |
|
|
|
|
if(!msg_len) console.error('no msg_len', msg_len); |
|
|
|
|
if(!msg_len) this.log.error('no msg_len', msg_len); |
|
|
|
|
|
|
|
|
|
return deserializer; |
|
|
|
|
} catch(e) { |
|
|
|
|
console.error('mtpSendPlainRequest: deserialization went bad', e); |
|
|
|
|
this.log.error('mtpSendPlainRequest: deserialization went bad', e); |
|
|
|
|
let error = Object.assign(baseError, {originalError: e}); |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
@ -142,15 +147,14 @@ export class Authorizer {
@@ -142,15 +147,14 @@ export class Authorizer {
|
|
|
|
|
// need
|
|
|
|
|
rsaKeysManager.prepare().then(() => {}); |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Send req_pq', auth.nonce.hex); |
|
|
|
|
this.log('Send req_pq', auth.nonce.hex); |
|
|
|
|
try { |
|
|
|
|
var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); |
|
|
|
|
} catch(error) { |
|
|
|
|
console.error(dT(), 'req_pq error', error.message); |
|
|
|
|
this.log.error('req_pq error', error.message); |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = deserializer.fetchObject('ResPQ'); |
|
|
|
|
|
|
|
|
|
if(response._ != 'resPQ') { |
|
|
|
@ -158,7 +162,7 @@ export class Authorizer {
@@ -158,7 +162,7 @@ export class Authorizer {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!bytesCmp(auth.nonce, response.nonce)) { |
|
|
|
|
console.log(auth.nonce, response.nonce); |
|
|
|
|
this.log.error(auth.nonce, response.nonce); |
|
|
|
|
throw new Error('[MT] resPQ nonce mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -167,7 +171,7 @@ export class Authorizer {
@@ -167,7 +171,7 @@ export class Authorizer {
|
|
|
|
|
auth.pq = response.pq; |
|
|
|
|
auth.fingerprints = response.server_public_key_fingerprints; |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); |
|
|
|
|
this.log('Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); |
|
|
|
|
|
|
|
|
|
let publicKey = await rsaKeysManager.select(auth.fingerprints); |
|
|
|
|
if(!publicKey) { |
|
|
|
@ -176,19 +180,19 @@ export class Authorizer {
@@ -176,19 +180,19 @@ export class Authorizer {
|
|
|
|
|
|
|
|
|
|
auth.publicKey = publicKey; |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'PQ factorization start', auth.pq); |
|
|
|
|
this.log('PQ factorization start', auth.pq); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
var pAndQ = await CryptoWorker.factorize(auth.pq); |
|
|
|
|
} catch(error) { |
|
|
|
|
console.error('worker error factorize', error); |
|
|
|
|
this.log.error('worker error factorize', error); |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auth.p = pAndQ[0]; |
|
|
|
|
auth.q = pAndQ[1]; |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'PQ factorization done', pAndQ); |
|
|
|
|
this.log('PQ factorization done', pAndQ); |
|
|
|
|
/* let p = new Uint32Array(new Uint8Array(auth.p).buffer)[0]; |
|
|
|
|
let q = new Uint32Array(new Uint8Array(auth.q).buffer)[0]; |
|
|
|
|
console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */ |
|
|
|
@ -253,18 +257,18 @@ export class Authorizer {
@@ -253,18 +257,18 @@ export class Authorizer {
|
|
|
|
|
|
|
|
|
|
let requestBytes = request.getBytes(true); |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Send req_DH_params', req_DH_params/* , requestBytes.hex */); |
|
|
|
|
this.log('Send req_DH_params', req_DH_params/* , requestBytes.hex */); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
var deserializer = await this.mtpSendPlainRequest(auth.dcID, requestBytes); |
|
|
|
|
} catch(error) { |
|
|
|
|
console.log(dT(), 'Send req_DH_params FAIL!', error); |
|
|
|
|
this.log('Send req_DH_params FAIL!', error); |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE'); |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Sent req_DH_params, response:', response); |
|
|
|
|
this.log('Sent req_DH_params, response:', response); |
|
|
|
|
|
|
|
|
|
if(response._ != 'server_DH_params_fail' && response._ != 'server_DH_params_ok') { |
|
|
|
|
throw new Error('[MT] Server_DH_Params response invalid: ' + response._); |
|
|
|
@ -292,7 +296,7 @@ export class Authorizer {
@@ -292,7 +296,7 @@ export class Authorizer {
|
|
|
|
|
try { |
|
|
|
|
await this.mtpDecryptServerDhDataAnswer(auth, response.encrypted_answer); |
|
|
|
|
} catch(e) { |
|
|
|
|
console.error(dT(), 'mtpDecryptServerDhDataAnswer FAILED!', e); |
|
|
|
|
this.log.error('mtpDecryptServerDhDataAnswer FAILED!', e); |
|
|
|
|
throw e; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -342,7 +346,7 @@ export class Authorizer {
@@ -342,7 +346,7 @@ export class Authorizer {
|
|
|
|
|
throw new Error('[MT] server_DH_inner_data serverNonce mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Done decrypting answer'); |
|
|
|
|
this.log('Done decrypting answer'); |
|
|
|
|
auth.g = response.g; |
|
|
|
|
auth.dhPrime = response.dh_prime; |
|
|
|
|
auth.gA = response.g_a; |
|
|
|
@ -362,13 +366,13 @@ export class Authorizer {
@@ -362,13 +366,13 @@ export class Authorizer {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public mtpVerifyDhParams(g: number, dhPrime: any, gA: any) { |
|
|
|
|
console.log(dT(), 'Verifying DH params'); |
|
|
|
|
this.log('Verifying DH params'); |
|
|
|
|
var dhPrimeHex = bytesToHex(dhPrime); |
|
|
|
|
if(g != 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') { |
|
|
|
|
// The verified value is from https://core.telegram.org/mtproto/security_guidelines
|
|
|
|
|
throw new Error('[MT] DH params are not verified: unknown dhPrime'); |
|
|
|
|
} |
|
|
|
|
console.log(dT(), 'dhPrime cmp OK'); |
|
|
|
|
this.log('dhPrime cmp OK'); |
|
|
|
|
|
|
|
|
|
var gABigInt = new BigInteger(bytesToHex(gA), 16); |
|
|
|
|
var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16); |
|
|
|
@ -380,7 +384,7 @@ export class Authorizer {
@@ -380,7 +384,7 @@ export class Authorizer {
|
|
|
|
|
if(gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) { |
|
|
|
|
throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1'); |
|
|
|
|
} |
|
|
|
|
console.log(dT(), '1 < gA < dhPrime-1 OK'); |
|
|
|
|
this.log('1 < gA < dhPrime-1 OK'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var two = new BigInteger(/* null */''); |
|
|
|
@ -393,7 +397,7 @@ export class Authorizer {
@@ -393,7 +397,7 @@ export class Authorizer {
|
|
|
|
|
if(gABigInt.compareTo(dhPrimeBigInt.subtract(twoPow)) >= 0) { |
|
|
|
|
throw new Error('[MT] DH params are not verified: gA > dhPrime - 2^{2048-64}'); |
|
|
|
|
} |
|
|
|
|
console.log(dT(), '2^{2048-64} < gA < dhPrime-2^{2048-64} OK'); |
|
|
|
|
this.log('2^{2048-64} < gA < dhPrime-2^{2048-64} OK'); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -433,7 +437,7 @@ export class Authorizer {
@@ -433,7 +437,7 @@ export class Authorizer {
|
|
|
|
|
encrypted_data: encryptedData |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Send set_client_DH_params'); |
|
|
|
|
this.log('Send set_client_DH_params'); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); |
|
|
|
@ -466,43 +470,43 @@ export class Authorizer {
@@ -466,43 +470,43 @@ export class Authorizer {
|
|
|
|
|
authKeyAux = authKeyHash.slice(0, 8), |
|
|
|
|
authKeyID = authKeyHash.slice(-8); |
|
|
|
|
|
|
|
|
|
console.log(dT(), 'Got Set_client_DH_params_answer', response._, authKey); |
|
|
|
|
this.log('Got Set_client_DH_params_answer', response._, authKey); |
|
|
|
|
switch(response._) { |
|
|
|
|
case 'dh_gen_ok': |
|
|
|
|
var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); |
|
|
|
|
//var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16);
|
|
|
|
|
|
|
|
|
|
if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); |
|
|
|
|
console.log('Auth successfull!', authKeyID, authKey, serverSalt); |
|
|
|
|
|
|
|
|
|
auth.authKeyID = authKeyID; |
|
|
|
|
auth.authKey = authKey; |
|
|
|
|
auth.serverSalt = serverSalt; |
|
|
|
|
|
|
|
|
|
return auth; |
|
|
|
|
break; |
|
|
|
|
var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); |
|
|
|
|
//var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16);
|
|
|
|
|
|
|
|
|
|
if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); |
|
|
|
|
this.log('Auth successfull!', authKeyID, authKey, serverSalt); |
|
|
|
|
|
|
|
|
|
auth.authKeyID = authKeyID; |
|
|
|
|
auth.authKey = authKey; |
|
|
|
|
auth.serverSalt = serverSalt; |
|
|
|
|
|
|
|
|
|
return auth; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'dh_gen_retry': |
|
|
|
|
//var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16);
|
|
|
|
|
var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); |
|
|
|
|
if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return this.mtpSendSetClientDhParams(auth); |
|
|
|
|
//var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16);
|
|
|
|
|
var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); |
|
|
|
|
if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return this.mtpSendSetClientDhParams(auth); |
|
|
|
|
|
|
|
|
|
case 'dh_gen_fail': |
|
|
|
|
//var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16);
|
|
|
|
|
var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); |
|
|
|
|
if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer fail'); |
|
|
|
|
//var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16);
|
|
|
|
|
var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); |
|
|
|
|
if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { |
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
throw new Error('[MT] Set_client_DH_params_answer fail'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|