jedisct1 / libsodium.js

libsodium compiled to Webassembly and pure JavaScript, with convenient wrappers.
Other
981 stars 140 forks source link

Missing `crypto_stream` APIs #101

Closed creationix closed 7 years ago

creationix commented 7 years ago

The README says it supports the crypto_stream functions:

  • crypto_stream (Salsa20, XSalsa20, ChaCha20, XChaCha20)

But in the normal build, it only has the key generation functions, and in the sumo build, it has a weird subset of them.

In particular I'm trying to implement the dat protocol. Digging into the implementation, it appears to use crypto_stream_xsalsa20_xor_ic https://github.com/sodium-friends/sodium-native/blob/67be609dd40ea42033747605e3819d913ea290cd/src/crypto_stream_xor_wrap.cc#L36

Is there a way I can make a custom build of the library with the exact set of functions I need? Or are there plans to add the missing stream functions?

jedisct1 commented 7 years ago

The crypto_stream* API is frequently misunderstood, and rarely useful now that we haverandombytes_deterministic().

Still, all the functions are available; what is actually missing is a bunch of high-level JS wrappers. A wrapper for a given function is defined as a JSON file in wrapper/symbols.

You add implement new ones based on the existing crypto_stream ones. Then, if you have emscripten installed, "make" will take forever, but will eventually rebuild the final JS files.

creationix commented 7 years ago

Thanks, I'll rebuild later to tweak the bundle for my app.

What would be the recommended way to get a TLS/SSL-like stream given a pair of shared keys (the output of key exchange)? Streams tend to be TCP, UDP, UTP, or Websocket. Websocket has message framing built in, but TCP doesn't and UDP doesn't guarantee order or reliability of messages. (I think UTP is similar to TCP in it's promises)

I could talk to the dat people and see if they'd like to evolve their protocol to use something better supported.

Also I'm still not sure I'm going to be using dat itself for my product. If I end up rolling my own protocol, then I surely have the flexibility to change the encryption.

jedisct1 commented 7 years ago

Do you need something that would work with all these transports (TCP + UDP + UTP + Websocket) or just one of them?

creationix commented 7 years ago

The protocol priorities are probably in this order: UTP, Websocket, TCP, WebRTC DataChannel (not sure which configuration), UDP.

I'm open to suggestions. Currently in DAT, they assume something with TCP-like promises.

creationix commented 7 years ago

Also, where is this randombytes_deterministic()? Sounds like a nice high-level version of the xor stream.

creationix commented 7 years ago

Found the randombytes, do you think this is safe?

window.sodium = { onload ({
  from_string,
  to_string,
  randombytes_SEEDBYTES,
  randombytes_buf,
  randombytes_buf_deterministic,
  crypto_generichash
}) {
  function encryptedStream (key) {
    let seed = key
    return function (message) {
      if (typeof message === 'string') message = from_string(message)
      seed = crypto_generichash(randombytes_SEEDBYTES, seed)
      let output = randombytes_buf_deterministic(message.length, seed)
      for (let i = 0; i < message.length; i++) {
        output[i] ^= message[i]
      }
      return output
    }
  }
  let key = randombytes_buf(randombytes_SEEDBYTES)
  let encrypt = encryptedStream(key)
  let decrypt = encryptedStream(key)

  let plain = 'Hello World\n'
  console.log({plain})
  let encrypted = encrypt(plain)
  console.log({encrypted})

  let original = to_string(decrypt(encrypted))

  console.log({original})
}}
creationix commented 7 years ago

The assumption here is key is derived from key exchange.

jedisct1 commented 7 years ago

Just wrote some fresh documentation for you!

https://download.libsodium.org/doc/secret-key_cryptography/secretstream.html https://download.libsodium.org/doc/secret-key_cryptography/encrypted-messages.html

jedisct1 commented 7 years ago

Your code snippet works, but the data can be modified without this being detected. Is it what you want?

creationix commented 7 years ago

You mean the stream is unauthenticated? Yes, I'm aware of that. The actual protocol over the wire uses merkle trees with blake2b hashes, and signed roots using ed25519.

But it sounds like the preferred way is to use the one-off authenticated encryption methods with counting nonces or something?

someburner commented 7 years ago

@jedisct1

I'm experimenting with writing a sub-protocol for websockets between a js client and the server being a little cpp wrapper for libsodium I'm working on. The point at which I started playing with the client-side was just after you updated the docs, and seeing this page, it sounded like crypto_aead_xchacha20poly1305_ietf_xx was appropriate for my immediate use-case with websockets, but that secretstream would be needed if order could not be guaranteed by the transport. But the ratcheting scheme sounds really cool so I wanted to try that out in the js client as well.

So my question is- are the noob-wrappers for js on the To-Do list?

jedisct1 commented 7 years ago

@someburner Looks like it would be a perfect fit for your use case.

Emscripten has the equivalent of malloc, which can be used to reserve enough space to store states, as it was already done for crypto_generichash for example.

I added the required type and pointer here: 9bfab917928f16f4a1e885ebd151abf1b5f8e2d7

someburner commented 7 years ago

@jedisct1 awesome, much appreciated!

jedisct1 commented 7 years ago

@someburner secretstream has been added :)

ljrk0 commented 3 years ago

It seems that randombytes_SEEDBYTES is missing again, it was deleted in 4c89e4fb3198eb51df4d20aed3680918ee3fa445, alongside with: