sodium-javascript icon indicating copy to clipboard operation
sodium-javascript copied to clipboard

Support for noise-protocol

Open bcomnes opened this issue 5 years ago • 17 comments

It would be great if the noise-protocol module would work with sodium universal.

From what I can gather the following parts of sodium need to be ported:

  • https://download.libsodium.org/doc/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#constants (encrypt and decrypt)
  • https://download.libsodium.org/doc/key_exchange#constants

The key exchange looks relatively straight forward, the crypto_aead_xchacha20poly1305_ietf end of things looks a bit more involved (e.g. reimplementing https://github.com/jedisct1/libsodium/blob/927dfe8e2eaa86160d3ba12a7e3258fbc322909c/src/libsodium/crypto_core/hchacha20/core_hchacha20.c or doing some kind of WAT rewrite like xsalsa20 ?

@emilbayes do you have any pointers on how you would go about doing that?

bcomnes avatar Jan 08 '20 02:01 bcomnes

I started some work on the easy parts here: https://github.com/bcomnes/sodium-javascript/tree/noise-support

bcomnes avatar Jan 08 '20 02:01 bcomnes

Those primitives will be good regardless, but I will change the primitives in noise-protocol soon so they conform with most other implementations out there. This means I will do chacha20-poly1305 soon

emilbayes avatar Jan 08 '20 08:01 emilbayes

Since these are brand new primitives, maybe you want to split them into a separate file like we started doing on some of the newly implemented ones?

emilbayes avatar Jan 08 '20 09:01 emilbayes

maybe you want to split them into a separate file like we started doing on some of the newly implemented ones?

Yeah agreed, I was sort of just feeling my way around. I'll split those out.

I will change the primitives in noise-protocol soon so they conform with most other implementations out there. This means I will do chacha20-poly1305 soon

You mean instead of the crypto_kx_* primitives, switch to the chacha20-poly1305 primitives?

I'm taking a stab at a hchacha20 .wat implementation. I'll post if I can get it working.

Thank you for the input!

bcomnes avatar Jan 08 '20 20:01 bcomnes

I started a WAT implementation in https://github.com/little-core-labs/hchacha20/blob/master/hchacha20.wat

Has anyone spotted a good way to port macro blocks?

https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/hchacha20/core_hchacha20.c#L8-L14

I presume just type it out is the most straight forward option.

bcomnes avatar Jan 10 '20 00:01 bcomnes

Completed hchacha20: https://github.com/little-core-labs/hchacha20/blob/master/hchacha20.wat

Time to see if it works.

bcomnes avatar Jan 10 '20 20:01 bcomnes

@bcomnes did it work? :)

cblgh avatar Mar 19 '20 12:03 cblgh

@cblgh had to step away from the project. 2 observations:

  • in our use case, we have a local node process we can run sodium native on, and use websocketsc over loopback, or use normal https. For now, this is the arrangement we opted for. Generally, this approach seems to be simplest approach instead of doing browser crypto.
  • While doing this, I found libsodium-js which provides a full JS API for libsodium, instead of the patchwork api that sodium-javascript supports. It seems to me the best bang for the buck would be to wrap libsodium-js in a way that make it uniform with sodium-native, and integrate that into sodium-universal. @emilbayes: Has that approach been considered? Part of my stall on this was due to that realization. If we can answer that, there should be a clear path forward.

bcomnes avatar Mar 27 '20 02:03 bcomnes

https://github.com/tinchoz49/workaround-hypercore8-browser

Hey everyone! this is a workaround example that I did using sodium-javascript and adding the missing crypto functions from libsodium.js.

It's just an example to check what we need to get hypercore 8 working on the browser.

libsodium.js has everything what we need but:

  1. The API is different from the sodium-friends approach.
  2. I did a benchmark comparing libsodium.js vs sodium-javascript and libsodium.js is like 6x slower than the javascript implementation.

So I think updating sodium-javascript could be the first option.

tinchoz49 avatar Mar 29 '20 07:03 tinchoz49

Nice demo @tinchoz49! Even with handrolled sodium-javascript I don't know what kind of performance we can expect, but probably 2 - 4 times slower than sodium-native (just from intuition, no benchmarks to base this on)

emilbayes avatar Mar 29 '20 20:03 emilbayes

Thank you @emilbayes ! My benchmark was very poor but it was more for investigation purpose, since I cannot test the xchacha20 functions in sodium-javascript i just run a common crypto_secretbox:

const bench = require('nanobench')
const sodium = require('sodium-native')
const sodiumjs = require('sodium-javascript')
const libsodium = require('libsodium-wrappers')

libsodium.ready.then(() => {
  bench('sodium-native', function (b) {
    b.start()

    for (let i = 0; i < 5000; i++) {
      var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES)
      var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer
      var message = Buffer.from('Hello, World!')
      var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES)

      sodium.randombytes_buf(nonce) // insert random data into nonce
      sodium.randombytes_buf(key) // insert random data into key

      // encrypted message is stored in ciphertext.
      sodium.crypto_secretbox_easy(ciphertext, message, nonce, key)

      var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES)

      sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)
    }

    b.end()
  })

  bench('sodium-javascript', function (b) {
    b.start()

    const sodium = sodiumjs

    for (let i = 0; i < 5000; i++) {
      var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES)
      var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer
      var message = Buffer.from('Hello, World!')
      var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES)

      sodium.randombytes_buf(nonce) // insert random data into nonce
      sodium.randombytes_buf(key) // insert random data into key

      // encrypted message is stored in ciphertext.
      sodium.crypto_secretbox_easy(ciphertext, message, nonce, key)

      var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES)

      sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)
    }

    b.end()
  })

  bench('libsodium.js', function (b) {
    b.start()

    const sodium = libsodium

    for (let i = 0; i < 5000; i++) {
      var key = Buffer.alloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer
      var message = Buffer.from('Hello, World!')
      var nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES)
      var ciphertext = sodium.crypto_secretbox_easy(message, nonce, key)
      var plainText = sodium.crypto_secretbox_open_easy(ciphertext, nonce, key)
    }

    b.end()
  })
})

sodium-native

ok ~162 ms (0 s + 161726505 ns)

sodium-javascript

ok ~101 ms (0 s + 100917442 ns)

libsodium.js

ok ~372 ms (0 s + 372270050 ns)

tinchoz49 avatar Mar 29 '20 22:03 tinchoz49

If we choose to use libsodium.js we can do a wrapper to provide the same API than sodium-native have. My only concern is about the async nature of loading a wasm library, that breaks everything.

We can compile libsodium.js using the emscripten flag BINARYEN_ASYNC_COMPILATION=0 ?

If we use libsodium.js we would have full support of sodium for the browser, that's a good point I guess.

tinchoz49 avatar Mar 29 '20 22:03 tinchoz49

We have plans to to the remaining ciphers in wasm :) The async'ness is unfortunately a question of binary size

emilbayes avatar Mar 29 '20 23:03 emilbayes

The async'ness is unfortunately a question of binary size

That's true.

I'm working on a minimal version of sodium in wasm based on libsodium.js that we can load sync in the browser (thinking about the 4kb limitation of chrome)

Initially, it will have only the necessary to run the missing operations that we need for noise-protocol:

  • crypto_kx_keypair
  • crypto_kx_seed_keypair
  • crypto_kx_client_session_keys
  • crypto_kx_server_session_keys
  • crypto_aead_xchacha20poly1305_ietf_encrypt
  • crypto_aead_xchacha20poly1305_ietf_decrypt

But my idea is to build something that we can extend in the future to add the rest of the operations to have everything in wasm.

I will try to publish next week :crossed_fingers:

tinchoz49 avatar Apr 04 '20 19:04 tinchoz49

https://github.com/geut/sodium-javascript-plus

Experimental support of xchacha20 and kx for sodium-javascript.

And updated the example: https://github.com/tinchoz49/workaround-hypercore8-browser

Performance is about 3x slow than sodium-native but faster than libsodium.js

tinchoz49 avatar Apr 06 '20 03:04 tinchoz49

👏 👏 👏 👏 👏 👏

nice job @tinchoz49!

cblgh avatar Apr 06 '20 06:04 cblgh

Great work @tinchoz49 !

bcomnes avatar Apr 06 '20 18:04 bcomnes