Code Monkey home page Code Monkey logo

react-native-webview-crypto's Introduction

webview-crypto

Build Status npm Dependency Status

Build Status

This repo provides some helper tools to run the Web Cryptography API in a WebView.

It is used in react-native-webview-crypto, nativescript-webview-crypto, and nativescript-angular-webview-crypto. It is not meant to be used directly, but simply serves as a common building block for those libraries.

Why?

The Web Cryptography API is implemented in all major browsers and provides performant and secure way of doing client side encryption in JavaScript. However it is not supported in NativeScript or React Native, which limits them from using Javascript libraries that depend on Web Crypto.

Luckily, the iOS and Android browser engines do support this API. We can use their implementations by creating a WebView and communicating with it asynchronously.

Usage

We provide two entrypoints in this repo.

Main Thread

MainWorker is used in your main thread. It communicates to the WebView asynchronously with string messages, providing a crypto attribute that fulfills the Crypto interface. If you set this to be globally defined, all applications that depend on window.crypto will work transperently.

import {MainWorker} from "webview-crypto";

function sendToWebView(message: string): void {
  // sends `message` to the webview
}

var mw = new MainWorker(sendToWebView); // optional second argument for debug on or off

// call `mw.onWebViewMessage` whenever you get a message from the WebView
onWebViewMessage(mv.onWebViewMessage.bind(mv));

mw.crypto.subtle.generateKey(
  // whatever
)

window.crypto = mw.crypto;

WebView

WebViewWorkerSource is a string that contains the source defining a WebViewWorker constructor that should be used in your WebView.

After loading that Javascript in the WebView, initialize WebViewWorker so that it can communicate with the main thread and do the work of executing the cryptography.

function sendToMain(message: string): void {
  // send `message` to the main thread
}
var wvw = new WebViewWorker(sendToMain);

// call `wvw.onMainMessage` whenever you get a message from the main thread
onMainMessage(wvw.onMainMessage.bind(wvw));

Tests

We have some unit tests for basic behavior here. Run npm run test:local to run them in a local browser. You also need to run npm run build:watch to recompute the webViewWorkerString injected as needed.

In Travis CI, they run on iOS, Android, and Chrome through SauceLabs.

While these tests do help catch some bugs, they do not provide any strong reassurance that this library will work in React Native and Typescript. That's because on those platforms, half the code is running in a WebView and the other half in their native JavaScript engine, which is either JavaScriptCore or V8. I haven't come up with a way to test this in an automated fashion.

So in addition to local unit tests, all code changes that might break something should be tested against the example repos (React Native and NativeScript) on both iOS and Android.

I welcome suggestions on improving this process and making it more automated.

Caveats

While this attempts to as stick to the Web Cryptography API as possible, this is impossible in a few situations due to the differing browser implementations.

Incomplete Support

This library is limited by the mobile browser's support. On iOS, the WebView's use WebKit, which has limited and incomplete support (example). If something isn't working, that might be why. Try it on Safari and see if it works there.

getRandomValues

Since this uses an asynchronous bridge to execute the crypto logic it can't quite execute crypto.getRandomValues correctly, because that method returns a value synchronously. It is simply impossible (as far as I know, please let me know if there any ways to get around this) to wait for the bridge to respond asynchronously before returning a value.

Instead, we add a _promise attribute to the TypedArray you passed in. This resolves when the TypedArray has been filled with random values.

Also, on all crypto.subtle methods that takes in TypedArrays, we will automatically wait for it to resolve. This means that if you are using the TypedArray in further cryptographic code, it will work transparently. So hopefully existing code that uses the Web Cryptography API will continue to work without modification.

CryptoKey

Since JavaScriptCore does not support window.Crypto, it also doesn't have a CryptoKey interface. So instead of returning an actual CryptoKey from subtle.generateKey() we instead return an object that confirms to the CryptoKey interface and has a _import property that has the value of the key exported as jwk or using the value for importing the key. This allows you to treat the CryptoKey as you would normally, and whenever you need to use it in some subtle method, we will automatically convert it back to a real CryptoKey from the _import string and the metadata.

This project was funded by Burke Software and Consulting LLC for passit.

react-native-webview-crypto's People

Contributors

fitouch avatar ofadiman avatar olehs avatar saulshanabrook avatar sirpy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-webview-crypto's Issues

Signing fails on ios for unknown reason

We have created a small sample app in the repo: https://github.com/svinayiw/webcrypto
to demonstrate this problem. The app runs without error on android, but when running on ios, it generates the error:

The operation failed for an operation-specific reason

We have used this bit of code on several platforms (web, node.js and react-native). It only seems to fail under react-native on ios. Can you help us understand:

  • Is this a bug/problem in react-native-webview-crypto or perhaps in our usage of it?
  • Is there a way to get a more descriptive error back from the method running in the webview

Sorry, there's some unnecessary junk in utils/crypto.ts building up the message to send to subtle.sign. I've tried setting message to something simpler, like just the string "This is a test message" and I still get the same error.

To replicate:
npm start
npm run ios
In the emulator, press the "Sign key" button
Observe the error message in the console

Can try on android:
hpm start
npm run android
Press button and no error message (and by the way, signing works correctly for us on android)

Could not resolve dependency with react-native 0.63.4

npm ERR! node_modules/react-native
npm ERR!   react-native@"0.63.4" from the root project
npm ERR!   peer react-native@">=0.56" from [email protected]
npm ERR!   node_modules/react-native-webview-crypto
npm ERR!     react-native-webview-crypto@"*" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer react-native@">=0.60 <0.62" from [email protected]
npm ERR! node_modules/react-native-webview
npm ERR!   react-native-webview@"*" from the root project
npm ERR!   peer react-native-webview@"^8.*" from [email protected]
npm ERR!   node_modules/react-native-webview-crypto
npm ERR!     react-native-webview-crypto@"*" from the root project

Is it possible to get this working with the latest react native?

InvalidAccessError: key is not extractable

When running generateKey with extractable set to false:

await generateKey({"name": "ECDSA", "namedCurve": "P-256"}, false, ["sign", "verify"])

the following error is always thrown:

stringify error InvalidAccessError: key is not extractable

This was encountered by others too: smallbets/userbase#275

The above code should generate a key pair with an unextractable private key.

Compatibility with React Native 0.69.5

I'm having issues on current React Native (0.69.5). Is the package being actively maintained? Open to P/R's? Available to ask questions?

Error:
Invariant Violation: EdgeInsetsPropType has been removed from React Native. Migrate to EdgeInsetsPropType exported from 'deprecated-react-native-prop-types'.

Possible Unhandled Promise Rejection when using crypto.subtle.decrypt on iOS / Android

I'm trying to decrypt data that I received from BE, I'm using the same generated aesKey for that and when I'm trying to call the decrypt method
await crypto.subtle.decrypt( { name: "AES-GCM", iv: iv, additionalData: authData }, aesKey, authTag)
then I receive an warning:
Possible Unhandled Promise Rejection (id: 12): Object { "message": "The operation failed for an operation-specific reason", "name": "OperationError", "stack": "", }
It occurs only on iOS / Android, when I'm using the same methods on web itself then it works perfectly. Is there any limitation due to using crypto in webview on mobile apps?

Solving the getRandomValues problem

One thing that I would like to change asap is that getRandomValues currently doesn't fill the buffer immediately. I can see why the current approach was done, but I think that it's a security bug waiting to happen.

Some alternatives as I see them:

  1. Build a native module that exports an initial seed derived from a secure RNG, then use that seed in a JavaScript implemented RNG that we call when getRandomValues is called. If done correctly, this should keep the properties of the underlying secure RNG.

  2. Keep a pool of random data that asynchronously fills by polling the webview for more data. If getRandomValue is called when there isn't enough random data, throw an error.

  3. Add getRandomValues directly into React Native as a pull request.

I would love to see option 3 here, but realistically I think that option 2 is the easiest to implement short term. Option 1 would require very much scrutiny and tests to make sure that our RNG maintains the security properties of the underlying. Basically, we would need to check which RNG e.g. Android uses and implement the same one in JavaScript.

I think I'll try to take a stab at option 2, since it should be a quick fix.

crypto.subtle.x() functions stuck when called on Expo Android

Hi! Thank you for checking out this issue! To keep it brief:

I'm staring a bare project using expo init, then follow this tutorial to fix Webview-Crypto imports. Using the latest version of react-native-webview-crypto at 0.0.24.

On iOS (hardware and emulator), when calling crypto.subtle.x() functions (eg generateKey()), they execute very quickly and efficiently.

On Android (hardware and emulator), the MainWorker seems to get stuck when executing the same code:

App.js looks like this:

<SafeAreaProvider>
      <PolyfillCrypto debug={(verbose) => console.log(verbose)} />
      <AuthProvider>
            <Navigator/>
      </AuthProvider>
</SafeAreaProvider>

Upon calling a crypt.subtle function, I can see the message being sent to the WebView, but nothing is printed after that. So when executing the following...

const hm = await global.crypto.subtle.generateKey({
            name: "RSA-OAEP",
            modulusLength: 4096,
            publicExponent: new Uint8Array([1, 0, 1]),
            hash: "SHA-256"
      },
      true,
      ["encrypt", "decrypt"]
)

I'm seeing the following (via the verbose console-debug-print in PolyfillCrypto), meaning that PolyfillCrypto is mounted.

[webview-crypto] Sending message: {"method":"subtle.generateKey","args":[{"name":"RSA-OAEP","modulusLength":4096,"publicExponent":{"0":1,"1":0,"2":1},"hash":"SHA-256"},true,["encrypt","decrypt"]],"payloadObject":{"method":"subtle.generateKey","id":"494f-a099-9147-18cb-7986-e1b7-7d03-515a","args":[{"name":"RSA-OAEP","modulusLength":4096,"publicExponent":{"0":1,"1":0,"2":1},"hash":"SHA-256"},true,["encrypt","decrypt"]]}}

On iOS, following above message, I see many more messages, and a generated key. On Android the call gets stuck.

The only thing that I haven't taken care of is the blank.html asset, mentioned in the README. Though when looking at the code in the latest version, the part of the code that requires that is commented out. Anyhow, I tried creating the blank.html page, and putting it in different places to no avail (Expo managed workflow has no separate android/.../src/assets folder).

Any ideas on how to make the PolyfillCrypto work for Expo Android? I've tested this behaviour on 3 different android phones, and two android emulators. Each time a subtle function is being called, the call gets stuck.

Thank you for your time and help! ๐Ÿ™

FEATURE: batch calls into the webview

me and @sleaper we develop an app which makes a lot of concurrent calls into the webview. Hundreds, possibly thousands at once.
Would be really cool if this could batch those into a single payload to avoid the overhead of sending hundreds of separate payloads there and back.

Webview-crypto won't work with last versions of Chrome webview

From https://www.chromium.org/blink/webcrypto

Access to the WebCrypto API is restricted to secure origins (which is to say https:// pages).

When react-native-webview-crypto creates the WebViewBridge it specifies for URI about:blank which isn't considered secure by chrome (And so most of newest androids) - As of now, if you open about:blank page in chrome (72.0), open the console and run window.crypto.subtle you'll get undefined. If you try the same on any HTTPS site you'll get the crypto API

A possible workaround would be for WebViewBridge to open a HTTPS website... but then that means that the crypto module will need an internet connection to run.

Is there any other way for the webview to accept an offline site as secure?

window.crypto.subtle is broken on iOS 15 Beta 3 onwards

Hi Folks,

I use the react native webview component to load up my log in screen on the Mobile application. With the latest Safari on iOS update in iOS 15, there is a need to make the webview load in a secure context. I use https:// to load the webview.
I use the PolyfillCrypto from this library which enables me to use window.crypto.subtle.
But when I try to use window.crypto.subtle.generateKey function, the method throws an exception saying undefined is not an object (evaluating 'a.subtle()[f]')

Is there some way I can load the webview in a secure context other than just having a https session?

Link from Apple team: https://trac.webkit.org/changeset/279628/webkit

Any help appreciated. Thanks.

Storing image data base64

@sirpy I got an error {"message": "exception in stringify-ing message: subtle.digest c796-dd57-8df1-2fcc-95f4-61f6-8e36-10d9", "reason": [RangeError: Maximum call stack size exceeded.]} when I tried to store image data base64 in gunDB user graph. It works fine in web. Do you have any idea how to fix it?

Encrypting Base64 Image data fails with "Maximum call stack size exceed"

Hi there,

As this closed issue points out #22

I am getting an error:

{
  "message": "exception in stringify-ing message: subtle.encrypt 6fd0-0d4a-2fda-9ba2-b4f4-86fc-dfc3-f529", 
  "reason": [RangeError: Maximum call stack size exceeded.]
}

The exception is thrown by this piece of code:

const cipherText = await crypto.subtle.encrypt(
    {
        name: "AES-CBC",
        iv: IV,
    },
    sessionKey,
    Buffer.from(message)
)

Where sessionKey is a CryptoKey and message is a JSON object as follows:

const message = JSON.stringify({
    type: "IMG",
    message: '<base64 JPG string 583,608 characters long>'
})

I am using the following versions:

  "react": "^18.0.0",
  "react-native": "^0.69.7",
  "buffer": "^6.0.3",
  "react-native-webview": "^11.23.1",
  "react-native-webview-crypto": "^0.0.25",

Any advice?

Thanks
Francesco

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.