diff --git a/src/SIPSorcery/net/DtlsSrtp/Lib/SRTP/SrtpContext.cs b/src/SIPSorcery/net/DtlsSrtp/Lib/SRTP/SrtpContext.cs
index 72cde1d4e..92f278f0b 100644
--- a/src/SIPSorcery/net/DtlsSrtp/Lib/SRTP/SrtpContext.cs
+++ b/src/SIPSorcery/net/DtlsSrtp/Lib/SRTP/SrtpContext.cs
@@ -60,55 +60,6 @@ public class SsrcSrtpContext
public uint S_l { get; private set; }
public bool S_l_set { get; private set; } = false;
- ///
- /// Checks and updates the replay window for the given sequence number.
- ///
- /// RTP/RTCP sequence number.
- /// true if the replay check passed, false when the packed was replayed.
- /// https://datatracker.ietf.org/doc/html/rfc2401 Appendix C
- public bool CheckAndUpdateReplayWindow(uint sequenceNumber)
- {
- int diff;
-
- if (sequenceNumber == 0)
- {
- if (!S_l_set)
- {
- S_l_set = true;
- return true; /* first is good */
- }
- return false; /* first == 0 or wrapped */
- }
- if (sequenceNumber > S_l)
- {
- /* new larger sequence number */
- diff = (int)(sequenceNumber - S_l);
- if (diff < REPLAY_WINDOW_SIZE)
- {
- /* In window */
- Bitmap = Bitmap << diff;
- Bitmap |= 1; /* set bit for this packet */
- }
- else
- {
- Bitmap = 1; /* This packet has a "way larger" */
- }
- S_l = sequenceNumber;
- return true; /* larger is good */
- }
- diff = (int)(S_l - sequenceNumber);
- if (diff >= REPLAY_WINDOW_SIZE)
- {
- return false; /* too old or wrapped */
- }
- if ((Bitmap & ((ulong)1 << diff)) == ((ulong)1 << diff))
- {
- return false; /* already seen */
- }
- Bitmap |= ((ulong)1 << diff); /* mark as seen */
- return true; /* out of order but good */
- }
-
///
/// Read-only replay check. Returns whether (a 32-bit packet
/// index) is acceptable, WITHOUT mutating any state. Per RFC 3711 section 3.3 the replay list,
@@ -909,146 +860,146 @@ public virtual int UnprotectRtp(ReadOnlyBytes input, Bytes output, out int outpu
try
{
- switch (context.Cipher)
- {
- case SrtpCiphers.NULL:
- {
- var dataLen = length - mki.Length - context.N_tag;
- input.Slice(0, dataLen).CopyTo(output.Slice(0, dataLen));
- outputBufferLength = dataLen;
- }
- break;
-
- case SrtpCiphers.AES_128_F8:
- {
- SRTP.Encryption.F8.GenerateRtpMessageKeyIV(context.PayloadF8, context.K_e, context.K_s, input, roc, context.Iv16);
- var decLen = length - mki.Length - context.N_tag;
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.F8.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
- outputBufferLength = decLen;
- }
- break;
-
- case SrtpCiphers.AES_128_CM:
- case SrtpCiphers.AES_192_CM:
- case SrtpCiphers.AES_256_CM:
- case SrtpCiphers.ARIA_128_CTR:
- case SrtpCiphers.ARIA_256_CTR:
- case SrtpCiphers.SEED_128_CTR:
- {
- SRTP.Encryption.CTR.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv16);
- var decLen = length - mki.Length - context.N_tag;
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.CTR.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
- outputBufferLength = decLen;
- }
- break;
-
- case SrtpCiphers.AEAD_AES_128_GCM:
- case SrtpCiphers.AEAD_AES_256_GCM:
- case SrtpCiphers.AEAD_ARIA_128_GCM:
- case SrtpCiphers.AEAD_ARIA_256_GCM:
- case SrtpCiphers.SEED_128_CCM:
- case SrtpCiphers.SEED_128_GCM:
- {
- SRTP.Encryption.AEAD.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv12);
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - mki.Length - offset), output.Slice(offset), context.Iv12, context.K_e, context.N_tag, input.Slice(0, offset));
- outputBufferLength = length - mki.Length - context.N_tag;
- }
- break;
-
- case SrtpCiphers.DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM:
- case SrtpCiphers.DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM:
- {
- // apply outer cryptographic algorithm
- var outerK_e = KeyParameter.Create(context.K_e.Slice(context.K_e.Length / 2));
- var outerK_s = context.K_s.AsSpan(context.K_s.Length / 2);
- SRTP.Encryption.AEAD.GenerateMessageKeyIV(outerK_s, ssrc, index, context.Iv12);
- SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - mki.Length - offset), output.Slice(offset), context.Iv12, outerK_e, context.N_tag / 2, input.Slice(0, offset));
-
- // copy header from input to output
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ switch (context.Cipher)
+ {
+ case SrtpCiphers.NULL:
+ {
+ var dataLen = length - mki.Length - context.N_tag;
+ input.Slice(0, dataLen).CopyTo(output.Slice(0, dataLen));
+ outputBufferLength = dataLen;
+ }
+ break;
- // calculate OHB size - it can now be larger than 1 byte if it was modified
- var lastOhbByteIndex = length - mki.Length - context.N_tag / 2 - 1;
- var ohbConfig = output[lastOhbByteIndex];
- var ohbLength = 1;
- if ((ohbConfig & 0x01) == 0x01)
+ case SrtpCiphers.AES_128_F8:
{
- ohbLength += 2;
+ SRTP.Encryption.F8.GenerateRtpMessageKeyIV(context.PayloadF8, context.K_e, context.K_s, input, roc, context.Iv16);
+ var decLen = length - mki.Length - context.N_tag;
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.F8.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
+ outputBufferLength = decLen;
}
- if ((ohbConfig & 0x02) == 0x02)
+ break;
+
+ case SrtpCiphers.AES_128_CM:
+ case SrtpCiphers.AES_192_CM:
+ case SrtpCiphers.AES_256_CM:
+ case SrtpCiphers.ARIA_128_CTR:
+ case SrtpCiphers.ARIA_256_CTR:
+ case SrtpCiphers.SEED_128_CTR:
{
- ohbLength += 1;
+ SRTP.Encryption.CTR.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv16);
+ var decLen = length - mki.Length - context.N_tag;
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.CTR.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
+ outputBufferLength = decLen;
}
+ break;
- // form a synthetic RTP packet
- var rtpHeaderLength = RtpReader.ReadHeaderLenWithoutExtensions(output);
- var rtpExtensionsLength = RtpReader.ReadExtensionsLength(output);
- var syntheticRtpPacketLen = length - rtpExtensionsLength - (context.N_tag / 2) - ohbLength;
- var syntheticRtpPacket = ArrayPool.Shared.Rent(syntheticRtpPacketLen);
+ case SrtpCiphers.AEAD_AES_128_GCM:
+ case SrtpCiphers.AEAD_AES_256_GCM:
+ case SrtpCiphers.AEAD_ARIA_128_GCM:
+ case SrtpCiphers.AEAD_ARIA_256_GCM:
+ case SrtpCiphers.SEED_128_CCM:
+ case SrtpCiphers.SEED_128_GCM:
+ {
+ SRTP.Encryption.AEAD.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv12);
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - mki.Length - offset), output.Slice(offset), context.Iv12, context.K_e, context.N_tag, input.Slice(0, offset));
+ outputBufferLength = length - mki.Length - context.N_tag;
+ }
+ break;
- try
+ case SrtpCiphers.DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM:
+ case SrtpCiphers.DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM:
{
- // copy header without extensions
- output.Slice(0, rtpHeaderLength).CopyTo(syntheticRtpPacket.AsSpan(0, rtpHeaderLength));
+ // apply outer cryptographic algorithm
+ var outerK_e = KeyParameter.Create(context.K_e.Slice(context.K_e.Length / 2));
+ var outerK_s = context.K_s.AsSpan(context.K_s.Length / 2);
+ SRTP.Encryption.AEAD.GenerateMessageKeyIV(outerK_s, ssrc, index, context.Iv12);
+ SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - mki.Length - offset), output.Slice(offset), context.Iv12, outerK_e, context.N_tag / 2, input.Slice(0, offset));
- // set X bit to 0
- syntheticRtpPacket[0] &= 0xEF;
+ // copy header from input to output
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- // restore original header values from the OHB
+ // calculate OHB size - it can now be larger than 1 byte if it was modified
+ var lastOhbByteIndex = length - mki.Length - context.N_tag / 2 - 1;
+ var ohbConfig = output[lastOhbByteIndex];
+ var ohbLength = 1;
if ((ohbConfig & 0x01) == 0x01)
{
- syntheticRtpPacket[2] = output[lastOhbByteIndex - ohbLength - 1];
- syntheticRtpPacket[3] = output[lastOhbByteIndex - ohbLength];
+ ohbLength += 2;
}
if ((ohbConfig & 0x02) == 0x02)
{
- var pt = output[lastOhbByteIndex - ohbLength];
- syntheticRtpPacket[1] = (byte)((syntheticRtpPacket[1] & 0x80) | (pt & 0x7F));
+ ohbLength += 1;
}
- if ((ohbConfig & 0x04) == 0x04)
- {
- var markerBit = (ohbConfig & 0x08) == 0x08;
- syntheticRtpPacket[1] = (byte)((markerBit ? 0x80 : 0x00) | (syntheticRtpPacket[1] & 0x7F));
- }
-
- // copy the payload including the inner authentication tag
- output.Slice(offset, length - offset - mki.Length - context.N_tag / 2 - ohbLength).CopyTo(syntheticRtpPacket.AsSpan(rtpHeaderLength, length - offset - mki.Length - context.N_tag / 2 - ohbLength));
-
- var innerSsrc = RtpReader.ReadSsrc(syntheticRtpPacket);
- var innerSequenceNumber = RtpReader.ReadSequenceNumber(syntheticRtpPacket);
- var innerIndex = SrtpContext.DetermineRtpIndex(lastSeq, innerSequenceNumber, lastRoc);
- // apply inner cryptographic algorithm
- var innerK_e = KeyParameter.Create(context.K_e.Slice(0, context.K_e.Length / 2));
- var innerK_s = context.K_s.AsSpan(0, context.K_s.Length / 2);
- SRTP.Encryption.AEAD.GenerateMessageKeyIV(innerK_s, innerSsrc, innerIndex, context.Iv12);
- SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, syntheticRtpPacket.Slice(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength), syntheticRtpPacket.Slice(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength), context.Iv12, innerK_e, context.N_tag / 2, syntheticRtpPacket.Slice(0, rtpHeaderLength));
-
- // copy the unprotected payload back to the output buffer
- syntheticRtpPacket.AsSpan(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2).CopyTo(output.Slice(offset, syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2));
+ // form a synthetic RTP packet
+ var rtpHeaderLength = RtpReader.ReadHeaderLenWithoutExtensions(output);
+ var rtpExtensionsLength = RtpReader.ReadExtensionsLength(output);
+ var syntheticRtpPacketLen = length - rtpExtensionsLength - (context.N_tag / 2) - ohbLength;
+ var syntheticRtpPacket = ArrayPool.Shared.Rent(syntheticRtpPacketLen);
- // copy the synthetic header back to the output buffer
- syntheticRtpPacket.AsSpan(0, rtpHeaderLength).CopyTo(output.Slice(0, rtpHeaderLength));
-
- // update the output buffer length
- outputBufferLength = offset + syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2;
+ try
+ {
+ // copy header without extensions
+ output.Slice(0, rtpHeaderLength).CopyTo(syntheticRtpPacket.AsSpan(0, rtpHeaderLength));
+
+ // set X bit to 0
+ syntheticRtpPacket[0] &= 0xEF;
+
+ // restore original header values from the OHB
+ if ((ohbConfig & 0x01) == 0x01)
+ {
+ syntheticRtpPacket[2] = output[lastOhbByteIndex - ohbLength - 1];
+ syntheticRtpPacket[3] = output[lastOhbByteIndex - ohbLength];
+ }
+ if ((ohbConfig & 0x02) == 0x02)
+ {
+ var pt = output[lastOhbByteIndex - ohbLength];
+ syntheticRtpPacket[1] = (byte)((syntheticRtpPacket[1] & 0x80) | (pt & 0x7F));
+ }
+ if ((ohbConfig & 0x04) == 0x04)
+ {
+ var markerBit = (ohbConfig & 0x08) == 0x08;
+ syntheticRtpPacket[1] = (byte)((markerBit ? 0x80 : 0x00) | (syntheticRtpPacket[1] & 0x7F));
+ }
+
+ // copy the payload including the inner authentication tag
+ output.Slice(offset, length - offset - mki.Length - context.N_tag / 2 - ohbLength).CopyTo(syntheticRtpPacket.AsSpan(rtpHeaderLength, length - offset - mki.Length - context.N_tag / 2 - ohbLength));
+
+ var innerSsrc = RtpReader.ReadSsrc(syntheticRtpPacket);
+ var innerSequenceNumber = RtpReader.ReadSequenceNumber(syntheticRtpPacket);
+ var innerIndex = SrtpContext.DetermineRtpIndex(lastSeq, innerSequenceNumber, lastRoc);
+
+ // apply inner cryptographic algorithm
+ var innerK_e = KeyParameter.Create(context.K_e.Slice(0, context.K_e.Length / 2));
+ var innerK_s = context.K_s.AsSpan(0, context.K_s.Length / 2);
+ SRTP.Encryption.AEAD.GenerateMessageKeyIV(innerK_s, innerSsrc, innerIndex, context.Iv12);
+ SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, syntheticRtpPacket.Slice(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength), syntheticRtpPacket.Slice(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength), context.Iv12, innerK_e, context.N_tag / 2, syntheticRtpPacket.Slice(0, rtpHeaderLength));
+
+ // copy the unprotected payload back to the output buffer
+ syntheticRtpPacket.AsSpan(rtpHeaderLength, syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2).CopyTo(output.Slice(offset, syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2));
+
+ // copy the synthetic header back to the output buffer
+ syntheticRtpPacket.AsSpan(0, rtpHeaderLength).CopyTo(output.Slice(0, rtpHeaderLength));
+
+ // update the output buffer length
+ outputBufferLength = offset + syntheticRtpPacketLen - rtpHeaderLength - context.N_tag / 2;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(syntheticRtpPacket);
+ }
}
- finally
+ break;
+
+ default:
{
- ArrayPool.Shared.Return(syntheticRtpPacket);
+ outputBufferLength = 0;
+ return ERROR_UNSUPPORTED_CIPHER;
}
- }
- break;
-
- default:
- {
- outputBufferLength = 0;
- return ERROR_UNSUPPORTED_CIPHER;
- }
- }
+ }
}
catch (Org.BouncyCastle.Crypto.InvalidCipherTextException)
{
@@ -1296,7 +1247,7 @@ public virtual int UnprotectRtcp(ReadOnlyBytes input, Bytes output, out int outp
}
}
- if (!ssrcContext.CheckAndUpdateReplayWindow(index))
+ if (!ssrcContext.CheckReplayWindow(index))
{
outputBufferLength = 0;
return ERROR_REPLAY_CHECK_FAILED;
@@ -1304,93 +1255,104 @@ public virtual int UnprotectRtcp(ReadOnlyBytes input, Bytes output, out int outp
if (isEncrypted)
{
- switch (context.Cipher)
+ try
{
- case SrtpCiphers.NULL:
- {
- var dataLen = length - 4 - context.N_tag - mki.Length;
- input.Slice(0, dataLen).CopyTo(output.Slice(0, dataLen));
- outputBufferLength = dataLen;
- }
- break;
-
- case SrtpCiphers.AES_128_F8:
- {
- var decLen = length - 4 - context.N_tag - mki.Length;
- var iv = SRTP.Encryption.F8.GenerateRtcpMessageKeyIV(context.PayloadF8, context.K_e, context.K_s, input, originalIndex);
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.F8.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), iv);
- outputBufferLength = decLen;
- }
- break;
-
- case SrtpCiphers.AES_128_CM:
- case SrtpCiphers.AES_192_CM:
- case SrtpCiphers.AES_256_CM:
- case SrtpCiphers.ARIA_128_CTR:
- case SrtpCiphers.ARIA_256_CTR:
- case SrtpCiphers.SEED_128_CTR:
- {
- var decLen = length - 4 - context.N_tag - mki.Length;
- SRTP.Encryption.CTR.GenerateMessageKeyIV(context.K_s, ssrc, ssrcContext.S_l, context.Iv16);
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.CTR.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
- outputBufferLength = decLen;
- }
- break;
+ switch (context.Cipher)
+ {
+ case SrtpCiphers.NULL:
+ {
+ var dataLen = length - 4 - context.N_tag - mki.Length;
+ input.Slice(0, dataLen).CopyTo(output.Slice(0, dataLen));
+ outputBufferLength = dataLen;
+ }
+ break;
- case SrtpCiphers.AEAD_AES_128_GCM:
- case SrtpCiphers.AEAD_AES_256_GCM:
- case SrtpCiphers.AEAD_ARIA_128_GCM:
- case SrtpCiphers.AEAD_ARIA_256_GCM:
- case SrtpCiphers.SEED_128_CCM:
- case SrtpCiphers.SEED_128_GCM:
- {
- SRTP.Encryption.AEAD.GenerateMessageKeyIV(context.K_s, ssrc, ssrcContext.S_l, context.Iv12);
- var associatedDataRented = ArrayPool.Shared.Rent(offset + 4);
- try
+ case SrtpCiphers.AES_128_F8:
{
- input.Slice(0, offset).CopyTo(associatedDataRented.AsSpan(0, offset));
- BinaryPrimitives.WriteUInt32BigEndian(associatedDataRented.AsSpan(offset, 4), originalIndex);
+ var decLen = length - 4 - context.N_tag - mki.Length;
+ var iv = SRTP.Encryption.F8.GenerateRtcpMessageKeyIV(context.PayloadF8, context.K_e, context.K_s, input, originalIndex);
input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - 4 - mki.Length - offset), output.Slice(offset), context.Iv12, context.K_e, context.N_tag, associatedDataRented.Slice(0, offset + 4));
- outputBufferLength = length - 4 - context.N_tag - mki.Length;
+ SRTP.Encryption.F8.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), iv);
+ outputBufferLength = decLen;
}
- finally
+ break;
+
+ case SrtpCiphers.AES_128_CM:
+ case SrtpCiphers.AES_192_CM:
+ case SrtpCiphers.AES_256_CM:
+ case SrtpCiphers.ARIA_128_CTR:
+ case SrtpCiphers.ARIA_256_CTR:
+ case SrtpCiphers.SEED_128_CTR:
+ {
+ var decLen = length - 4 - context.N_tag - mki.Length;
+ SRTP.Encryption.CTR.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv16);
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.CTR.Encrypt(context.PayloadCTR, input.Slice(offset, decLen - offset), output.Slice(offset, decLen - offset), context.Iv16);
+ outputBufferLength = decLen;
+ }
+ break;
+
+ case SrtpCiphers.AEAD_AES_128_GCM:
+ case SrtpCiphers.AEAD_AES_256_GCM:
+ case SrtpCiphers.AEAD_ARIA_128_GCM:
+ case SrtpCiphers.AEAD_ARIA_256_GCM:
+ case SrtpCiphers.SEED_128_CCM:
+ case SrtpCiphers.SEED_128_GCM:
{
- ArrayPool.Shared.Return(associatedDataRented);
+ SRTP.Encryption.AEAD.GenerateMessageKeyIV(context.K_s, ssrc, index, context.Iv12);
+ var associatedDataRented = ArrayPool.Shared.Rent(offset + 4);
+ try
+ {
+ input.Slice(0, offset).CopyTo(associatedDataRented.AsSpan(0, offset));
+ BinaryPrimitives.WriteUInt32BigEndian(associatedDataRented.AsSpan(offset, 4), originalIndex);
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - 4 - mki.Length - offset), output.Slice(offset), context.Iv12, context.K_e, context.N_tag, associatedDataRented.Slice(0, offset + 4));
+ outputBufferLength = length - 4 - context.N_tag - mki.Length;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(associatedDataRented);
+ }
}
- }
- break;
+ break;
- case SrtpCiphers.DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM:
- case SrtpCiphers.DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM:
- {
- // RTCP under Double AEAD is protected only with the outer layer
- var outerK_e = KeyParameter.Create(context.K_e.Slice(context.K_e.Length / 2));
- var outerK_s = context.K_s.AsSpan(context.K_s.Length / 2);
- SRTP.Encryption.AEAD.GenerateMessageKeyIV(outerK_s, ssrc, ssrcContext.S_l, context.Iv12);
- var associatedDataRented = ArrayPool.Shared.Rent(offset + 4);
- try
+ case SrtpCiphers.DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM:
+ case SrtpCiphers.DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM:
{
- input.Slice(0, offset).CopyTo(associatedDataRented.AsSpan(0, offset));
- BinaryPrimitives.WriteUInt32BigEndian(associatedDataRented.AsSpan(offset, 4), originalIndex);
- input.Slice(0, offset).CopyTo(output.Slice(0, offset));
- SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - 4 - mki.Length - offset), output.Slice(offset), context.Iv12, outerK_e, context.N_tag / 2, associatedDataRented.Slice(0, offset + 4));
- outputBufferLength = length - 4 - context.N_tag / 2 - mki.Length;
+ // RTCP under Double AEAD is protected only with the outer layer
+ var outerK_e = KeyParameter.Create(context.K_e.Slice(context.K_e.Length / 2));
+ var outerK_s = context.K_s.AsSpan(context.K_s.Length / 2);
+ SRTP.Encryption.AEAD.GenerateMessageKeyIV(outerK_s, ssrc, index, context.Iv12);
+ var associatedDataRented = ArrayPool.Shared.Rent(offset + 4);
+ try
+ {
+ input.Slice(0, offset).CopyTo(associatedDataRented.AsSpan(0, offset));
+ BinaryPrimitives.WriteUInt32BigEndian(associatedDataRented.AsSpan(offset, 4), originalIndex);
+ input.Slice(0, offset).CopyTo(output.Slice(0, offset));
+ SRTP.Encryption.AEAD.Encrypt(context.PayloadAEAD, false, input.Slice(offset, length - 4 - mki.Length - offset), output.Slice(offset), context.Iv12, outerK_e, context.N_tag / 2, associatedDataRented.Slice(0, offset + 4));
+ outputBufferLength = length - 4 - context.N_tag / 2 - mki.Length;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(associatedDataRented);
+ }
}
- finally
+ break;
+
+ default:
{
- ArrayPool.Shared.Return(associatedDataRented);
+ outputBufferLength = 0;
+ return ERROR_UNSUPPORTED_CIPHER;
}
- }
- break;
-
- default:
- {
- outputBufferLength = 0;
- return ERROR_UNSUPPORTED_CIPHER;
- }
+ }
+ }
+ catch (Org.BouncyCastle.Crypto.InvalidCipherTextException)
+ {
+ // AEAD (GCM/CCM) authentication failed. Drop the packet WITHOUT advancing the replay
+ // window / ROC, so a single unauthenticated, corrupted or reordered packet cannot desync
+ // the ROC and cause every subsequent packet to fail to decrypt. RFC 3711 section 3.3.
+ outputBufferLength = 0;
+ return ERROR_HMAC_CHECK_FAILED;
}
}
else
@@ -1400,6 +1362,10 @@ public virtual int UnprotectRtcp(ReadOnlyBytes input, Bytes output, out int outp
outputBufferLength = dataLen;
}
+ // The packet has now been authenticated (HMAC above for HMAC profiles, or the AEAD decrypt
+ // for GCM/CCM profiles). Only now is it safe to advance the replay window.
+ ssrcContext.UpdateReplayWindow(index);
+
return 0;
}