mirror of
https://github.com/amark/gun.git
synced 2025-06-24 23:12:32 +00:00
170 lines
5.1 KiB
JavaScript
170 lines
5.1 KiB
JavaScript
import serializeError from 'serialize-error';
|
|
import { parse, stringify } from './serializeBinary';
|
|
const SUBTLE_METHODS = [
|
|
'encrypt',
|
|
'decrypt',
|
|
'sign',
|
|
'verify',
|
|
'digest',
|
|
'generateKey',
|
|
'deriveKey',
|
|
'deriveBits',
|
|
'importKey',
|
|
'exportKey',
|
|
'wrapKey',
|
|
'unwrapKey',
|
|
];
|
|
/*
|
|
MainWorker provides a `crypto` attribute that proxies method calls
|
|
to the webview.
|
|
|
|
It sends strings to the webview in the format:
|
|
|
|
{
|
|
id: <id>,
|
|
method: getRandomValues | subtle.<method name>,
|
|
args: [<serialized arg>]
|
|
}
|
|
|
|
When the webview succeeds in completeing that method, it gets backs:
|
|
|
|
{
|
|
id: <id>,
|
|
value: <serialized return value>
|
|
}
|
|
|
|
And when it fails:
|
|
|
|
{
|
|
id: <id>,
|
|
reason: <serialized rejected reason>,
|
|
}
|
|
|
|
*/
|
|
export default class MainWorker {
|
|
// sendToWebView should take a string and send that message to the webview
|
|
constructor(sendToWebView, debug = false) {
|
|
this.sendToWebView = sendToWebView;
|
|
this.debug = debug;
|
|
// hold a queue of messages to send, in case someone calls crypto
|
|
// before the webview is initialized
|
|
this.toSend = [];
|
|
this.readyToSend = false;
|
|
// Holds the `resolve` and `reject` function for all the promises
|
|
// we are working on
|
|
this.messages = {};
|
|
}
|
|
get crypto() {
|
|
const callMethod = this.callMethod;
|
|
return {
|
|
subtle: this.subtle,
|
|
getRandomValues: this.getRandomValues.bind(this),
|
|
fake: true,
|
|
};
|
|
}
|
|
get subtle() {
|
|
const s = {};
|
|
for (const m of SUBTLE_METHODS) {
|
|
s[m] = (...args) => {
|
|
return this.callMethod(`subtle.${m}`, args, true);
|
|
};
|
|
}
|
|
return s;
|
|
}
|
|
// http://stackoverflow.com/a/105074/907060
|
|
static uuid() {
|
|
function s4() {
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
.toString(16)
|
|
.substring(1);
|
|
}
|
|
return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`;
|
|
}
|
|
onWebViewMessage(message) {
|
|
// first message just tells us the webview is ready
|
|
if (!this.readyToSend) {
|
|
if (this.debug) {
|
|
console.log('[webview-crypto] Got first message; ready to send');
|
|
}
|
|
this.readyToSend = true;
|
|
for (const m of this.toSend) {
|
|
this.sendToWebView(m);
|
|
}
|
|
return;
|
|
}
|
|
parse(message)
|
|
.then(({ id, value, reason }) => {
|
|
if (this.debug) {
|
|
console.log('[webview-crypto] Received message:', JSON.stringify({
|
|
id,
|
|
value,
|
|
reason,
|
|
}));
|
|
}
|
|
if (!id) {
|
|
console.warn('[webview-crypto] no ID passed back from message:', JSON.stringify(serializeError(reason)));
|
|
return;
|
|
}
|
|
const { resolve, reject } = this.messages[id];
|
|
if (value) {
|
|
resolve(value);
|
|
}
|
|
else {
|
|
reject(reason);
|
|
}
|
|
delete this.messages[id];
|
|
})
|
|
.catch((reason) => {
|
|
console.warn('[webview-crypto] error in `parse` of message:', JSON.stringify(message), 'reason:', JSON.stringify(serializeError(reason)));
|
|
});
|
|
}
|
|
getRandomValues(array) {
|
|
const promise = this.callMethod('getRandomValues', [array], false);
|
|
// make the _promise not enumerable so it isn't JSON stringified,
|
|
// which could lead to an infinite loop with Angular's zone promises
|
|
Object.defineProperty(array, '_promise', {
|
|
value: promise,
|
|
configurable: true,
|
|
enumerable: false,
|
|
writable: true,
|
|
});
|
|
promise.then((updatedArray) => {
|
|
array.set(updatedArray);
|
|
});
|
|
return array;
|
|
}
|
|
callMethod(method, args, waitForArrayBufferView) {
|
|
const id = MainWorker.uuid();
|
|
// store this promise, so we can resolve it when we get a message
|
|
// back from the web view
|
|
const promise = new Promise((resolve, reject) => {
|
|
this.messages[id] = { resolve, reject };
|
|
});
|
|
const payloadObject = { method, id, args };
|
|
if (this.debug) {
|
|
console.log('[webview-crypto] Sending message:', JSON.stringify({
|
|
method,
|
|
args,
|
|
payloadObject,
|
|
}));
|
|
}
|
|
stringify(payloadObject, waitForArrayBufferView)
|
|
.then((message) => {
|
|
if (this.readyToSend) {
|
|
this.sendToWebView(message);
|
|
}
|
|
else {
|
|
this.toSend.push(message);
|
|
}
|
|
})
|
|
.catch((reason) => {
|
|
this.messages[id].reject({
|
|
message: `exception in stringify-ing message: ${method} ${id}`,
|
|
reason,
|
|
});
|
|
delete this.messages[id];
|
|
});
|
|
return promise;
|
|
}
|
|
}
|
|
//# sourceMappingURL=MainWorker.js.map
|