Node: DTLS Discussion

Created on 16 Aug 2015  Â·  80Comments  Â·  Source: nodejs/node

There's been an ongoing, unresolved discussion around adding DTLS support to joyent/node.
The original PR (https://github.com/joyent/node/pull/6704) is not going to be able to land and needs to continue here. Discussion also happening here: https://github.com/joyent/node/issues/25354. I am closing the original PR but want to make sure the conversation is not lost.

@indutny @rgillan @natbro @migounette

dgram tls

Most helpful comment

+1 DTLS will enable ability to implement secure IoT servers and clients in node.js

All 80 comments

so @migounette, implicit answer to your question is: here in the io.js fork :) i have time this week to help move your PR (joyent/node#6704) over here if you want to share a branch? or just let me know and i can help test & work on samples + documentation for now. happy to give API feedback as well when you're ready.
looking through the original PR, it seems like getting ipv6 and DTLS 1.2 wired up would be useful (i might even suggest ditching DTLS 1.0. io.js uses >= OpenSSL 1.0.2 and may be worth leaving DTLS 1.0 cruft behind).
I'm USA west-coast time-zone, UTC−08:00. lmk if you prefer email or glitter chat to chatting here.

Well... it's not _technically_ an io.js fork any more ;-). But updating this would be awesome.

sorry, trigger word :) let's just say HEAD

@natbro GMT+1 :) but working late
I agree DTLS 1.2 will be the target
I will re-start the activity Thursday or Friday

I don't think adding DTLS to core would be as profitable as improving UDP performance and adding support for userspace networking (skipping the kernel control plane to avoid unnecessary memcopies, system calls etc.).

DTLS is also quite old now compared to a protocol such as QUIC, which has advantages over DTLS (e.g. packet-aligned encryption), although QUIC is also complex.

I have been working on a simpler protocol taking many ideas from Dan Bernstein's protocols, QUIC etc. and combining these. Improving UDP performance and Crypto performance would really help.

I think DTLS is used in WebRTC, which is quite popular in messaging/communications apps.

@YuriSolovyov thanks, then it would be good to do I guess.

Hello,

DTLS is a requirement for many IoT applications and it isn't relevant whether people have opinions on how efficient it is. Standards fora such as oneM2M and LWM2M have already decided to use DTLS, as well as WebRTC. Always happy to have constructive involvement but please read back through the thread before adding to this discussion

Cheers Rob

On Aug 18, 2015, at 5:36 AM, Joran Dirk Greef [email protected] wrote:

@YuriSolovyov thanks, then it would be good to do I guess.

—
Reply to this email directly or view it on GitHub.

Yes. While DTLS is not "optimal" or "best" - it's used which makes it important and required for many scenarios, the main ones for Node are WebRTC and IoT as stated above.

Also tying DTLS directly to UDP will make it challenging to implement WebRTC on top of it. WebRTC multiplexes different protocols on top of the same UDP socket - some of which use DTLS and some which don't use.

Essentially WebRTC would require a DTLS implementation similar to the TLSSocket that can be invoked from JS code with user defined packets. This way the user code can first resolve whether the packets use DTLS or not according to WebRTC specification and then pass the DTLS packets to the DTLS implementation.

I've been a bit silent on this front lately seeing where and how the feature gets momentum. While I still want to finish the node-dtls project I started, I'm having difficulties figuring out what to do next given there's just so many different things that would need improvement.

If @migounette is continuing the native DTLS implementation, you can count me in for whatever resources. Be it code (to some extent anyway), API design, test cases - either for unit testing or integration for WebRTC purposes. I'm happy as long as Node gets some way to use DTLS.

:+1: Shure, DTLS support for WebRTC will be market-changer for Node.

Just adding that DTLS is needed for the CoAP protocol see https://github.com/mcollina/node-coap/issues/11.

The need for DTLS to be in core comes from OpenSSL. Maintaining another version of OpenSSL inside an user-land module is extremely complicated.

I'm :+1: in having a separate module, plus maybe a stream-like interface for DTLS over UDP (but that can be on NPM).

+1 DTLS will enable ability to implement secure IoT servers and clients in node.js

@mcollina - when you say "stream-like interface for DTLS over UDP" what do you mean? in terms of writing without specifying a destination host+port as currently required with dgram.send? in terms of in-order delivery/re-assembly of longer-than-MTU sized buffers?

DTLS maintains the concept of a connection/stream. It needs to do packet
reordering and the like, because it works like TLS.

If anybody interested in this comes to nodeconfeu, let's meet up!

I can confirm I will have time to spend on this starting from October.
Il giorno gio 20 ago 2015 alle 20:23 Nat Brown [email protected]
ha scritto:

@mcollina https://github.com/mcollina - when you say "stream-like
interface for DTLS over UDP" what do you mean? in terms of writing without
specifying a destination host+port as currently required with dgram.send?
in terms of in-order delivery/re-assembly of longer-than-MTU sized buffers?

—
Reply to this email directly or view it on GitHub
https://github.com/nodejs/node/issues/2398#issuecomment-133108541.

Just to clear this up: DTLS maintains the concept of a session, but not stream. It doesn't do payload packet reordering or even guarantee that all payload packets are received and decrypted.

Once the security parameters are negotiated and confirmed, every individual packet in the session can be decrypted independent of other packets in the same epoch.

Still, an objectMode Node stream should match the DTLS behavior even if there is no guarantee that the packets are read in order - or even that all packets are read in case of packet loss.

Essentially initializing a client-mode DTLSSocket should emit the initial handshake datagrams just the same as in the case the socket is bound to an UDP socket. Feeding these packets to the server-mode DTLSSocket should cause the server socket to emit the datagrams required to continue the handshake and so on.

+1 thanks, @rantanen - exactly, that's why i asked what @mcollina meant :) there should definitely no in-order or guaranteed delivery happening in a dtls implementation.
but from an API-perspective there are perhaps two ways to go:

  1. both your Rantanen/node-dtls and @migounette's joyent/node#6704 offer up an equivalent objectMode stream object (dtls.connectand dtls.createServer equivalents to tls.connect and tls.createServer). this effectively contextualizes the security negotiation (and renegotiation/reconnect) in these paired objects under the covers. it does somewhat confuse the unreliable/non-delivery/out-of-order nature of datagram, though; some people may think it's just a typical stream. could get around this with warnings in the documentation.
  2. another (or additional) approach might be to allow flowing dtls security context objects into/through dgram and bubble up security negotiation into events that could be over-ridden on the client & the server. for example, some kind of dtls.context object containing ciphers, etc that could be passed into dgram.send and would flow to a server and whose negotiation might be over-ridable or hookable with new baked-in dtls events or parameters ala dgram.on('message', function(message, context). this approach doesn't conflate streams/reliability and might give servers a bit more flexibility to host multiple services with different DTLS security contexts multiplexed on the same port.

@rantanen great job done on the js side. But I have a concern with this
approach, I prefer a native approach because a native implementation can
inherit of security fixes from OpenSSL main stream. We see how TLS/SSL
protocol needs to be improved and how some implementations have to be
patched for bugs found after years -
https://www.openssl.org/news/vulnerabilities.html#y2015
So maintaining a DTLS library at the js level is not the best thing for
node (my point of view)

Regarding the point 2, it's a good point, but a bit more difficult to
implement...

Happy to see discussion on this topic, waiting for a while 😴

My 2 cents

On Thu, Aug 20, 2015 at 11:48 PM, Nat Brown [email protected]
wrote:

+1 thanks, @rantanen https://github.com/rantanen - exactly, that's why
i asked what @mcollina https://github.com/mcollina meant :) there
should definitely no in-order or guaranteed delivery happening in a dtls
implementation.
but from an API-perspective there are perhaps two ways to go:

  1. both your Rantanen/node-dtls https://github.com/Rantanen/node-dtls
    and @migounette https://github.com/migounette's joyent/node#6704
    https://github.com/joyent/node/pull/6704 offer up an equivalent
    objectMode stream object (dtls.connectand dtls.createServer
    equivalents to tls.connect and tls.createServer). this effectively
    contextualizes the security negotiation (and renegotiation/reconnect) in
    these paired objects under the covers. it does somewhat confuse the
    unreliable/non-delivery/out-of-order nature of datagram, though; some
    people may think it's just a typical stream. could get around this with
    warnings in the documentation.
  2. another (or additional) approach might be to allow flowing dtls
    security context objects into/through dgram and bubble up security
    negotiation into events that could be over-ridden on the client & the
    server. for example, some kind of dtls.context object containing
    ciphers, etc that could be passed into dgram.send and would flow to a
    server and whose negotiation might be over-ridable or hookable with new
    baked-in dtls events or parameters ala dgram.on('message',
    function(message, context). this approach doesn't conflate
    streams/reliability and might give servers a bit more flexibility to host
    multiple services with different DTLS security contexts multiplexed on the
    same port.

—
Reply to this email directly or view it on GitHub
https://github.com/nodejs/node/issues/2398#issuecomment-133187818.

Some more thoughts:

1) I don't like the dgram API, and I think it's simplicistic. It does not
support pairing sockets for example. Also, it does a dns lookup for every
message sent. It also allocates a lot of callbacks, and it's not that
performant. I would not add functionality there, nor I would recommend
using it as the basis for any other work.

2) Ideally we should rethink the whole UDP business on node, with a focus
on performance. As far as I know, there is no UDP wg, but I might be wrong.

3) As far as I understand it, DTLS offers message reordering, meaning that
a stream interface might be a good fit:
https://tools.ietf.org/html/rfc4347#section-3.2.2. The problem is that it
would create a complete discrepancy between the dgram and the dtls API. But
DTLS is really different, so I think that's ok to mimic the tls module.

What's the purpose of the security context API? Is that needed somewhere?

Il giorno ven 21 ago 2015 alle 00:39 Yann Stephan [email protected]
ha scritto:

@rantanen great job done on the js side. But I have a concern with this
approach, I prefer a native approach because a native implementation can
inherit of security fixes from OpenSSL main stream. We see how TLS/SSL
protocol needs to be improved and how some implementations have to be
patched for bugs found after years -
https://www.openssl.org/news/vulnerabilities.html#y2015
So maintaining a DTLS library at the js level is not the best thing for
node (my point of view)

Regarding the point 2, it's a good point, but a bit more difficult to
implement...

Happy to see discussion on this topic, waiting for a while 😴

My 2 cents

On Thu, Aug 20, 2015 at 11:48 PM, Nat Brown [email protected]
wrote:

+1 thanks, @rantanen https://github.com/rantanen - exactly, that's why
i asked what @mcollina https://github.com/mcollina meant :) there
should definitely no in-order or guaranteed delivery happening in a dtls
implementation.
but from an API-perspective there are perhaps two ways to go:

  1. both your Rantanen/node-dtls https://github.com/Rantanen/node-dtls
    and @migounette https://github.com/migounette's joyent/node#6704
    https://github.com/joyent/node/pull/6704 offer up an equivalent
    objectMode stream object (dtls.connectand dtls.createServer
    equivalents to tls.connect and tls.createServer). this effectively
    contextualizes the security negotiation (and renegotiation/reconnect) in
    these paired objects under the covers. it does somewhat confuse the
    unreliable/non-delivery/out-of-order nature of datagram, though; some
    people may think it's just a typical stream. could get around this with
    warnings in the documentation.
  2. another (or additional) approach might be to allow flowing dtls
    security context objects into/through dgram and bubble up security
    negotiation into events that could be over-ridden on the client & the
    server. for example, some kind of dtls.context object containing
    ciphers, etc that could be passed into dgram.send and would flow to a
    server and whose negotiation might be over-ridable or hookable with new
    baked-in dtls events or parameters ala dgram.on('message',
    function(message, context). this approach doesn't conflate
    streams/reliability and might give servers a bit more flexibility to host
    multiple services with different DTLS security contexts multiplexed on
    the
    same port.

—
Reply to this email directly or view it on GitHub
https://github.com/nodejs/node/issues/2398#issuecomment-133187818.

—
Reply to this email directly or view it on GitHub
https://github.com/nodejs/node/issues/2398#issuecomment-133203737.

totally agree doing it native makes the most sense (and i think @Rantanen has also said it's his preference, he just began building his JS version since no native was pending) - i was just commenting on the API choice you both made is similar (dtls.createServer).
agree 2 is harder, exposing the negotiation stages. but the multiplexing of security may be useful for some folks. i just wanted to bring it up as one option and describe the tradeoffs i see. i might take a look at the cost/potential once you've got a branch/PR going - looking forward to it :)

@migounette - Yes, I prefer native - but given node was in a bit of a turmoil back when I wanted DTLS, I decided to go with a JS module that could be released on NPM instead of waiting for a PR merge and Node release.

@mcollina, that reordering only affects the handshake messages. The actual encrypted content is not reordered or resent in case of packet loss. The main benefit of UDP is to get packets with as little delay as possible across the network after all. :) Given the handshake messages shouldn't be something the user needs to care about anyway, the fact that they are reordered shouldn't be a consideration when designing the API. Node's TLS module manages to abstract these away - same should be attempted with DTLS.

@natbro, I think abstracting DTLS away from dgram would allow multiplexing different services over single dgram socket with more flexibility as it would allow using DTLS with non-UDP transports in theory. Isn't dgram in Node currently limited to UDP?

However at the same time I believe the DTLSSocket (or whatever its name would be) should implement the same API as dgram.Socket so that it can easily be used as a drop in replacement in places that currently use dgram.Socket. @mcollina raised concern over that API, but I don't see what's the problem in the actual API. I know the implementation is quite iffy given the problems with DNS lookups, etc, but I don't remember any choices there that would prevent it from being improved. Though haven't really looked at it _that_ much.

Let me rephrase, if dtls is going to base itself on dgram, than probably might be a good thing to refactor/revise that implementation. One thing that might be problematic is the use of a lot of positional arguments, that makes the API difficult to change.

From a client perspective, a DTLS context pairs something local to something remote: it is stateful. Currently all of dgram is stateless, it can be used to send and receive messages to multiple peers.
This is similar to the missing connect API from C for UDP (it allows to send messages without specifying the address). I think what we are missing is the concept of an 'udp pair', which it encapsulates the context in DTLS, but should probably support non-encrypted data as well. Having the same API is a good thing.

From a personal taste, I don't like that you need the rsinfo object to reply, and pass data back to send(). This means moving two objects around to send a reply (the server and the rsinfo): adding the DTLS context would mean moving around three objects. Then, the context would need to be one added parameter to send().

To my best understanding of the problem, this are the changes needed to have the less impact in the API:

1) Change socket.send(buf, offset, length, port, address, cb) to socket.send(buf, offset, length, { port: port, address: address }, cb). This allows to pass the rsinfo object as is when replying.
2) Make dtls.on('message') emit the DTLS context as rsinfo, so replying would just mean passing that to send.
3) Add a dtls.createContext() for the client to use to fulfill the handshake.

Another completely different API is:

1) introducing the concept of a UDP pair, with a send() method without a destination, this should probably be the result of a udp.connect() method
2) adding a udp.createServer() thing to support that paired concept
3) implement DTLS upon that pair, by wrapping it.

I just started playing around with a streaming IoT application in Node and it seemed like udp + dtls might fit the bill quite nicely. I was sad to see the discussion died in August. Any updates or progress on this issue? I don't have much bandwidth but would be open to lending a hand when I can.

@mcollina DTLS is reodering/unfragmenting packets only in the handshake/re-handshake phase not in the application phase, so a datagram based attraction fit in most language (because the handshake phase is hidden). This make easier to move non DTLS UDP code to DTLS.
The tricky API part is to "attach" the identity/security level used for DTLS message.
You can trigger the handshake when the first UDP message is sent to a ip/port pair.

@jvermillard yes, I agree.

Attaching the context can be easy API-wise:

var dtls = require('dtls')
var socket = dtls.createSocket()
var context = dtls.createContext(url) // or socket.createContext(url)

dtls.send(new Buffer('mybuf'), context, (err) => console.log('err', err))
const dtls = require('dtls');
const server = dtls.createSocket('udp4', {
  key: mykey,
  cert: mycert
});

server.on('error', (err) => {
  console.log(`server error:\n${err.stack}`);
  server.close();
});

server.on('message', (msg, rinfo) => {
  server.send(msg, rinfo, () => console.log('reply sent'))
});

server.on('listening', () => {
  var address = server.address();
  console.log(`server listening ${address.address}:${address.port}`);
});

server.bind(41234);

Nice! I just found this thread. Sounds like the general consensus is that DTLS is a reasonable addition to node core. Excellent!

It's required for IoT and WebRTC use cases, it's super difficult to do in user-land, and it's already implemented in OpenSSL so it just needs to be exposed via a JavaScript API.

What are the next steps? Sounds like someone should take the old PR and rebase it onto master to get things going again.

I'm happy to help with testing, code review, however I can.

I tried rebasing the original PR onto current node, but I don't think that's going to work.

  • Things have changed a lot since then, and the PR doesn't apply cleanly
  • The original PR has some questionable parts and very few comments, and worst of all
  • It basically copy-pastes the large, complex, and security critical _tls_wrap.js file into a second file, _dtls_wrap.js with modifications.

I'm going to try to redo the PR manually without code duplication.

@dcposch I didn't agree too much about the original PR structure and APIs: what do you think about my proposed API in https://github.com/nodejs/node/issues/2398#issuecomment-182787889?

@mcollina For WebRTC purposes we would also need an implementation that is not tied to an underlying network socket - something similar to: https://nodejs.org/api/tls.html#tls_class_tls_tlssocket.

WebRTC multiplexes various protocols over the same connection. Only some of the packets will use DTLS. There is a need for the user to be able to examine the datagrams coming over the network and then make the choice of passing some of these to the DTLS context for decryption.

I don't know how TLS is implemented, but I'd imagine from architectural point of view, we could expose the OpenSSL DTLS implementation as a pair of streams without tying it to any sockets in the native code. Once this is done, wrapping sockets inside of it would be rather trivial.


dtls.createSecurePair(...)

Creates two duplex object streams that accept buffers (with optional encoding?). This is what would enable various DTLS use cases in Node - and could even act as the "security context" mentioned above.


The proposed server API looks good. I'm assuming the server socket holds onto various security contexts and is able to look up the correct one to use based on the rinfo on both decoding and encoding.

I don't like the client interface though. First of all, it should be socket.send (but I guess this is what you meant and dtls.send was a typo?), but more over I think we should also do similar rinfo-lookup on the send based on the address and port parameters. This would allow Node to keep the same API between dgram and dtls if the user could write:

const dtls = require('dtls');
const message = new Buffer('Some bytes');
const client = dtls.createSocket('udp4');

// Does a lookup for security context on `localhost:41234`.
// On the first packet, as the context doesn't exist, does a handshake.
client.send(message, 41234, 'localhost', (err) => {
  client.close();
});

Although I do agree on what you said earlier about redesigning the whole dgram interfaces. Being able to pass in rinfo to the client.send would be a nice feature among other things - but then again, you kind of hinted at that being a possibility in the server example.

In addition to the dgram interface, I guess it is obvious the dtls sockets would need to expose various other DTLS-specific members. Examples that come to mind include a handshake(addr, port method to force the handshake without sending any application data and a getSecurityContext(addr, port) to examine the DTLS context of a specific connection. Also events for newly negotiated connection and dropped sessions would be beneficial.

The dtls sockets also need some kind of (customizable) expiration policy for the negotiated security info.

Finally for WebRTC, it would be important to also consider the use_srtp DTLS extension and make sure whatever implementation Node adpots for DTLS, it doesn't make it overly difficult to implement the use_srtp extension on top of DTLS.

@Rantanen all good, apart that we cannot use streams here: streams implies a message ordering, which we cannot guarantee (being on UDP and DTLS). We need to design a different low-level interface that you can use on top of any transports (as createSecurePair() in tls), and use that to wrap up on top of dgram for simple use cases.

I'm not suggesting we expose the secure pair streams straight out of the UDP sockets. However given the createSecurePair isn't tied to UDP, I don't see why it couldn't be implemented with object streams.

I'll try clarifying how I convinced myself of why there is nothing wrong with this. Essentially the two streams are not UDP+DTLS socket streams as much as communication interfaces between user code and the DTLS implementation:

The createSecurePair creates a local object. A kind of a "machine" that handles DTLS locally if you will.

This machine has two sides to it, represented by two streams. These are called encrypted and cleartext. Both of these streams are implemented locally and guarantee message ordering within the machine interface: Incoming DTLS packets are written to the encrypted stream and outgoing cleartext packets are written to the cleartext stream. Both of these streams are processed in order _by the machine_.

While the machine is processing the packets, it may trigger other packets to be emitted out of it. This includes handshake messages, encrypted application packets or decrypted cleartext packets. These packets are pushed out through the encrypted and cleartext streams. Again, these streams guarantee message ordering between the machine and the user code consuming the streams.

What happens afterwards has nothing to do with the two streams. The packets might be pushed out over UDP with half of them disappearing and the remaining half being received in reverse - but this has nothing to do with the streams that were used during the local encryption/decryption.

@Rantanen streams are nice, however they introduce a lot of overhead, and backpressure does not have much meaning on UDP. After the initial handshake, you need two (synchronous?) methods: encode and decode.

@mcollina If we ignore the handshake, that's close enough. But the handshake/renegotiate causes problems.

I'd imagine an implementation with (synchronous) encode and decode methods would look like the following:

var dtlsContext = createDtlsContext({ mode: 'client' });
var socket = acquireSomeSocketFromSomewhere();

// Start the initial handshake.
var packets = dtlsContext.handshake();
packets.forEach( p => socket.send( p ) );

// receive encrypted packets from the socket
socket.on( 'message', in => {

  // Processing the incoming encrypted packet may result in new packets.
  // Some of these are encrypted packets while some are cleartext packets.
  var packets = dtlsContext.process( in );

  // Some of the packets (such as handshake) are encrypted and should be sent
  // back to the client.
  packets.filter( p => p.encrypted )
        .forEach( p => socket.send( p.data ) );

  // The rest of the packets are clear text that should be emitted to user code.
  // DTLS also allows merging multiple packets into a single datagram.
  packets.filter( p => p.cleartext )
        .forEach( p => emit( p ) );
});

var sendCleartext = function( msg ) {
  // Encrypt is easy enough as it should produce the encrypted packets always.
  var packets = dtlsContext.encrypt( msg );
  packets.forEach( p => socket.send( p ) );
}

With Streams I envisioned something similar to:

var dtlsContext = createDtlsContext({ mode: 'client' });
var socket = acquireSomeSocketFromSomewhere();

// No extra dtlsContext.handshake() needed.
// The context can queue the packets on the stream which are passed out through the
// 'data' event when the handler is registered and the stream goes to flowing mode.

// Encrypted output data gets written to the socket.
dtlsContext.encrypted.on( 'data', p => socket.write( p ) );

// Encrypted input data gets pushed into the context.
socket.on( 'message', msg => dtlsContext.encrypted.push( msg ) );

// Decrypted cleartext data is emitted to user.
dtlsContext.cleartext.on( 'data', msg => emit( msg ) );

// Sending data pushes the cleartext message to the context.
var sendCleartext = function( msg ) {
  dtlsContext.cleartext.push( msg );
};

Without knowing the full impact of the stream implementation, I feel from an API point of view the streams would result in a much more approachable interface.

Would it be worth it to provide both? The native context would provide something similar to the first option that would be used as part of the dgram integration in the core while being exposed as a module with the stream API.

I thought the same, but streams are the wrong kind of interface for non-streaming data. I _agree_ that streams looks nicer, but they imply a contract between the parties that cannot be respected over DTLS. We do not want it to be compatible with streams, because otherwise it will confuse people and they will start abusing this.

Would it be okay to still use event emitters though? I feel an event based interface would be the best for outputting the encrypted packets at least.

dtlsContext.on( 'encrypted', packet => socket.write( packet ) );
dtlsContext.on( 'cleartext', msg => emit( msg );

socket.on( 'message', msg => dtlsContext.decode( msg ) );
var sendMessage = msg => dtlsContext.encode( msg );

The naming of the decode/encode methods is up in the air. The alternatives I came up with quickly are:

| Incoming DTLS data | Cleartext output |
| --- | --- |
| decode() | encode() |
| read() | write() |
| decrypt() | encrypt() |

I disliked the decode at first until I realized the semantic difference between that and decrypt. Given the method needs to decode the DTLS packets without necessarily doing any decrypting (as the first packets aren't encrypted) I'm quite liking it. :)

I think and EE interface is a good starting point, we'll see in the actual PR how well that works :). We'll still need to implement dgram's API on top of it.

ping ... any progress on this?

@jasnell absolutely none. I got swamped into other things, and I feel I won't have that long stretch of time to crack this. I can probably help on reviewing and testing at the API level.

I dived into the Node core a week ago when @jasnell brought this up again.

Unfortunately I quickly got lost in the internal StreamBase/HandleWrap/AsyncWrap and lost some of the motivation. Don't suppose there's some architectural documentation of Node internals that would explain the internal concepts such as the ones above? :|

Nothing new related to this? It would be great to be able to implement something like webrtc datachannels in pure js :)

Any new info on this? DTLS is a fairly standard part of a number of IoT standards, so having it available in node would enable several of these.

I am currently working at it (master branch), after digging, I found some changes in the implementation of the TLS Wrap.

I will be glad to see nodejs to be a CoAP player.

Nodes collaborators have added the notion of the stream, so now when data arrives on a socket stream the handler is a Wrap Stream (Readable | Writable Stream) and then the stream is pushed to a TLS wrap for handling encryption.

Question for @walterbm @indutny @Trott:
I am planning to have for the UDP a Duplex and Transform Streams like TCP.
Do you see any problem to extend udp to add the stream notion and in this case DTLS it will have the same behavior from TLS and DTLS.

udp \
---> Stream --> TLS
tcp /

Thanks for your lights

@migounette Can you make the native implementation an object stream?

Normal byte stream would be problematic since UDP is a datagram protocol. The DTLS stream should expose individual datagrams as opposed to a stream of binary data like TLS.

There was also discussion between stream based API and event based API earlier. The consensus in the end seemed to be that a stream based API would be a bit misleading given DLTS doesn't guarantee the order of the packets or even whether all of them are delivered.

However if we get the native bits of DTLS implemented with any kind of an API, we can then figure out a way to wrap that internal native API in a public javascript API in a way that makes sense to the public.

Question for @walterbm @indutny @Trott:

Any chance you meant to @-mention someone else instead of me? I don't have any particular expertise on this.

@migounette DTLS is not a stream, as it does not guarantee that if we send message A before message B, message A will arrive first. A general rule to know if something is a stream is: can I send a file reliably over it?

If you need any help in designing the API, feel free to ping me.

@mcollina I agree, DTLS is datagram socket whereas TCP is a socket stream. If you want to send a file reliably after it you can use SCTP over DTLS (like WebRTC definition). It's up to the application to create the reliability (e.g.. SIP over UDP with the CSEQ), and it also guarantees ordering.

But my question raised is around the node stream definition. Streams are objects that let you read data from a source or write data to a destination in a continuous fashion from an application point of view.

I was thinking of RTP protocol over UDP, by pushing data to the steam, we can read and detect the boundary. But it does not cover a raw socket that wants encrypt or decrypt, in this case if may be difficult to handle a chunk of data due to the missing boundary limit (message size).
So, the proposal may be DGRAM Socket --> TLS Wrap (but the problem is that TLS Wrap is based on a stream concept!!!) the code will be a bit trickier, thanks for your comments @mcollina

Let continue the discussion... :)

Yeah, I encountered that same issue when I was looking at the native code in August. The current Node internals make it rather difficult to reuse the TLS bits (namely the TLSWrap) for non-stream sockets.

an idea might be to have it _interally_ as a TLSWrap, but not externally, and wrap the TLSWrap into the rsinfo  object.

Is implementation work happening somewhere? Can we build some roadmap?

With some of the recent changes (you can now use openssl symbols on Windows), it should be possible to prototype this as an add-on rather than building it into core straightaway.

While this might be comfortable for core, it is is a non-starter for the ecosystem to ever pick it up, since it is security from untrustworthy sources, no? :(

@bnoordhuis can we link OpenSSL symbols from Node on Linux or Mac OS X? As far I understood, that was not possible.

since it is security from untrustworthy sources

@eljefedelrodeodeljefe Is the implication that core is so trustworthy? I take it you've never seen my tax filing, it's a work of great fiction.

can we link OpenSSL symbols from Node on Linux or Mac OS X? As far I understood, that was not possible.

@mcollina That always worked (at least on Linux), if only by accident. I'm comfortable declaring it fully supported though, now that we have an add-on test for it.

@bnoordhuis just for some context, do you have an example of that linking? Is it documented somewhere how to do it? Last time I checked for nodegit, it was not possible (it seems they still vendor https://github.com/nodegit/nodegit/tree/master/vendor/openssl, I moved on from the project).

@bnoordhuis I trust you very much. Seriously. Also the fact that this here has a higher life expectancy due it's constitution.

@mcollina You don't need any special linker flags, the challenge is the include path for openssl's headers. They're in a different location when you build against a node source tree vs. the headers tarball that node-gyp downloads. I don't think that's currently documented.

@eljefedelrodeodeljefe Thank you. :) What I mean is, the module can live under the nodejs GitHub organization and be published by the Node.js Foundation. That will give it the veneer of officialness and trustworthiness that you want, but it will be much easier to prototype and iterate on than if it was in core out of the gate.

@bnoordhuis sounds good to me. Thx for clarifying.

Should this remain open?

Yes. It is still a major lack in Node.js.

Is this something recent additions to libuv might be helpful with. E.g. libuv-extras (didn't have a look yet)

It could, it would make it useful to a larger audience, but you'd still need some glue code for node.js.

@eljefedelrodeodeljefe @bnoordhuis can you add a link?

@mcollina https://github.com/libuv/libuv-extras - the idea is to have a place for things that can be layered on top of libuv but don't belong in libuv proper (like TLS / DTLS; a C cluster implementation would be another example.)

Not much in there yet, though.

Not much in there yet, though.

@bnoordhuis yeah, I see that. I do not think there is anything we can use here, or maybe I am mistaken.

dragging in @saghul. Context: Whether it would be interesting to have DTLS in libuv-extras to enable Node community easy access.

The main issue remains the API, who can drive the discussion in order to get the right API for node (Stream, TLS, UDP) and libuv
I will be happy to participate to the discussion (IOT and webRTC require it) but core team leaders are required
@saghul @mcollina @bnoordhuis

you can close the thread

I am happy to join a biweekly team, if someone has time to put into the implementation.

I'm happy to participate in discussions or code review, but I'm afraid I won't be able to contribute code myself. Agreed with Ben, libuv-extras seems like the right place for it.

Would absolutely love to see this happen. I think it would help a lot since IoT is becoming very popular, especially in connection with Node.js.

It's time for DTLS !!! @saghul you have a project but seems to be empty: https://github.com/libuv/libuv-extras
Any clue for starting ?

@migounette Creating the source and header files, together with a simple Makefile should be enough to get the ball rolling. We'll see where we go from there :-) (I've been afk for some time, so I don't know if any decisions have been made regarding the extras repo in the mean time.)

Few month ago i`m started working on pure js dtls implementation. Here is my experimental work:
https://github.com/nodertc/dtls. AEAD, ECDSA, mtu, reordering, defragmentation already implemented.

Hello everyone. Needing to bridge this gap ASAP we at Krekeltronics have looked for a solution. The best we have found so far is Spark's module for inclusion of mbedtls library into node. Since we did not get the impression that there was any maintaining or further development going on in that repo or in others downstream, we have decided to take on further developing the module.

We would appreciate any input you may have toward getting this code production ready. Feel free to comment on the roadmap and the tickets, help us test the code or submit pull requests.

Take a look at the project: https://github.com/krekeltronics/node-mbed-dtls

Join the discussion on Gitter: Gitter

Closing due to lack of activity

Has this been implemented? Isn't it considered as an option to add it to the core?

@jasnell @mcollina @bnoordhuis @saghul is there any chance of reopening this? DTLS continues to be a missing piece to secure IoT

I am eager to pariticapte to DTLS implementation
But currently it's not very clear where changes.

I have a native implementation of DTLS on top of nodejs 10 (NAPI) for our WebRTC product
DTLS is widely used by WebRTC and IO-T, in java the need is covered with COaP but in Node.JS no efficient solution.

DTLS (UDP) uses 80% of TLS code and it requires only a few hooks in order to support key exchange (such as https://tools.ietf.org/html/rfc5764)

LibUV/No LibUV, in Core/No Core.... pushing our work to community will be great just let us know where to push it and how we can discuss API changes, impacts and documentation.

The real question, we have stuff to push but we need some to lead the job in order to take decisions where it can be put.

My 2 cents

@migounette It would be good if you could issue a PR adding DTLS to Node Core. Why would you need changes in libuv?

Wow @migounette ! It would be amazing if you could create a PR with DTLS

I have a native implementation of DTLS on top of nodejs 10 (NAPI) for our WebRTC product

LibUV/No LibUV, in Core/No Core.... pushing our work to community will be great just let us know where to push it and how we can discuss API changes, impacts and documentation.

@migounette I think that the official guide "Contributing a new API to N-API" can provide some guidance

Was this page helpful?
0 / 5 - 0 ratings