dtsl: add some documentation
authorRaffaele Rossi <rarossi@cisco.com>
Fri, 22 Jan 2016 16:49:57 +0000 (16:49 +0000)
committerTim-Philipp Müller <tim@centricular.com>
Mon, 18 Apr 2016 11:35:40 +0000 (12:35 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=760994

ext/dtls/README [new file with mode: 0644]

diff --git a/ext/dtls/README b/ext/dtls/README
new file mode 100644 (file)
index 0000000..8d165ec
--- /dev/null
@@ -0,0 +1,153 @@
+INTRODUCTION
+============
+This document is an attempt to describe the basics of the DTLS element.
+It hasn't been written by the author(s) and so, besides being incomplete,
+*IT MIGHT ALSO BE INCORRECT*. So take it with a pinch of salt.
+
+As always, if in doubt ask the #gstreamer IRC channel.
+
+THE INTERNALS
+=============
+This plugin provides two main elements (dtlssrtpdec and dtlssrtpenc) and a few
+minor elements necessary to implement them. The two elements dtlssrtpdec and
+dtlssrtpenc are the only ones you are supposed to include in, respectively, the
+RX and TX pipelines of your DTLS-enabled application. This means you're not
+supposed to directly include those minor elements in your pipelines.
+
+dtlssrtpenc
+-----------
+This element is to be included in the TX pipeline and will initiate the DTLS
+handshake if configured to be the client. Its main configuration parameters are:
+
+  - connection-id: a string that must match the connection-id in dtlssrtpdec;
+  - is-client: a boolean that indicates whether this is going to be the client
+                                                        or the server during the DTLS handshake.
+
+Internally this element comprises the standard srtpenc element, the dtlsenc
+element and a funnel to connect both these elements to one single output.
+The srtpenc can be used to encrypt SRTP/SRTCP packets while the dtlsenc can be
+used to encrypt generic data, e.g. for non-SRTP applications.
+
+NB With the current implementation the TX pipeline containing the dtlssrtpenc
+        must be created *AFTER* the RX pipeline.
+
+dtlssrtpdec
+-----------
+It is to be included in the RX pipeline. Its main configuration parameters are:
+
+  - connection-id: a string that must match the connection-id in dtlssrtpenc;
+       - pem: a string that can be used to provide your own certificate *AND* private
+                                key in PEM format. The private key is required to carry out the
+                                handshake so do not forget it or the DTLS negotiation will fail;
+  - peer_pem: a read only parameter that can be used to retrieve the
+                                                       certificate sent from the other party in PEM format once the
+                                                       handshake is completed.
+
+Internally this element comprises a dtlssrtpdemux, a standard srtpdec element
+and the dtlsdec element. The dtlssrtpdemux element switches SRT(C)P packets to
+the srtpdec element and DTLS packets to the dtlsdec element and discards any
+other unknown packet. So, similarly for the dtlssrtpenc case, DTLS-SRTP
+applications would exercise the srtpdec element and any other non-SRTP
+application would exercise the dtlsdec element.
+
+NB With the current implementation the RX pipeline containing the dtlssrtpdec
+        must be created *BEFORE* the TX pipeline.
+
+EXAMPLE PIPELINE
+================
+The following is an example usage of the DTLS plugin. It is a python script that
+creates two endpoints that exchange encrypted audio using DTLS to exchange the
+encryption keys.
+
+NB In theory we would show an example gst-launch command. However that would not
+        be enough because you need two pairs of TX/RX pipelines for a proper
+        handshake and you can't use gst-launch two start 4 different pipelines.
+        This why there is a python script in here.
+
+```
+#!/usr/bin/env python3
+
+# create two endpoints, each with tx and rx pipelines using the DTLS
+# elements and let audio flowing for a while to give time for a packet capture
+
+import time
+from gi.repository import Gst, GObject, GLib
+GObject.threads_init()
+Gst.init(None)
+
+
+def _start_pipeline(pipeline):
+    pipeline.set_state(Gst.State.PLAYING)
+    pipeline.get_state(Gst.CLOCK_TIME_NONE)
+
+
+def _sleep_while_iterating_gloop(secs):
+    """ block for secs seconds but iterate the gloop while you do """
+    for _ in range(10 * secs):
+        gloop = GLib.MainLoop()
+        gloop_context = gloop.get_context()
+        gloop_context.iteration(may_block=False)
+        time.sleep(0.1)
+
+def dtls_tx_pipeline_description(name, is_client, port):
+    return ' ! '.join([
+        'audiotestsrc is-live=true',
+        'audio/x-raw, rate=8000, format=S16LE, channels=1',
+        'opusenc frame-size=10',
+        'rtpopuspay pt=103',
+        '.rtp_sink_0 dtlssrtpenc connection-id={name} is-client={client} .src',
+        'udpsink port={port}'
+    ]).format(name=name, client=is_client, port=port)
+
+
+def dtls_rx_pipeline_description(name, port):
+    return ' ! '.join([
+        'udpsrc port={port}',
+        '.sink dtlssrtpdec connection-id={name} .rtp_src',
+        'queue',
+        'fakesink async=false'
+    ]).format(name=name, port=port)
+
+
+class Endpoint:
+    def __init__(self, name, is_client, tx_port, rx_port):
+        self.name = name
+        tx_pipeline_description = dtls_tx_pipeline_description(
+            name, is_client, tx_port
+        )
+        rx_pipeline_description = dtls_rx_pipeline_description(name, rx_port)
+        print(rx_pipeline_description)
+        print(tx_pipeline_description)
+
+        self.rx_pipeline = Gst.parse_launch(rx_pipeline_description)
+        self.tx_pipeline = Gst.parse_launch(tx_pipeline_description)
+
+    def start(self):
+                               # Start RX first, otherwise it fails due to the current implementation
+        self.start_rx_pipeline()
+        self.start_tx_pipeline()
+
+    def start_rx_pipeline(self):
+        _start_pipeline(self.rx_pipeline)
+
+    def start_tx_pipeline(self):
+        _start_pipeline(self.tx_pipeline)
+
+    def stop(self):
+        def stop_pipeline(p):
+            p.set_state(Gst.State.NULL)
+            p.get_state(Gst.CLOCK_TIME_NONE)
+        stop_pipeline(self.tx_pipeline)
+        stop_pipeline(self.rx_pipeline)
+
+blue = Endpoint("blue", is_client=True, tx_port=23000, rx_port=23002)
+red = Endpoint("red", is_client=False, tx_port=23002, rx_port=23000)
+
+red.start()
+blue.start()
+
+_sleep_while_iterating_gloop(3)
+
+red.stop()
+blue.stop()
+```