Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add use_srtp extension #30

Open
ibc opened this issue Apr 18, 2019 · 9 comments
Open

Add use_srtp extension #30

ibc opened this issue Apr 18, 2019 · 9 comments

Comments

@ibc
Copy link

ibc commented Apr 18, 2019

It would be super cool if the Socket class would include:

  • Ability to negotiate SRTP keys.
  • An API to send SRTP packets (and events for receiving them).

Of course, a Node RTP parser/factory library would aso be needed. I've found this one (which seems to be unmaintained). SRTP encryption/decryption capabilities would also be needed obviously (and that would be a hard work).

@reklatsmasters
Copy link
Member

This is rfc5764. To be clear, SRTP I/O should do any other package. This package only for dtls. The use_srtp extension might be implemented by this package.The internal socket of dtls I/O might be reused. It's just a duplex stream:

function connect(options = {}, callback) {
if (!isDuplexStream(options.socket)) {
options.socket = unicast.createSocket(options);
}
const socket = new Socket(options);
socket.connect(callback);
return socket;

Anyway, SRTP is a part of Media API which is not my priority. I focused only on datachannels. Only any business interests and support may change my internal priority 😸 .

@ibc
Copy link
Author

ibc commented Apr 19, 2019

Let my clarify that I'm not asking for this dtls lib to do SRTP I/O at all. I just meant that it may enable it by negotiating use_srtp DTLS extension in the handshake.

Now, sorry for the off-topic:

What it comes to my mind when I see all these stun, ice, dtls, etc great libs is the ability to create a modular DataChannel or a SRTP stack in Node. Let me show it in pseudo-code:

const ice = require('@nodertc/ice');
const dtls = require('@nodertc/dtls');
const rtp = require('@foo/rtp');
const srtp = require('@foo/srtp');
const is_rtp = require('is-rtp');

// Create a ICE connection.
const iceConnection = ice.connect(
  {
    remoteCandidates : [ {}, {}, {}... ],
    userFrag : 'iaasdgjahsdgjh',
    password : '1234'
  });

// Wait for ICE to be established.
await new Promise((resolve) => iceConnection.on('connected', resolve));

// Create a DTLS association on top of the ICE connection.
// Note that iceConnection.getSocket() does not return a net.Socket
// but a special object with similar interface. This is because ICE
// may move to a different ip:port tuple at any time due to reconnections
// after ICE disconnections.
const dtlsConnection = dtls.connect(
  {
    socket  : iceConnection.getSocket(),
    useSrtp : true
  });

// Wait for DTLS to be connected.
await new Promise((resolve) => dtlsConnection.on('connected', resolve));

// Create a SRTP session with the material negotiated via DTLS.
const srtpSession = srtp.createSession(
  {
    keys : dtlsConnection.getSrtpKeys()
  });

// Create a dummy RTP packet.
const rtpPacket = rtp.createPacket(
  {
    payloadType : 111,
    seq         : 12345,
    timestamp   : Date.now(),
    payload     : new Buffer(...)
  });

// Encrypt the packet with SRTP.
const srtpPacket = srtpSession.encrypt(rtpPacket);

// Send the SRTP packet.
iceConnection.send(srtpPacket.getRaw());

// Listen for incoming SRTP packets.
iceConnection.on('packet', (packet) =>
{
  if (is_rtp(packet))
  {
    const srtpPacket = rtp.parse(packet);
    const rtpPacket = srtpSession.decrypt(srtpPacket);

    console.log(
      'received RTP packet [payloadType:%d, seq:%d]',
      rtpPacket.getPayloadType(), rtpPacket.getSeq());
  }
});

Do you have something like this in mind? Jjust ignore the RTP/SRTP stuff above, please, it can be done by a 3rd party library.

@reklatsmasters
Copy link
Member

Yes, you're right. It may look somethings like this. One note: srtp/rtp should wait for complete dtls connection:

//...
dtlsConnection.once('connect', () => {
	// ready for any i/o.
});
//...

@ibc
Copy link
Author

ibc commented Apr 19, 2019

Yes, I already waited for DTLS connection in my pseudo code above:

// Wait for DTLS to be connected.
await new Promise((resolve) => dtlsConnection.on('connect', resolve));

:)

@ibc
Copy link
Author

ibc commented Apr 19, 2019

Let me just one question more, please.

I'm looking for the best way to implement DataChannel in my SFU mediasoup. mediasoup is Node with C++ subprocesses that handle media (UDP, TCP, ICE, DTLS, SRTP, etc). The Node layer controls those C++ subprocesses via UnixSocket.

Once the DTLS is established, I already have a C++ API to send and receive "DTLS application data":

If we assume that those "DTLS application data" are SCTP packets, I can push them verbatim to the mediasoup Node.js layer and use your sctp and datachannel libs to process them, am I right?

Assuming that, it's not clear to me how to combine both sctp and datachannel libs. I expect that received DTLS data should be given to the sctp lib. However, it seems that sctp requires a transport (got via dtls.connect()), and dtls requires a UDP socket which breaks my modular design. Basically I don't want that Node.js does networking at all, I already do networking at C++ level.

Is my use case possible using your libs? Perhaps the dtls Socket can be provided with a Node Stream pair instead of having to pass a UDP socket?

P.S. I do not see any API in sctp for sending data to the remote endpoint. Do I miss something?

@reklatsmasters
Copy link
Member

First, sctp was implemented by not me. I fixed only 2 things:

  • i deleted native dependencies
  • internal udp transport might be just a duplex stream

This module may have bugs and do not follow my standards of code quality. You may ask @latysheff as original author sctp about stability.

As i sayd before, after my fixes tansport may be just a duplex stream. See

https://github.com/nodertc/nodertc/blob/a7bd7aca00bd389723f3cdc665653459667c408a/index.js#L328-L350

for details. It's nodertc prototype.

@latysheff
Copy link

P.S. I do not see any API in sctp for sending data to the remote endpoint. Do I miss something?

The API of sctp module is the same as Node's Net module. That is, use socket.write() to send data. There are also examples of how to use sctp sockets.
p.s. For now, there is no active support for the module, code is not covered by unit tests, but overall stability is fairly good, if viewed as black box. I have conducted long-running load tests, compatibility tests, etc, and noticed no memory leaks or crashes.

@ibc
Copy link
Author

ibc commented Apr 20, 2019

Thanks to both for your comments. So now there are two Node SCTP implementations that can run over DTLS:

@reklatsmasters, if you are building a complete DataChannel stack I assume you'll have to eventually work on @nodertc/sctp as a core component of that DataChannel stack, am I right?

@reklatsmasters
Copy link
Member

@reklatsmasters reklatsmasters changed the title Reuse the socket to send SRTP Add use_srtp extension Apr 20, 2019
@reklatsmasters reklatsmasters added this to the v0.6.0 milestone Apr 20, 2019
@reklatsmasters reklatsmasters modified the milestones: v0.6.0, v0.7.0 May 5, 2019
@reklatsmasters reklatsmasters removed this from the v0.7.0 milestone Aug 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants