staticrypt icon indicating copy to clipboard operation
staticrypt copied to clipboard

Replace crypto-js by browser native WebCrypto ?

Open robinmoisson opened this issue 8 years ago • 6 comments

Pro/Cons that I see:

Pros

  • no dependencies at all
  • smaller output (not a big issue I think, minified crypto-js is quite lean already)
  • no need to update the lib, always as up to date as the browser

Cons

  • at encryption time: I see it as going from using the crypto implementation of a single library that can be audited to leaving it to each user's browser, so the responsability of checking that their browser is up to date/bugfree on that point falls on each user.

    Makes sense ? Is there any data on the crypto implementation of that API in browsers ?

  • it might be nice to have a cli tool that can be inserted into one's workflow to update the encrypted files automatically. We can't use the browser API for that, crypto-js as a node library could also be used. Though the website and cli could use different tools.

  • probably wider support (for now) for crypto-js but that'd need to be checked and by how much :)

Keeping in mind that it would be possible to use different solutions for encrypting/decrypting: encrypting needs to be the safest option, decrypting the most convenient one.

robinmoisson avatar Jun 17 '17 18:06 robinmoisson

Looks like it is supported for 88% of users: https://caniuse.com/#feat=cryptography

Chrome is only supported over secure connections. That is how the w2c spec is defined though, so other browsers might follow.

Supported algorithms: https://diafygi.github.io/webcrypto-examples/

AES-GCM - encrypt

window.crypto.subtle.encrypt(
    {
        name: "AES-GCM",

        //Don't re-use initialization vectors!
        //Always generate a new iv every time your encrypt!
        //Recommended to use 12 bytes length
        iv: window.crypto.getRandomValues(new Uint8Array(12)),

        //Additional authentication data (optional)
        additionalData: ArrayBuffer,

        //Tag length (optional)
        tagLength: 128, //can be 32, 64, 96, 104, 112, 120 or 128 (default)
    },
    key, //from generateKey or importKey above
    data //ArrayBuffer of data you want to encrypt
)
.then(function(encrypted){
    //returns an ArrayBuffer containing the encrypted data
    console.log(new Uint8Array(encrypted));
})
.catch(function(err){
    console.error(err);
});

AES-GCM - decrypt

window.crypto.subtle.decrypt(
    {
        name: "AES-GCM",
        iv: ArrayBuffer(12), //The initialization vector you used to encrypt
        additionalData: ArrayBuffer, //The addtionalData you used to encrypt (if any)
        tagLength: 128, //The tagLength you used to encrypt (if any)
    },
    key, //from generateKey or importKey above
    data //ArrayBuffer of the data
)
.then(function(decrypted){
    //returns an ArrayBuffer containing the decrypted data
    console.log(new Uint8Array(decrypted));
})
.catch(function(err){
    console.error(err);
});

tarpdalton avatar Feb 25 '18 05:02 tarpdalton

Thanks for the data @tarpdalton! I agree it would be nice to use the webcrypto API - that would solve any potential adblocker misconfiguration and remove one more dependency. From your link local files seem to be considered a secure ressource by chrome which is good news.

The interface is less straightforward to deal with than cryptoJS but I've started looking into the implementation and hope to merge soon.

robinmoisson avatar Mar 05 '18 13:03 robinmoisson

This gist helped me understand the interface better. I'm not great with javascript, but it looks like the steps would be:

Encrypt

  1. encode then digest/hash the password
  2. generate the IV
  3. encode then encrypt the text
  4. encode, convert to hex, and concatenate the IV to the ciphertext

Decrypt

  1. encode then digest/hash the password
  2. split the IV off of the encrypted data
  3. decode then decrypt

web crypto is promise based so it would require extra changes to the code

tarpdalton avatar Mar 19 '18 23:03 tarpdalton

Here is a jsfiddle I got working with a password. It's pretty messy right now but it works. The annoying part is switching all the encodings back and forth.

tarpdalton avatar Mar 20 '18 16:03 tarpdalton

Thanks for the good work @tarpdalton !

Yep, the pain point definitely is the encoding part. The fiddle is quite nice, but when we use asciiToUint8Array we assume the text to encrypt is ascii - if we try to encrypt/decrypt test 🐳 for example we get back test =3.

We could first convert the string to hex (https://stackoverflow.com/questions/21647928/javascript-unicode-string-to-hex) then use the hexStringToUint8Array to pass it to the webcrypto API and I'm guessing that should make us pretty much encoding agnostic.

robinmoisson avatar Mar 21 '18 12:03 robinmoisson

I got it to work for unicode instead of ascii, and I opened a PR #111 https://developers.google.com/web/updates/2014/08/Easier-ArrayBuffer-String-conversion-with-the-Encoding-API

There is still more work to get the CLI to work with webcrypto

tarpdalton avatar Jun 21 '18 18:06 tarpdalton

but when we use asciiToUint8Array we assume the text to encrypt is ascii

The browser has this build in via TextEncoder/TextDecoder.

https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API

tom-sherman avatar Feb 18 '23 22:02 tom-sherman