Skip to content

Commit f5e6b20

Browse files
committed
Added DTLS and SRTP port from @rafcsoares. DTLS handshake works with no native libs required!
1 parent 1f99b45 commit f5e6b20

21 files changed

+4799
-89
lines changed

examples/icecmdline/Program.cs

+138-82
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using WebSocketSharp;
3131
using WebSocketSharp.Net.WebSockets;
3232
using WebSocketSharp.Server;
33+
using Org.BouncyCastle.Crypto.DtlsSrtp;
3334

3435
namespace SIPSorcery.Examples
3536
{
@@ -169,14 +170,20 @@ private static RTCPeerConnection Createpc(WebSocketContext context)
169170
credentialType = RTCIceCredentialType.password
170171
}
171172
},
172-
iceTransportPolicy = RTCIceTransportPolicy.relay
173+
iceTransportPolicy = RTCIceTransportPolicy.all
173174
};
174175

175176
var pc = new RTCPeerConnection(pcConfiguration);
176177

177178
#if DTLS_IS_ENABLED
178-
SIPSorceryMedia.DtlsHandshake dtls = new SIPSorceryMedia.DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);
179-
dtls.Debug = true;
179+
//SIPSorceryMedia.DtlsHandshake dtls = new SIPSorceryMedia.DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);
180+
//dtls.Debug = true;
181+
182+
//var certificate = DtlsUtils.CreateSelfSignedCert();
183+
//DtlsSrtpTransport dtlsHandle = new DtlsSrtpTransport(
184+
// pc.IceRole == IceRolesEnum.active ?
185+
// (IDtlsSrtpPeer)new DtlsSrtpClient(certificate) :
186+
// (IDtlsSrtpPeer)new DtlsSrtpServer(certificate));
180187
#endif
181188

182189
// Add inactive audio and video tracks.
@@ -216,24 +223,36 @@ private static RTCPeerConnection Createpc(WebSocketContext context)
216223
else
217224
{
218225
#if DTLS_IS_ENABLED
219-
if (pc.IceRole == IceRolesEnum.active)
220-
{
221-
logger.LogDebug("Starting DLS handshake as client task.");
222-
_ = Task.Run(() =>
223-
{
224-
bool handshakedResult = DoDtlsHandshake(pc, dtls, true, pc.RemotePeerDtlsFingerprint);
225-
logger.LogDebug($"DTLS handshake result {handshakedResult}.");
226-
});
227-
}
228-
else
226+
DtlsSrtpTransport dtlsHandle = new DtlsSrtpTransport(
227+
pc.IceRole == IceRolesEnum.active ?
228+
(IDtlsSrtpPeer)new DtlsSrtpClient(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH) :
229+
(IDtlsSrtpPeer)new DtlsSrtpServer(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH));
230+
231+
logger.LogDebug($"Starting DLS handshake with role {pc.IceRole}.");
232+
Task.Run(async () =>
229233
{
230-
logger.LogDebug("Starting DLS handshake as server task.");
231-
_ = Task.Run(() =>
232-
{
233-
bool handshakedResult = DoDtlsHandshake(pc, dtls, false, pc.RemotePeerDtlsFingerprint);
234-
logger.LogDebug($"DTLS handshake result {handshakedResult}.");
235-
});
236-
}
234+
var dtlsResult = await DoDtlsHandshake(pc, dtlsHandle);
235+
logger.LogDebug($"DTLS handshake result {dtlsResult}.");
236+
});
237+
238+
//if (pc.IceRole == IceRolesEnum.active)
239+
//{
240+
// logger.LogDebug("Starting DTLS handshake as client task.");
241+
// _ = Task.Run(() =>
242+
// {
243+
// bool handshakedResult = DoDtlsHandshake(pc, dtls, true, pc.RemotePeerDtlsFingerprint);
244+
// logger.LogDebug($"DTLS handshake result {handshakedResult}.");
245+
// });
246+
//}
247+
//else
248+
//{
249+
// logger.LogDebug("Starting DTLS handshake as server task.");
250+
// _ = Task.Run(() =>
251+
// {
252+
// bool handshakedResult = DoDtlsHandshake(pc, dtls, false, pc.RemotePeerDtlsFingerprint);
253+
// logger.LogDebug($"DTLS handshake result {handshakedResult}.");
254+
// });
255+
//}
237256
#endif
238257
}
239258
}
@@ -304,84 +323,121 @@ private static void AddConsoleLogger()
304323

305324
#if DTLS_IS_ENABLED
306325

307-
/// <summary>
308-
/// Hands the socket handle to the DTLS context and waits for the handshake to complete.
309-
/// </summary>
310-
/// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
311-
private static bool DoDtlsHandshake(RTCPeerConnection pc, SIPSorceryMedia.DtlsHandshake dtls, bool isClient, byte[] sdpFingerprint)
326+
/* DtlsHandshake requires DtlsSrtpTransport to work.
327+
* DtlsSrtpTransport is similar to C++ Dtls class combined with Srtp class and can perform Handshake as Server or Client in same call.
328+
* The constructor of transport require a DtlsStrpClient or DtlsSrtpServer to work and the method DoHandshake require a socket with
329+
* support to IPV6 Multiplex and a RemoteEndPoint (RemoteEndPoint will be discarded when performing as Server) */
330+
private static async Task<bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsSrtpTransport dtlsHandle)
312331
{
313-
logger.LogDebug("DoDtlsHandshake started.");
332+
Console.WriteLine("DoDtlsHandshake started.");
314333

315-
if (!File.Exists(DTLS_CERTIFICATE_PATH))
316-
{
317-
throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
318-
}
319-
else if (!File.Exists(DTLS_KEY_PATH))
320-
{
321-
throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
322-
}
334+
var rtpChannel = peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio);
323335

324-
int res = 0;
325-
bool fingerprintMatch = false;
336+
dtlsHandle.OnDataReady += (buf) => rtpChannel.SendAsync(RTPChannelSocketsEnum.RTP, peerConnection.AudioDestinationEndPoint, buf);
337+
peerConnection.OnDtlsPacket += (buf) => dtlsHandle.WriteToRecvStream(buf);
326338

327-
if (isClient)
328-
{
329-
logger.LogDebug($"DTLS client handshake starting to {pc.AudioDestinationEndPoint}.");
339+
var res = dtlsHandle.DoHandshake();
330340

331-
// For the DTLS handshake to work connect must be called on the socket so openssl knows where to send.
332-
var rtpSocket = pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket;
333-
rtpSocket.Connect(pc.AudioDestinationEndPoint);
341+
Console.WriteLine("DtlsContext initialisation result=" + res);
334342

335-
byte[] fingerprint = null;
336-
var peerEP = pc.AudioDestinationEndPoint;
337-
res = dtls.DoHandshakeAsClient((ulong)rtpSocket.Handle, (short)peerEP.AddressFamily.GetHashCode(), peerEP.Address.GetAddressBytes(), (ushort)peerEP.Port, ref fingerprint);
338-
if (fingerprint != null)
339-
{
340-
logger.LogDebug($"DTLS server fingerprint {ByteBufferInfo.HexStr(fingerprint)}.");
341-
fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint);
342-
}
343-
}
344-
else
343+
if (dtlsHandle.IsHandshakeComplete())
345344
{
346-
byte[] fingerprint = null;
347-
res = dtls.DoHandshakeAsServer((ulong)pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref fingerprint);
348-
if (fingerprint != null)
349-
{
350-
logger.LogDebug($"DTLS client fingerprint {ByteBufferInfo.HexStr(fingerprint)}.");
351-
fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint);
352-
}
353-
}
345+
Console.WriteLine("DTLS negotiation complete.");
354346

355-
logger.LogDebug("DtlsContext initialisation result=" + res);
347+
peerConnection.SetSecurityContext(
348+
dtlsHandle.ProtectRTP,
349+
dtlsHandle.UnprotectRTP,
350+
dtlsHandle.ProtectRTCP,
351+
dtlsHandle.UnprotectRTCP);
356352

357-
if (dtls.IsHandshakeComplete())
358-
{
359-
logger.LogDebug("DTLS negotiation complete.");
353+
await peerConnection.Start();
360354

361-
if (!fingerprintMatch)
362-
{
363-
logger.LogWarning("DTLS fingerprint mismatch.");
364-
return false;
365-
}
366-
else
367-
{
368-
var srtpSendContext = new SIPSorceryMedia.Srtp(dtls, isClient);
369-
var srtpReceiveContext = new SIPSorceryMedia.Srtp(dtls, !isClient);
370-
371-
pc.SetSecurityContext(
372-
srtpSendContext.ProtectRTP,
373-
srtpReceiveContext.UnprotectRTP,
374-
srtpSendContext.ProtectRTCP,
375-
srtpReceiveContext.UnprotectRTCP);
376-
377-
return true;
378-
}
355+
return true;
379356
}
380357
else
381358
{
382359
return false;
383360
}
384361
}
362+
363+
/// <summary>
364+
/// Hands the socket handle to the DTLS context and waits for the handshake to complete.
365+
/// </summary>
366+
/// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
367+
//private static bool DoDtlsHandshake(RTCPeerConnection pc, SIPSorceryMedia.DtlsHandshake dtls, bool isClient, byte[] sdpFingerprint)
368+
//{
369+
// logger.LogDebug("DoDtlsHandshake started.");
370+
371+
// if (!File.Exists(DTLS_CERTIFICATE_PATH))
372+
// {
373+
// throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
374+
// }
375+
// else if (!File.Exists(DTLS_KEY_PATH))
376+
// {
377+
// throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
378+
// }
379+
380+
// int res = 0;
381+
// bool fingerprintMatch = false;
382+
383+
// if (isClient)
384+
// {
385+
// logger.LogDebug($"DTLS client handshake starting to {pc.AudioDestinationEndPoint}.");
386+
387+
// // For the DTLS handshake to work connect must be called on the socket so openssl knows where to send.
388+
// var rtpSocket = pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket;
389+
// rtpSocket.Connect(pc.AudioDestinationEndPoint);
390+
391+
// byte[] fingerprint = null;
392+
// var peerEP = pc.AudioDestinationEndPoint;
393+
// res = dtls.DoHandshakeAsClient((ulong)rtpSocket.Handle, (short)peerEP.AddressFamily.GetHashCode(), peerEP.Address.GetAddressBytes(), (ushort)peerEP.Port, ref fingerprint);
394+
// if (fingerprint != null)
395+
// {
396+
// logger.LogDebug($"DTLS server fingerprint {ByteBufferInfo.HexStr(fingerprint)}.");
397+
// fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint);
398+
// }
399+
// }
400+
// else
401+
// {
402+
// byte[] fingerprint = null;
403+
// res = dtls.DoHandshakeAsServer((ulong)pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref fingerprint);
404+
// if (fingerprint != null)
405+
// {
406+
// logger.LogDebug($"DTLS client fingerprint {ByteBufferInfo.HexStr(fingerprint)}.");
407+
// fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint);
408+
// }
409+
// }
410+
411+
// logger.LogDebug("DtlsContext initialisation result=" + res);
412+
413+
// if (dtls.IsHandshakeComplete())
414+
// {
415+
// logger.LogDebug("DTLS negotiation complete.");
416+
417+
// if (!fingerprintMatch)
418+
// {
419+
// logger.LogWarning("DTLS fingerprint mismatch.");
420+
// return false;
421+
// }
422+
// else
423+
// {
424+
// var srtpSendContext = new SIPSorceryMedia.Srtp(dtls, isClient);
425+
// var srtpReceiveContext = new SIPSorceryMedia.Srtp(dtls, !isClient);
426+
427+
// pc.SetSecurityContext(
428+
// srtpSendContext.ProtectRTP,
429+
// srtpReceiveContext.UnprotectRTP,
430+
// srtpSendContext.ProtectRTCP,
431+
// srtpReceiveContext.UnprotectRTCP);
432+
433+
// return true;
434+
// }
435+
// }
436+
// else
437+
// {
438+
// return false;
439+
// }
440+
//}
385441
#endif
386442
}
387443
}

examples/icecmdline/icecmdline.csproj

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
1717
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
1818
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
19-
<PackageReference Include="SIPSorcery" Version="4.0.55-pre" />
2019
<PackageReference Include="SIPSorcery.WebSocketSharp" Version="0.0.1" />
21-
<PackageReference Include="SIPSorceryMedia" Version="4.0.55-pre" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="..\..\src\SIPSorcery.csproj" />
2224
</ItemGroup>
2325

2426
<ItemGroup>

examples/icecmdline/icecmdline.sln

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29911.84
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "icecmdline", "icecmdline.csproj", "{57F5815C-E435-4AC0-9AC1-A6502F4BE345}"
77
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIPSorcery", "..\..\src\SIPSorcery.csproj", "{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}"
9+
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1012
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,18 @@ Global
2729
{57F5815C-E435-4AC0-9AC1-A6502F4BE345}.Release|x64.Build.0 = Release|Any CPU
2830
{57F5815C-E435-4AC0-9AC1-A6502F4BE345}.Release|x86.ActiveCfg = Release|Any CPU
2931
{57F5815C-E435-4AC0-9AC1-A6502F4BE345}.Release|x86.Build.0 = Release|Any CPU
32+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|x64.ActiveCfg = Debug|Any CPU
35+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|x64.Build.0 = Debug|Any CPU
36+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|x86.ActiveCfg = Debug|Any CPU
37+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Debug|x86.Build.0 = Debug|Any CPU
38+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|Any CPU.Build.0 = Release|Any CPU
40+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|x64.ActiveCfg = Release|Any CPU
41+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|x64.Build.0 = Release|Any CPU
42+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|x86.ActiveCfg = Release|Any CPU
43+
{D1CE96A3-7591-486F-A7C3-5ABC41EBE8AE}.Release|x86.Build.0 = Release|Any CPU
3044
EndGlobalSection
3145
GlobalSection(SolutionProperties) = preSolution
3246
HideSolutionNode = FALSE

src/SIPSorcery.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
</ItemGroup>
2222

2323
<ItemGroup>
24+
<PackageReference Include="Portable.BouncyCastle" Version="1.8.6.7" />
2425
<PackageReference Include="DnsClient" Version="1.3.2" />
2526
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions">
2627
<Version>1.0.0</Version>

0 commit comments

Comments
 (0)