1INTRODUCTION
2============
3This document is an attempt to describe the basics of the DTLS element.
4It hasn't been written by the author(s) and so, besides being incomplete,
5*IT MIGHT ALSO BE INCORRECT*. So take it with a pinch of salt.
6
7As always, if in doubt ask the #gstreamer IRC channel.
8
9THE INTERNALS
10=============
11This plugin provides two main elements (dtlssrtpdec and dtlssrtpenc) and a few
12minor elements necessary to implement them. The two elements dtlssrtpdec and
13dtlssrtpenc are the only ones you are supposed to include in, respectively, the
14RX and TX pipelines of your DTLS-enabled application. This means you're not
15supposed to directly include those minor elements in your pipelines.
16
17dtlssrtpenc
18-----------
19This element is to be included in the TX pipeline and will initiate the DTLS
20handshake if configured to be the client. Its main configuration parameters are:
21
22 - connection-id: a string that must match the connection-id in dtlssrtpdec;
23 - is-client: a boolean that indicates whether this is going to be the client
24 or the server during the DTLS handshake.
25
26Internally this element comprises the standard srtpenc element, the dtlsenc
27element and a funnel to connect both these elements to one single output.
28The srtpenc can be used to encrypt SRTP/SRTCP packets while the dtlsenc can be
29used to encrypt generic data, e.g. for non-SRTP applications.
30
31NB With the current implementation the TX pipeline containing the dtlssrtpenc
32 must be created *AFTER* the RX pipeline.
33
34dtlssrtpdec
35-----------
36It is to be included in the RX pipeline. Its main configuration parameters are:
37
38 - connection-id: a string that must match the connection-id in dtlssrtpenc;
39 - pem: a string that can be used to provide your own certificate *AND* private
40 key in PEM format. The private key is required to carry out the
41 handshake so do not forget it or the DTLS negotiation will fail;
42 - peer_pem: a read only parameter that can be used to retrieve the
43 certificate sent from the other party in PEM format once the
44 handshake is completed.
45
46Internally this element comprises a dtlssrtpdemux, a standard srtpdec element
47and the dtlsdec element. The dtlssrtpdemux element switches SRT(C)P packets to
48the srtpdec element and DTLS packets to the dtlsdec element and discards any
49other unknown packet. So, similarly for the dtlssrtpenc case, DTLS-SRTP
50applications would exercise the srtpdec element and any other non-SRTP
51application would exercise the dtlsdec element.
52
53NB With the current implementation the RX pipeline containing the dtlssrtpdec
54 must be created *BEFORE* the TX pipeline.
55
56EXAMPLE PIPELINE
57================
58The following is an example usage of the DTLS plugin. It is a python script that
59creates two endpoints that exchange encrypted audio using DTLS to exchange the
60encryption keys.
61
62NB In theory we would show an example gst-launch command. However that would not
63 be enough because you need two pairs of TX/RX pipelines for a proper
64 handshake and you can't use gst-launch two start 4 different pipelines.
65 This why there is a python script in here.
66
67```
68#!/usr/bin/env python3
69
70# create two endpoints, each with tx and rx pipelines using the DTLS
71# elements and let audio flowing for a while to give time for a packet capture
72
73import time
74from gi.repository import Gst, GObject, GLib
75GObject.threads_init()
76Gst.init(None)
77
78
79def _start_pipeline(pipeline):
80 pipeline.set_state(Gst.State.PLAYING)
81 pipeline.get_state(Gst.CLOCK_TIME_NONE)
82
83
84def _sleep_while_iterating_gloop(secs):
85 """ block for secs seconds but iterate the gloop while you do """
86 for _ in range(10 * secs):
87 gloop = GLib.MainLoop()
88 gloop_context = gloop.get_context()
89 gloop_context.iteration(may_block=False)
90 time.sleep(0.1)
91
92def dtls_tx_pipeline_description(name, is_client, port):
93 return ' ! '.join([
94 'audiotestsrc is-live=true',
95 'audio/x-raw, rate=8000, format=S16LE, channels=1',
96 'opusenc frame-size=10',
97 'rtpopuspay pt=103',
98 '.rtp_sink_0 dtlssrtpenc connection-id={name} is-client={client} .src',
99 'udpsink port={port}'
100 ]).format(name=name, client=is_client, port=port)
101
102
103def dtls_rx_pipeline_description(name, port):
104 return ' ! '.join([
105 'udpsrc port={port}',
106 '.sink dtlssrtpdec connection-id={name} .rtp_src',
107 'queue',
108 'fakesink async=false'
109 ]).format(name=name, port=port)
110
111
112class Endpoint:
113 def __init__(self, name, is_client, tx_port, rx_port):
114 self.name = name
115 tx_pipeline_description = dtls_tx_pipeline_description(
116 name, is_client, tx_port
117 )
118 rx_pipeline_description = dtls_rx_pipeline_description(name, rx_port)
119 print(rx_pipeline_description)
120 print(tx_pipeline_description)
121
122 self.rx_pipeline = Gst.parse_launch(rx_pipeline_description)
123 self.tx_pipeline = Gst.parse_launch(tx_pipeline_description)
124
125 def start(self):
126 # Start RX first, otherwise it fails due to the current implementation
127 self.start_rx_pipeline()
128 self.start_tx_pipeline()
129
130 def start_rx_pipeline(self):
131 _start_pipeline(self.rx_pipeline)
132
133 def start_tx_pipeline(self):
134 _start_pipeline(self.tx_pipeline)
135
136 def stop(self):
137 def stop_pipeline(p):
138 p.set_state(Gst.State.NULL)
139 p.get_state(Gst.CLOCK_TIME_NONE)
140 stop_pipeline(self.tx_pipeline)
141 stop_pipeline(self.rx_pipeline)
142
143blue = Endpoint("blue", is_client=True, tx_port=23000, rx_port=23002)
144red = Endpoint("red", is_client=False, tx_port=23002, rx_port=23000)
145
146red.start()
147blue.start()
148
149_sleep_while_iterating_gloop(3)
150
151red.stop()
152blue.stop()
153```
154