mtproto-core
mtproto-core copied to clipboard
Adding react-native environment
This adds a working react-native environment to mtproto-core. I was unable to get things working with the canonical expo-crypto because it does not accept Uint8Array
s as inputs to it's hashing methods, so I grabbed some off-the-shelf modules to polyfill all the required methods and built-ins.
Switched this PR to use text-encoding instead of web-encoding because web-encoding broke after ejecting my expo app.
Friendly bump on this :)
Hello, any updates? :) By the way, can I somehow extract this PR into my own local copy of repository and merge it? Maybe make a patch file from this PR for patch-package? I really want to use it in my project! (expo)
I was able to use this but I ran into some problems:
- You have to replace deprecated asyncstorage from react-native with community package
- Some methods are sooooooo slow. Like crypto.getSRPParams takes 2 minutes just to generate parameters. Maybe I'm doing something wrong but I tried running it in isolation with timer and it takes too much time and this method is required for 2fa. Imagine wasting 5 minutes on trying different passwords in order to log in while official clients and nodejs allows you to make requests almost immediately :(
It seems to be problem with TextEncoder
Benchmark:
class Crypto {
constructor({ SHA1, SHA256, PBKDF2, getRandomBytes }) {
this.SHA1 = SHA1;
this.SHA256 = SHA256;
this.PBKDF2 = PBKDF2;
this.getRandomBytes = getRandomBytes;
this.rsa = new RSA({ SHA1 });
}
async getSRPParams({ g, p, salt1, salt2, gB, password }) {
const H = this.SHA256;
const SH = (data, salt) => {
return this.SHA256(concatBytes(salt, data, salt));
};
const PH1 = async (password, salt1, salt2) => {
return await SH(await SH(password, salt1), salt2);
};
const PH2 = async (password, salt1, salt2) => {
return await SH(
await this.PBKDF2(await PH1(password, salt1, salt2), salt1, 100000),
salt2
);
};
const encoder = new TextEncoder();
// == SECTION 1 == //
const gBigInt = bigInt(g);
const gBytes = bigIntToBytes(gBigInt, 256);
const pBigInt = bytesToBigInt(p);
const aBigInt = bytesToBigInt(this.getRandomBytes(256));
const gABigInt = gBigInt.modPow(aBigInt, pBigInt);
const gABytes = bigIntToBytes(gABigInt);
const gBBytes = bytesToBigInt(gB);
// == SECTION 2 == //
const [k, u, x] = await Promise.all([
H(concatBytes(p, gBytes)),
H(concatBytes(gABytes, gB)),
PH2(encoder.encode(password), salt1, salt2),
]);
// == SECTION 3 == //
const kBigInt = bytesToBigInt(k);
const uBigInt = bytesToBigInt(u);
const xBigInt = bytesToBigInt(x);
const vBigInt = gBigInt.modPow(xBigInt, pBigInt);
const kVBigInt = kBigInt.multiply(vBigInt).mod(pBigInt);
let tBigInt = gBBytes.subtract(kVBigInt).mod(pBigInt);
if (tBigInt.isNegative()) {
tBigInt = tBigInt.add(pBigInt);
}
const sABigInt = tBigInt.modPow(
aBigInt.add(uBigInt.multiply(xBigInt)),
pBigInt
);
const sABytes = bigIntToBytes(sABigInt);
// == SECTION 4 == //
const kA = await H(sABytes);
// == SECTION 5 == //
const M1 = await H(
concatBytes(
xorBytes(await H(p), await H(gBytes)),
await H(salt1),
await H(salt2),
gABytes,
gB,
kA
)
);
// == SECTION 6 == //
return { A: gABytes, M1 };
}
}
Section | Timer |
---|---|
1-2 | 20.97 seconds |
2-3 | 136.16 seconds |
3-4 | 23.15 seconds |
4-5 | 0.01 seconds |
5-6 | 0.00 seconds |
Result: 180.29 seconds (3 minutes)
I ran additional test to find execution time for this line: PH2(encoder.encode(password), salt1, salt2),
:
TextEncoder.encode() function takes 0.01 seconds to complete, and PH2 is 135.61 seconds, so it's a problem with crypto module.
I noticed PBKDF2 takes iterations number as argument in function, which is hardcoded to 100000
. Is there any way to optimize it?
Also account.getPassword method takes about 30 seconds to complete. Maybe it's because I'm running it twice? First time I execute it with new socket, it responds immediately.
Update: I think PBKDF2 doesn't work at all. It always gives me incorrect 2fa password error.