matrix-org / matrix-js-sdk

Matrix Client-Server SDK for JavaScript
Apache License 2.0
1.56k stars 583 forks source link

Adding `.initRustCrypto()` support to Expo (React Native) #4150

Closed greynewell closed 4 months ago

greynewell commented 6 months ago

Howdy! I've been working to enable initRustCrypto() inside of Expo: https://github.com/wdym-chat/expo-matrix-js-sdk/issues/5

I was able to sideload WASM support inside React Native using Cawfree's package, but ran into an issue: https://github.com/cawfree/react-native-webassembly/issues/28

ERROR  Error: Unable to bind Webassembly to React Native JSI., js engine: hermes 
    at ContextNavigator (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:148978:24)
    at ExpoRoot (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:148934:28)
    at App
    at ErrorToastContainer (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:378338:24)
    at ErrorOverlay
    at withDevTools(ErrorOverlay) (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:377872:27)
    at RCTView
    at View (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:40346:43)
    at RCTView
    at View (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:40346:43)
    at AppContainer (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:40157:36)
    at main(RootComponent) (http://<MY_IP:PORT>/node_modules/expo-router/entry.bundle//&platform=ios&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:123933:28)

I wanted to ask, has anyone here successfully initialized Rust crypto (or even the legacy crypto) in React Native, and if so, how did you achieve that?

I've been researching this issue pretty hard, and the working solutions out there seem to rely on extremely dated libs/runtimes and to be exclusive of Expo, so any suggestions would be welcomed.

greynewell commented 6 months ago

Interestingly, I seem to run into the same roadblock with the legacy initCrypto() after polyfilling crypto, fs, and path with Browserify:

Will try uninstalling cawfree's package to see if I can unblock the legacy path another way.

greynewell commented 6 months ago

Sadly, it appears WebAssembly support is required to load even the legacy Olm based crypto:

 DEBUG  Crypto: Starting up crypto store...
 DEBUG  Crypto: initialising roomlist...
 DEBUG  Crypto: initialising crypto object...
 DEBUG  Crypto: initialising Olm...
 WARN  Aborted(no native wasm support detected)
 ERROR  no native wasm support detected

Will keep digging to see if I can find a workaround.

t3chguy commented 5 months ago

Sadly, it appears WebAssembly support is required to load even the legacy Olm based crypto:

Yes, Olm is a C++ library so cannot be loaded without WASM

Johennes commented 5 months ago

@greynewell I haven't yet attempted this myself but what if you connected olm on the native side? https://github.com/jitsi/react-native-olm appears to go down that route.

Johennes commented 5 months ago

I was able to get https://github.com/jitsi/react-native-olm to compile and run. It only includes the Account and Session classes though and merely a subset of the available methods on either. We'd have to add the remaining methods and classes to match https://gitlab.matrix.org/matrix-org/olm/-/blob/master/javascript/index.d.ts. It's not exactly little work but then again it's probably a matter of diligence given that things should largely follow what already exists in the library.

Additionally to the above, the library also needs fixes for both platforms to work with recent React Native versions. I put in issues with details upstream.

Johennes commented 5 months ago

Sadly, it appears WebAssembly support is required to load even the legacy Olm based crypto:

Yes, Olm is a C++ library so cannot be loaded without WASM

Actually, seems like there is a legacy-legacy version of Olm that does not depend on WASM.

  • Using olm/olm.js will use the WebAssembly version of the library. For environments that do not support WebAssembly, use olm/olm_legacy.js.

https://gitlab.matrix.org/matrix-org/olm/-/blob/master/CHANGELOG.rst#L278-279

Johennes commented 5 months ago

Confirming that decryption works in React Native without WASM by using olm_legacy and https://github.com/margelo/react-native-quick-crypto. 🤩

greynewell commented 5 months ago

@Johennes Care to drop a working example?

Johennes commented 5 months ago

I only have this set up in a private app at the moment, unfortunately, and only in PoC form. I'm also not using Expo, so I'm not sure if what I've done transfers easily. But, for what it's worth, here are the steps I took:

There is more work needed to create a crypto store that persists keys and such across app relaunches (the ones that ship with matrix-js-sdk don't work in React Native). But at least, as a puncture, I was able to decrypt events this way.

richvdh commented 4 months ago

Unfortunately, it's unlikely we'll support the Rust crypto library in an environment that doesn't support WASM. [I don't know if building it as a native node module or something might work in future, but that's not likely to happen in the short term.]

You might be able to get the legacy library working for now, but we'll be removing it soon (https://github.com/element-hq/element-web/issues/26922). https://github.com/element-hq/element-web/issues/26922#issuecomment-2090068611 discusses ways we might be able to improve this in future.

nitesh-habilelabs commented 3 months ago

I only have this set up in a private app at the moment, unfortunately, and only in PoC form. I'm also not using Expo, so I'm not sure if what I've done transfers easily. But, for what it's worth, here are the steps I took:

  • Add @matrix-org/olm & react-native-quick-crypto as a dependency
  • Map crypto imports to react-native-quick-crypto via metro.config.js
resolveRequest: (context, moduleName, platform) => {
  // Map crypto imports from olm to react-native-quick-crypto
  if (moduleName === 'crypto') {
    return context.resolveRequest(context, 'react-native-quick-crypto', platform);
  }
}
  • Import react-native-quick-crypto and set Olm global In index.js
import 'react-native-quick-crypto';
global.Olm = require('@matrix-org/olm/olm_legacy'); <<< Note how this is using olm_legacy
  • Call client.initCrypto(); inbetween createClient and client.startClient
  • Try using client.decryptEventIfNeeded(event) to decrypt new events

There is more work needed to create a crypto store that persists keys and such across app relaunches (the ones that ship with matrix-js-sdk don't work in React Native). But at least, as a puncture, I was able to decrypt events this way.

hi , can you please mention specific version of react-native-quick-crypto which you used. i am also trying to integrate matrix in my react native app. and i am getting error that TypeError: Cannot read property 'getRandomValues' of undefined if i don’t import it in index.js or anywhere. and if i import it then i get the error TypeError: Cannot read property 'slice' of undefined, js engine: hermes and i am unable to solve it. thank you in advance.

My RN Version: 0.72.3 "react-native-quick-crypto": "^0.6.1", "@matrix-org/olm": "^3.2.15",

Johennes commented 3 months ago

We probably shouldn't be discussing on a closed issue but for lack of a better place and future reference:

0.6.1 should have crypto.getRandomValues so your problem is likely a case of making crypto available to your dependencies. I did both, redirect the crypto import and import react-native-quick-crypto in index.js.

That being said, I have since given up on trying to use matrix-js-sdk with React Native as it kept feeling like an uphill battle. There's nothing wrong with matrix-js-sdk. It's just that RN's environment is peculiar enough that trying to use a non-trivial library that wasn't specifically written for RN can be a royal pain in the ****. The further issues I ran into were that https://github.com/matrix-org/matrix-encrypt-attachment doesn't work with RN out of the box and that react-native-quick-crypto has very limited support for crypto.subtle.

I have started an effort to make matrix-rust-sdk available in RN via a turbo module but it's still very far from being usable.