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 Uint8Arrays 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.