kevlened / isomorphic-webcrypto

:game_die: webcrypto library for Node, React Native and IE11+
https://www.w3.org/TR/WebCryptoAPI/
MIT License
116 stars 43 forks source link

RSA generatekey, unsupported algorithm #34

Open davidcallanan opened 4 years ago

davidcallanan commented 4 years ago

This is literally the only package I have found that supports webcrypto with react native...

But I really need to be able to generate the following RSA key (for compatibility with the rest of our system)...

await crypto.subtle.generateKey(
    {
      name: "RSA-PSS",
      hash: "SHA-256",
      modulusLength: 4096,
      publicExponent: new Uint8Array([1, 0, 1]),
    },
    true,
    ["sign", "verify"],
  );

Any plans to support this? I would be really appreciative.

kevlened commented 4 years ago

The react native implementation of WebCrypto is in js, so generating an RSA key would be remarkably slow. The original version of msrCrypto didn't support this, but perhaps the latest version does. I intend to integrate the latest version at some point (no timeline), but you're welcome to test it: https://github.com/microsoft/MSR-JavaScript-Crypto.

davidcallanan commented 4 years ago

If the latest supports it I will definitely test it :)

Even if it takes 20 or 30 seconds, if I generate it the second the app launches and the user doesn't make any action that requires it for that time, it is ok, and I will also be caching the result in localstorage.

kevlened commented 4 years ago

Ah, I see. Because it's all in js, you'll lock RN's js thread for that time. The app would basically freeze. That said, there may be a workaround, but I haven't investigated.

As for the latest msrCrypto, after a brief glance, it appears to support generating RSA keys, but I haven't tried it. If you do, lmk.

davidcallanan commented 4 years ago

The javascript appears to run in a worker, so shouldn't hang the main thread. But I'm having no luck getting the code to run in react native.

kevlened commented 4 years ago

RN doesn’t support workers, so you have to hardcode it to work in a single thread (that’s how I modified the old msrCrypto). I’d test it in a browser. If it works in a browser, I’ll modify it to work in RN.

On Fri, Aug 7, 2020 at 16:20 David Callanan notifications@github.com wrote:

The javascript appears to run in a worker, so shouldn't hang the main thread. But I'm having no luck getting the code to run in react native.

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/kevlened/isomorphic-webcrypto/issues/34#issuecomment-670697166, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHJHMDRRF7C4IBFE6BTC3DR7RO2VANCNFSM4PX5H6RA .

davidcallanan commented 4 years ago

Not looking promising, it's hung my chrome window, no results after a few minutes.

cryptoAlgorithm commented 3 years ago

I hope this method of generation would be implemented soon. It would be really helpful in my project to have full WebCrypto support.

davidcallanan commented 3 years ago

@cryptoAlgorithm If I remember correctly, in the end I just created a rest api for this and ran the crypto code on the backend. Not the best solution, but might be a hackish option for the time being.

cryptoAlgorithm commented 3 years ago

Hmm i guess i could do that, but the entire reason i wrote the app in React Native (other than being cross platform) was that i could mostly reuse code from our web client which is also written in React. Unfortunately, there aren't libraries that support this RSA algorithm, which is a bit disappointing.

kevlened commented 3 years ago

It’s possible to generate these keys in a WebView. The catch is the WebView must be in the render tree (it can be hidden though). Here’s an implementation that renders a hidden webview and proxies WebCrypto calls: https://github.com/webview-crypto/react-native-webview-crypto

cryptoAlgorithm commented 3 years ago

Yeah but if i understand correctly the key generation will still be running in JavaScript but not natively right(?) If thats the case, what's the difference between running it in react native itself?

cryptoAlgorithm commented 3 years ago

Regarding that library, that might help with my issue. I'll give it a try right now

kevlened commented 3 years ago

In browsers, WebCrypto is implemented in native code, you just access it with js. The performance is significantly better.

In React Native, all js is run on the main thread. That means even if an implementation could generate a key in 5 seconds, the UI would freeze for 5 seconds. In addition to faster generation, the proxy method offloads the work from the main thread to the WebView, so your UI remains responsive.

cryptoAlgorithm commented 3 years ago

Yeah I made a mistake in one of my comments. I commented before looking at the library, and assumed that they were running a JS implimentation of webcrypto in a WebView, which I found rather meaningless. Turns out they were using the native browser implimentation. Although now that I've added the libraries, my app no longer compiles for some duplicated method/symbol error...

davidcallanan commented 3 years ago

@kevlened That's a genius idea to use a WebView

davidcallanan commented 3 years ago

@cryptoAlgorithm

Hmm i guess i could do that, but the entire reason i wrote the app in React Native (other than being cross platform) was that i could mostly reuse code from our web client which is also written in React.

This is a big problem I see all the time. For one of my recent projects I decided to obtain all dependencies through dependency injection (passing in dependencies into functions) and I used factory functions to abstract away implementation details of certain dependencies. I refused to ever import a dependency directly (like a singleton).

With that in place, 90% of my code was reusable. I was able to switch my entire frontend to React Native in a day without needing to rewrite the majority of my core logic. (Obviously the UI part was not reusable). For any libraries that were not available in React Native (such as cryptography or fetch api), I would just dependency-inject an alternative library.

When I failed to find a webcrypto implementation, I just quickly wrote a hackish implementation that communicated with my backend to do it, and I didn't have to change any other code in my project. Obviously only a short-term solution, but I think it is very important to organize your code in a way that allows you to easily plug in and out different implementations of dependencies.

(Sorry this is just me ranting about architecture, feel free to ignore this message).

cryptoAlgorithm commented 3 years ago

@davidcallanan that seems like a very interesting approach... Might try it with a future project. But if the RSA keypair is generated in the backend, there are so many things that can go wrong. Like man in the middle attacks, backend vulnerabilities etc. Even if everything could be 100% secure you no longer have truly end to end encryption since the key originates from your server. That's one of the shortcomings that came to my mind when you mentioned this hackish method. (Not trying to be negative or anything, just trying to point out potential security flaws)

davidcallanan commented 3 years ago

@cryptoAlgorithm This idea with the backend is only supposed to be a temporary solution until a better one arises. I was mostly trying to show my approach for code re-use and how you should be able to switch between different solutions as they come to being without sacrificing code re-use. I definitely agree that using a backend defeats the purpose of end-to-end encryption. (In my case I wasn't using it for end-to-end encryption so it didn't affect me).

kevlened commented 3 years ago

@cryptoAlgorithm @davidcallanan The recommended library runs code in the WebView without hitting a remote server. Here's the code for the suggested library (basically loads an empty html file, then injects js that runs client side). The WebView method is more secure than the js version today, because it's built on top of the platform's native crypto APIs.

There is one real limitation to any polyfill I've seen in React Native: non-exportable keys. In a browser, you can generate key pairs where the private portion can't be serialized to a jwk or pem. This prevents an xss from exfiltrating a private key. In the browser, not being able to serialize the key means you have to store the key in IndexedDB if you want to use it in future sessions (IndexedDB can store some objects without serializing them). There is no way to simulate the functionality in React Native unless you use a WebCrypto polyfill built on top of the iOS and Android crypto primitives.

The WebView method is certainly the most secure, reliable, and up-to-date method. I've considered moving this library to use the WebView method by default. The only caveat is the extra step of including the WebView in the render tree. This extra step just becomes cumbersome if you're shipping a library built on top of isomorphic-webcrypto to end-users.

Hope that helps with your decision.

cryptoAlgorithm commented 3 years ago

@kevlened, I've ran into some issues with this WebView WebCrypto approach, namely the fact that Safari on iOS doesn't fully support RSA-OEAP encryption/decryption with SHA-512/SHA-256 hashes. Only SHA-1 is supported, which won't do. I look forward to the day when Safari finally decides to fix this (its a bug that has been around for ages), but for now I can't really use this approach.