/// <summary> /// SRTP implementations use an "implicit" packet index for sequencing, /// i.e., not all of the index is explicitly carried in the SRTP packet. /// For the pre-defined transforms, the index i is used in replay /// protection (Section 3.3.2), encryption (Section 4.1), message /// authentication (Section 4.2), and for the key derivation (Section /// 4.3). /// /// https://tools.ietf.org/html/rfc3711#section-3.3.1 /// </summary> private static int DetermineSrtpPacketIndex(RtpPacket rtp, SrtpStreamContext srtpStreamContext) { if (rtp.SequenceNumber - srtpStreamContext.PreviousSequenceNumber < ushort.MinValue) { srtpStreamContext.RollOverCounter++; } srtpStreamContext.PreviousSequenceNumber = rtp.SequenceNumber; var packetIndex = srtpStreamContext.RollOverCounter << 16 | rtp.SequenceNumber; return(packetIndex); }
/// <summary> /// Recall that an RTP session for each participant is defined [RFC3550] /// by a pair of destination transport addresses (one network address /// plus a port pair for RTP and RTCP), and that a multimedia session is /// defined as a collection of RTP sessions. For example, a particular /// multimedia session could include an audio RTP session, a video RTP /// session, and a text RTP session. /// /// A cryptographic context SHALL be uniquely identified by the triplet /// context identifier: /// /// context id = <SSRC, destination network address, destination /// transport port number> /// /// where the destination network address and the destination transport /// port are the ones in the SRTP packet. It is assumed that, when /// presented with this information, the key management returns a context /// with the information as described in Section 3.2. /// /// As noted above, SRTP and SRTCP by default share the bulk of the /// parameters in the cryptographic context. Thus, retrieving the crypto /// context parameters for an SRTCP stream in practice may imply a /// binding to the correspondent SRTP crypto context. It is up to the /// implementation to assure such binding, since the RTCP port may not be /// directly deducible from the RTP port only. Alternatively, the key /// management may choose to provide separate SRTP- and SRTCP- contexts, /// duplicating the common parameters (such as master key(s)). The /// latter approach then also enables SRTP and SRTCP to use, e.g., /// distinct transforms, if so desired. Similar considerations arise /// when multiple SRTP streams, forming part of one single RTP session, /// share keys and other parameters. /// /// If no valid context can be found for a packet corresponding to a /// certain context identifier, that packet MUST be discarded. /// /// https://tools.ietf.org/html/rfc3711#section-3.2.3 /// </summary> private SrtpStreamContext DetermineCryptoStreamContext(RtpPacket rtp, IPEndPoint remoteEndPoint) { var key = new SrtpStreamContextKey(rtp.SynchronizationSource, remoteEndPoint); if (_streams.TryGetValue(key, out var srtpStream)) { return(srtpStream); } srtpStream = new SrtpStreamContext { SynchronizationSource = rtp.SynchronizationSource }; _streams.AddOrUpdate(key, k => srtpStream, (k, v) => srtpStream); return(srtpStream); }
/// <summary> /// Regardless of the encryption or message authentication transform that /// is employed (it may be an SRTP pre-defined transform or newly /// introduced according to Section 6), interoperable SRTP /// implementations MUST use the SRTP key derivation to generate session /// keys. Once the key derivation rate is properly signaled at the start /// of the session, there is no need for extra communication between the /// parties that use SRTP key derivation. /// /// https://tools.ietf.org/html/rfc3711#section-4.3 /// </summary> private void DeriveKeys(SrtpStreamContext srtpStreamContext, int packetIndex, byte[] masterKey, byte[] masterSalt) { /* * At least one initial key derivation SHALL be performed by SRTP, i.e., * the first key derivation is REQUIRED. Further applications of the * key derivation MAY be performed, according to the * "key_derivation_rate" value in the cryptographic context. The key * derivation function SHALL initially be invoked before the first * packet and then, when r > 0, a key derivation is performed whenever * index mod r equals zero. This can be thought of as "refreshing" the * session keys. The value of "key_derivation_rate" MUST be kept fixed * for the lifetime of the associated master key. */ /* * Let "a DIV t" denote integer division of a by t, rounded down, and * with the convention that "a DIV 0 = 0" for all a. We also make the * convention of treating "a DIV t" as a bit string of the same length * as a, and thus "a DIV t" will in general have leading zeros. * * Let r = index DIV key_derivation_rate (with DIV as defined above). */ var kdr = srtpStreamContext.KeyDerivationRate; var r = (uint)(kdr == 0 ? 0 : packetIndex / kdr); var shouldDeriveKeys = srtpStreamContext.DerivedKeys is null || r > 0 && packetIndex % r == 0; if (!shouldDeriveKeys) { return; } var derivedKeys = new SrtpDerivedkeys(); derivedKeys.SessionKey = DeriveKey(masterKey, masterSalt, r, SrtpEncryptionKeyLabel.SrtpEncryptionKey, SrtpConstants.SrtpDefaultEncryptionSessionKeyLength); derivedKeys.AuthKey = DeriveKey(masterKey, masterSalt, r, SrtpEncryptionKeyLabel.SrtpMessageAuthenticationKey, SrtpConstants.SrtpDefaultAuthSessionKeyLength); derivedKeys.CipherSalt = DeriveKey(masterKey, masterSalt, r, SrtpEncryptionKeyLabel.SrtpSaltingKey, SrtpConstants.SrtpDefaultSaltSessionKeyLength); srtpStreamContext.DerivedKeys = derivedKeys; }