PeculiarVentures / webcrypto-liner

webcrypto-liner is a polyfill that let's down-level User Agents (like IE/Edge) use libraries that depend on WebCrypto. (Keywords: Javascript, WebCrypto, Shim, Polyfill)
MIT License
149 stars 26 forks source link

Doesn't work in Safari #44

Open borisreitman opened 6 years ago

borisreitman commented 6 years ago

I cloned the project, installed webpack, and ran webpack that generated files in dist/ directory. However, when I try to run the example in examples/src/index.html on Safari, I get an error.

The first error I get is "unable to delete property" on line 3719:

3710 Object.defineProperty(exports, "__esModule", { value: true });
3711 var index_1 = __webpack_require__(6);
3712 var w = self;
3713 // Object.freeze(Math);
3714 // Object.freeze(Math.random);
3715 // Object.freeze((Math as any).imul);
3716 if (index_1.nativeCrypto) {
3717     Object.freeze(index_1.nativeCrypto.getRandomValues);
3718 }
3719 delete self.crypto;
3720 w.crypto = new index_1.Crypto();
3721 Object.freeze(w.crypto);
3722 exports.crypto = w.crypto;
3723 

When I actually try to use the sample, I get the error:

undefined is not an object (evaluating 'crypto.subtle.generateKey')

Safari version: Version 9.1.3 (11601.7.8) on a MacBook Pro, OSX version 10.11.6 (15G1004)

rmhrisk commented 6 years ago

Sounds like you didn't follow the steps in https://github.com/PeculiarVentures/webcrypto-liner#installation

Specifically if your going to run in a browser that doesn't support ES6 you need to be sure to convert to ES2015

borisreitman commented 6 years ago

Ok, I now followed the steps, the error now occurs on a different line, but the error is the same "Unable to delete property".

2413    n.nativeCrypto && Object.freeze(n.nativeCrypto.getRandomValues), delete self.crypto, a.crypto = new n.Crypto, Object.freeze(a.crypto), r.crypto = a.crypto

Basically, if I would try to manually do: delete window.crypto I get the same error. It appears that this property is read-only in Safari. Doing the same in Google Chrome works and gives no errors.

borisreitman commented 6 years ago

I tried this fix, and got past that error,

diff --git a/src/shim.ts b/src/shim.ts
index d136bde..06b6dc2 100644
--- a/src/shim.ts
+++ b/src/shim.ts
@@ -10,8 +10,12 @@ if (nativeCrypto) {
     Object.freeze(nativeCrypto.getRandomValues);
 }

-delete (self as any).crypto;
-w.crypto = new Crypto();
+try {
+  delete (self as any).crypto;
+  w.crypto = new Crypto();
+} catch(e){
+  w.crypto.subtle = new Crypto().subtle;
+}
 Object.freeze(w.crypto);

 export const crypto = w.crypto;

However, when I try to use the sample to "Sign", I get error from App.sign() call, that I tracked down to this line in file subtle.ts:

                if (!key.key) {
                    throw new LinerError("Cannot export native CryptoKey from JS implementation");
                }
rmhrisk commented 6 years ago

Works in Safari without code modification as we use it in several projects, ill have @microshine take a look when he gets online.

borisreitman commented 6 years ago

Hi Ryan ,

Can you recommend something that I can use in Cordova to encrypt with AES-GCM on the mobile phone? Some Cordova plugin perhaps ?

Thanks , Boris

On 2017-12-19 at 7:27 PM, "Ryan Hurst" notifications@github.com wrote:

Works in Safari without code modification as we use it in several projects, ill have @microshine take a look when he gets online.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/PeculiarVentures/webcrypto- liner/issues/44#issuecomment-352954831

rmhrisk commented 6 years ago

Boris,

I am not too familiar with cordova/phonegap but I understand you end up with JS also using this should work.

Ryan

microshine commented 6 years ago

@borisreitman Could you try https://github.com/PeculiarVentures/webcrypto-liner/blob/master/dist/webcrypto-liner.lib.js? This variant of webcrypto-liner doesn't delete native crypto object. This script uses global liner variable which has own crypto object. We use webcrypto-liner.lib.js for Web Worker, because some browsers don't allow to remove native crypto

Example

<script src="https://peculiarventures.github.io/pv-webcrypto-tests/src/asmcrypto.js"></script>
<script src="webcrypto-liner.lib.js"></script>
liner.crypto.subtle.generateKey({name: "AES-GCM", length: 256}, false, ["encrypt"]).then((k) => {
  console.log(k);
})
borisreitman commented 6 years ago

Yes, this version worked fine.

Quick question: does liner work in a browser that has no native window.crypto at all ?

microshine commented 6 years ago

@borisreitman Yes, it does. You can use this script to do it. webcrypto-liner needs getRandomValues for key generation. So if you don't have crypto object you must implement your own getRandomValues

Example

function getRandomArbitrary(min, max)
{
    return self.Math.random() * (max - min) + min;
}

function getRandomValues(buffer)
{
    // TODO: seed random
    const buf = new Uint8Array(buffer.buffer);
    let i = 0;

    while(i < buf.length)
        buf[i++] = getRandomArbitrary(0, 255);

    return buffer;
}

if(!self.crypto) {
    self.crypto = { getRandomValues: getRandomValues };
    Object.freeze(self.crypto);
}
borisreitman commented 6 years ago

For anyone else who may benefit from reading this thread, here's a tip on where to get native random numbers in Cordova.

var crypto = new AeroGear.Crypto();
crypto.getRandomValue();

https://github.com/aerogear/aerogear-cordova-crypto

EDIT: I just checked that both Android Webview and iOS Safari (and probably the iOS Webview) have window.crypto.getRandomValues() and the whole window.crypto.subtle suite. Someone has reported on StackOverflow that Android has no subtle, but it was his error. It's there, as long as user is using https:// to browse a website. And, it is there in the Cordova webview.