Helper code for crypto polyfil in a webview
npm install webview-crypto
This repo provides some helper tools to run the Web Cryptography API
in a WebView.
It is used inreact-native-webview-crypto,nativescript-webview-crypto, andnativescript-angular-webview-crypto. It is not meant to be used directly, but simply serves as a common building
block for those libraries.
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.
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.
``javascript
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;
`
WebViewWorkerSource is a string that contains the source definingWebViewWorker
a 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.
`javascript
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));
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 runnpm 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.
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 resolvesTypedArray
when the has been filled with random values.
Also, on all crypto.subtle methods that takes inTypedArrays, we will automatically wait for it to resolve. This means that if you TypedArray
are using the in further cryptographic code, it will work transparently.
So hopefully existing code that uses the Web Cryptography API will continue to work
without modification.
, 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.