Skip to content

Commit 415e3f3

Browse files
authored
SRTP Performance improvements and bugfixes #1555
SRTP Performance improvements and bugfixes
2 parents cf3123e + 706cb63 commit 415e3f3

24 files changed

Lines changed: 1163 additions & 667 deletions

src/SIPSorcery/net/DtlsSrtp/DtlsSrtpTransport.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private void DtlsSrtpTransport_OnAlert(object sender, DtlsAlertEventArgs args)
7171

7272
public bool DoHandshake(out string handshakeError)
7373
{
74-
DtlsTransport transport = _connection.DoHandshake(out handshakeError, this, null, (remoteEndpoint) => this);
74+
DtlsTransport transport = _connection.DoHandshake(out handshakeError, this, null);
7575
Transport = transport;
7676
return string.IsNullOrEmpty(handshakeError);
7777
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using Org.BouncyCastle.Crypto;
2+
using Org.BouncyCastle.Crypto.Modes;
3+
using Org.BouncyCastle.Crypto.Parameters;
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
7+
public static class BouncyCastleExtensions
8+
{
9+
extension(KeyParameter)
10+
{
11+
#if NET8_0_OR_GREATER
12+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
13+
public static KeyParameter Create(ReadOnlyMemory<byte> key)
14+
{
15+
return new KeyParameter(key.Span);
16+
}
17+
18+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
19+
public static KeyParameter Create(ReadOnlySpan<byte> key)
20+
{
21+
return new KeyParameter(key);
22+
}
23+
#else
24+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
25+
public static KeyParameter Create(ReadOnlyMemory<byte> memory)
26+
{
27+
if (System.Runtime.InteropServices.MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> segment))
28+
{
29+
return KeyParameter.Create(segment);
30+
}
31+
// Fallback for non-array-backed memory
32+
return new KeyParameter(memory.ToArray());
33+
}
34+
#endif
35+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36+
public static KeyParameter Create(ArraySegment<byte> key)
37+
{
38+
return new KeyParameter(key.Array, key.Offset, key.Count);
39+
}
40+
}
41+
42+
#if !NET8_0_OR_GREATER
43+
extension(IBlockCipher engine)
44+
{
45+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46+
public int ProcessBlock(ArraySegment<byte> input, ArraySegment<byte> output)
47+
{
48+
return engine.ProcessBlock(input.Array, input.Offset, output.Array, output.Offset);
49+
}
50+
51+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52+
public int ProcessBlock(byte[] input, byte[] output)
53+
{
54+
return engine.ProcessBlock(input, 0, output, 0);
55+
}
56+
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public int ProcessBlock(byte[] input, ArraySegment<byte> output)
59+
{
60+
return engine.ProcessBlock(input, 0, output.Array, output.Offset);
61+
}
62+
}
63+
64+
extension(IAeadBlockCipher engine)
65+
{
66+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
67+
public int ProcessBytes(ArraySegment<byte> input, ArraySegment<byte> output)
68+
{
69+
return engine.ProcessBytes(input.Array, input.Offset, input.Count, output.Array, output.Offset);
70+
}
71+
72+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
73+
public void ProcessAadBytes(ArraySegment<byte> input)
74+
{
75+
engine.ProcessAadBytes(input.Array, input.Offset, input.Count);
76+
}
77+
78+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
79+
public int DoFinal(ArraySegment<byte> output)
80+
{
81+
return engine.DoFinal(output.Array, output.Offset);
82+
}
83+
}
84+
#endif
85+
}

src/SIPSorcery/net/DtlsSrtp/Lib/DTLS/DtlsCertificateUtils.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ public static (Certificate Certificate, AsymmetricKeyParameter PrivateKey) Gener
107107

108108
var crypto = new BcTlsCrypto();
109109
var tlsCertificate = crypto.CreateCertificate(x509Certificate.GetEncoded());
110-
var certificate = new Certificate(new TlsCertificate[] { tlsCertificate });
110+
111+
// certificateRequestContext = TlsUtilities.EmptyBytes for TLS/DTLS 1.3, null for TLS/DTLS 1.2
112+
var certificate = new Certificate(null, new[] { new CertificateEntry(tlsCertificate, null) });
111113

112114
return (certificate, privateKey);
113115
}
@@ -166,7 +168,9 @@ public static (Certificate certificate, AsymmetricKeyParameter key) GenerateECDS
166168

167169
var crypto = new BcTlsCrypto();
168170
var tlsCertificate = crypto.CreateCertificate(x509Certificate.GetEncoded());
169-
var certificate = new Certificate(new[] { tlsCertificate });
171+
172+
// certificateRequestContext = TlsUtilities.EmptyBytes for TLS/DTLS 1.3, null for TLS/DTLS 1.2
173+
var certificate = new Certificate(null, new[] { new CertificateEntry(tlsCertificate, null) });
170174

171175
return (certificate, privateKey);
172176
}
@@ -204,7 +208,7 @@ public static bool IsHashSupported(string algStr)
204208
// This method has an unfortunate consequence of actually creating the digest, so the call is expensive.
205209
digest = DigestUtilities.GetDigest(algStr.ToUpperInvariant());
206210
}
207-
catch(Exception)
211+
catch (Exception)
208212
{
209213
return false;
210214
}

src/SIPSorcery/net/DtlsSrtp/Lib/DTLS/DtlsClient.cs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ namespace SIPSorcery.Net.SharpSRTP.DTLS
3333
{
3434
public class DtlsClient : DefaultTlsClient, IDtlsPeer
3535
{
36-
protected DatagramTransport _clientDatagramTransport; // valid only for the current session
37-
36+
private readonly object _syncRoot = new object();
37+
protected DatagramTransport _clientDatagramTransport = null;
3838
private TlsSession _session;
3939

4040
public bool AutogenerateCertificate { get; set; } = true;
@@ -136,26 +136,28 @@ protected override int[] GetSupportedCipherSuites()
136136
}
137137
}
138138

139-
public virtual DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, Func<string> getRemoteEndpoint = null, Func<string, DatagramTransport> createClientDatagramTransport = null)
139+
public virtual DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, DtlsRequest request = null)
140140
{
141-
DtlsTransport transport = null;
142-
143-
try
141+
lock (_syncRoot)
144142
{
145-
DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
143+
DtlsTransport transport = null;
146144

147-
_clientDatagramTransport = datagramTransport;
148-
transport = clientProtocol.Connect(this, datagramTransport);
149-
_clientDatagramTransport = null;
150-
}
151-
catch (Exception ex)
152-
{
153-
handshakeError = ex.Message;
154-
return null;
155-
}
145+
try
146+
{
147+
DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
148+
_clientDatagramTransport = datagramTransport;
149+
transport = clientProtocol.Connect(this, datagramTransport);
150+
_clientDatagramTransport = null;
151+
}
152+
catch (Exception ex)
153+
{
154+
handshakeError = ex.Message;
155+
return null;
156+
}
156157

157-
handshakeError = null;
158-
return transport;
158+
handshakeError = null;
159+
return transport;
160+
}
159161
}
160162

161163
public override TlsSession GetSessionToResume()

src/SIPSorcery/net/DtlsSrtp/Lib/DTLS/DtlsServer.cs

Lines changed: 21 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
using Org.BouncyCastle.Utilities.Encoders;
2828
using System;
2929
using System.Collections.Generic;
30-
using System.Text;
3130

3231
namespace SIPSorcery.Net.SharpSRTP.DTLS
3332
{
3433
public class DtlsServer : DefaultTlsServer, IDtlsPeer
3534
{
36-
protected DatagramTransport _clientDatagramTransport; // valid only for the current session
35+
private readonly object _syncRoot = new object();
36+
protected DatagramTransport _clientDatagramTransport = null;
3737

3838
public int TimeoutMilliseconds { get; set; } = 20000;
3939

@@ -131,81 +131,33 @@ protected override int[] GetSupportedCipherSuites()
131131
}
132132
}
133133

134-
public virtual DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, Func<string> getRemoteEndpoint, Func<string, DatagramTransport> createClientDatagramTransport)
134+
public virtual DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, DtlsRequest request = null)
135135
{
136-
if (datagramTransport == null)
136+
lock (_syncRoot)
137137
{
138-
throw new ArgumentNullException(nameof(datagramTransport));
139-
}
140-
141-
if (createClientDatagramTransport == null)
142-
{
143-
throw new ArgumentNullException(nameof(createClientDatagramTransport));
144-
}
145-
146-
DtlsTransport transport = null;
147-
148-
try
149-
{
150-
DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
151-
DtlsRequest request = null;
152-
string remoteEndpoint = null;
153-
154-
if (getRemoteEndpoint != null)
138+
if (datagramTransport == null)
155139
{
156-
// Use DtlsVerifier to require a HelloVerifyRequest cookie exchange before accepting
157-
DtlsVerifier verifier = new DtlsVerifier(Crypto);
158-
int receiveLimit = datagramTransport.GetReceiveLimit();
159-
byte[] buf = new byte[receiveLimit];
160-
int receiveAttemptCounter = 0;
161-
162-
do
163-
{
164-
const int RECEIVE_TIMEOUT = 100;
165-
int length = datagramTransport.Receive(buf, 0, receiveLimit, RECEIVE_TIMEOUT);
166-
if (length > 0)
167-
{
168-
remoteEndpoint = getRemoteEndpoint();
169-
if (string.IsNullOrEmpty(remoteEndpoint))
170-
{
171-
throw new InvalidOperationException();
172-
}
173-
174-
byte[] clientID = Encoding.UTF8.GetBytes(remoteEndpoint);
175-
request = verifier.VerifyRequest(clientID, buf, 0, length, datagramTransport);
176-
}
177-
else
178-
{
179-
receiveAttemptCounter++;
180-
181-
if (receiveAttemptCounter * RECEIVE_TIMEOUT >= TimeoutMilliseconds) // 20 seconds so that we don't wait forever
182-
{
183-
handshakeError = "HelloVerifyRequest cookie exchange could not be verified due to a timeout";
184-
return null;
185-
}
186-
}
187-
}
188-
while (request == null);
140+
throw new ArgumentNullException(nameof(datagramTransport));
189141
}
190142

191-
var clientDatagramTransport = createClientDatagramTransport(remoteEndpoint);
143+
DtlsTransport transport = null;
192144

193-
// store the current client datagram transport for this session
194-
_clientDatagramTransport = clientDatagramTransport;
195-
196-
transport = serverProtocol.Accept(this, clientDatagramTransport, request);
145+
try
146+
{
147+
DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
148+
_clientDatagramTransport = datagramTransport;
149+
transport = serverProtocol.Accept(this, datagramTransport, request);
150+
_clientDatagramTransport = null;
151+
}
152+
catch (Exception ex)
153+
{
154+
handshakeError = ex.Message;
155+
return null;
156+
}
197157

198-
// clear the reference to the transport after handshake is done
199-
_clientDatagramTransport = null;
200-
}
201-
catch (Exception ex)
202-
{
203-
handshakeError = ex.Message;
204-
return null;
158+
handshakeError = null;
159+
return transport;
205160
}
206-
207-
handshakeError = null;
208-
return transport;
209161
}
210162

211163
public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, Exception cause)

src/SIPSorcery/net/DtlsSrtp/Lib/DTLS/IDtlsPeer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,6 @@ public interface IDtlsPeer
123123
short CertificateHashAlgorithm { get; }
124124
void SetCertificate(Certificate certificate, AsymmetricKeyParameter privateKey, short signatureAlgorithm, short hashAlgorithm);
125125

126-
DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, Func<string> getRemoteEndpoint, Func<string, DatagramTransport> createClientDatagramTransport);
126+
DtlsTransport DoHandshake(out string handshakeError, DatagramTransport datagramTransport, DtlsRequest request = null);
127127
}
128128
}

src/SIPSorcery/net/DtlsSrtp/Lib/DTLSSRTP/DtlsSrtpClient.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
using SIPSorcery.Net.SharpSRTP.SRTP;
2828
using System;
2929
using System.Collections.Generic;
30-
using System.Linq;
3130

3231
namespace SIPSorcery.Net.SharpSRTP.DTLSSRTP
3332
{
@@ -42,11 +41,11 @@ public DtlsSrtpClient(Certificate certificate = null, AsymmetricKeyParameter pri
4241
this(new BcTlsCrypto(), certificate, privateKey, certificateSignatureAlgorithm, certificateHashAlgorithm, session)
4342
{ }
4443

45-
public DtlsSrtpClient(TlsCrypto crypto, Certificate certificate = null, AsymmetricKeyParameter privateKey = null, short certificateSignatureAlgorithm = SignatureAlgorithm.ecdsa, short certificateHashAlgorithm = HashAlgorithm.sha256, TlsSession session = null) :
44+
public DtlsSrtpClient(TlsCrypto crypto, Certificate certificate = null, AsymmetricKeyParameter privateKey = null, short certificateSignatureAlgorithm = SignatureAlgorithm.ecdsa, short certificateHashAlgorithm = HashAlgorithm.sha256, TlsSession session = null) :
4645
base(crypto, session, certificate, privateKey, certificateSignatureAlgorithm, certificateHashAlgorithm)
4746
{
4847
int[] protectionProfiles = GetSupportedProtectionProfiles();
49-
48+
5049
byte[] mki = DtlsSrtpProtocol.GenerateMki(MkiLength);
5150
this._srtpData = new UseSrtpData(protectionProfiles, mki);
5251

@@ -59,10 +58,10 @@ private void DtlsSrtpClient_OnHandshakeCompleted(object sender, DtlsHandshakeCom
5958
Certificate peerCertificate = e.SecurityParameters.PeerCertificate;
6059
OnSessionStarted?.Invoke(this, new DtlsSessionStartedEventArgs(context, peerCertificate, base._clientDatagramTransport));
6160
}
62-
61+
6362
public void SetMKI(byte[] mki)
6463
{
65-
if(mki == null)
64+
if (mki == null)
6665
{
6766
MkiLength = 0;
6867
mki = new byte[0];
@@ -82,7 +81,7 @@ public void SetMKI(byte[] mki)
8281

8382
protected virtual int[] GetSupportedProtectionProfiles()
8483
{
85-
return new int[]
84+
return new int[]
8685
{
8786
ExtendedSrtpProtectionProfile.DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM,
8887
ExtendedSrtpProtectionProfile.DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM,
@@ -124,13 +123,13 @@ public override void ProcessServerExtensions(IDictionary<int, byte[]> serverExte
124123

125124
// verify that the server has selected a profile we support
126125
int selectedProfile = serverSrtpExtension.ProtectionProfiles[0];
127-
if (!clientSupportedProfiles.Contains(selectedProfile))
126+
if (Array.IndexOf(clientSupportedProfiles, selectedProfile) < 0)
128127
{
129128
throw new TlsFatalAlert(AlertDescription.internal_error);
130129
}
131130

132131
// verify the mki sent by the server matches our mki
133-
if (_srtpData.Mki != null && serverSrtpExtension.Mki != null && !Enumerable.SequenceEqual(_srtpData.Mki, serverSrtpExtension.Mki))
132+
if (_srtpData.Mki != null && serverSrtpExtension.Mki != null && !_srtpData.Mki.AsSpan().SequenceEqual(serverSrtpExtension.Mki))
134133
{
135134
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
136135
}

0 commit comments

Comments
 (0)