Skip to content

Instantly share code, notes, and snippets.

@lgrahl
Last active May 6, 2019 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lgrahl/bc5a715b835e82ecc4fcea8d0443e420 to your computer and use it in GitHub Desktop.
Save lgrahl/bc5a715b835e82ecc4fcea8d0443e420 to your computer and use it in GitHub Desktop.
SaltyRTC v2

SaltyRTC v2

Rationale for Changes

  • Why send the cookie all the time? It's known after the initial message towards the other peer.
  • Make sending application data more efficient (v1 requires embedding data into a msgpack object).
  • Allow chunking of data (designed for but not limited to application data).
  • Design application data to not suffer from head-of-line blocking by using a stream identifier along with chunking.
    • Unsolved so far: This requires a flow control that is carefully designed to prevent filling its send buffer too much. It may even require its own acknowledgement protocol in order to do so.
  • Allow group communication of up to 65534 participants.
  • Get rid of the initiator and responder role.

To Do & Discuss Topics

  • Spec: This is not only a signalling protocol. It's also a way to exchange arbitrary data over an end-to-end encrypted transport.
  • Spec: Do we want to separate SaltyRTC WebSocket Transport and SaltyRTC Protocol?
  • Spec: Do we want to replace MessagePack with FlatBuffers or the like?
  • Feature: Do we want task chaining?
  • Speed: Do we want (optional) 0-RTT support? Ideally, this would skip all client-to-server and all client-to-client messages (at least for 1-to-1 communication) and there would be only one RTT for the client's 'auth' messages. It could be done by negotiating a session id for both server and client.
  • Speed: RTT implications of sending two messages after another from the same source. Should these be avoided?
  • Speed: Chunk-then-encrypt, rather than encrypt-then-chunk?
  • Speed: Use little endian instead of big endian?
  • Security: Implications of having a secret key in a QR code instead of public key || token.
  • Security: DoS protection is gone as path cleaning isn't possible anymore
  • Security: Can we encrypt the header as well?
  • Instead of cookie being an optional part of the header, 'server-hello' and 'client-hello' could contain a cookie field and before 'key' there could be an unencrypted 'cookie' message. Depends on the outcome of RTT implications of sending two messages after another from the same source.
  • Should we keep the 3xxx range of close codes or go for the 4xxx range? If we stay at 3xxx, we need to register our close codes at the IANA WebSocket Protocol Registries

Crypto

  • Use (the original or the IETF variant of) the AEAD ChaCha20-Poly1305 construction or libsodium's AEAD XChaCha20-Poly1305 construction. This allows us to have additional authenticated data that does not need to be part of the nonce, nor does it need to be encrypted.
  • Clients use a shared permanent secret key (which is an implicit token) instead of permanent public/private key pairs.
  • Clients use an ephemeral key pair towards the server.

Path

The path is a hash of the permanent secret key.

Header

  • Flags:

    • C: Cookie included (between sequence number and data)
    • E: End-of-message (message is now complete)
    • A: Application data
  • Stream is the stream identifier (for application data but could also be used for parallel tasks)

  • First 8 bytes are additionally authenticated data with AEAD cipher

  • Nonce := Sequence Number || Cookie.

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |C|E|A|R|R|R|R|R|     DUNNO     |            Stream             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |            Source             |          Destination          |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                        Sequence Number                        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                                                               |
    |                            Cookie                             |
    |                                                               |
    |                                                               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                             Data                          ...
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    

Messages

Client-to-Server

  • Set the cookie field in the header for 'server-hello' and 'client-hello'
  • Always use 'client-hello'.
  • List of connected clients in 'server-auth'
  • 'new-client' announces that a new client is connected
  • 'new-initiator', 'new-responder' and 'drop-responder' are gone
  • For 'send-error', split the fields id into source, destination and sequence_number

Client-to-Client

  • Set the cookie field in the header for the 'key' message
  • Clients send the 'key' message directly (async) which is encrypted by the shared permanent secret key.
  • The client with the lower assigned identity is called the controlling client, the other one the controlled client.
  • 'auth' is sent with the tasks field by the controlled client first, then 'auth' is sent with the task field by the controlling client.
  • 'token' message is gone
@lgrahl
Copy link
Author

lgrahl commented Aug 5, 2017

@dbrgn My idea would be to discuss this here (or at least write down the outcome of our private discussions). I haven't opened an issue in saltyrtc-meta as I don't want to give people the impression that SaltyRTC v1 is moot before we've even officially released v1. Although the ideas here are called v2, it's more of an evolutionary step.

Edit: In the end this is of course still an open source project and we should give everyone the chance to participate in the discussion about v2. But I see releasing v1 as a necessity before this discussion can be moved into saltyrtc-meta.

@lgrahl
Copy link
Author

lgrahl commented Aug 12, 2017

I've considered replacing MessagePack with Cap'n Proto but dropped the thought. Here is why:

Pros:

  • It's extremely fast to encode/decode.
  • Allows us to define a schema that can be applied to every implementation easily.

Cons:

  • It's a more complicated format whereas MessagePack is pretty simple.
  • I don't think encoding/decoding efficiency is our problem as we have relatively few messages and are limited by RTT. And application data is not affected.
  • Would be a major change regarding the protocol.

@dbrgn: Comments on that welcome.

@lgrahl
Copy link
Author

lgrahl commented Oct 4, 2017

Just another idea: Deposit a last will in terms of generic encrypted data (encrypted by which keys needs to be discussed). This could be useful for various tasks in case two peers aren't permanently online.

@dbrgn: For example, in case Threema Web allows caching messages in the future (to drop the permanently connected requirement), the cached messages (as a result of currently not being connected to the app) can be deposited as the tasks last will on the signalling path. This prevents messages from being lost when there's no connection to the app but the user is closing the browser. The app would pick up the deposited message as soon as it wakes up from the push notification and send it.

@dbrgn
Copy link

dbrgn commented Oct 5, 2017

Regarding Cap'n Proto, the pros are pretty great, especially the schema. But I think I agree about your cons and conclusion. I don't think it's worthwhile right now.

If there would be any change to serialization, I think it's important that the message type can be determined without parsing the entire message. Cap'n Proto would be able to do that I think, since the schema indicates where the type field starts. You might want to add that to the list.

@dbrgn
Copy link

dbrgn commented Oct 5, 2017

Regarding the last will, I can't quite follow. So your use case is:

  • User is connected to server, app isn't
  • User sends a few messages
  • User closes the browser before the app has connected to the server
  • User disconnects from the server
  • App connects to the server and downloads the cached messages

Isn't that an edge case? If the user doesn't close the browser immediately, then the app will have to immediately connect anyways, since the user expects that messages are sent immediately when submitting them from the browser.

@lgrahl
Copy link
Author

lgrahl commented Oct 30, 2017

If the user doesn't close the browser immediately, then the app will have to immediately connect anyways, since the user expects that messages are sent immediately when submitting them from the browser.

It is an edge case to a problem that would become annoying very quickly... but I'm not sure if that will help as we would need knowledge when the user is taking the device into suspend, etc. I mean, we can come back to this if we need it and release it as a minor change.

@lgrahl
Copy link
Author

lgrahl commented Nov 15, 2017

What we should learn from saltyrtc/saltyrtc-server-python#67 is that the wonky state where both 'client-hello' (not encrypted) and 'client-auth' (encrypted) are acceptable from the server's perspective is dangerous. This should not be in a security-relevant protocol and needs to go.

@lgrahl
Copy link
Author

lgrahl commented Feb 21, 2018

I'm also thinking of dropping the secure data channel and chunking from the WebRTC/ORTC tasks and instead just require the main spec to provide a way to expose an API that allows user applications to encrypt and decrypt arbitrary data based on the established session keys.

Rationale for both is w3c/webrtc-pc#1732 which will a) get rid of the need for userland fragmentation/reassembly and b) allow to transform and therefore encrypt streams on-the-fly. Maybe, b) will still require a specific chunk size if we want to verify data in chunks (the general AEAD problem with streams) but it will be much simpler to implement. Also, I'm open for negotiating a specific chunk size via task data.

@lgrahl
Copy link
Author

lgrahl commented Jul 24, 2018

We reevaluated the MessagePack vs FlatBuffers (or whatever is the "best" one today)... having a schema would be very nice and would reduce a lot of parsing overhead.

@lgrahl
Copy link
Author

lgrahl commented Oct 31, 2018

Something we should also consider is the complexity of a flow control for forwarding to arbitrary clients. This could easily result in deadlocks.

And another thing: QUIC could be an interesting transport since it supports multiple streams out of the box.

@lgrahl
Copy link
Author

lgrahl commented May 6, 2019

A peer should:

  • discard messages from the server that contain an invalid header, nonce, crypto errors, ... before decryption but treat them as protocol errors when data inside the already decrypted and authenticated (!) payload is invalid.
  • same as above for messages from other clients but since the peer is authenticated, not only discard messages but also ignore the other client until the server announces that the other client has disconnected.

Rationale: In v1, it is possible to connect to a path and trigger protocol errors that force the clients to close their connections. When not using WebSocket with TLS, it is also possible to fake WebSocket packets that trigger a protocol error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment