gstreamer/ext/dtls
Sebastian Dröge 7fcfb6c6c5 dtls: Keep track of the connection state and signal it through all the layers
This allows the application to keep track of the underlying DTLS
connection state and act accordingly.
2020-01-19 11:16:34 +00:00
..
gstdtlsagent.c dtlsagent: Clear the certificate upon finalize 2019-06-25 20:37:57 +02:00
gstdtlsagent.h dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
gstdtlscertificate.c Run gst-indent through the files 2018-11-28 14:18:26 +00:00
gstdtlscertificate.h dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
gstdtlsconnection.c dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlsconnection.h dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlsdec.c dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlsdec.h dtlsdec: Fix critical warning "got data flow before stream-start event" 2015-06-03 17:12:49 +02:00
gstdtlsenc.c dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlsenc.h dtls: Propagate write errors backwards through dtlsenc/dtlsconnection 2020-01-19 11:16:34 +00:00
gstdtlssrtpbin.c dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
gstdtlssrtpbin.h dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
gstdtlssrtpdec.c dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlssrtpdec.h dtlssrtpdec: Don't merge RTP and RTCP streams that were just split by srtpdec 2015-05-07 21:04:30 +02:00
gstdtlssrtpdemux.c bad: use new gst_element_class_add_static_pad_template() 2016-03-24 14:56:51 +02:00
gstdtlssrtpdemux.h dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
gstdtlssrtpenc.c dtls: Keep track of the connection state and signal it through all the layers 2020-01-19 11:16:34 +00:00
gstdtlssrtpenc.h dtls: Fix some search & replace mistakes from renaming the elements 2015-03-18 09:46:40 +01:00
meson.build docs: Build documentation with hotdoc 2019-05-13 17:00:00 -04:00
plugin.c dtls: Re-namespace from Er to Gst 2015-03-16 18:23:27 +01:00
README dtsl: add some documentation 2016-04-18 12:35:40 +01:00

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()
```