Skip to content

Commit 18f93b0

Browse files
committed
Fix 2FA again
Changed SHA from lib to browser's crypto
1 parent 847a504 commit 18f93b0

12 files changed

+103
-43
lines changed

‎babel.config.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const config2 = {
1+
/* const config2 = {
22
"presets": [
33
"@babel/preset-typescript",
44
@@ -38,4 +38,12 @@ const config3 = {
3838
]
3939
};
4040
41-
module.exports = config2;
41+
module.exports = config2; */
42+
43+
module.exports = {
44+
presets: [
45+
['@babel/preset-env', {targets: {node: 'current'}}],
46+
'@babel/preset-typescript',
47+
]/* ,
48+
plugins: ["@babel/plugin-syntax-dynamic-import"] */
49+
};

‎jest.setup.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
import { Crypto } from "@peculiar/webcrypto";
1+
const webCrypto = require('@peculiar/webcrypto');
2+
const textEncoding = require('text-encoding');
23

3-
window.crypto = new Crypto();
4+
window.crypto = new webCrypto.Crypto();
5+
window.TextEncoder = textEncoding.TextEncoder;
6+
7+
const a = 1;

‎package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"serviceworker-webpack-plugin": "^1.0.1",
7272
"style-loader": "^1.3.0",
7373
"terser-webpack-plugin": "^3.1.0",
74+
"text-encoding": "^0.7.0",
7475
"ts-jest": "^24.3.0",
7576
"ts-loader": "^6.2.2",
7677
"typescript": "^3.9.7",

‎src/helpers/bytes.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ export function convertToArrayBuffer(bytes: any | ArrayBuffer | Uint8Array) {
112112
return bytesToArrayBuffer(bytes);
113113
}
114114

115-
export function convertToUint8Array(bytes: Uint8Array | number[]): Uint8Array {
115+
export function convertToUint8Array(bytes: Uint8Array | ArrayBuffer | number[] | string): Uint8Array {
116116
if((bytes as Uint8Array).buffer !== undefined) {
117117
return bytes as Uint8Array;
118+
} else if(typeof(bytes) === 'string') {
119+
return new TextEncoder().encode(bytes);
118120
}
119121

120122
return new Uint8Array(bytes);

‎src/lib/crypto/crypto_methods.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import { convertToArrayBuffer } from "../../helpers/bytes";
22
import type { InputCheckPasswordSRP } from "../../layer";
3-
import type { aesEncryptSync } from "./crypto_utils";
3+
import { aesEncryptSync, aesDecryptSync, sha256HashSync, sha1HashSync, bytesModPow } from "./crypto_utils";
44

55
export default abstract class CryptoWorkerMethods {
66
abstract performTaskWorker<T>(task: string, ...args: any[]): Promise<T>;
77

8-
public sha1Hash(bytes: number[] | ArrayBuffer | Uint8Array): Promise<Uint8Array> {
8+
public sha1Hash(bytes: Parameters<typeof sha1HashSync>[0]): Promise<Uint8Array> {
99
return this.performTaskWorker<Uint8Array>('sha1-hash', bytes);
1010
}
1111

12-
public sha256Hash(bytes: any) {
12+
public sha256Hash(bytes: Parameters<typeof sha256HashSync>[0]) {
1313
return this.performTaskWorker<number[]>('sha256-hash', bytes);
1414
}
1515

1616
public pbkdf2(buffer: Uint8Array, salt: Uint8Array, iterations: number) {
1717
return this.performTaskWorker<ArrayBuffer>('pbkdf2', buffer, salt, iterations);
1818
}
1919

20-
public aesEncrypt(bytes: any, keyBytes: any, ivBytes: any) {
20+
public aesEncrypt(bytes: Parameters<typeof aesEncryptSync>[0],
21+
keyBytes: Parameters<typeof aesEncryptSync>[1],
22+
ivBytes: Parameters<typeof aesEncryptSync>[2]) {
2123
return this.performTaskWorker<ReturnType<typeof aesEncryptSync>>('aes-encrypt', convertToArrayBuffer(bytes),
2224
convertToArrayBuffer(keyBytes), convertToArrayBuffer(ivBytes));
2325
}
2426

25-
public aesDecrypt(encryptedBytes: any, keyBytes: any, ivBytes: any): Promise<ArrayBuffer> {
27+
public aesDecrypt(encryptedBytes: Parameters<typeof aesDecryptSync>[0],
28+
keyBytes: Parameters<typeof aesDecryptSync>[1],
29+
ivBytes: Parameters<typeof aesDecryptSync>[2]): Promise<ArrayBuffer> {
2630
return this.performTaskWorker<ArrayBuffer>('aes-decrypt',
2731
encryptedBytes, keyBytes, ivBytes)
2832
.then(bytes => convertToArrayBuffer(bytes));
@@ -36,8 +40,8 @@ export default abstract class CryptoWorkerMethods {
3640
return this.performTaskWorker<[number[], number[], number]>('factorize', [...bytes]);
3741
}
3842

39-
public modPow(x: any, y: any, m: any) {
40-
return this.performTaskWorker<number[]>('mod-pow', x, y, m);
43+
public modPow(x: Parameters<typeof bytesModPow>[0], y: Parameters<typeof bytesModPow>[1], m: Parameters<typeof bytesModPow>[2]) {
44+
return this.performTaskWorker<ReturnType<typeof bytesModPow>>('mod-pow', x, y, m);
4145
}
4246

4347
public gzipUncompress<T>(bytes: ArrayBuffer, toString?: boolean) {
@@ -47,4 +51,4 @@ export default abstract class CryptoWorkerMethods {
4751
public computeSRP(password: string, state: any, isNew = false): Promise<InputCheckPasswordSRP> {
4852
return this.performTaskWorker('computeSRP', password, state, isNew);
4953
}
50-
}
54+
}

‎src/lib/crypto/crypto_utils.ts

+25-12
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
* https://github.com/zhukov/webogram/blob/master/LICENSE
1010
*/
1111

12-
import sha1 from '@cryptography/sha1';
13-
import sha256 from '@cryptography/sha256';
12+
//import sha1 from '@cryptography/sha1';
13+
//import sha256 from '@cryptography/sha256';
1414
import {IGE} from '@cryptography/aes';
1515

1616
// @ts-ignore
@@ -21,9 +21,11 @@ import {str2bigInt, bpe, equalsInt, greater,
2121
divide_, one, bigInt2str, powMod, bigInt2bytes} from '../../vendor/leemon';//from 'leemon';
2222

2323
import { addPadding } from '../mtproto/bin_utils';
24-
import { bytesToWordss, bytesFromWordss, bytesToHex, bytesFromHex } from '../../helpers/bytes';
24+
import { bytesToWordss, bytesFromWordss, bytesToHex, bytesFromHex, convertToUint8Array } from '../../helpers/bytes';
2525
import { nextRandomInt } from '../../helpers/random';
2626

27+
const subtle = typeof(window) !== 'undefined' && 'crypto' in window ? window.crypto.subtle : self.crypto.subtle;
28+
2729
export function longToBytes(sLong: string): Array<number> {
2830
/* let perf = performance.now();
2931
for(let i = 0; i < 1000000; ++i) {
@@ -45,8 +47,11 @@ export function longToBytes(sLong: string): Array<number> {
4547
return bytes;
4648
}
4749

48-
export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
49-
//console.trace(dT(), 'SHA-1 hash start', bytes);
50+
export function sha1HashSync(bytes: Uint8Array | ArrayBuffer | string) {
51+
return subtle.digest('SHA-1', convertToUint8Array(bytes)).then(b => {
52+
return new Uint8Array(b);
53+
});
54+
/* //console.trace(dT(), 'SHA-1 hash start', bytes);
5055
5156
const hashBytes: number[] = [];
5257
@@ -58,18 +63,27 @@ export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
5863
5964
//console.log(dT(), 'SHA-1 hash finish', hashBytes, bytesToHex(hashBytes));
6065
61-
return new Uint8Array(hashBytes);
66+
return new Uint8Array(hashBytes); */
6267
}
6368

6469
export function sha256HashSync(bytes: Uint8Array | ArrayBuffer | string) {
65-
//console.log(dT(), 'SHA-256 hash start');
70+
return subtle.digest('SHA-256', convertToUint8Array(bytes)).then(b => {
71+
//console.log('legacy', performance.now() - perfS);
72+
return new Uint8Array(b);
73+
});
74+
/* //console.log('SHA-256 hash start');
75+
76+
let perfS = performance.now();
77+
6678
67-
let words = typeof(bytes) === 'string' ? bytes : bytesToWordss(bytes);
79+
let perfD = performance.now();
80+
let words = typeof(bytes) === 'string' ? bytes : bytesToWordss(bytes as any);
6881
let hash = sha256(words);
82+
console.log('darutkin', performance.now() - perfD);
6983
70-
//console.log(dT(), 'SHA-256 hash finish', hash);
84+
//console.log('SHA-256 hash finish', hash, sha256(words, 'hex'));
7185
72-
return bytesFromWordss(hash);
86+
return bytesFromWordss(hash); */
7387
}
7488

7589
export function aesEncryptSync(bytes: ArrayBuffer, keyBytes: ArrayBuffer, ivBytes: ArrayBuffer) {
@@ -114,7 +128,6 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes
114128
}
115129

116130
export async function hash_pbkdf2(/* hasher: 'string', */buffer: any, salt: any, iterations: number) {
117-
let subtle = typeof(window) !== 'undefined' && 'crypto' in window ? window.crypto.subtle : self.crypto.subtle;
118131
// @ts-ignore
119132
let importKey = await subtle.importKey(
120133
"raw", //only "raw" is allowed
@@ -252,7 +265,7 @@ export function pqPrimeLeemon(what: any) {
252265
return [bigInt2bytes(P), bigInt2bytes(Q), it];
253266
}
254267

255-
export function bytesModPow(x: any, y: any, m: any) {
268+
export function bytesModPow(x: number[] | Uint8Array, y: number[] | Uint8Array, m: number[] | Uint8Array) {
256269
try {
257270
var xBigInt = str2bigInt(bytesToHex(x), 16);
258271
var yBigInt = str2bigInt(bytesToHex(y), 16);

‎src/lib/crypto/srp.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ const log = logger('SRP', LogTypes.Error);
1212
//MOUNT_CLASS_TO && Object.assign(MOUNT_CLASS_TO, {str2bigInt, bigInt2str, int2bigInt});
1313

1414
export async function makePasswordHash(password: string, client_salt: Uint8Array, server_salt: Uint8Array): Promise<number[]> {
15-
let clientSaltString = '';
16-
for(let i = 0; i < client_salt.length; i++) clientSaltString += String.fromCharCode(client_salt[i]);
17-
18-
let buffer: any = await CryptoWorker.sha256Hash(clientSaltString + password + clientSaltString);
15+
// ! look into crypto_methods.test.ts
16+
let buffer: any = await CryptoWorker.sha256Hash(bufferConcats(client_salt, new TextEncoder().encode(password), client_salt));
1917
//log('encoded 1', bytesToHex(new Uint8Array(buffer)));
2018

2119
buffer = bufferConcats(server_salt, buffer, server_salt);
@@ -71,7 +69,7 @@ export async function computeSRP(password: string, state: AccountPassword, isNew
7169
//log('computed pw_hash:', pw_hash, x, bytesToHex(new Uint8Array(pw_hash)));
7270

7371
const padArray = function(arr: any[], len: number, fill = 0) {
74-
return Array(len).fill(fill).concat(arr).slice(-len);
72+
return new Uint8Array(Array(len).fill(fill).concat(arr).slice(-len));
7573
};
7674

7775
const pForHash = padArray(bytesFromHex(bigInt2str(p, 16)), 256);

‎src/lib/mtproto/authorizer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ export class Authorizer {
527527
}
528528

529529
//var authKeyHash = sha1BytesSync(authKey),
530-
let authKeyHash = await CryptoWorker.sha1Hash(authKey),
530+
let authKeyHash = await CryptoWorker.sha1Hash(new Uint8Array(authKey)),
531531
authKeyAux = authKeyHash.slice(0, 8),
532532
authKeyId = authKeyHash.slice(-8);
533533

‎src/lib/mtproto/networker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ export default class MTPNetworker {
986986
};
987987
}
988988

989-
public getDecryptedMessage(msgKey: Uint8Array | number[], encryptedData: Uint8Array | number[]): Promise<ArrayBuffer> {
989+
public getDecryptedMessage(msgKey: Uint8Array, encryptedData: Uint8Array): Promise<ArrayBuffer> {
990990
// this.log('get decrypted start')
991991
return this.getAesKeyIv(msgKey, false).then((keyIv) => {
992992
// this.log('after msg key iv')

‎src/lib/storage.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
2727

2828
private keysToSet: Set<keyof Storage> = new Set();
2929
private saveThrottled: () => void;
30-
private saveResolve: () => void;
30+
private saveDeferred = deferredPromise<void>();
3131

3232
constructor(storageOptions: Omit<IDBOptions, 'storeName' | 'stores'> & {stores?: DatabaseStore[], storeName: DatabaseStoreName}) {
3333
this.storage = new IDBStorage(storageOptions);
3434

3535
AppStorage.STORAGES.push(this);
3636

3737
this.saveThrottled = throttle(async() => {
38+
const deferred = this.saveDeferred;
39+
this.saveDeferred = deferredPromise<void>();
40+
3841
if(this.keysToSet.size) {
3942
const keys = Array.from(this.keysToSet.values()) as string[];
4043
this.keysToSet.clear();
@@ -51,10 +54,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
5154
}
5255
}
5356

54-
if(this.saveResolve) {
55-
this.saveResolve();
56-
this.saveResolve = undefined;
57-
}
57+
deferred.resolve();
5858
}, 16, false);
5959

6060
this.getThrottled = throttle(async() => {
@@ -151,9 +151,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
151151
}
152152
}
153153

154-
return new Promise<void>((resolve) => {
155-
this.saveResolve = resolve;
156-
});
154+
return this.saveDeferred;
157155
}
158156

159157
public async delete(key: keyof Storage, saveLocal = false) {

‎src/tests/crypto_methods.test.ts

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { bytesFromArrayBuffer, bytesFromHex } from '../helpers/bytes';
1+
import { bytesFromArrayBuffer, bytesFromHex, bytesToHex } from '../helpers/bytes';
22
import CryptoWorker from '../lib/crypto/cryptoworker';
33

44
test('factorize', () => {
@@ -11,7 +11,8 @@ test('factorize', () => {
1111
});
1212

1313
test('sha1', () => {
14-
CryptoWorker.sha1Hash(bytesFromHex('ec5ac983081eeb1da706316227000000044af6cfb1000000046995dd57000000d55105998729349339eb322d86ec13bc0884f6ba0449d8ecbad0ef574837422579a11a88591796cdcc4c05690da0652462489286450179a635924bcc0ab83848'))
14+
const bytes = new Uint8Array(bytesFromHex('ec5ac983081eeb1da706316227000000044af6cfb1000000046995dd57000000d55105998729349339eb322d86ec13bc0884f6ba0449d8ecbad0ef574837422579a11a88591796cdcc4c05690da0652462489286450179a635924bcc0ab83848'));
15+
CryptoWorker.sha1Hash(bytes)
1516
.then(buffer => {
1617
//console.log(bytesFromArrayBuffer(buffer));
1718

@@ -26,9 +27,34 @@ test('sha1', () => {
2627
});
2728

2829
test('sha256', () => {
29-
CryptoWorker.sha256Hash(new Uint8Array([112, 20, 211, 20, 106, 249, 203, 252, 39, 107, 106, 194, 63, 60, 13, 130, 51, 78, 107, 6, 110, 156, 214, 65, 205, 10, 30, 150, 79, 10, 145, 194, 232, 240, 127, 55, 146, 103, 248, 227, 160, 172, 30, 153, 122, 189, 110, 162, 33, 86, 174, 117])).then(bytes => {
30+
CryptoWorker.sha256Hash(new Uint8Array([112, 20, 211, 20, 106, 249, 203, 252, 39, 107, 106, 194, 63, 60, 13, 130, 51, 78, 107, 6, 110, 156, 214, 65, 205, 10, 30, 150, 79, 10, 145, 194, 232, 240, 127, 55, 146, 103, 248, 227, 160, 172, 30, 153, 122, 189, 110, 162, 33, 86, 174, 117]))
31+
.then(bytes => {
3032
expect(bytes).toEqual(new Uint8Array([158, 59, 39, 247, 130, 244, 235, 160, 16, 249, 34, 114, 67, 171, 203, 208, 187, 72, 217, 106, 253, 62, 195, 242, 52, 118, 99, 72, 221, 29, 203, 95]));
3133
});
34+
35+
const client_salt = new Uint8Array([58, 45, 208, 42, 210, 96, 229, 224, 220, 241, 61, 180, 91, 93, 132, 127, 29, 81, 244, 35, 114, 240, 134, 109, 60, 129, 157, 117, 214, 173, 161, 93, 61, 215, 199, 129, 184, 20, 247, 52]);
36+
37+
// ! ! ! ! ! ! ! ! ! ! THIS IS WRONG WAY TO ENCODE AND CONCAT THEM AFTER ! ! ! ! ! ! ! ! ! ! ! ! !
38+
/* let clientSaltString = '';
39+
for(let i = 0; i < client_salt.length; i++) clientSaltString += String.fromCharCode(client_salt[i]); */
40+
41+
const payload = [
42+
['£', 'b4fe151e413445357b1c0935e7cf04a429492ebd23dc62bfadb2f898c431c1fd'],
43+
['haha', '090b235e9eb8f197f2dd927937222c570396d971222d9009a9189e2b6cc0a2c1'],
44+
['😂😘❤️😍😊😁👁👍🏿', 'f3cd34d2345934e10d95d01c7eae9040a6f3c4e20a02a392078b762d876ece8a'],
45+
['$', '09fc96082d34c2dfc1295d92073b5ea1dc8ef8da95f14dfded011ffb96d3e54b'],
46+
//[clientSaltString + '😂😘❤️😍😊😁👁👍🏿' + clientSaltString, 'c2ac294f00e8ac4db6b94099f2014d763315cb2127b1e1ea178cfc3f302680d0'],
47+
[new Uint8Array(Array.from(client_salt).concat(Array.from(new TextEncoder().encode('😂😘❤️😍😊😁👁👍🏿')), Array.from(client_salt))), 'f11950fb40baf391b06a57e7490c8ad4d99ec0c1516c2bc7e529895296616ea7']
48+
];
49+
50+
payload.forEach(pair => {
51+
//const uint8 = new TextEncoder().encode(pair[0]);
52+
//CryptoWorker.sha256Hash(new Uint8Array(pair[0].split('').map(c => c.charCodeAt(0)))).then(bytes => {
53+
CryptoWorker.sha256Hash(pair[0]).then(bytes => {
54+
const hex = bytesToHex(bytes);
55+
expect(hex).toEqual(pair[1]);
56+
});
57+
});
3258
});
3359

3460
test('pbkdf2', () => {

0 commit comments

Comments
 (0)